From 72b3436a242b87478a6d0c18928034047fe76d44 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Mon, 2 Feb 2026 13:54:19 -0500 Subject: [PATCH] feat(contract-validator): add gate contract versioning - design-gate.md and data-gate.md declare gate_contract: v1 - domain-consultation.md Gate Command Reference includes Contract column - validate_workflow_integration now checks contract version compatibility - Tests added for match, mismatch, and missing contract scenarios Co-Authored-By: Claude Opus 4.5 --- .doc-guardian-queue | 8 ++ .../contract-validator/mcp_server/server.py | 17 ++- .../mcp_server/validation_tools.py | 41 ++++++- .../tests/test_validation_tools.py | 105 ++++++++++++++++++ .../skills/mcp-tools-reference.md | 7 +- .../skills/validation-rules.md | 4 +- plugins/data-platform/commands/data-gate.md | 1 + plugins/projman/skills/domain-consultation.md | 10 +- plugins/viz-platform/commands/design-gate.md | 1 + 9 files changed, 182 insertions(+), 12 deletions(-) diff --git a/.doc-guardian-queue b/.doc-guardian-queue index 9c60170..7ff06a0 100644 --- a/.doc-guardian-queue +++ b/.doc-guardian-queue @@ -15,3 +15,11 @@ 2026-02-02T13:46:21 | commands | /home/lmiranda/claude-plugins-work/plugins/projman/commands/sprint-status.md | docs/COMMANDS-CHEATSHEET.md README.md 2026-02-02T13:46:38 | agents | /home/lmiranda/claude-plugins-work/plugins/projman/agents/planner.md | README.md CLAUDE.md 2026-02-02T13:46:57 | agents | /home/lmiranda/claude-plugins-work/plugins/projman/agents/code-reviewer.md | README.md CLAUDE.md +2026-02-02T13:49:13 | commands | /home/lmiranda/claude-plugins-work/plugins/viz-platform/commands/design-gate.md | docs/COMMANDS-CHEATSHEET.md README.md +2026-02-02T13:49:24 | commands | /home/lmiranda/claude-plugins-work/plugins/data-platform/commands/data-gate.md | docs/COMMANDS-CHEATSHEET.md README.md +2026-02-02T13:49:35 | skills | /home/lmiranda/claude-plugins-work/plugins/projman/skills/domain-consultation.md | README.md +2026-02-02T13:50:04 | mcp-servers | /home/lmiranda/claude-plugins-work/mcp-servers/contract-validator/mcp_server/validation_tools.py | docs/COMMANDS-CHEATSHEET.md CLAUDE.md +2026-02-02T13:50:59 | mcp-servers | /home/lmiranda/claude-plugins-work/mcp-servers/contract-validator/mcp_server/server.py | docs/COMMANDS-CHEATSHEET.md CLAUDE.md +2026-02-02T13:51:32 | mcp-servers | /home/lmiranda/claude-plugins-work/mcp-servers/contract-validator/tests/test_validation_tools.py | docs/COMMANDS-CHEATSHEET.md CLAUDE.md +2026-02-02T13:51:49 | skills | /home/lmiranda/claude-plugins-work/plugins/contract-validator/skills/validation-rules.md | README.md +2026-02-02T13:52:07 | skills | /home/lmiranda/claude-plugins-work/plugins/contract-validator/skills/mcp-tools-reference.md | README.md diff --git a/mcp-servers/contract-validator/mcp_server/server.py b/mcp-servers/contract-validator/mcp_server/server.py index 3891576..1d3c504 100644 --- a/mcp-servers/contract-validator/mcp_server/server.py +++ b/mcp-servers/contract-validator/mcp_server/server.py @@ -133,7 +133,7 @@ class ContractValidatorMCPServer: ), Tool( name="validate_workflow_integration", - description="Validate that a domain plugin exposes the required advisory interfaces (gate command, review command, advisory agent) expected by projman's domain-consultation skill", + description="Validate that a domain plugin exposes the required advisory interfaces (gate command, review command, advisory agent) expected by projman's domain-consultation skill. Also checks gate contract version compatibility.", inputSchema={ "type": "object", "properties": { @@ -144,6 +144,10 @@ class ContractValidatorMCPServer: "domain_label": { "type": "string", "description": "The Domain/* label it claims to handle, e.g. Domain/Viz" + }, + "expected_contract": { + "type": "string", + "description": "Expected contract version (e.g., 'v1'). If provided, validates the gate command's contract matches." } }, "required": ["plugin_path", "domain_label"] @@ -261,9 +265,16 @@ class ContractValidatorMCPServer: """Validate agent data flow""" return await self.validation_tools.validate_data_flow(agent_name, claude_md_path) - async def _validate_workflow_integration(self, plugin_path: str, domain_label: str) -> dict: + async def _validate_workflow_integration( + self, + plugin_path: str, + domain_label: str, + expected_contract: str = None + ) -> dict: """Validate domain plugin exposes required advisory interfaces""" - return await self.validation_tools.validate_workflow_integration(plugin_path, domain_label) + return await self.validation_tools.validate_workflow_integration( + plugin_path, domain_label, expected_contract + ) # Report tool implementations (Issue #188) diff --git a/mcp-servers/contract-validator/mcp_server/validation_tools.py b/mcp-servers/contract-validator/mcp_server/validation_tools.py index 1aa550a..585dadb 100644 --- a/mcp-servers/contract-validator/mcp_server/validation_tools.py +++ b/mcp-servers/contract-validator/mcp_server/validation_tools.py @@ -72,6 +72,7 @@ class WorkflowIntegrationResult(BaseModel): domain_label: str valid: bool gate_command_found: bool + gate_contract: Optional[str] = None # Contract version declared by gate command review_command_found: bool advisory_agent_found: bool issues: list[ValidationIssue] = [] @@ -349,22 +350,32 @@ class ValidationTools: return result.model_dump() - async def validate_workflow_integration(self, plugin_path: str, domain_label: str) -> dict: + async def validate_workflow_integration( + self, + plugin_path: str, + domain_label: str, + expected_contract: Optional[str] = None + ) -> dict: """ Validate that a domain plugin exposes required advisory interfaces. Checks for: - Gate command (e.g., /design-gate, /data-gate) - REQUIRED + - Gate contract version (gate_contract in frontmatter) - INFO if missing - Review command (e.g., /design-review, /data-review) - recommended - Advisory agent referencing the domain label - recommended Args: plugin_path: Path to the domain plugin directory domain_label: The Domain/* label it claims to handle (e.g., Domain/Viz) + expected_contract: Expected contract version (e.g., 'v1'). If provided, + validates the gate command's contract matches. Returns: Validation result with found interfaces and issues """ + import re + plugin_path_obj = Path(plugin_path) issues = [] @@ -383,6 +394,7 @@ class ValidationTools: # Check for gate command commands_dir = plugin_path_obj / "commands" gate_command_found = False + gate_contract = None gate_patterns = ["pass", "fail", "PASS", "FAIL", "Binary pass/fail", "gate"] if commands_dir.exists(): @@ -392,6 +404,13 @@ class ValidationTools: content = cmd_file.read_text() if any(pattern in content for pattern in gate_patterns): gate_command_found = True + # Parse frontmatter for gate_contract + frontmatter_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if frontmatter_match: + frontmatter = frontmatter_match.group(1) + contract_match = re.search(r'gate_contract:\s*(\S+)', frontmatter) + if contract_match: + gate_contract = contract_match.group(1) break if not gate_command_found: @@ -441,11 +460,31 @@ class ValidationTools: suggestion=f"Create agents/{domain_short}-advisor.md referencing '{domain_label}'" )) + # Check gate contract version + if gate_command_found: + if not gate_contract: + issues.append(ValidationIssue( + severity=IssueSeverity.INFO, + issue_type=IssueType.MISSING_INTEGRATION, + message=f"Gate command does not declare a contract version", + location=str(commands_dir), + suggestion="Consider adding `gate_contract: v1` to frontmatter for version tracking" + )) + elif expected_contract and gate_contract != expected_contract: + issues.append(ValidationIssue( + severity=IssueSeverity.WARNING, + issue_type=IssueType.INTERFACE_MISMATCH, + message=f"Contract version mismatch: gate declares {gate_contract}, projman expects {expected_contract}", + location=str(commands_dir), + suggestion=f"Update domain-consultation.md Gate Command Reference table to {gate_contract}, or update gate command to {expected_contract}" + )) + result = WorkflowIntegrationResult( plugin_name=plugin_name, domain_label=domain_label, valid=gate_command_found, # Only gate is required for validity gate_command_found=gate_command_found, + gate_contract=gate_contract, review_command_found=review_command_found, advisory_agent_found=advisory_agent_found, issues=issues diff --git a/mcp-servers/contract-validator/tests/test_validation_tools.py b/mcp-servers/contract-validator/tests/test_validation_tools.py index 2ebfacd..6af39eb 100644 --- a/mcp-servers/contract-validator/tests/test_validation_tools.py +++ b/mcp-servers/contract-validator/tests/test_validation_tools.py @@ -404,3 +404,108 @@ async def test_validate_workflow_integration_nonexistent_plugin(validation_tools assert "error" in result assert "not found" in result["error"].lower() + + +# --- Gate Contract Version Tests --- + +@pytest.fixture +def domain_plugin_with_contract(tmp_path): + """Create domain plugin with gate_contract: v1 in frontmatter""" + plugin_dir = tmp_path / "viz-platform-versioned" + plugin_dir.mkdir() + (plugin_dir / ".claude-plugin").mkdir() + (plugin_dir / "commands").mkdir() + (plugin_dir / "agents").mkdir() + + # Gate command with gate_contract in frontmatter + gate_cmd = plugin_dir / "commands" / "design-gate.md" + gate_cmd.write_text("""--- +description: Design system compliance gate (pass/fail) +gate_contract: v1 +--- + +# /design-gate + +Binary pass/fail validation gate for design system compliance. + +## Output + +- **PASS**: All design system checks passed +- **FAIL**: Design system violations detected +""") + + # Review command + review_cmd = plugin_dir / "commands" / "design-review.md" + review_cmd.write_text("""# /design-review + +Comprehensive design system audit. +""") + + # Advisory agent + agent = plugin_dir / "agents" / "design-reviewer.md" + agent.write_text("""# design-reviewer + +Design system compliance auditor for Domain/Viz issues. +""") + + return str(plugin_dir) + + +@pytest.mark.asyncio +async def test_validate_workflow_contract_match(validation_tools, domain_plugin_with_contract): + """Test that matching expected_contract produces no warning""" + result = await validation_tools.validate_workflow_integration( + domain_plugin_with_contract, + "Domain/Viz", + expected_contract="v1" + ) + + assert "error" not in result + assert result["valid"] is True + assert result["gate_contract"] == "v1" + + # Should have no warnings about contract mismatch + warning_issues = [i for i in result["issues"] if i["severity"].value == "warning"] + contract_warnings = [i for i in warning_issues if "contract" in i["message"].lower()] + assert len(contract_warnings) == 0 + + +@pytest.mark.asyncio +async def test_validate_workflow_contract_mismatch(validation_tools, domain_plugin_with_contract): + """Test that mismatched expected_contract produces WARNING""" + result = await validation_tools.validate_workflow_integration( + domain_plugin_with_contract, + "Domain/Viz", + expected_contract="v2" # Gate has v1 + ) + + assert "error" not in result + assert result["valid"] is True # Contract mismatch doesn't affect validity + assert result["gate_contract"] == "v1" + + # Should have warning about contract mismatch + warning_issues = [i for i in result["issues"] if i["severity"].value == "warning"] + contract_warnings = [i for i in warning_issues if "contract" in i["message"].lower()] + assert len(contract_warnings) == 1 + assert "mismatch" in contract_warnings[0]["message"].lower() + assert "v1" in contract_warnings[0]["message"] + assert "v2" in contract_warnings[0]["message"] + + +@pytest.mark.asyncio +async def test_validate_workflow_no_contract(validation_tools, domain_plugin_complete): + """Test that missing gate_contract produces INFO suggestion""" + result = await validation_tools.validate_workflow_integration( + domain_plugin_complete, + "Domain/Viz" + ) + + assert "error" not in result + assert result["valid"] is True + assert result["gate_contract"] is None + + # Should have info issue about missing contract + info_issues = [i for i in result["issues"] if i["severity"].value == "info"] + contract_info = [i for i in info_issues if "contract" in i["message"].lower()] + assert len(contract_info) == 1 + assert "does not declare" in contract_info[0]["message"].lower() diff --git a/plugins/contract-validator/skills/mcp-tools-reference.md b/plugins/contract-validator/skills/mcp-tools-reference.md index 1f36dd2..df8d352 100644 --- a/plugins/contract-validator/skills/mcp-tools-reference.md +++ b/plugins/contract-validator/skills/mcp-tools-reference.md @@ -16,7 +16,7 @@ Available MCP tools for contract-validator operations. | `validate_compatibility` | Check two plugins for conflicts | | `validate_agent_refs` | Check agent tool references exist | | `validate_data_flow` | Verify data flow through agent sequence | -| `validate_workflow_integration` | Check domain plugin exposes required advisory interfaces | +| `validate_workflow_integration` | Check domain plugin exposes required advisory interfaces and gate contract version | ### Report Tools | Tool | Description | @@ -57,8 +57,9 @@ Available MCP tools for contract-validator operations. ### Workflow Integration Check ``` 1. validate_workflow_integration(plugin_path, domain_label) # Check single domain plugin -2. For each domain in domain-consultation.md detection rules: - validate_workflow_integration(domain_plugin_path, domain_label) +2. validate_workflow_integration(plugin_path, domain_label, expected_contract="v1") # With contract version check +3. For each domain in domain-consultation.md detection rules: + validate_workflow_integration(domain_plugin_path, domain_label, expected_contract) ``` ## Error Handling diff --git a/plugins/contract-validator/skills/validation-rules.md b/plugins/contract-validator/skills/validation-rules.md index 1c92f25..db7faa7 100644 --- a/plugins/contract-validator/skills/validation-rules.md +++ b/plugins/contract-validator/skills/validation-rules.md @@ -35,7 +35,9 @@ Rules for validating plugin compatibility and agent definitions. 2. Gate command produces binary PASS/FAIL output 3. Review command exists (WARNING if missing, not ERROR) 4. Advisory agent exists referencing the domain label -- Severity: ERROR for missing gate, WARNING for missing review/agent +5. Gate command declares `gate_contract` version in frontmatter +6. If expected version provided, gate contract version matches expected +- Severity: ERROR for missing gate, WARNING for missing review/agent or contract mismatch, INFO for missing contract ## Severity Levels diff --git a/plugins/data-platform/commands/data-gate.md b/plugins/data-platform/commands/data-gate.md index 527cf53..5014c25 100644 --- a/plugins/data-platform/commands/data-gate.md +++ b/plugins/data-platform/commands/data-gate.md @@ -1,5 +1,6 @@ --- description: Data integrity compliance gate (pass/fail) for sprint execution +gate_contract: v1 arguments: - name: path description: File or directory to validate diff --git a/plugins/projman/skills/domain-consultation.md b/plugins/projman/skills/domain-consultation.md index 4f7c229..2ed817d 100644 --- a/plugins/projman/skills/domain-consultation.md +++ b/plugins/projman/skills/domain-consultation.md @@ -154,10 +154,12 @@ This pattern ensures domain expertise stays in domain plugins while projman orch ## Gate Command Reference -| Domain | Gate Command | Review Command | Advisory Agent | -|--------|--------------|----------------|----------------| -| Viz | `/design-gate ` | `/design-review ` | `design-reviewer` | -| Data | `/data-gate ` | `/data-review ` | `data-advisor` | +| Domain | Gate Command | Contract | Review Command | Advisory Agent | +|--------|--------------|----------|----------------|----------------| +| Viz | `/design-gate ` | v1 | `/design-review ` | `design-reviewer` | +| Data | `/data-gate ` | v1 | `/data-review ` | `data-advisor` | Gate commands return binary PASS/FAIL for automation. Review commands return detailed reports for human review. + +**Contract Version:** Gate commands declare `gate_contract: vN` in their frontmatter. The version in this table is what projman expects. If a gate command bumps its contract version, this table must be updated to match. The `contract-validator` plugin checks this automatically via `validate_workflow_integration`. diff --git a/plugins/viz-platform/commands/design-gate.md b/plugins/viz-platform/commands/design-gate.md index c5cb418..127e737 100644 --- a/plugins/viz-platform/commands/design-gate.md +++ b/plugins/viz-platform/commands/design-gate.md @@ -1,5 +1,6 @@ --- description: Design system compliance gate (pass/fail) for sprint execution +gate_contract: v1 arguments: - name: path description: File or directory to validate