Adds the correct grade-raising "wire" the field substrate was missing — so cga_inner
can operate on RELATIONS among entities (lines/planes/incidence), not just pairwise
point distance. Built only from existing Cl(4,1) primitives (geometric_product,
grade_project) + the pseudoscalar; no normalization, no approximation, versor_condition
path untouched (flats are null-cone wedges, not unit versors).
- outer_product: DOCSTRING-ONLY honesty fix (behavior byte-identical, every caller
unchanged). It is the commutator 0.5*(XY-YX) = the wedge ONLY for grade-1 vectors;
for higher grades it is the Lie bracket, NOT the wedge, and does NOT build a k-blade
by repetition. Existing callers consume it as an opaque cga_inner-reduced feature
(none read it by grade), so the relabel is safe. Points to graded_wedge for the real
exterior product.
- graded_wedge(X,Y) = <XY>_{grade(X)+grade(Y)} — the true wedge; agrees with
outer_product on grade-1, differs above (pinned by test). Builds lines/planes.
- is_incident(point, flat): EXACT zero-test (point^flat == 0, no float tolerance to
admit — near-incident is refused, per wrong=0). Exact at scale in f64.
- dual(X) = X*I5^{-1} (I5^2=-1 confirmed); involutive up to sign.
- meet(A,B) = dual(dual(A)^dual(B)): correct for spanning operands (two planes -> their
line, incidence verified). HONEST ENVELOPE: degenerates for non-spanning operands
(coplanar lines) — returns the ZERO multivector (detectable, documented, tested),
never a silent wrong value. The general coplanar intersection needs the join-relative
meet, deliberately NOT faked here.
Green: smoke 87, algebra 82, incidence 8, outer_product consumers + invariants 109;
zero regressions (outer_product behavior unchanged).
Extends evals/deductive_logic/grounding.py from unary predicates / single-var rules to
binary relations + multi-variable universal rules, still by FINITE PROPOSITIONAL
grounding into the regime the ROBDD engine + the independent truth-table oracle both
decide. wrong==0 stays structural. This is the real capability step a RuleTaker/
ProofWriter-style mirror needs (the unary fragment alone is trivial).
- atom_n lowers pred(a,b) -> pred__a__b; arity-1 is byte-identical to the old atom, so
the live unary panel lowers unchanged (proven by an exact-string back-compat test).
- multi-variable universal rules ground over n^k assignments — transitivity now decides.
- range-restriction: a rule with a head variable unbound in the body refuses (unsafe_rule)
— it grounds soundly but is outside the clean regime real benchmarks use.
- typed refusals: arity>=3/functions, explicit quantifiers, variable-free rules, bounds.
Honest ceilings (documented in docs/analysis/relational-grounding-extension-2026-06-04.md):
- THE binding constraint is the GOLD, not the grammar: the truth-table oracle is
O(2^atoms), so grounding refuses above MAX_GROUND_ATOMS=20 => binary problems cap at
~4 entities/predicate. A real lift needs a 2nd genuinely-independent sub-enumeration
oracle (not built).
- OPEN-WORLD only: RuleTaker/ProofWriter's main splits are closed-world + NAF; a future
adapter MUST refuse CWA/NAF (mapping CWA "False"->"refuted" is a wrong=0 breach).
- arity <= 2, function-free.
Validated: held-out differential fuzz (400 random binary problems, oracle-golded) = 0
engine/oracle mismatches; unary back-compat byte-identical; INV-25b reproducibility green;
deductive lane wrong=0 16/16; smoke 87.
The falsifiable experiment's measurements #2 (ablation) and #3 (diversity). Builds the
competent, code-disjoint SYMBOLIC reader (the control arm AND the C3 capability path)
and the ablation instrument that runs both readers through the real
verify_tier2_agreement gate.
VERDICT: C3 — the field is decoration on this domain (a sanctioned, honest negative):
- field_wrong_commits = [] (wrong=0 holds; the per-step drift guard refuses bad ints)
- field_caught_symbolic_errors = [] (the field caught ZERO symbolic errors)
- per-class diversity = 0 everywhere (both readers agree and are both correct)
- the only admitted-set change is the field LOSING coverage at the precision ceiling.
Insight: on forward-substitutable relations, geometric translation IS arithmetic
addition, so there is no metric over-determination for the field to exploit — field and
symbol are common-mode (Knight-Leveson), not a genuine second derivation. This is the
deductive finding's twin: logic was combinatorial (field can't earn it), additive is
arithmetically trivial (field adds nothing). The field needs metric-nontrivial AND
arithmetically-hard structure to earn a reasoning role — dedicated research, not
near-term. Field-as-reasoner is NOT earned; no field vote enters any serving path; the
field stays a servant. Capability path = symbolic (C3), not shipped here.
- generate/relational_symbolic_reader.py: competent independent reader (pure int).
- evals/relational_metric/ablation.py: the reusable decoration instrument.
- docs/analysis/field-wedge-ablation-result-2026-06-04.md: the recorded verdict.
All prior artifacts STAY (field reader = real wrong=0 read demo + 3rd panel domain).
Green: full wedge suite 104; 53 architectural invariants.
Measurement #1 of the field-reasoner falsifiable experiment: does the CL(4,1) field,
given an honest metric encoding, read forward-substitutable quantitative-relational
problems from TEXT with wrong==0? It does — 14/15 correct, 0 wrong, 1 refused
(precision ceiling), scored against an independent arithmetic oracle.
- generate/relational_field_reader.py: reads problem text into conformal points on
the e1 number line; additive/part-whole relations are conformal TRANSLATOR versors
(versor_apply(T_delta, embed[x]) == embed[x+delta], exact); the answer reads back
by projective dehomogenization. Refusal-first: fences multiplicative/ratio (the
sign/orientation-blind cases), the precision ceiling, non-forward-substitutable
references, negatives. A per-step exactness self-check turns any f64 translator
drift into a refusal (precision_drift) — it NEVER commits a wrong integer. Its
parser is an independent reimplementation importing no generate.derivation/math_*.
- evals/relational_metric/: independent arithmetic oracle (computes gold from the
STRUCTURE, shares no code with the reader), 15-case fixture, and a runner that
enforces gold integrity + wrong==0.
- INV-25: relational_metric registered in INDEPENDENT_GOLD_LANES (oracle proven
code-disjoint from the field reader and the algebra engine). The independently
golded panel is now three domains: deductive, dimensional, relational-metric.
Green: smoke 87, 53 architectural invariants, 16 new tests; deductive + dimensional
lanes unperturbed (wrong=0).
Phase 0 of the field-reasoner wedge — net hardening regardless of the
experiment's outcome.
- algebra/cga.py: embed_point gains a dtype kwarg (f32 default byte-unchanged;
cl41.geometric_product already preserves f64) + read_scalar_e1 projective
dehomogenization read-back (weight-invariant; correct for dilations, where a
raw distance-from-origin is wrong) + EMBED_EXACT_MAX pinned magnitude ceiling.
f32 silently collapsed integer coordinates past ~1e4.
- core/reasoning/evidence.py: verify_tier2_agreement now keys independence on a
reader_lineage pathway token (refuses SAME_READER_LINEAGE), replacing the
label-only len(set(signatures))<2 check a single reader could satisfy by
relabeling. reader_lineage is excluded from canonical serialization, so the
entailment trace_hash is unchanged.
- tests INV-27: transitive reader-disjointness over TIER2_READER_PATHWAYS makes
the lineage check load-bearing (distinct lineage => proven import-disjoint
pathway). The two seeded readers share zero transitive first-party modules.
Green: smoke 87, algebra 82, cognition 121, 53 architectural invariants,
reasoning/deductive/r1 50; 16 new f64-exactness tests; zero regressions.
The first non-GSM8K consumer of the binding-graph interlingua's unit algebra as a
load-bearing reasoner: given two units and an operation, decide the result's
dimension. SUT = generate.binding_graph.units; gold = evals/dimensional/oracle.py,
a genuinely INDEPENDENT dimensional reasoner (own unit->base-exponent table, own
exponent arithmetic, own canonical-string renderer; shares no code with the SUT).
12 cases (area / speed / wage / mass-density / dimensionless / 2 refused) gated by
SUT == oracle == gold (wrong=0). Registered in INV-25's INDEPENDENT_GOLD_LANES,
proving the independent-gold discipline generalizes to a SECOND oracle.
This is the 3rd structurally-distinct golded domain (logic / grounding / dimensional)
— the anti-overfit >=2-domain panel is now real, and the interlingua is load-bearing
beyond GSM8K.
The first comprehension->structure compiler: evals/deductive_logic/grounding.py
lowers a typed finite-entity problem (finite entities + unary predicates +
single-variable universal rules) into the propositional regime the ADR-0206
entailment operator decides, refusal-first with a closed typed reason vocabulary
(unsafe_symbol / unknown_entity / unsupported_predicate_arity / unsupported_
quantifier / malformed_case / empty_case) and collision-safe atom slugging.
finite_entity/v1/cases.jsonl: 8 cases with INDEPENDENT (oracle-derived) gold
(4 entailed, 2 unknown, 1 refuted, 1 refused) — chained rules, conjunctive
bodies, negative heads, inconsistent premises. 20 tests gate engine==oracle==gold.
This is the second diversity-panel domain (distinct comprehension, same checkable
substrate) — the universal-structure thesis validated on a different problem shape,
with the anti-overfit >=2-domain discipline now live.
Phase 1.5 finding recorded: a clean geometric/algebraic propositional decoder
agrees 716/716 with the oracle but is O(2^n) (enumeration-class), so logic is the
wrong first domain for field-as-reasoner; the wedge redirects to quantitative-
relational structure where the field is the natural non-redundant decoder.
Declares SemanticSymbolicBindingGraph the universal problem-structure interlingua
(the corpus callosum where the geometric field and symbolic ROBDD decodings meet
and must agree). INV-26 enforces that neutrality structurally: 26a no binding-graph
module imports field/algebra/eval/vault/chat/core/sensorium; 26b the core imports
no domain reader (only allowlisted bridges adapter/question_target may); 26c proven
non-vacuous (flags pipeline.py's field import + the adapter's domain import).
Amends the plan doc with the structurally-diverse checkable panel (logic/grounding/
dimensional/execution/constraint, each with independent gold) and the 'a capability
change must move >=2 structurally-distinct domains or it is suspected overfitting'
rule, woven in from Phase 2 — the anti-overfit instrument the train_sample breach
proved we need.
Validated: architectural invariants 49 + binding_graph model = 118 passed.
INV-25 makes the GSM8K lesson structural: no capability claim is valid unless
its gold is computed by a procedure sharing no code with the system under test.
Three meaningfully-failing checks (proven able to fail): 25a oracle imports no
SUT module (AST); 25b every committed deductive gold is reproduced by the
independent oracle AND matched by the engine; 25c an unsound engine disagrees
on committed cases.
SHA-pin the deductive lane (deductive_logic_v1, dev+holdout+external 716/716,
wrong=0, refused=0) via a deterministic --report writer + run_as_module (the
lane dir's local generate.py shadows the package in script mode) + CLAIMS regen.
Fix drift the review surfaced: contract.md + pivot doc claimed an 8,000/7,340
fuzz and an 'external mirror' -> corrected to the real gated 3,000-case fuzz
(2,796 definite) and 'hand-authored, NOT a published-benchmark mirror'; pivot
doc GSM8K holdout 0->5 (all 5 are R1 reconstruction; composer scored 0).
Validated: smoke 78, deductive 23, proof 29, invariants 44, lane-sha+claims 9.
The first SIZEABLE, honestly-verified reasoning capability — built on CORE's
own terrain (exact, verifiable, deterministic), not GSM8K's stochastic terrain.
THE OPERATOR (generate/proof_chain/entail.py, ADR-0206):
- evaluate_entailment(premises, query) -> entailed | refuted | unknown | refused.
- The multi-hop inference operator evals/symbolic_logic/gaps.md said did not exist
("no operator that takes A->B, B->C and returns A->C") and ADR-0205 deferred.
- Built on the ADR-0201 ROBDD canonicalizer: premises |= Q iff (AND P) -> Q is a
tautology. SOUND AND COMPLETE for propositional logic, not single-step.
- wrong=0 is structural: an exact tautology check refuses (LogicError) on
malformed / out-of-decidable-regime (quantified/predicate) input, never guesses.
THE HONEST METRIC (evals/deductive_logic/):
- holdout v1 (500 cases): 500 correct / 0 WRONG, incl. 227 non-trivial deductions
(117 entailed + 110 refuted). dev (200): 200/0.
- Gold from an INDEPENDENT truth-table oracle (oracle.py) sharing zero code with
the engine. 8,000-case fuzz across two independent decision procedures:
0 disagreements. This is the soundness evidence the GSM8K composer could never
produce (it could not separate its 2 right from its 87 wrong answers).
- contract.md states the load-bearing honesty boundary: PROPOSITIONAL ONLY, and
given-formulas (NL->logic grounding is a separate later layer, kept out of scope
so we do not re-step on the GSM8K natural-language rake).
TESTS (17, all green): classic inference shapes (MP, multi-hop chain, modus
tollens, disjunctive/hypothetical syllogism, conjunctive rules, genuine unknown),
refusal boundary (inconsistent / quantified / predicate / malformed), and a
deterministic engine-vs-oracle fuzz cross-check.
Pure new module — does NOT touch serving. Smoke 73 passed; invariants 40 passed.
The 2026-06-04 sealed-breach post-mortem proved the 50-case train_sample has ZERO
predictive validity (its 4 "correct" are overfit; they hid a 5-wrong sealed breach).
This adds the instrument we never had: 500 real GSM8K cases CORE was NOT built on —
the train split minus the 50 train_sample, deterministic sha256(question) sort.
Same scorer as train_sample + the sealed lane, so the three are directly comparable:
train_sample(50): 4/0/46 holdout_dev(500): 0/0/500 sealed test(1319): 0/0/1319
Real GSM8K capability is 0%. The 4 train "correct" generalize to NOT ONE of 500
held-out cases. wrong=0 holds (refuses, never confabulates).
- evals/gsm8k_math/holdout_dev/v1/: cases.jsonl (500), runner, report (0/0/500), README.
- tests/test_holdout_dev_lane.py: floor (wrong==0, forever) + baseline snapshot (0/500).
Discipline: iterate here (open, large enough to resist trivial overfit); the sealed
test stays the final arbiter. wrong=0 is the floor; correct rising is the goal;
"refuse everything" is the FAILING baseline to beat, not a pass. Non-serving (eval only).
The FIRST real sealed measurement (operator-decrypted 1,319 held-out GSM8K)
found `0 correct / 5 WRONG` — a wrong=0 breach hidden for weeks because the
working metric was the 50-case train sample the bridges were tuned to. Bisection
isolated it to the product_bridge serving promotion (ADR-0195).
- generate/math_candidate_graph.py: REMOVE both serving promotion bridges
(product_bridge + goal_residual/ADR-0207 §5 step 2). Serving = main-graph-only.
Restores sealed 0/0/1319 (verified by bisect: disabling product_bridge -> 0 wrong).
Production modules remain in generate/derivation/; only serving promotion is
unwired, until a gate is proven wrong=0 on the SEALED set (never the train sample).
- Honest numbers everywhere: train_sample 7/43/0 -> 4/46/0 (the bridges' "correct"
was train-overfit). report.json + coverage probe regenerated. 7 ADR test lanes
de-pinned from the inflated count. corpus: cv-0005 (R4) reverts to refuse; cv-0020
(a "baseline control" that solved ONLY via product_bridge) reclassified.
- docs/claims_ledger.md: dated wrong=0-breach-and-remediation note + the rule:
the train_sample number had ZERO predictive validity for the exam; never the score.
- docs/analysis/gsm8k-lift-program-strategy: the program to actually move the 1,319.
NOTE the exit gate stays `correct>=10 AND wrong==0` — refusing-everything is an
explicit FAIL, not a wrong=0 pass; serving still commits (main graph). Verified:
broad regression 848 passed, smoke 73 passed.
Wires the R4 goal-residual production to serving via
resolve_promotable_goal_residual (math_candidate_graph.py, mirroring
product_bridge). cv-0005 / train_sample 0037 now solves on serving as
goal - Σprogress = 10 - 3 - 4 = 3. First Phase-5b composition lift on serving.
wrong=0 preserved on every runnable surface:
- train_sample 6/44/0 -> 7/43/0 (0037 added; 6 prior correct intact; wrong=0).
- Fires on 2/455 visible GSM8K cases, both correct, ZERO wrong.
- Gain-goal divergence firewall proves it reads the GOAL, not a possession.
- smoke 73, math+invariants 53, derivation/pool/practice 341, corpus, all green.
Lockstep updates (the ratified metric move, 6/44/0 -> 7/43/0):
- report.json; 7 ADR test lanes that pinned 6/44/0; corpus cv-0005 baseline
fields + snapshot (4/18 -> 5/17) + contract; plan-doc cv-0018 control fix.
⚠ SEALED MEASUREMENT REQUIRED — NOT DONE. The sealed 1,319 (encrypted, not
CI-reproducible) is the real bar (ADR-0207 §6) and was NOT re-measured. The
operator/CI must decrypt+run it and confirm sealed wrong==0; if wrong>0, revert
the resolve_promotable_goal_residual block (isolated). See
docs/handoff/sealed-measurement-obligation-2026-06-04.md.
DefaultEfferentGate is a capability/shape pre-filter only; it does not
lower decoded actions into safety/ethics pack verdicts (ADR-0198 §3 /
§1.2 Gap B). ModalityRegistry.decode/decode_batch now refuse fail-closed
any emission through a gate whose enforces_action_verdicts is False,
unless an explicit allow_unverified_efferent sandbox opt-in is set.
A real motor decoder thus cannot emit through the capability-only gate;
the §3 verdict-lowering gate must be built and installed first. No
production caller of the decode path exists today, so this closes the
latent hazard before a motor decoder makes it load-bearing. Adds two
falsifiable tests (fail-closed refusal; verdict-enforcing gate allowed).
Disjoint from the GSM8K serving path.
First composition lift-target built end-to-end: cv-0005 (train_sample 0037)
now resolves in the sealed pool as goal - Σprogress = 10 - 3 - 4 = 3.
- generate/derivation/goal_residual.py: new R4 production. Reads a GOAL anchor
(goal-intent lexeme) + a residual question, subtracts each same-referent
progress quantity (progress reduces the residual regardless of world-polarity).
Gated by the unchanged self-verification gate.
- wrong=0 firewall (test_reads_goal_not_possession): on a gain goal the
goal-residual (20-5-6=9) DIVERGES from possession-accumulation (20+5+6=31);
the production gives 9 and is all-subtract -> it reads the goal, not the
possession. This is the coincidental-correctness trap cv-0005 alone hides
(10-3-4 == 10-(3+4)).
- pool.py: goal_residual added to pooled_candidates (sealed). Verified: fires on
exactly one train_sample case (0037, correct), zero new pool wrong-commits
(the 8 are pre-existing, gated off serving by product_bridge).
Does NOT move the serving metric: train_sample stays 6/44/0 byte-identical
(serving = candidate-graph; product_bridge promotes only pure products, never a
subtract chain). The serving promotion gate for goal-residual is the next,
separately-gated step (needs the sealed 1,319 verdict). Smoke 73, math 4,
derivation/pool/practice 196, corpus, completeness-guard all green.
The composition_validation/v1 corpus shipped as data + a prose contract
with no executing test -- decoration until a test can fail (CLAUDE.md,
Schema-Defined Proof Obligations). Add the load-bearing gate:
- forever-invariants: wrong=0 firewall (admit => ==gold; null-gold =>
refuse), baseline-control regression net, permanent-refusal permanence,
and frozen baseline-field/tree match for the non-positive rows.
- current snapshot: aggregate 4 solve / 16 refuse / 0 wrong -- the single
assertion a Phase 5b slice updates when it flips a positive.
Future positives (5b-* gates) are checked by the firewall only, so a
refuse->solve flip at 5b stays green without rewriting frozen rows.
Verified: 44 passed; falsified (corrupting a control's gold fails the
firewall + control + snapshot tests); completeness guard still 21 passed.
Adds the first consumer of CORE's two built epistemic substrates — the
decode-state taxonomy (core/epistemic_state.py) and the risk-reward
reliability gate (core/reliability_gate/, ADR-0175) — beginning to close
the LABEL-ONLY consumption gap they sat behind.
Ships the scaffold only:
- core/response_governance/policy.py — ReachLevel (STRICT < APPROXIMATE <
EXTRAPOLATE < CREATIVE), ReachPolicy, govern_response (STRICT-only stub),
shape_surface (STRICT = identity transform; higher levels real but
unreachable in production), and the 9/5/1 EpistemicState partition.
- chat/runtime.py — cognition-path seam: the response surface now flows
through shape_surface(govern_response(...)). STRICT = identity, so the
path is byte-identical to pre-bridge. reach_level carried on ChatResponse.
- core/physics/identity.py — reach_level on TurnEvent (default "strict").
wrong==0 untouched: select_self_verified is NOT touched (ADR-0206 §5);
reach_level is NOT added to the telemetry JSONL dict, so pinned lane SHAs
stay byte-identical. Widening, SITUATE/FEED-BACK, the math-serving seam,
and the EVIDENCED reconcile are deferred to their own PRs.
Tests (tests/test_response_governance.py): STRICT-only contract over all
states x license x stakes, STRICT identity, live-wiring proof (forces
APPROXIMATE -> policy-sensitive surface), total+disjoint taxonomy
partition. Each fails loudly under the violation it guards.
The thesis demo ratified the unknown word 'sees' in train_sample case 0040 and
asserted the refusal moved. The reader has since advanced past that barrier —
case 0040 now first-refuses at quantity_extraction@s0 and never reaches 'sees',
and no train_sample case cleanly first-refuses at an unknown word anymore. So
the fixture was stale, not the machinery (which is unit-tested in
test_math_lexical_ratification.py + 9 others).
Repoint to a reader-stable synthetic statement ('Sam zorps 5 apples and
quibbles 3 oranges...'): 'zorps' first-refuses at lexicon_entry, gets ratified
as a drain_token and resolves, and the second unknown verb 'quibbles' keeps the
statement refused (wrong=0). Removed the now-dead RATIFICATION_TARGET_* consts.
- propose-from-exemplars: --all now proposes 6 exemplar corpora (the ME-1..ME-5
matcher waves added currency_amount, discrete_count_statement,
multiplicative_aggregation). All pending. Updated the pinned set + renamed
the test off 'three_corpora'.
- adr_0163_d2 pronoun: the design moved from hard-refusing a bare pronoun
subject at extraction to admitting it tagged requires_pronoun_resolution=True,
with the candidate-graph (math_candidate_graph.py:704/723) refusing to commit
unless it resolves. Verified end-to-end: 'He has 5 apples. How many...' still
returns answer=None. Pin the defensive flag (its absence re-opens the ADR-0174
wrong=0 hazard) instead of the obsolete None return.
A refactor split the revision helper into public get_git_revision() with
_git_revision() as a backward-compat alias, and pointed load_manifest() at
get_git_revision(). The revision-mismatch tests still patched _git_revision,
so the patch was a no-op and load_manifest read the real HEAD SHA — emitting
(or suppressing) warnings against the wrong 'current' revision. Patch the
function load_manifest calls. Feature unchanged; only the test seam was stale.
(The reboot path in chat/runtime.py still uses _git_revision, tested by
test_adr_0158 — left as-is.)
The telemetry tests built ChatRuntime(config=RuntimeConfig()) against the
default (shared) engine_state/ store. In a full-suite run, earlier runtime
tests persist a checkpoint there, so these tests restored ambient state and
emitted an extra 'reboot' telemetry event (restored_turn_count=NNNN), breaking
the 'one line per turn' counts (assert 3 == 2). They passed alone, failed
after siblings — a classic order-dependent isolation bug, not a telemetry
regression.
Build with no_load_state=True so each test exercises clean per-turn emission.
No telemetry test asserts the reboot/restore path, so none lose coverage.
(engine_state/ data files are already gitignored per ADR-0146.)
build_refusal_taxonomy_cases._STATEMENT_RE only matched the old 'no admissible
candidate for ...' shape, so post-#359 'recognizer matched but produced no
injection ... (category=X)' refusals were silently dropped (44 refusals -> 12
extracted). Extend it to both shapes (same gap fixed in rescan_v4 before that
layer was retired).
The lane mirrored 50 cases from the all-refused era; the reader now admits 6,
so it covers the 44 refused. Regenerated the cases fixture + committed
report.json and updated the count pins (50 -> 44).
Removed the perverse categorized_rate >= 0.95 floor: the exact histogram is
already pinned by test_committed_report_matches_categorizer, and the rate
drifts DOWN as the reader graduates categorized refusals — it fought reader
progress. Replaced with a sanity floor.
adr_0126: the unparseable 'contemplates' input still refuses (wrong=0); only
the reason wording changed (#359). Accept either non-admission phrasing.
The rescan_v2/v3/v4 chain has zero runtime consumers (nothing in core/,
generate/, teaching/, chat/ imports it). v2/v3 are frozen disk-snapshot
tests; v4 alone re-ran the LIVE reader and asserted a barrier-shift count +
admission set, so it broke on every legitimate reader improvement (it was 8
of the red tests). Its only real invariant (wrong=0) is guarded directly by
the serving runner and test_runtime_wrong_zero_preserved.
No ADR doc references the v4 layer (only S2/S3-post-rescan exist). Remove the
brittle live test plus the now-orphaned generator and its two artifacts.
Keep the frozen, tested v2/v3 layer as the historical record.
Net: -8 brittle tests, removes a maintenance-only archaeology surface rather
than greening it.
- recognizer_registry: ratified registry grew past round-1 (later ratification
rounds); assert the Phase C three remain present (subset) rather than pinning
exactly 3.
- brief_11b: 0021 joined the pre_frame_filler set as the verb-class auditor
evolved; it refuses, rows[0]=pre_frame_filler, and post-skip still refuses
(zero-lift verified). Canary 0050 untouched; wrong=0 unaffected (audit-only).
- cli_test_suites: assert the packs invocation against the live _TEST_SUITES
definition instead of a hardcoded file list (suite grew).
build_rescan's _FIRST_REFUSAL_RE only matched 'no admissible candidate for
...' and required the quote at end-of-string. PR #359 added the
'recognizer matched but produced no injection ... (category=X)' shape, so
extraction returned None and manufactured spurious shifts. Extend the regex
to both shapes and tolerate the trailing (category=...).
With extraction fixed, the live reader legitimately diverges from the v3
baseline on 10 cases (was 2 at the S.4 cut): 5 more first-refusals shifted
one sentence deeper (0019/0023/0025/0027/0047 — overrides added) and 3
refusals became correct admissions (0003/0021/0024 — the +3 that moved
serving 3->6). wrong stays 0; no admission lost. Tests retargeted to the
live counts and artifacts regenerated.
Follow-up: the v4 tests re-run the live reader, so they will drift again on
the next advance; a frozen v5 snapshot (or artifact-derived expectations)
would decouple them.
Phase 2.3: the first inference rule + the wrong=0 mechanism for proofs.
- generate/proof_chain/rules.py: evaluate_modus_ponens / evaluate_proof_conclusion.
Proof-layer dispatch (Option B) over proposition FORMULAS via the canonicalizer;
never touches check_admissibility/_resolve_dep_units (proofs have no units).
Disagreement rule = the select_self_verified twin: pool ALL admissible single-step
MP derivations, require a unique canonical key == declared conclusion. Pooling (not
filter-to-declared-first) is the soundness mechanism.
- generate/logic_canonical.py: parse_top_implication (+ _unparse) — recovers an
implication's syntactic antecedent/consequent (the ROBDD form doesn't preserve it).
- Closed typed-reason set; the corpus's finer labels consolidate (6 disagreement
refuse-labels -> conclusion_disagreement; 4 antecedent-flavor labels ->
unestablished_antecedent — same redundancy, same mechanism-makes-one-distinction
principle).
- Honesty boundary (exact scope): guarantees a unique conclusion among SINGLE-STEP MP
over the premises, NOT "uniquely entailed" by all strategies.
Cross-check: all 24 GPT-5.5 adversarial corpus cases agree on OUTCOME against the real
rule (no rule bug / no corpus outcome-misread); reasons consolidate as above.
Mutation: filter-to-declared-first makes DISAGREE-007/010 wrongly admit -> pooling
tests fail (pooling load-bearing).
Drive-by fix (cleanup-as-you-find): merged ADR-0204 ProofNode.__post_init__ was
dedented to module level -> all ProofNode validation was silently DEAD (smoke skips
the dedicated test file; the smoke != full-suite hazard). Re-indented; validation
restored.
Additive (math lane untouched). Full binding-graph surface green; smoke 67.
Phase 2.2, structure only (no inference rule — modus_ponens is 2.3). Translates a
Proof into a SemanticSymbolicBindingGraph; the ADR-0203 acyclicity guard + ADR-0132
referential integrity fire at construction.
- generate/proof_chain/model.py: the one canonical proof input shape (ProofNode/Proof
+ proof_from_premises desugaring of the corpus premises/conclusion shape).
- generate/proof_chain/builder.py: build_proof_graph — node→symbol+equation;
canonical_key→rhs_canonical, depends_on→dependencies, rule→operation_kind;
premises = empty-deps/op="premise"; unit_proof=PROOF_NO_UNIT, admissibility="pending";
conclusion tracked as conclusion_symbol_id.
- tests: 9 — valid DAG (PC-MP-001 desugared) + multistep construct; PC-CYCLE-001
refuses THROUGH the real builder (circular_dependency); canonical_key round-trips
byte-identical + equivalent formulas share rhs_canonical; admissibility-dispatch
confirmation; self/dangling refusals; out-of-regime node refuses. Mutation-verified
(drop dep-wiring -> cycle admitted -> test fails).
Admissibility dispatch confirmed graceful on proof operation_kinds: unknown kinds
-> AdmissibilityError(unknown_operation), unitless deps -> unit_unbound; NEVER
misroutes into _check_additive. Named 2.3 constraint: check_admissibility runs
_resolve_dep_units before dispatch, so modus_ponens must bypass unit-resolution.
Open items named for 2.3 (ADR-0204): conclusion typing (BoundUnknown revisit),
semantic_role="unknown" (closed vocab has no "proposition"), unit_proof sentinel.
Additive (first consumer; math lane untouched). Full binding-graph surface green;
smoke 67. Honesty boundary: through 2.3, sound over declared atoms, not grounded in input.
GPT-5.5's independent corpus caught that the canonicalizer refused quantified/
predicate input only by accident (tokenizer chokes on '.'), not by design — a
by-luck-not-by-design refusal the wrong=0 discipline rejects. ADR-0202 §3 names a
typed `out_of_decidable_regime` refusal; the keystone emitted a generic grammar error.
- logic_canonical.py: LogicRegimeError(LogicError) + OUT_OF_DECIDABLE_REGIME;
_reject_out_of_regime_text (quantifier words forall/exists + symbols ∀/∃, pre-scan)
and _reject_out_of_regime_tokens (predicate-application ATOM-then-LPAREN), run BEFORE
the generic grammar error. Refusal only — no predicate/FOL capability added.
- logic_equivalence.py: typed regime branch (before the generic LogicError branch).
- tests: 43 total (10 new) — OOR refuses with typed reason; equivalence path too;
genuine grammar errors stay plain LogicError (no over-fire); `not (P)` not mistaken
for predicate application. Mutation-verified by-design (neuter -> falls through to
generic grammar error).
- ADR-0201.1: additive sub-ADR of 0201 (not an amendment; sub-number preserves the
landed ADR-0203 forward refs + phase-2 plan numbering). Honesty boundary load-bearing.
Corpus now 22/22 (PC-OOR-001/002 agree on the principled reason). Full canonicalizer
suite green; smoke 67 passed. modus_ponens rule-reasons remain deferred to ADR-0205 (2.3).
proof_chain phase 2.1: the acyclicity guard at the shared binding-graph
construction boundary, before phase 2.2 wiring can build a cyclic-capable structure.
- generate/binding_graph/acyclicity.py: pure find_cycle(adjacency) detector
(deterministic three-colour DFS; isolated, no model import).
- model.py __post_init__: builds {lhs: deps} adjacency over equations and raises
BindingGraphError(circular_dependency ...) on a cycle. Runs on every binding
graph (math + future proof) — illegal states unrepresentable for all consumers.
- tests/test_binding_graph_acyclicity.py: 17 tests (pure checker + construction
enforcement); mutation-verified non-vacuous.
- ADR-0203: new ADDITIVE invariant referencing ADR-0132 (not an amendment —
preserves the why-added-later history).
Math-lane regression proof: the only producer (math adapter) is acyclic by
construction (fresh result symbol per op, deps point backward); full
binding-graph + admissibility surface 392 green; guard refuses no existing graph.
Honesty boundary (load-bearing): through phase 2.3, proof_chain is SOUND OVER
DECLARED ATOMS, not grounded in recognized input (grounding is phase 2.4).
full binding-graph/admissibility surface: 392 passed. smoke: 67 passed.
Reconcile every artifact that asserted the (since auto-reverted) mathematics_logic
expert promotion to the live machine state. Determinism proven intact (Week-1a):
the digest divergence is genuine single-source evidence-drift (GSM8K coverage probe
3/47 -> 4/46 via #310/#488), not a non-determinism defect. ADR-0120's fail-closed
property fired as designed; CORE revoked its own expert claim.
History keeps receipts; current-state reconciles to truth:
- Regenerate expert_claims_math_v1_signed.json -> promote_admitted:false,
reviewer_signature_matches:false, digest 02f6d3c8.
- reviewers.yaml math_expert_claims: quarantine note; entry kept (mismatch-refusal
keeps firing); intentionally NOT re-signed.
- ADR-0120-math-expert-ledger-flip: dated valid-at/auto-reverted header note.
- README: "next gate" narrative -> built-attempted-reverted; refresh stale count.
- docs/decisions/README: revert note + ADR-0200 index row.
- 3 fail-closed tests (2 files): "is-expert" -> fail-closed-revert assertions.
Were RED on main; now green (30 passed).
No eval gate, threshold, or safety boundary changed.