Repository navigation
Conversation
The live processor requires spec.skills, while spec.includes is the ADR membership list. Keep the old dependsOn/dependencyOf graph so the pipeline stays green, and teach the same shape in the validator and authoring skills. Co-authored-by: Cursor <cursoragent@cursor.com>
r2dedios
left a comment
There was a problem hiding this comment.
A couple of structural suggestions on the new _validate_plugin_dual_write / includes validation, from a review pass on this PR.
| if member_type is not None and member_type not in _ALLOWED_INCLUDE_TYPES: | ||
| errors.append(f"{loc} type must be skill or mcp-server, got {member_type!r}") | ||
| if member_type == "plugin": | ||
| errors.append(f"{loc} must not include a plugin member") |
There was a problem hiding this comment.
Missing continue after rejecting a plugin member
if member_type == "plugin":
errors.append(f"{loc} must not include a plugin member")This branch doesn't continue, so a rejected entry still falls through to the entityRef prefix classification a few lines below (entity_ref.startswith("airesource:ai5-marketplace/")) and gets added to include_skills. If that entityRef isn't also in spec.skills, the same bad line produces a second, unrelated-looking error (spec.includes lists unknown skills [...]) on top of "must not include a plugin member".
Example: {entityRef: airesource:ai5-marketplace/ocp-admin, type: plugin} raises both messages today for one bad line. Adding continue right after errors.append(...) here would report the one real problem once; the surrounding for loop still validates every other spec.includes entry — this wouldn't abort the check early.
| if entry.get("gitRef") is not None: | ||
| errors.append(f"{loc} must not set gitRef") | ||
| member_type = entry.get("type") | ||
| if member_type is not None and member_type not in _ALLOWED_INCLUDE_TYPES: |
There was a problem hiding this comment.
Request: tighten includes validation to match what this PR documents
Compass's own schema doesn't catch either of these for us — I validated a few spec.includes payloads directly against the live Compass validate-entity endpoint, and it accepts entries with no type at all, and even accepts a type that contradicts the entityRef prefix (e.g. {entityRef: mcpserver:..., type: skill} validates cleanly). So our own validator is the only thing that can enforce this:
-
typeis currently optional (member_type = entry.get("type"), only checkedif member_type is not None) and never cross-checked against theentityRefprefix classification below. Since{entityRef, type}is the whole point of the new structured format, couldtypebe made required and verified against the ref (airesource:→skill,mcpserver:→mcp-server)? -
_check_skill_conventionsdoesn't rejectspec.includes/spec.skills/spec.systemon skill manifests, even thoughrelationship-rules.md(added in this PR) documents them as plugin-only: "includes/spec.skills/spec.systemon skills — plugins only for the first two". A copy-pasted plugin block landing on a skill'scatalog-info.yamlwould currently passmake validate-compass-manifests.
Happy to help with either if useful.
|
Add a written reference for "Phase C H.5" / "H.4" This PR introduces "Phase C H.5" and "H.4" as the trigger for retiring the skill→plugin
We checked the repo and its git history and couldn't find this defined anywhere in writing — it's unrelated to the older "Phase A/B/C" rollout in Could you add a short written reference (a ticket link, or even just one sentence) for what "Phase C H.5"/"H.4" means and when it's considered reached? Right now the only record of it seems to be a verbal agreement from a meeting, so there's no way for anyone outside that conversation — or for CI — to know when the dual-write and the extra |
r2dedios
left a comment
There was a problem hiding this comment.
A few more findings from a deeper pass on the includes validation logic and the retired-dependencyOf doc wording.
| if entity_ref in seen: | ||
| errors.append(f"{rel}: duplicate includes entityRef {entity_ref}") | ||
| seen.add(entity_ref) | ||
| if entity_ref == plugin_ref: |
There was a problem hiding this comment.
Same missing-continue bug as the type: plugin check, here too
if entity_ref == plugin_ref:
errors.append(f"{rel}: includes must not self-reference the plugin")No continue here either, so a self-referencing entry falls through to the entityRef prefix classification just below and gets added to include_skills (the plugin's own airesource: ref matches that prefix). On rh-basic, a self-referencing includes entry produces three errors for one bad line: the correct self-reference message, a bogus spec.includes lists unknown skills ['rh-basic'], and a cascading roster-mismatch error. Same fix as the type: plugin case: add continue right after errors.append(...) here.
| name = entity_ref.split("/", 1)[1] | ||
| include_skills.add(name) | ||
| elif entity_ref.startswith("mcpserver:"): | ||
| if entity_ref.startswith("mcpserver:redhat/"): |
There was a problem hiding this comment.
Canonical-MCP check misses mcpserver:default/*
if entity_ref.startswith("mcpserver:redhat/"):
errors.append(f"{loc} canonical MCP {entity_ref} must not be in includes")This hardcodes "mcpserver:redhat/", but the file already has a shared _CANONICAL_MCP_PREFIXES = ("mcpserver:redhat/", "mcpserver:default/") tuple used elsewhere (e.g. _is_allowed_dangling_mcp). A canonical ref under mcpserver:default/* placed in includes doesn't get this specific error — it only hits the generic spec.includes lists unexpected MCPs fallback, and if that ref also happens to be in owned_mcps, nothing would catch it at all. Worth using _CANONICAL_MCP_PREFIXES here instead of the literal string.
| for ref in skill_refs | ||
| if ref.startswith("airesource:ai5-marketplace/") | ||
| } | ||
| if skill_names_legacy and skill_names_legacy != disk: |
There was a problem hiding this comment.
Roster-mismatch checks silently no-op when every spec.skills entry is malformed
skill_names_legacy = {
ref.split("/", 1)[1]
for ref in skill_refs
if ref.startswith("airesource:ai5-marketplace/")
}
if skill_names_legacy and skill_names_legacy != disk:skill_names_legacy silently drops entries that fail the airesource: prefix check instead of counting them as wrong. If every spec.skills entry is malformed, skill_names_legacy ends up empty, and this if skill_names_legacy and ... guard — plus the parallel spec.skills vs spec.includes check further down — both get skipped entirely, even though the roster genuinely diverges from disk. The equivalent spec.includes check (include_skills != disk) isn't guarded this way, so the two checks aren't actually at parity today.
| #### Entity Relationships | ||
|
|
||
| Use `dependsOn` / `dependencyOf` for custom kinds (`AiResource`, `MCPServer`); do not use `partOf` / `hasPart` on those kinds. Compass does not auto-generate inverse relations (COMPASS-1288) — declare both sides on every edge. | ||
| Plugin **membership** is `spec.includes` (skills + owned MCP) **and** legacy `spec.skills` (skill refs only, required by the live processor). Functional deps stay on `dependsOn` (skill→MCP, skill→skill, plugin MCP union, skill→plugin until Phase C H.5). Do **not** list skills on plugin `dependencyOf` — that roster is retired. |
There was a problem hiding this comment.
This sentence contradicts itself for the skill→plugin edge
Functional deps stay on
dependsOn(skill→MCP, skill→skill, plugin MCP union, skill→plugin until Phase C H.5). ... Do not list skills on plugindependencyOf— that roster is retired.
This classifies skill→plugin dependsOn as a functional edge, and the sentence right before this paragraph (in the surrounding section) states Compass doesn't auto-generate inverse relations for custom kinds (COMPASS-1288) and that we "dual-write dependsOn/dependencyOf on every functional edge." But this same sentence then forbids the plugin-side dependencyOf inverse for that identical edge. relationship-rules.md#L15 repeats the same pattern — "Every functional relationship must be declared on both sides" immediately followed by a table row exempting Skill→Plugin from it.
Since compass-manifest-maintenance reads relationship-rules.md as its own reference when authoring manifests, this reads as a genuine contradiction rather than a documented exception — could the skill→plugin edge be called out explicitly as the exception to the bidirectional rule, rather than appearing to violate the rule stated one sentence earlier?
|
Two more issues from a deeper pass on this PR 1.
2. All 4 registered plugins now carry two different values for the same field:
Same field name, two unreconciled values per plugin, and nothing validates that they match. Any downstream consumer reading one source vs. the other (catalog pipeline vs. Compass) would report a different support status for the same plugin. Worth picking one source of truth, or syncing the two. |
|
Hi @r2dedios for following issues
|
The live processor requires spec.skills, while spec.includes is the ADR membership list. Keep the old dependsOn/dependencyOf graph so the pipeline stays green, and teach the same shape in the validator and authoring skills.
Summary
Dual-write Compass plugin membership on the four registered plugins: structured
spec.includes(skills + owned MCP) and legacyspec.skills(skill refs only). The live ModelProcessor still requiresskills;includesis the ADR membership list. FunctionaldependsOn/dependencyOfare unchanged so the publication pipeline needs no engine change.GitLab ingest YAML is already merged. This is the public mirror, plus GitHub-only validator and authoring templates. Lightspeed stays
mcpserver:redhat/red-hat-lightspeed-mcp-serveron this tree (nomcps/lightspeed-mcp.yaml); it is not inincludes. Canonicalredhat/security-mcp-serveronrh-basicstaysdependsOnonly.Also updates
validate_compass_manifests.py(reject empty/duplicate/self/plugin/string includes), tests,compass-manifest-maintenance,agentic-contribution-skill,CLAUDE.md, andCONTRIBUTING.mdso the next skill is not authored againstdependencyOfalone.Plugin(s) affected
ocp-adminrh-ai-engineerrh-automationrh-basicrh-developerrh-srerh-virtChange type
mcps.json)Contribution method
/agentic-contribution-skillmake validate+make validate-skill-design-changed)Plugin-persona alignment (new skills only)
N/A — no new skills.
AGENTS.md compliance
N/A — Compass descriptor / tooling only; no skill or agent behavior change.
Validation
make validate-compass-manifestsandscripts/test_validate_compass_layout.pypass locallymake validatefull suite (run before merge if not already)name,description) — N/Aname,description) — N/A