| Field | Value |
|---|---|
| Status | Implemented |
| Version | 1.0 |
| Date | 2026-04-11 |
| Issue | GH-320 (PR #335) |
When contract-first decomposition generates interface contracts for multiple domains, those contracts may define the same named type with incompatible field types. For example:
- Weather service contract:
WidgetPosition { x: int, y: int, width: int } - Time widget contract:
WidgetPosition { x: string, y: string, width: string }
If both contracts reach implementation agents, each agent writes code that compiles against its own contract but fails at integration. The type mismatch is invisible until runtime.
check_contract_cross_file_consistency in src/integrations/contract_validation.py scans all in-memory contract artifacts for type contradictions before decomposition proceeds. This is called Invariant 5 and is the only hard gate in the contract-first pipeline.
The function receives the contract artifacts dict (keyed by domain name) and:
-
Filters to interface contracts by filename pattern: keeps only artifacts where
"interface-contracts"appears in the filename. This is a deliberate choice over filtering byartifact_typebecause the live generator emitsartifact_type="specification"for interface contracts (a naming mismatch caught by Codex P1 review). -
Extracts type definitions from each contract document. Types are identified by common patterns:
class/interface/type/structdeclarations, TypeScript interfaces, Python dataclasses, and Pydantic models. -
Cross-references types across domains. When the same type name appears in multiple domains, the function compares field names and field types.
-
Reports contradictions. A contradiction exists when two domains define a type with the same field name but different field types. Field presence differences (domain A has a field that domain B lacks) are not contradictions -- they indicate one contract is a superset, which is safe.
check_contract_cross_file_consistency(contract_artifacts)
├── No contradictions found → returns success, pipeline continues
└── Contradictions found → returns failure with details
└── _try_contract_first_decomposition falls back to feature-based
The gate is binary: any type contradiction triggers fallback. There is no "partial accept" mode because a single type mismatch can cascade through the entire integration boundary.
Invariant 5 checks only for type contradictions, not for:
- Missing types (a domain references a type no other domain defines)
- Missing verbs (contracts omit user-facing actions from the PRD)
- Incomplete coverage (contracts do not cover all requirements)
These are quality issues, not correctness failures. A missing verb produces a product that lacks a feature; a type contradiction produces a product where two modules cannot communicate. The first is fixable by the integration agent; the second is not.
A verb-coverage gate was proposed and rejected during GH-320 review. A six-verb hard-coded checklist was too brittle (false positives on non-UI projects) and too destructive (discarding the 55/45 coordination win). See Contract-First Decomposition for the full rationale.
- Semantic contradictions: domain A says "temperature in Celsius" while domain B assumes Fahrenheit. These are natural-language disagreements that text parsing cannot detect reliably.
- Behavioral contradictions: domain A expects synchronous calls while domain B provides async-only APIs. These require deeper analysis than type comparison.
- Schema shape differences: domain A nests
positioninsidewidgetwhile domain B uses a flat structure. Both may defineWidgetPositionconsistently but use it differently.
These gaps are acknowledged. Invariant 5 catches the most common and most dangerous class of cross-contract error. Future invariants may extend coverage.
src/integrations/contract_validation.py
def check_contract_cross_file_consistency(
contract_artifacts: dict[str, list[dict[str, str]]]
) -> tuple[bool, list[str]]:
"""
Check generated contracts for type contradictions across domains.
Parameters
----------
contract_artifacts : dict[str, list[dict[str, str]]]
Contract artifacts keyed by domain name. Each value is a list
of artifact dicts with "filename" and "content" keys.
Returns
-------
tuple[bool, list[str]]
(is_consistent, contradiction_descriptions).
is_consistent is True when no type contradictions are found.
contradiction_descriptions lists human-readable descriptions
of each contradiction found.
"""Called inside _try_contract_first_decomposition in src/integrations/nlp_tools.py, between contract generation and decompose_by_contract:
is_consistent, contradictions = check_contract_cross_file_consistency(
contract_artifacts
)
if not is_consistent:
logger.warning(
"Contract cross-file inconsistency detected, "
"falling back to feature-based: %s",
contradictions,
)
return self._feature_based_fallback(...)tests/unit/integrations/test_contract_validation.py -- 7 tests:
- Consistent contracts pass validation
- Type contradictions are detected across two domains
- Type contradictions are detected across three or more domains
- Field presence differences (superset) do not trigger contradiction
- Non-interface-contract artifacts are ignored
- Empty contract artifacts pass validation
- Single-domain contracts pass validation (no cross-reference possible)
- Contract-First Pipeline -- Full pipeline specification
- Contract-First Decomposition -- Conceptual overview and decisions
- Quality Assurance -- Marcus's broader quality framework