Merge pull request 'fix(schema): move domain to metadata.json (v9.1.2)' (#449) from fix/domain-schema-compliance into development

Reviewed-on: #449
This commit was merged in pull request #449.
This commit is contained in:
2026-02-07 22:04:54 +00:00
46 changed files with 192 additions and 129 deletions

View File

@@ -6,7 +6,7 @@
}, },
"metadata": { "metadata": {
"description": "Project management plugins with Gitea and NetBox integrations", "description": "Project management plugins with Gitea and NetBox integrations",
"version": "9.1.1" "version": "9.1.2"
}, },
"plugins": [ "plugins": [
{ {
@@ -27,8 +27,7 @@
"gitea", "gitea",
"project-management" "project-management"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "doc-guardian", "name": "doc-guardian",
@@ -47,8 +46,7 @@
"drift-detection", "drift-detection",
"sync" "sync"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "code-sentinel", "name": "code-sentinel",
@@ -70,8 +68,7 @@
"refactoring", "refactoring",
"vulnerabilities" "vulnerabilities"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "project-hygiene", "name": "project-hygiene",
@@ -91,8 +88,7 @@
"maintenance", "maintenance",
"manual-check" "manual-check"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "cmdb-assistant", "name": "cmdb-assistant",
@@ -117,8 +113,7 @@
"data-quality", "data-quality",
"validation" "validation"
], ],
"license": "MIT", "license": "MIT"
"domain": "ops"
}, },
{ {
"name": "claude-config-maintainer", "name": "claude-config-maintainer",
@@ -137,8 +132,7 @@
"configuration", "configuration",
"optimization" "optimization"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "clarity-assist", "name": "clarity-assist",
@@ -161,8 +155,7 @@
"clarification", "clarification",
"nd-friendly" "nd-friendly"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "git-flow", "name": "git-flow",
@@ -185,8 +178,7 @@
"commits", "commits",
"branching" "branching"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "pr-review", "name": "pr-review",
@@ -206,8 +198,7 @@
"security", "security",
"quality" "quality"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "data-platform", "name": "data-platform",
@@ -229,8 +220,7 @@
"data-engineering", "data-engineering",
"etl" "etl"
], ],
"license": "MIT", "license": "MIT"
"domain": "data"
}, },
{ {
"name": "viz-platform", "name": "viz-platform",
@@ -253,8 +243,7 @@
"theming", "theming",
"dmc" "dmc"
], ],
"license": "MIT", "license": "MIT"
"domain": "data"
}, },
{ {
"name": "contract-validator", "name": "contract-validator",
@@ -276,8 +265,7 @@
"interfaces", "interfaces",
"cross-plugin" "cross-plugin"
], ],
"license": "MIT", "license": "MIT"
"domain": "core"
}, },
{ {
"name": "saas-api-platform", "name": "saas-api-platform",
@@ -299,8 +287,7 @@
"express", "express",
"openapi" "openapi"
], ],
"license": "MIT", "license": "MIT"
"domain": "saas"
}, },
{ {
"name": "saas-db-migrate", "name": "saas-db-migrate",
@@ -322,8 +309,7 @@
"sql", "sql",
"schema" "schema"
], ],
"license": "MIT", "license": "MIT"
"domain": "saas"
}, },
{ {
"name": "saas-react-platform", "name": "saas-react-platform",
@@ -345,8 +331,7 @@
"frontend", "frontend",
"components" "components"
], ],
"license": "MIT", "license": "MIT"
"domain": "saas"
}, },
{ {
"name": "saas-test-pilot", "name": "saas-test-pilot",
@@ -368,8 +353,7 @@
"playwright", "playwright",
"coverage" "coverage"
], ],
"license": "MIT", "license": "MIT"
"domain": "saas"
}, },
{ {
"name": "data-seed", "name": "data-seed",
@@ -390,8 +374,7 @@
"fixtures", "fixtures",
"database" "database"
], ],
"license": "MIT", "license": "MIT"
"domain": "data"
}, },
{ {
"name": "ops-release-manager", "name": "ops-release-manager",
@@ -412,8 +395,7 @@
"versioning", "versioning",
"tags" "tags"
], ],
"license": "MIT", "license": "MIT"
"domain": "ops"
}, },
{ {
"name": "ops-deploy-pipeline", "name": "ops-deploy-pipeline",
@@ -434,8 +416,7 @@
"caddy", "caddy",
"cicd" "cicd"
], ],
"license": "MIT", "license": "MIT"
"domain": "ops"
}, },
{ {
"name": "debug-mcp", "name": "debug-mcp",
@@ -456,8 +437,7 @@
"server", "server",
"development" "development"
], ],
"license": "MIT", "license": "MIT"
"domain": "debug"
} }
] ]
} }

View File

@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
## [9.1.2] - 2026-02-07
### Fixed
- **BREAKING FIX:** Removed `"domain"` field from all `marketplace.json` and `plugin.json` files — Claude Code's strict schema validator rejects unrecognized keys, causing `Failed to load marketplace` error
- Domain metadata moved to `metadata.json` per plugin (same pattern as `mcp_servers`)
- `validate-marketplace.sh` updated to read domain from `metadata.json` instead of `marketplace.json`/`plugin.json`
- All documentation updated to reference `metadata.json` as domain source
## [9.1.1] - 2026-02-07 ## [9.1.1] - 2026-02-07
### Changed ### Changed

View File

@@ -154,7 +154,7 @@ When user says "fix the sprint plan command", edit the SOURCE code.
## Project Overview ## Project Overview
**Repository:** leo-claude-mktplace **Repository:** leo-claude-mktplace
**Version:** 9.1.1 **Version:** 9.1.2
**Status:** Production Ready **Status:** Production Ready
A plugin marketplace for Claude Code containing: A plugin marketplace for Claude Code containing:
@@ -424,19 +424,18 @@ Stored in Gitea Wiki under `lessons-learned/sprints/`.
### Adding a New Plugin ### Adding a New Plugin
1. Create `plugins/{name}/.claude-plugin/plugin.json` — must include `"domain"` field (`core`, `data`, `saas`, `ops`, or `debug`) 1. Create `plugins/{name}/.claude-plugin/plugin.json` (standard schema fields only — no custom fields)
2. Add entry to `.claude-plugin/marketplace.json` with category, tags, license, and `"domain"` field (must match plugin.json) 2. Create `plugins/{name}/.claude-plugin/metadata.json` — must include `"domain"` field (`core`, `data`, `saas`, `ops`, or `debug`)
3. Create `claude-md-integration.md` 3. Add entry to `.claude-plugin/marketplace.json` with category, tags, license (no custom fields — Claude Code schema is strict)
4. If using new MCP server, add to root `mcp-servers/` and update `.mcp.json` 4. Create `claude-md-integration.md`
5. Run `./scripts/validate-marketplace.sh` — rejects plugins without valid `domain` field 5. If using new MCP server, add to root `mcp-servers/` and update `.mcp.json`
6. Update `CHANGELOG.md` 6. Run `./scripts/validate-marketplace.sh` — rejects plugins without valid `domain` field
7. Update `CHANGELOG.md`
**Domain field is required (v8.0.0+):** **Domain field is required in metadata.json (v8.0.0+, moved from plugin.json in v9.1.2):**
```json ```json
{ {
"name": "plugin-name", "domain": "core"
"domain": "core",
...
} }
``` ```

View File

@@ -1,4 +1,4 @@
# Leo Claude Marketplace — v9.1.1 # Leo Claude Marketplace — v9.1.2
A plugin marketplace for Claude Code providing sprint management, code review, security scanning, infrastructure automation, and development workflow tools. 20 plugins across 5 domains, backed by 5 shared MCP servers. A plugin marketplace for Claude Code providing sprint management, code review, security scanning, infrastructure automation, and development workflow tools. 20 plugins across 5 domains, backed by 5 shared MCP servers.

View File

@@ -384,14 +384,13 @@ All MCP servers are defined in `.mcp.json` at repository root:
## Domain Metadata ## Domain Metadata
### Domain Field Locations ### Domain Field Location
Both manifest files require a `domain` field (v8.0.0+): Domain metadata is stored in `metadata.json` (v9.1.2+, moved from plugin.json/marketplace.json for Claude Code schema compliance):
| Location | Field | Example | | Location | Field | Example |
|----------|-------|---------| |----------|-------|---------|
| `plugins/{name}/.claude-plugin/plugin.json` | `"domain": "core"` | `plugins/projman/.claude-plugin/plugin.json` | | `plugins/{name}/.claude-plugin/metadata.json` | `"domain": "core"` | `plugins/projman/.claude-plugin/metadata.json` |
| `.claude-plugin/marketplace.json` | `"domain": "core"` per plugin entry | `.claude-plugin/marketplace.json` |
### Allowed Domain Values ### Allowed Domain Values
@@ -413,10 +412,15 @@ Both manifest files require a `domain` field (v8.0.0+):
```bash ```bash
# List all plugins in a domain # List all plugins in a domain
jq '.plugins[] | select(.domain=="saas") | .name' .claude-plugin/marketplace.json for p in plugins/*; do
d=$(jq -r '.domain // empty' "$p/.claude-plugin/metadata.json" 2>/dev/null)
[[ "$d" == "saas" ]] && basename "$p"
done
# Count plugins per domain # Count plugins per domain
jq '[.plugins[] | .domain] | group_by(.) | map({domain: .[0], count: length})' .claude-plugin/marketplace.json for p in plugins/*; do
jq -r '.domain // empty' "$p/.claude-plugin/metadata.json" 2>/dev/null
done | sort | uniq -c | sort -rn
``` ```
--- ---
@@ -425,6 +429,7 @@ jq '[.plugins[] | .domain] | group_by(.) | map({domain: .[0], count: length})' .
| Date | Change | By | | Date | Change | By |
|------|--------|-----| |------|--------|-----|
| 2026-02-07 | v9.1.2: Moved domain field from plugin.json/marketplace.json to metadata.json for Claude Code schema compliance | Claude Code |
| 2026-02-07 | v9.1.0: Removed deleted dirs (architecture/, prompts/, project-lessons-learned/), added Phase 3 plugins, added ARCHITECTURE.md, MIGRATION-v9.md, updated Domain table, removed stale hooks/ dirs | Claude Code | | 2026-02-07 | v9.1.0: Removed deleted dirs (architecture/, prompts/, project-lessons-learned/), added Phase 3 plugins, added ARCHITECTURE.md, MIGRATION-v9.md, updated Domain table, removed stale hooks/ dirs | Claude Code |
| 2026-02-06 | v8.0.0: Added domain metadata section, Phase 1a paths, future plugin paths | Claude Code | | 2026-02-06 | v8.0.0: Added domain metadata section, Phase 1a paths, future plugin paths | Claude Code |
| 2026-02-04 | v7.1.0: Added profile configs, prompts/, project-lessons-learned/, metadata.json, deprecated switch-profile.sh | Claude Code | | 2026-02-04 | v7.1.0: Added profile configs, prompts/, project-lessons-learned/, metadata.json, deprecated switch-profile.sh | Claude Code |

View File

@@ -0,0 +1,3 @@
{
"domain": "core"
}

View File

@@ -18,6 +18,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "core"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["netbox"]} {
"mcp_servers": [
"netbox"
],
"domain": "ops"
}

View File

@@ -21,6 +21,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "ops"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "core"
}

View File

@@ -18,6 +18,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["contract-validator"]} {
"mcp_servers": [
"contract-validator"
],
"domain": "core"
}

View File

@@ -19,6 +19,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["data-platform"]} {
"mcp_servers": [
"data-platform"
],
"domain": "data"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "data"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "data"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "data"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "debug"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "debug"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["gitea"]} {
"mcp_servers": [
"gitea"
],
"domain": "core"
}

View File

@@ -18,6 +18,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["gitea"]} {
"mcp_servers": [
"gitea"
],
"domain": "core"
}

View File

@@ -18,6 +18,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "ops"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "ops"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "ops"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "ops"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["gitea"]} {
"mcp_servers": [
"gitea"
],
"domain": "core"
}

View File

@@ -18,6 +18,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "core"
}

View File

@@ -15,7 +15,6 @@
"maintenance", "maintenance",
"manual-check" "manual-check"
], ],
"domain": "core",
"commands": [ "commands": [
"./commands/" "./commands/"
] ]

View File

@@ -1 +1,6 @@
{"mcp_servers": ["gitea"]} {
"mcp_servers": [
"gitea"
],
"domain": "core"
}

View File

@@ -18,6 +18,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "core"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "saas"
}

View File

@@ -21,6 +21,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "saas"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "saas"
}

View File

@@ -20,6 +20,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "saas"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "saas"
}

View File

@@ -21,6 +21,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "saas"
} }

View File

@@ -0,0 +1,3 @@
{
"domain": "saas"
}

View File

@@ -21,6 +21,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "saas"
} }

View File

@@ -1 +1,6 @@
{"mcp_servers": ["viz-platform"]} {
"mcp_servers": [
"viz-platform"
],
"domain": "data"
}

View File

@@ -21,6 +21,5 @@
], ],
"commands": [ "commands": [
"./commands/" "./commands/"
], ]
"domain": "data"
} }

View File

@@ -92,17 +92,23 @@ for i in $(seq 0 $((PLUGIN_COUNT - 1))); do
exit 1 exit 1
fi fi
# v8.0.0: Check domain field # v8.0.0/v9.1.2: Check domain field in metadata.json (moved from marketplace.json for schema compliance)
PLUGIN_DOMAIN=$(jq -r ".plugins[$i].domain // empty" "$MARKETPLACE_JSON") PLUGIN_SOURCE=$(jq -r ".plugins[$i].source" "$MARKETPLACE_JSON")
METADATA_JSON="$ROOT_DIR/$PLUGIN_SOURCE/.claude-plugin/metadata.json"
if [[ -f "$METADATA_JSON" ]]; then
PLUGIN_DOMAIN=$(jq -r '.domain // empty' "$METADATA_JSON")
else
PLUGIN_DOMAIN=""
fi
if [[ -z "$PLUGIN_DOMAIN" ]]; then if [[ -z "$PLUGIN_DOMAIN" ]]; then
echo "ERROR: Missing 'domain' in marketplace entry for $PLUGIN_NAME (required v8.0.0+)" echo "ERROR: Missing 'domain' in $PLUGIN_NAME/.claude-plugin/metadata.json (required v8.0.0+)"
exit 1 exit 1
fi fi
if ! echo "$VALID_DOMAINS" | grep -qw "$PLUGIN_DOMAIN"; then if ! echo "$VALID_DOMAINS" | grep -qw "$PLUGIN_DOMAIN"; then
echo "ERROR: Invalid domain '$PLUGIN_DOMAIN' in marketplace entry for $PLUGIN_NAME (allowed: $VALID_DOMAINS)" echo "ERROR: Invalid domain '$PLUGIN_DOMAIN' in $PLUGIN_NAME/metadata.json (allowed: $VALID_DOMAINS)"
exit 1 exit 1
fi fi
echo " ✓ domain: $PLUGIN_DOMAIN" echo " ✓ domain: $PLUGIN_DOMAIN (from metadata.json)"
echo "✓ Marketplace entry $PLUGIN_NAME valid" echo "✓ Marketplace entry $PLUGIN_NAME valid"
done done
@@ -158,17 +164,22 @@ for plugin_dir in "$PLUGINS_DIR"/*/; do
echo "WARNING: Missing 'keywords' array in $plugin_name/plugin.json" echo "WARNING: Missing 'keywords' array in $plugin_name/plugin.json"
fi fi
# v8.0.0: Check domain field in plugin.json # v8.0.0/v9.1.2: Check domain field in metadata.json (moved from plugin.json for schema compliance)
PLUGIN_DOMAIN_PJ=$(jq -r '.domain // empty' "$plugin_json") metadata_json="$plugin_dir.claude-plugin/metadata.json"
if [[ -z "$PLUGIN_DOMAIN_PJ" ]]; then if [[ -f "$metadata_json" ]]; then
echo "ERROR: Missing 'domain' in $plugin_name/plugin.json (required v8.0.0+)" PLUGIN_DOMAIN_MD=$(jq -r '.domain // empty' "$metadata_json")
else
PLUGIN_DOMAIN_MD=""
fi
if [[ -z "$PLUGIN_DOMAIN_MD" ]]; then
echo "ERROR: Missing 'domain' in $plugin_name/.claude-plugin/metadata.json (required v8.0.0+)"
exit 1 exit 1
fi fi
if ! echo "$VALID_DOMAINS" | grep -qw "$PLUGIN_DOMAIN_PJ"; then if ! echo "$VALID_DOMAINS" | grep -qw "$PLUGIN_DOMAIN_MD"; then
echo "ERROR: Invalid domain '$PLUGIN_DOMAIN_PJ' in $plugin_name/plugin.json (allowed: $VALID_DOMAINS)" echo "ERROR: Invalid domain '$PLUGIN_DOMAIN_MD' in $plugin_name/metadata.json (allowed: $VALID_DOMAINS)"
exit 1 exit 1
fi fi
echo " ✓ domain: $PLUGIN_DOMAIN_PJ" echo " ✓ domain: $PLUGIN_DOMAIN_MD (from metadata.json)"
# Check README exists # Check README exists
if [[ ! -f "$plugin_dir/README.md" ]]; then if [[ ! -f "$plugin_dir/README.md" ]]; then
@@ -324,20 +335,23 @@ if [[ ! -f "$ROOT_DIR/.mcp.json" ]]; then
fi fi
echo "✓ .mcp.json configuration exists" echo "✓ .mcp.json configuration exists"
# v8.0.0: Cross-validate domains match between marketplace.json and plugin.json # v8.0.0/v9.1.2: Validate domain in metadata.json for all plugins (single source of truth)
echo "" echo ""
echo "=== Cross-Validating Domain Fields ===" echo "=== Validating Domain Fields (metadata.json) ==="
for i in $(seq 0 $((PLUGIN_COUNT - 1))); do for plugin_dir in "$PLUGINS_DIR"/*/; do
PLUGIN_NAME=$(jq -r ".plugins[$i].name" "$MARKETPLACE_JSON") plugin_name=$(basename "$plugin_dir")
MARKETPLACE_DOMAIN=$(jq -r ".plugins[$i].domain" "$MARKETPLACE_JSON") metadata_file="$plugin_dir.claude-plugin/metadata.json"
PLUGIN_JSON_PATH="$PLUGINS_DIR/$PLUGIN_NAME/.claude-plugin/plugin.json" if [[ -f "$metadata_file" ]]; then
if [[ -f "$PLUGIN_JSON_PATH" ]]; then domain=$(jq -r '.domain // empty' "$metadata_file")
PLUGIN_DOMAIN=$(jq -r '.domain' "$PLUGIN_JSON_PATH") if [[ -n "$domain" ]]; then
if [[ "$MARKETPLACE_DOMAIN" != "$PLUGIN_DOMAIN" ]]; then echo "$plugin_name domain: $domain"
echo "ERROR: Domain mismatch for $PLUGIN_NAME: marketplace.json='$MARKETPLACE_DOMAIN' vs plugin.json='$PLUGIN_DOMAIN'" else
echo "ERROR: $plugin_name/metadata.json exists but missing domain field"
exit 1 exit 1
fi fi
echo "$PLUGIN_NAME domain consistent: $MARKETPLACE_DOMAIN" else
echo "ERROR: $plugin_name missing .claude-plugin/metadata.json"
exit 1
fi fi
done done