core/generate/comprehension
Shay 3357c5fc71 feat(adr-0174-phase2): continuous constraint propagation in comprehension reader
ADR-0174 Phase 2 — hoist _initial_admissible / roundtrip_admissible into
hypothesis-based constraint checks with structured elimination tracing.
Admission semantics are byte-equivalent to today; the change is structural.

Adds generate/comprehension/constraint_propagation.py:
- VALID_PREDICATE_NAMES: closed set of 17 sub-check names spanning
  initial / composed_initial / operation admissibility predicates.
  Adding new names requires an ADR amendment (structural contract with
  reader_trace consumer).
- ConstraintResult dataclass: admitted bool + predicates_run trace +
  elimination_reason. Validates admitted-vs-reason consistency.
- Elimination dataclass: confidence_rank + predicate + reason for one
  hypothesis being eliminated.  Serialisable as a reader_trace event.
- hypothesis_from_initial / hypothesis_from_operation: adapters wrapping
  CandidateInitial / CandidateOperation as Phase-1 Hypothesis objects
  with caller-supplied confidence_rank.
- _check_initial / _check_composed_initial / _check_operation:
  decomposed sub-check implementations of the existing admissibility
  predicates with first-failure short-circuit (matches current
  semantics).  Each sub-check populates predicates_run with (name, ok|
  fail|skip) so the consumer sees exactly which predicate decided.
- check_constraints: dispatches on candidate type.
- eliminate_violating: bulk filter; returns (survivors, eliminations);
  survivors are re-densified to satisfy ProblemReadingState's
  open_hypotheses post_init invariant (dense-from-0 ranks);
  eliminations carry the original confidence_rank for trace fidelity.

Wires into generate/math_candidate_graph.py at the recognizer
injection site (line 825+): replaces inline _initial_admissible /
roundtrip_admissible dispatch with eliminate_violating. Elimination
events become JSON entries in reader_trace with layer=
'constraint_propagation', phase=2, predicate, reason, sentence_index.

Phase 2 acceptance verified:
- 24/24 ADR-0174 Phase 2 tests pass (emission, parity with existing
  predicates on 9 admit/reject cases, redensification, dataclass
  invariants, integration).
- 71/71 existing reader + Phase 1 tests still pass.
- Smoke 67/67, packs 141/141, lanes 8/8.
- train_sample/v1 byte-identical across two runs with use_reader=True.
- Score preserved: correct=3 refused=47 wrong=0 — semantics identical
  because the decomposed sub-checks short-circuit on the same predicates
  the inline checks would have caught.

Trace-event behavior: today's injectors are conservative enough that
zero eliminations occur on train_sample/v1 (no false positives, no
mid-pipeline failures).  The wiring is exercised by
test_phase2_event_shape_when_synthesized which proves the trace shape
on a synthetic CandidateInitial that fails initial.unit_grounds.  When
Phase 3 begins emitting partial hypotheses from apply_word, the
elimination path will fire on real candidates and the trace will
populate.

Stacks on Phase 1 (feat/adr-0174-phase1-held-hypothesis-state, PR
#416).  Merges cleanly into main after PR #416 lands.
2026-05-28 10:16:33 -07:00
..
__init__.py
audit.py feat(brief-11/11B): reader closure audit artifact — full taxonomy + rejected naive fix (#345) 2026-05-27 05:35:06 -07:00
composition_registry.py feat(consumption-wiring): CW-1 + CW-2 — Frame + Composition registry loaders (#398) 2026-05-27 16:17:03 -07:00
constraint_propagation.py feat(adr-0174-phase2): continuous constraint propagation in comprehension reader 2026-05-28 10:16:33 -07:00
frame_registry.py feat(consumption-wiring): CW-1 + CW-2 — Frame + Composition registry loaders (#398) 2026-05-27 16:17:03 -07:00
lexeme_primitives.py feat(comprehension/8.2): universal proper_noun_token primitive (#333) 2026-05-26 22:16:34 -07:00
lexicon.py feat(comprehension): operational lexicon loader for en_core_math_v1 (ADR-0164 §Decision §1) (#325) 2026-05-26 20:08:27 -07:00
lifecycle.py feat(comprehension/10): Phase 2 statement-frame reader (ADR-0164.4) (#335) 2026-05-27 05:03:56 -07:00
lifecycle_runtime_adapter.py feat(ADR-0164.P1): reader/regex hybrid coexistence + Phase 1 measurement gate (#331) 2026-05-26 21:14:11 -07:00
state.py feat(adr-0174-phase1): held-hypothesis state primitive in comprehension reader 2026-05-28 08:09:00 -07:00