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 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 13:54:19 -05:00
parent bea46d7689
commit 72b3436a24
9 changed files with 182 additions and 12 deletions

View File

@@ -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: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: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: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

View File

@@ -133,7 +133,7 @@ class ContractValidatorMCPServer:
), ),
Tool( Tool(
name="validate_workflow_integration", 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={ inputSchema={
"type": "object", "type": "object",
"properties": { "properties": {
@@ -144,6 +144,10 @@ class ContractValidatorMCPServer:
"domain_label": { "domain_label": {
"type": "string", "type": "string",
"description": "The Domain/* label it claims to handle, e.g. Domain/Viz" "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"] "required": ["plugin_path", "domain_label"]
@@ -261,9 +265,16 @@ class ContractValidatorMCPServer:
"""Validate agent data flow""" """Validate agent data flow"""
return await self.validation_tools.validate_data_flow(agent_name, claude_md_path) 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""" """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) # Report tool implementations (Issue #188)

View File

@@ -72,6 +72,7 @@ class WorkflowIntegrationResult(BaseModel):
domain_label: str domain_label: str
valid: bool valid: bool
gate_command_found: bool gate_command_found: bool
gate_contract: Optional[str] = None # Contract version declared by gate command
review_command_found: bool review_command_found: bool
advisory_agent_found: bool advisory_agent_found: bool
issues: list[ValidationIssue] = [] issues: list[ValidationIssue] = []
@@ -349,22 +350,32 @@ class ValidationTools:
return result.model_dump() 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. Validate that a domain plugin exposes required advisory interfaces.
Checks for: Checks for:
- Gate command (e.g., /design-gate, /data-gate) - REQUIRED - 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 - Review command (e.g., /design-review, /data-review) - recommended
- Advisory agent referencing the domain label - recommended - Advisory agent referencing the domain label - recommended
Args: Args:
plugin_path: Path to the domain plugin directory plugin_path: Path to the domain plugin directory
domain_label: The Domain/* label it claims to handle (e.g., Domain/Viz) 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: Returns:
Validation result with found interfaces and issues Validation result with found interfaces and issues
""" """
import re
plugin_path_obj = Path(plugin_path) plugin_path_obj = Path(plugin_path)
issues = [] issues = []
@@ -383,6 +394,7 @@ class ValidationTools:
# Check for gate command # Check for gate command
commands_dir = plugin_path_obj / "commands" commands_dir = plugin_path_obj / "commands"
gate_command_found = False gate_command_found = False
gate_contract = None
gate_patterns = ["pass", "fail", "PASS", "FAIL", "Binary pass/fail", "gate"] gate_patterns = ["pass", "fail", "PASS", "FAIL", "Binary pass/fail", "gate"]
if commands_dir.exists(): if commands_dir.exists():
@@ -392,6 +404,13 @@ class ValidationTools:
content = cmd_file.read_text() content = cmd_file.read_text()
if any(pattern in content for pattern in gate_patterns): if any(pattern in content for pattern in gate_patterns):
gate_command_found = True 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 break
if not gate_command_found: if not gate_command_found:
@@ -441,11 +460,31 @@ class ValidationTools:
suggestion=f"Create agents/{domain_short}-advisor.md referencing '{domain_label}'" 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( result = WorkflowIntegrationResult(
plugin_name=plugin_name, plugin_name=plugin_name,
domain_label=domain_label, domain_label=domain_label,
valid=gate_command_found, # Only gate is required for validity valid=gate_command_found, # Only gate is required for validity
gate_command_found=gate_command_found, gate_command_found=gate_command_found,
gate_contract=gate_contract,
review_command_found=review_command_found, review_command_found=review_command_found,
advisory_agent_found=advisory_agent_found, advisory_agent_found=advisory_agent_found,
issues=issues issues=issues

View File

@@ -404,3 +404,108 @@ async def test_validate_workflow_integration_nonexistent_plugin(validation_tools
assert "error" in result assert "error" in result
assert "not found" in result["error"].lower() 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()

View File

@@ -16,7 +16,7 @@ Available MCP tools for contract-validator operations.
| `validate_compatibility` | Check two plugins for conflicts | | `validate_compatibility` | Check two plugins for conflicts |
| `validate_agent_refs` | Check agent tool references exist | | `validate_agent_refs` | Check agent tool references exist |
| `validate_data_flow` | Verify data flow through agent sequence | | `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 ### Report Tools
| Tool | Description | | Tool | Description |
@@ -57,8 +57,9 @@ Available MCP tools for contract-validator operations.
### Workflow Integration Check ### Workflow Integration Check
``` ```
1. validate_workflow_integration(plugin_path, domain_label) # Check single domain plugin 1. validate_workflow_integration(plugin_path, domain_label) # Check single domain plugin
2. For each domain in domain-consultation.md detection rules: 2. validate_workflow_integration(plugin_path, domain_label, expected_contract="v1") # With contract version check
validate_workflow_integration(domain_plugin_path, domain_label) 3. For each domain in domain-consultation.md detection rules:
validate_workflow_integration(domain_plugin_path, domain_label, expected_contract)
``` ```
## Error Handling ## Error Handling

View File

@@ -35,7 +35,9 @@ Rules for validating plugin compatibility and agent definitions.
2. Gate command produces binary PASS/FAIL output 2. Gate command produces binary PASS/FAIL output
3. Review command exists (WARNING if missing, not ERROR) 3. Review command exists (WARNING if missing, not ERROR)
4. Advisory agent exists referencing the domain label 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 ## Severity Levels

View File

@@ -1,5 +1,6 @@
--- ---
description: Data integrity compliance gate (pass/fail) for sprint execution description: Data integrity compliance gate (pass/fail) for sprint execution
gate_contract: v1
arguments: arguments:
- name: path - name: path
description: File or directory to validate description: File or directory to validate

View File

@@ -154,10 +154,12 @@ This pattern ensures domain expertise stays in domain plugins while projman orch
## Gate Command Reference ## Gate Command Reference
| Domain | Gate Command | Review Command | Advisory Agent | | Domain | Gate Command | Contract | Review Command | Advisory Agent |
|--------|--------------|----------------|----------------| |--------|--------------|----------|----------------|----------------|
| Viz | `/design-gate <path>` | `/design-review <path>` | `design-reviewer` | | Viz | `/design-gate <path>` | v1 | `/design-review <path>` | `design-reviewer` |
| Data | `/data-gate <path>` | `/data-review <path>` | `data-advisor` | | Data | `/data-gate <path>` | v1 | `/data-review <path>` | `data-advisor` |
Gate commands return binary PASS/FAIL for automation. Gate commands return binary PASS/FAIL for automation.
Review commands return detailed reports for human review. 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`.

View File

@@ -1,5 +1,6 @@
--- ---
description: Design system compliance gate (pass/fail) for sprint execution description: Design system compliance gate (pass/fail) for sprint execution
gate_contract: v1
arguments: arguments:
- name: path - name: path
description: File or directory to validate description: File or directory to validate