18 KiB
ADR-0225: ContractResidual Read-Model
Status: Accepted Author: Antigravity Date: 2026-06-22
Ratification note
Accepted for implementation after #860 review.
This ADR authorizes exactly one diagnostic-only implementation PR for the ContractResidual projection over existing ContractAssessment outputs.
The authorization does not include SearchGate, ComputeBudgetPolicy, GeometricSearchRun, Workbench implementation, serving behavior, teaching/proposal/report/eval mutation, or runtime repair. Those remain separately gated by future ADRs/PRs.
Context
CORE has introduced a proposal-first diagnostic construction pipeline for representing mathematical word problems and relations within the semantic substrate. In this architecture:
- Surface and process evidence is scanned to produce hypothesis-only
ConstructionProposalrecords withstatus="proposed". - Mentions, bindings, and local roles are bound to ground these proposals.
ContractAssessmentserves as the sole runnable/refused authority, verifying the algebraic and semantic validity of the candidate structures.- PR #859 aligned the
state_change.unary_delta(candidate organ"unary_delta_transition") implementation with its accepted spec.
As the pipeline scales, downstream planning needs a deterministic, structured mechanism to understand why a particular contract was refused (i.e., why a diagnostic organ did not close), without introducing unreviewed search behavior, serving authority, or mutative repair loops.
Problem
Currently, ContractAssessment records capture diagnostic failure information using:
candidate_organmissing_bindingsunresolved_hazardsrunnable(boolean status)explanation(prose description)evidence_spans
While this structure is sufficient for simple validation, downstream planning and operator reasoning require a normalized, structured read-model to classify failures into fine-grained, structured categories, such as:
- Missing required role bindings
- Ambiguity in roles or relations
- Inexact provenance spans
- Unsupported topologies
- Blocking hazards
This read-model must classify the diagnostics deterministically, without changing the underlying contract definitions, making search decisions, or granting compute budget.
Decision
We propose the introduction of a scoped, read-only ContractResidual read-model.
Normative Authority Rule
Important
ContractAssessmentremains the sole runnable/refused authority.ContractResidualis a pure read-only projection overContractAssessmentrecords. It never authorizes serving, search, mutation, learning, answer production, or repair of theProblemFrame.
Conceptual Shape
The conceptual schema for ContractResidual is defined as follows:
@dataclass(frozen=True, slots=True)
class ContractResidual:
residual_id: str
candidate_organ: str
family_id: str | None
residual_kind: ResidualKind
residual_code: str
source_axis: ResidualSourceAxis
evidence_spans: tuple[SourceSpan, ...]
explanation: str
(Note: This is documentation only. The Python classes are not implemented in this PR.)
ResidualKind Taxonomy
We define a closed taxonomy of ResidualKind to classify existing ContractAssessment labels:
- MISSING_PROPOSAL: The required construction proposal is absent.
- MISSING_RELATION: The underlying relation edge (e.g.,
quantity_entity) is missing. - MISSING_ROLE: A required role slot on the relation is unbound.
- AMBIGUOUS_RELATION: Multiple candidate relations exist with no unique resolution.
- AMBIGUOUS_ROLE: A role binds to multiple candidate mentions ambiguously.
- INEXACT_PROVENANCE: Spans or quantities lack exact, source-grounded provenance.
- NONLOCAL_BINDING: The binding requires non-local references (e.g., pronoun resolution or cross-sentence leaps).
- UNSUPPORTED_TOPOLOGY: The clause contains grammar or structure unsupported by the family (e.g., passive voice, multiple coordinate actors).
- UNIT_OBJECT_CONFLICT: Grounded quantity units conflict with the object type (e.g., count vs. measurement mismatch).
- HAZARD_BLOCKED: An active semantic confuser or hazard blocks the contract.
- TARGET_UNBOUND: The question target cannot be bound or is missing.
- CONTRACT_GAP_UNCLASSIFIED: A fallback classification for unmapped contract failures (non-searchable).
Rules
- The taxonomy classifies existing
ContractAssessmentlabels; it does not replace them. residual_codemust preserve the original missing binding or unresolved hazard label exactly.CONTRACT_GAP_UNCLASSIFIEDis restricted to non-searchable diagnostic fallback only.- Broad, vague, or non-deterministic kinds (e.g.,
MATH_FAILED,REASONING_FAILED,UNKNOWN_TRUTH) are architecturally forbidden and structurally disallowed.
ResidualSourceAxis
To decouple the kind of failure from the structural layer that produced it, we define ResidualSourceAxis:
- PROPOSAL: Bounded proposal detection layer.
- RELATION: Binding graph/relational edge layer.
- ROLE: Slot-binding role layer.
- PROVENANCE: Source-grounding and span layer.
- LOCALITY: Clause and sentence distance bounds.
- TOPOLOGY: Grammatical and structural patterns.
- HAZARD: Cognitive/confuser hazard detection.
- TARGET: Question target binding.
- UNIT_OBJECT: Count/measurement unit boundary.
- UNKNOWN: Fallback for unmapped boundaries.
Mapping from Existing Labels
The mapping from existing contract labels (missing bindings and unresolved hazards) to ResidualKind and ResidualSourceAxis is deterministic. The following table maps the labels from current contract families:
| Existing Contract Label | ResidualKind | ResidualSourceAxis | Notes / Rationale |
|---|---|---|---|
Quantity Entity (binding.quantity_entity) |
|||
quantity_entity_proposal_required |
MISSING_PROPOSAL |
PROPOSAL |
Missing the core proposal |
quantity_unbound |
MISSING_ROLE |
ROLE |
Missing the quantity role |
entity_unbound |
MISSING_ROLE |
ROLE |
Missing the entity role |
quantity_ambiguous |
AMBIGUOUS_ROLE |
ROLE |
Multiple candidate quantities |
entity_ambiguous |
AMBIGUOUS_ROLE |
ROLE |
Multiple candidate entities |
local_binding_relation_unbound |
MISSING_RELATION |
RELATION |
No linking relation |
local_binding_relation_ambiguous |
AMBIGUOUS_RELATION |
RELATION |
Multiple local bindings |
quantity_kind_unresolved |
MISSING_ROLE |
ROLE |
Missing type disposition |
unit_kind_conflict |
UNIT_OBJECT_CONFLICT |
UNIT_OBJECT |
Count vs. measure conflict |
provenance_span_inexact |
INEXACT_PROVENANCE |
PROVENANCE |
Spans do not match exactly |
quantity_entity_nonlocal |
NONLOCAL_BINDING |
LOCALITY |
Binding spans are non-local |
competing_family_context |
HAZARD_BLOCKED |
HAZARD |
Hazard from competing triggers |
percent_change_vs_percent_of |
HAZARD_BLOCKED |
HAZARD |
Percentage ambiguity hazard |
Fraction Decrease (proportional_change.decrease_to_fraction) |
|||
decrease_relation_ambiguous |
AMBIGUOUS_RELATION |
RELATION |
Ambiguous relation path |
base_quantity_unbound |
MISSING_ROLE |
ROLE |
Missing base quantity role |
scale_unbound |
MISSING_ROLE |
ROLE |
Missing scale fraction role |
state_entity_unbound |
MISSING_ROLE |
ROLE |
Missing state entity role |
base_quantity_provenance_missing |
INEXACT_PROVENANCE |
PROVENANCE |
Base quantity lacks provenance |
scale_provenance_missing |
INEXACT_PROVENANCE |
PROVENANCE |
Scale lacks provenance |
unit_continuity_unproven |
UNIT_OBJECT_CONFLICT |
UNIT_OBJECT |
Mismatched units along continuity axis |
delta_decrease_target_unbound |
TARGET_UNBOUND |
TARGET |
Unbound decrease target |
delta_decrease_target_required |
TARGET_UNBOUND |
TARGET |
Required decrease target is missing |
state_entity_continuity_unproven |
HAZARD_BLOCKED |
HAZARD |
Ambiguity in entity tracking |
scale_out_of_range |
HAZARD_BLOCKED |
HAZARD |
Scale value outside (0, 1) bounds |
Percent Partition (partition.percent_partition) |
|||
grounded_partition_subgroup |
MISSING_ROLE |
ROLE |
Missing partition part roles |
grounded_whole_entity |
MISSING_ROLE |
ROLE |
Missing whole role entity |
original_whole_unbound |
MISSING_ROLE |
ROLE |
Whole is unbound |
multiple_original_whole_candidates |
AMBIGUOUS_ROLE |
ROLE |
Ambiguity between whole candidates |
partition_subgroups_not_distinct |
HAZARD_BLOCKED |
HAZARD |
Subgroups share entities/mentions |
percent_subgroup_links_incomplete |
UNSUPPORTED_TOPOLOGY |
TOPOLOGY |
Incomplete partition topology |
grounded_question_target |
TARGET_UNBOUND |
TARGET |
Unbound question target |
forward_aggregate_target_required |
TARGET_UNBOUND |
TARGET |
Aggregate target required for partition |
inverse_topology_unlicensed |
UNSUPPORTED_TOPOLOGY |
TOPOLOGY |
Inverse query topology unsupported |
Unary Delta (state_change.unary_delta) |
|||
unary_delta_proposal_required |
MISSING_PROPOSAL |
PROPOSAL |
Missing the core proposal |
unary_delta_relation_ambiguous |
AMBIGUOUS_RELATION |
RELATION |
Ambiguous relation path |
action_cue_unbound |
MISSING_ROLE |
ROLE |
Missing action cue role |
delta_quantity_unbound |
MISSING_ROLE |
ROLE |
Missing quantity role |
delta_quantity_ambiguous |
AMBIGUOUS_ROLE |
ROLE |
Multiple candidate quantities |
changed_object_unbound |
MISSING_ROLE |
ROLE |
Missing changed object role |
changed_object_ambiguous |
AMBIGUOUS_ROLE |
ROLE |
Multiple candidate objects |
local_binding_relation_unbound |
MISSING_RELATION |
RELATION |
No linking relation |
direction_unbound |
MISSING_ROLE |
ROLE |
Missing direction role |
quantity_kind_unresolved |
MISSING_ROLE |
ROLE |
Missing type disposition |
unit_object_conflict |
UNIT_OBJECT_CONFLICT |
UNIT_OBJECT |
Count vs. measure conflict |
provenance_span_inexact |
INEXACT_PROVENANCE |
PROVENANCE |
Spans do not match exactly |
quantity_entity_nonlocal |
NONLOCAL_BINDING |
LOCALITY |
Spans are non-local |
pronoun_antecedent_unresolved |
NONLOCAL_BINDING |
LOCALITY |
Pronoun requires reference resolution |
event_assertion_unlicensed |
UNSUPPORTED_TOPOLOGY |
TOPOLOGY |
Unlicensed event assertion topology |
passive_voice_unsupported |
UNSUPPORTED_TOPOLOGY |
TOPOLOGY |
Passive voice topology is unsupported |
multiple_actor_surface |
UNSUPPORTED_TOPOLOGY |
TOPOLOGY |
Multiple coordinate actors unsupported |
(Note: No new runtime labels are created in this PR. If a label is unclear or needs modification, it must go through a formal preflight review before implementation.)
Non-Authority Guarantees
To prevent scope creep and preserve the deterministic nature of the cognitive engine:
- No proposals: Residuals describe why a candidate contract failed to close; they are never proposals and cannot be registered in the construction catalog.
- No proofs: Residuals carry no proof value and do not establish the validity of any mathematical assertion.
- No search commands: Residuals do not instruct the engine to expand the candidate graph, change search paths, or adjust retry strategies.
- No budget grants: Residuals do not allocate compute budget, trigger model-based retries, or define practice lanes.
- No teaching proposals: Residuals are not mutation proposals for training, packs, or policies.
- No Workbench actions: Residuals do not mutate the database or trigger execution. They are strictly read-only diagnostics for visual inspection.
- No serving decisions: Serving paths remain entirely frozen. Residuals cannot flip a contract to
runnable, repair aProblemFrame, or generate answers.
Relationship to SearchGate
The boundary between ContractResidual and the future SearchGate is strictly delineated:
- Future Input Only: SearchGate may later consume
ContractResidualrecords as input to make search decisions, but SearchGate is a separate future ADR/PR. - Forbidden on
ContractResidual:- No
search_eligibleboolean onContractResidual. - No compute budget configuration.
- No retry policy or loop rules.
- No search run schema.
- No candidate expansion or node generation algorithms.
- No sealed or practice lane integration.
- No
Relationship to Workbench
The boundary between ContractResidual and Workbench is defined as:
- Read-Only Display Only: Workbench may later display
ContractResidualrecords as read-only operator diagnostics. - Forbidden in Workbench:
- No UI implementation of fix actions (e.g., "fix this" buttons).
- No mutation controls.
- No editing of proposals from the residual interface.
- No human feedback or interactive correction flow linked to residuals.
Determinism and Replay
ContractResidual projections must be fully deterministic and replay-stable:
- Stable IDs: The
residual_idmust be constructed using a stable hashing mechanism from its constituent fields:
(Note: Hashing code is not implemented in this PR.)residual_id = stable_hash(candidate_organ, residual_kind, residual_code, ordered_evidence_spans) - Preserved Spans: Source spans must be preserved exactly without widening, concatenation, or synthesis.
- Deterministic Ordering: Residuals must be ordered deterministically based on their
residual_id. - No Side Effects: Residual generation must not contain timestamps, random IDs, environment-dependent data, or model-generated text.
Examples
Example A: Missing Changed Object / Missing Relation
Input: "Tom gained 3."
- Behavior: A unary-delta proposal exists based on the exact cue
"gained". - Contract Assessment:
ContractAssessment(candidate_organ="unary_delta_transition")refuses due to a missing changed object and local relation. - Residual Projection:
ContractResidual( residual_id="stable_hash_a", candidate_organ="unary_delta_transition", family_id="state_change.unary_delta", residual_kind=ResidualKind.MISSING_ROLE, residual_code="changed_object_unbound", source_axis=ResidualSourceAxis.ROLE, evidence_spans=(SourceSpan(text="gained", start=4, end=10),), explanation="The changed object role is unbound.", ) - Status: No answer/search/serving is triggered.
Example B: Missing Delta Quantity
Input: "Tom gained apples."
- Behavior: The cue
"gained"proposes. The quantity is missing. - Contract Assessment: Refuses due to missing
delta_quantity. - Residual Projection:
ContractResidual( residual_id="stable_hash_b", candidate_organ="unary_delta_transition", family_id="state_change.unary_delta", residual_kind=ResidualKind.MISSING_ROLE, residual_code="delta_quantity_unbound", source_axis=ResidualSourceAxis.ROLE, evidence_spans=(SourceSpan(text="gained", start=4, end=10),), explanation="The delta quantity role is unbound.", ) - Status: No synthetic quantity is introduced; remains refused.
Example C: Unsupported Passive Topology
Input: "3 apples were gained by Tom."
- Behavior: The passive voice cue structures are unsupported by the closed recognizer.
- Contract Assessment: Refuses due to passive topology / event assertion unlicensed.
- Residual Projection:
ContractResidual( residual_id="stable_hash_c", candidate_organ="unary_delta_transition", family_id="state_change.unary_delta", residual_kind=ResidualKind.UNSUPPORTED_TOPOLOGY, residual_code="passive_voice_unsupported", source_axis=ResidualSourceAxis.TOPOLOGY, evidence_spans=(SourceSpan(text="gained", start=14, end=20),), explanation="Passive voice topology is unsupported.", ) - Status: No role repair is attempted.
Example D: Unit-Object Conflict
Input: "Tom gained 3 degrees."
- Behavior: Quantity unit
"degrees"conflicts with object count/entity expectations. - Contract Assessment: Refuses due to
unit_object_conflict. - Residual Projection:
ContractResidual( residual_id="stable_hash_d", candidate_organ="unary_delta_transition", family_id="state_change.unary_delta", residual_kind=ResidualKind.UNIT_OBJECT_CONFLICT, residual_code="unit_object_conflict", source_axis=ResidualSourceAxis.UNIT_OBJECT, evidence_spans=(SourceSpan(text="degrees", start=13, end=20),), explanation="Quantity unit conflicts with object type.", ) - Status: No arithmetic or state update is performed.
Non-Goals
This ADR explicitly does not cover:
- Implementation of
ContractResidualor Python dataclasses. - Integration with SearchGate, ComputeBudgetPolicy, or GeometricSearchRun.
- Mutating or displaying in Workbench.
- Serving path alterations or runtime dispatch.
- Adding new contract labels or modifying the existing assessment logic.
- Adding tests, reports, evals, teaching loops, or semantic pack modifications.
- Performing arithmetic, answer derivation, or solving.
Acceptance Criteria
This ADR is successfully ratified only if:
- It is documentation-only.
- It defines
ContractResidualas a strict read-only projection. - It preserves
ContractAssessmentas the sole runnable authority. - It defines a small closed taxonomy for
ResidualKindandResidualSourceAxis. - It deterministically maps existing labels without inventing runtime labels.
- It freezes SearchGate/ComputeBudget/Workbench implementation boundaries.
git diff --checkpasses cleanly.