Archaeology (six worktree measurements) shows public_demo's stale pin
(7d8ba0db..., last set 2026-07-14) predates two commits that changed its
content deterministically: e7d116c9 (trace-hash format widening, benign)
and e0d1b475 (the register-axis regression's introduction, benign for
this lane at that point). But starting at 5a343b49 (T13's back-stamp,
2026-07-22) public_demo's own all_claims_supported case genuinely FAILS
and stays failing through 5224b5e0 (2026-07-24), fixed only as a side
effect of PR #110's unrelated register-axis restoration (f95ac26e) —
public_demo embeds the same register-tour scene demo_composition does.
The standing "public_demo red = the env-timeout flake, ignore it" memory
was true only for the runtime_under_budget case; it caused this real
all_claims_supported regression to hide for two days behind the same
label. Corrected the memory to triage by which case failed, not by lane
name. Re-pinned to da7fad65... (current main, 4/4 passing, verified
stable across three independent measurements) and regenerated CLAIMS.md.
Ratified by Shay 2026-07-24. Default flipped False -> True.
Evidence at ratification:
- 25 shape-bands, each holding SERVE at 720/720 wrong=0 on the SHA-sealed
ratified ledger (theta_SERVE=0.99, n >= 657 committed per band);
- deduction-serve lane 166/166 wrong=0 across six hand-authored splits;
- commit gate narrow by construction — a sentence-initial "therefore" IS an
argument, so the composer cannot claim turns that are not one;
- an unearned shape is still served DISCLOSED, never asserted.
Blast radius verified, not assumed: core test --suite deductive 268 passed
with the flag ON by default; a live default-config ChatRuntime decides real
English and existential arguments through grounding_source="deduction" while
ordinary turns still fall through to pack grounding; lane pins 10/11 match —
unchanged by the flip (the single miss is the pre-existing public_demo drift
documented for Tier S).
Rollback is flipping this back: flag-off remains byte-identical to pre-arc
dispatch and leaves no residue, pinned by
test_flag_off_preserves_pack_token_gloss_byte_identity.
curriculum_serving_enabled stays OFF — every curriculum band is unearned, and
the Sonnet brief now records the explicit bar for revisiting it.
The curriculum composer committed on question SHAPE — any `Does …?` text —
and then, being fail-closed, always returned a surface. With the flag on,
"Does the build pass?" would have been taken away from the rest of dispatch
and answered "I haven't been taught the or pass"; "Does anyone know the
time?" likewise. Found by checking the flag's readiness before recommending
ratification; never live, since the flag is default-off.
The asymmetry is the lesson: the deduction composer may commit on shape
because a sentence-initial "therefore" IS a signal of intent — text shaped
that way is an argument. `Does …?` is one of the most common ways to open
any English question and signals nothing. A fail-closed composer is only as
safe as its commit gate is narrow.
The gate is now `is_curriculum_question`: claim the turn only when the
question parses to three tokens AND its terms are vocabulary a served
subject actually teaches. Everything past the gate stays fail-closed —
including `ambiguous_reading` (both terms taught, two subjects claim them)
and `out_of_curriculum` (terms taught, relation unknown), which are real
curriculum questions with honest answers. The DECIDER is unchanged, so the
lane still records untaught-vocabulary probes as declined: the curriculum
path declines them AND does not speak for them.
[Verification]: tests/test_curriculum_serve.py 28 passed (+8: five ordinary
`Does …?` questions pass through untouched, three routable ones still
answered); core test --suite deductive 268 passed; curriculum lane 32/32
wrong=0 with its pinned report SHA unchanged.
Records the four completed Opus units and their commits, the server-side
push blocker (corrupt object behind PR #111's head ref blocks every push to
the repo), three measured corrections to the plan's ground truth (no biology
chain corpus; Phase 4.1 already built with null yield; no curriculum band can
earn a license from present data), the deterministic public_demo drift
evidence for S2, and a new S6 item quantifying the curriculum-volume ask.
Re-measured the frozen holdout_dev/v1 500: correct=5 wrong=0 refused=495 —
exactly the tune-3 + measure-2 increment 1 recorded, no drift, wrong=0 held.
Seeding-sentence injection is already in the tree (NEUTRAL_COUNT_VERBS in
math_roundtrip.py, consumed by the seed matcher and the comparison anchor);
its measured yield when it shipped was 0 conversions, verified four ways.
Extending the allowlist further is the move ADR-0251 and the overfit
inventory forbid — per-case pattern growth previously produced lift that
committed WRONG answers on the real exam.
Recommendation: mark Phase 4.1 complete-with-null-yield and re-point Phase 4
at the reader arc's own live recommendation — increment-2 CASE-FIRST on the
already-identified closest cases (0000/0001/0148/0082).
Phase 3.3 of the generalization arc. Estimation (ADR-0175), deduction
serving (ADR-0256) and curriculum serving (ADR-0262) had each written the
same seal -> ratify -> SHA-verify -> serve-gate machinery. core/ratified_ledger.py
now owns it and states the four rules once: only sealed practice writes;
tamper-evidence is structural (a load that cannot reproduce content_sha256
REFUSES); ceilings are not negotiable at the call site; absent evidence is
never a license.
Each capability keeps a thin adapter that names its artifact and preserves
its public API. One real difference is now declared rather than implied:
missing_ok distinguishes a ledger a capability SHIPS with (absence = broken
deployment, refuse) from one whose practice volume is still being built
(absence = nothing earned yet, serve disclosed) — curriculum serving is the
second kind today.
Safety property is byte-identity, asserted not assumed: re-sealing the
committed 25-band deduction ledger through the bridge reproduces it
byte-for-byte, so no artifact and no lane pin moves.
Effect: a new subject arena now needs a gold corpus and a band key, not a
re-implementation of ratification — which is what §3 meant by sequencing
the bridge ahead of the second subject.
[Verification]: tests/test_ratified_ledger_bridge.py 8 passed; core test
--suite deductive 252 passed; estimation/license test set 355 passed;
committed deduction ledger byte-identical after reseal.
Phase 2 of the generalization arc, implementing the plan's §4
curriculum-entailment gold contract. "Does force cause acceleration?" is
answered from the ratified physics chain corpus and nothing else; "Does
gravity cause acceleration?" is declined because gravity is in no pack
CORE has been taught; "Does force cause motion?" is unsettled even though
the curriculum contains both links, because nothing ratified says
causation composes.
Path (flag-gated, default off): closed question grammar -> subject routing
by ratified vocabulary -> family-scoped premise compilation from reviewed,
pack-resident chains -> the SAME argument bands (ADR-0260/0261) -> the
ROBDD engine. Zero subject-specific decision code: physics differs from
philosophy only in which rows load.
Epistemology enforced mechanically, not by convention:
- gold is a function of (curriculum, question); cases pin chain ids and
the runner FAILS a case whose pinned chain is absent or unratified;
- untaught => UNKNOWN, never "no" (open-world; silence is not denial);
- an independent oracle (own loader, ratification predicate, family table,
agreement rules, verdict rule) re-derives every gold — it disagreed once,
on "entropy reveals energy" vs "entropy causes energy", and the ORACLE
was the side that was wrong;
- anti-recall probes are a lane GUARD: a split without >=3 true-but-untaught
probes refuses to run.
Findings recorded rather than worked around (ADR-0262 §5):
- every curriculum band is UNEARNED and every answer is hedged. A band needs
n>=657 with a real outcome mix; physics teaches 7 causal + 9 modal
relations, so at most 16 questions in the subject can ever be ENTAILED.
A balanced band needs ~219 taught relations per subject×family — a ~25x
gap that only ratified curriculum content closes. Phase 2's blocker is
curriculum volume, not machinery.
- REFUTED is unreachable from present corpora (every chain is positive).
- there is NO biology domain-chain corpus; the biology OOD lane measures
fluency, not truth. The four subjects with ratified chains and mounted
packs are physics, mathematics_logic, systems_software,
philosophy_theology — the composer serves all four.
[Verification]: curriculum lane 32/32 wrong=0 with 5 anti-recall probes and
all three contract guards passing; tests/test_curriculum_serve.py 20 passed;
core test --suite deductive 252 passed; lane pinned as curriculum_serve_v1.
Completes the all/no/some square. `some` is read for the first time:
"All mammals are vertebrates. Some whales are mammals. Therefore some
whales are vertebrates." is decided entailed; "All unicorns are horned.
Therefore some unicorns are horned." is decided unsettled, with the
no-existential-import reading disclosed in the surface.
Mechanism (generate/proof_chain/exist.py): v5-VP's per-individual
lowering over a domain widened by one Skolem witness per existential
premise and — the load-bearing part — one ARBITRARY element per
existential conclusion, at which every universal still instantiates and
about which no premise asserts anything. Refuting an existential means
deriving a universal, and the arbitrary element is what makes that
derivation genuine instead of an assumption of domain closure: without
it "Rex is a wolf. Rex is not tame. Therefore some wolves are tame."
reads as REFUTED from a domain of one wolf. With it, UNKNOWN.
Four `en_exist_*` bands earned SERVE at 720x wrong=0 on the first arena
seal (25-band ledger); the 32-case hand-authored v2_exist split passed
32/32 first run; full lane 166/166 wrong=0 across six splits.
Also fixes a wrong-answer path this work uncovered in Band v1b
(ADR-0261 §5.1): `to_syllogism` FILTERED premises it could not express
out of the projection and answered from the remainder, so "Aristotle is
a philosopher. All philosophers are scholars. Therefore some scholars
are philosophers." lost its only witness and was served "that doesn't
follow". It now refuses — matching its propositional sibling
`to_deductive_logic`, which always has — and the argument falls through
to the bands that can hold a singular fact. The categorical band
re-earned SERVE 720/720 after the change; both examples are pinned as
regression cases.
Promotion: `ds-mem-0020` declined -> unknown (ADR-0258's existential
scope-out, now read; an anonymous witness never transfers to a named
individual, so UNKNOWN is its honest verdict, not entailed).
[Verification]: core test --suite deductive 232 passed; reader tests
33/33; register+surface tests 52 passed; arena 25 bands x 720 wrong=0
(all SERVE); lane 166/166 wrong=0; lane SHAs 9/10 match (public_demo
drift is pre-existing on the base commit and unrelated — evidence in
the Tier-S brief).
Generalization arc Phase 1.1 (Tier F, docs/plans/generalization-arc-2026-07-24.md):
"All philosophers teach. Socrates is a philosopher. Therefore Socrates
teaches." now reads and decides — the ADR-0258 §6.3 verb-predicate
scope-out, and the reading gate for Phase 2's subject serving.
generate/proof_chain/verb.py extends v3-MEM's per-individual lowering with
a second atom family in one shared space: membership atoms (v3-MEM's own
parsers reused verbatim) + verb atoms (individual, verb-lemma-group,
object-term). Verb universals instantiate as mem(i,C) -> [~]verb(i), so a
copula-minted membership fact discharges a verb rule. The ONE new semantic
identification is closed 3sg agreement (verb-specific irregular table +
the three regular suffix rules — deliberately NOT the noun table, which
would misroute "lives"/"leaves"). Scope-tight segmentation: single-token
name/class/verb/object shapes only; everything longer refuses typed
(tense_out_of_band joins the closed reason vocabulary). Rendered by
render_entailment_verb (member surface, UNKNOWN scoping extended to the
verb reading). Rides deduction_serving_enabled (default off); pure
widening — every previously-served argument byte-identical, no existing
lane case changed outcome.
Earned: 4 en_verb_* bands x 720 arena wrong=0 (first seal), ledger resealed
at 21 bands; corpus soundness asserted against the independent truth-table
oracle over each template's intended lowering (15,120 cases, INV-25).
Lane: v2_verb 28 hand-authored real cases 28/28 first run; full lane
134/134 wrong=0 across five splits. Surgical single-line pin update +
CLAIMS regen.
Stacked on feat/generalization-phase0 (merge that first — shared
verify_lane_shas.py / CLAIMS.md / generate_claims.py lane-ADR mapping).
[Verification]: verb reader tests 34/34; deduction battery 86/86
(surface +5, e2e +1, lane +1) in-worktree; arena seal + oracle
cross-check green; smoke + warmed_session via pre-push gate.
Plan of record for the generalization arc lands here
(docs/plans/generalization-arc-2026-07-24.md) with the risk-tiered
handoff pack (docs/handoff/generalization-2026-07/); Phase 0.1 executed.
Root-caused the three stale lane pins (docs/research/lane-drift-
investigation-2026-07-24.md):
- miner/curriculum: 5c69b741 (ADR-0244 §2.7) widened content digests
16→64 hex; intentional, pins were simply never updated. Re-pinned
surgically; SHAs triple-run deterministic.
- demo_composition: a REAL serving regression. #76's canonical-first
base precedence in resolve_surface, activated when #96 routed pipeline
turns through the resolver, served the pre-R6 canonical — the register
axis (ADR-0077 R6 substantive + ADR-0071 R4 decoration) was silently
stripped from every pipeline-served turn. T13's back-stamp then made
telemetry mirror the stripped bytes; register-tour claims went red and
the e2e tests pinning the axis (outside smoke) were failing on main.
Fix: response-first base precedence (served bytes = post-guard, post-R6,
post-R4) + new SurfaceResolution.hash_surface carrying the truth-path
canonical-precedence bytes through substrate overrides, folds, hedge,
logos-morph, and the speculative marker. compute_trace_hash folds
hash_surface, so the register axis varies the served surface ONLY and
trace_hash stays register-invariant (ADR-0069 inv C). _prior_surface
(correction binding) stays on the truth path. Dead FailureClass import
and _assessment_residual removed.
Also: generate_claims.py had raised on every run since the
deduction_serve_v1 pin landed without a _LANE_ADR entry — mapping added
(ADR-0256), CLAIMS.md regenerated, --check green.
[Verification]: register tour 6/6 claims (substantive-differs restored,
all_trace_hashes_identical held); 88 tests green in-worktree
(test_surface_resolution incl. 2 new fallback tests,
test_register_substantive_consumption incl. previously-red e2e tests,
test_grounded_open_hedge_arm, test_cognitive_turn_pipeline,
test_warmed_session_lane 10/10 T13 consistency); miner/curriculum lane
SHAs byte-identical across three runs; deduction_serve + deductive_logic
lane reports byte-identical to their unchanged pins; smoke suite via
pre-push gate.
Composes v2-EN's connective grammar (if/then, or, and, either) with
v3-MEM's singular-membership sentence reading over one shared
per-individual atom space, so "If Socrates is a man then Socrates is
mortal. Socrates is a man. Therefore Socrates is mortal." decides — the
exact gap ADR-0258 §6.1 reserved. Genuinely fuses the two mechanisms: a
bare universal's instantiated atom can unify (via the same closed
morphology relation) with a connective leaf's atom, deciding arguments
neither band alone could.
- generate/proof_chain/cond_member.py: new reader. A sentence is checked
for a connective token BEFORE the universal-lead check (so a stray
connective can never leak into an opaque name/class run); reuses v3-MEM's
own _parse_singular/_parse_universal verbatim (already negation-aware,
so no separate negation layer is needed, unlike v2-EN's opaque minter).
Four new shape-bands (fused/disjunctive/chain/conditional), each earning
its own SERVE license.
- chat/deduction_surface.py: new fallback tier tried strictly after v3-MEM
— pure widening, every previously-served argument stays byte-identical.
- evals/deduction_serve/practice/gold.py: 4 new synthetic template groups
(20 templates), reusing the v3-MEM case generator; intended formulas
cross-checked against the independent oracle (INV-25).
- evals/deduction_serve/v2_condmem/: 26 hand-authored real-English cases,
content-disjoint from the synthetic corpus.
- ds-mem-0024 promoted declined -> unknown (not entailed — the antecedent
is never asserted in that text; this is the first promotion in the
corpus that doesn't land on entailed).
- Ledger re-sealed to 17 bands; deduction_serve_v1 lane SHA re-pinned
(surgical single-line edit, not a blanket --update).
Verification: core test --suite deductive 160 passed (31 new reader
tests); practice arena 17 bands x 720 wrong=0, all SERVE-licensed; lane
splits v1 28/28, v2_en 26/26, v2_member 26/26, v2_condmem 26/26; smoke 180
passed; warmed_session 10 passed. Flag deduction_serving_enabled stays
default-off.
CORE now decides natural-English deductive arguments end-to-end:
'If it rains then the ground is wet. It rains. Therefore the ground
is wet.' -> ENTAILED, rendered over the user's own clauses. Both of
ADR-0256's documented boundary cases (multiword conditional, nested-
negation contraposition) are now decided; their lane gold updated
declined->entailed.
- generate/proof_chain/english.py (new): closed function-word grammar
over OPAQUE clause-atoms (minted ids); if/then, [either] or, and,
three negation forms (leading not / sentential / copular incl.
contractions); typed refusals; honesty caps. Soundness: positive
verdicts are substitution-closed; UNKNOWN is scoped + guarded
(quantifier-led, is-a membership, unnormalizable negation refuse
out of the opaque band).
- render_entailment_english: verdicts quoted back verbatim; UNKNOWN
phrasing explicitly scoped to the opaque reading.
- chat/deduction_surface.py: v2-EN fallback tier AFTER v1/v1b —
monotone widening, byte-identical on previously-served arguments.
- Four en_* shape-bands EARN SERVE via the ADR-0199 arena (720/band,
wrong=0, reliability 0.99087 >= 0.99); 9-band ledger re-sealed.
- evals/deduction_serve/v2_en: 26 hand-authored real-English cases
(content disjoint from the synthetic lexicon), wrong=0; report
re-pinned.
- realizer guard (ADR-0075): deduction surfaces exempt — quoted
templates are not slot-composed articulations (pack 'open'=VERB
was rejecting an honest quoted 'the door is not open'); pinned
cold+warm in e2e.
[Verification]: deductive 84 passed; smoke 180 passed; cognition 122
passed 1 skipped; arena 9x720 wrong=0 all SERVE; lanes v1 28/28,
v2_en 26/26.
Pins the arc's original goal against the ACTUAL serving spine (ChatRuntime,
the driver core chat constructs), not just the composer: a user asks a basic
logic question -> CORE decides -> an articulated, deterministic, telemetered
response comes back. Covers propositional + categorical, flag-off byte-identity
across both bands, honest reader-refusal on out-of-regime input (INV-34), and
the peripheral-systems wiring.
Peripheral systems verified (nothing to build, all automatic): deduction
turns are well-formed served turns (turn_log grows, _context.turn advances)
so ADR-0255 discovery-yield counts them; they carry grounding_source=
'deduction' on ChatResponse + TurnEvent and an observable DispatchAttempt;
they emit no discovery candidates (decided answer, not a would-have-grounded
gap) so they lift the yield denominator without inflating the numerator.
Scope-out doc (docs/research/deduction-serve-arc-completion-and-scope-outs-
2026-07-23.md) records the whole arc + everything deliberately deferred with
rationale: multi-word English reader relaxation (risky, shared reader, its
own pass), multi-step proof recap (Phase-6 core_logos), accrue_realized_
knowledge default, ADR-0246 s3.7 (blocked on research evidence not wiring),
Tier-2 (waits for field reader), Hamiltonian compiler (eval-side), and the
GroundingSource enum registration.
New: tests/test_deduction_serve_e2e.py (5 tests through the real REPL path).
[Verification]: core test --suite deductive 50 passed; smoke 180 passed;
cognition 122 passed/1 skipped.
core chat now decides Aristotelian syllogisms end-to-end -- the marquee
'basic logical work' widening. Closes Phase 0's fork: the ONLY categorical
decider was evals/syllogism/oracle.py (the sealed independence oracle serving
must not import; INV-25). This ships a production decider.
generate/proof_chain/categorical.py lowers a categorical argument to the
propositional regime (one Boolean atom per term-membership profile) and rides
the already-verified ROBDD engine -- no new decision procedure, soundness
inherits from the flagship. Sound AND complete for the modern/Boolean reading
(Darapti + existential-import-only forms correctly INVALID), with no reliance
on a lucky domain size. Independent of the eval oracle by mechanism (ROBDD-
over-profiles vs brute-force finite-model enumeration); proven by case-for-case
agreement with it across the whole syllogism gold lane.
New: generate/proof_chain/categorical.py, render_syllogism (deterministic
valid/invalid/inconsistent templates), CATEGORICAL shape-band, arena
categorical band (4 valid forms + 2 invalid, by-construction gold cross-checked
vs the independent syllogism oracle), tests/test_categorical_decider.py (8
tests incl. the independence-by-agreement proof).
Changed: chat/deduction_surface.py (categorical branch, license-gated via a
shared _license_gate helper; propositional path byte-identical), arena
DeductionSolver dual-path in lock-step with the composer, re-sealed ledger (5
bands, categorical earns SERVE at 0.99087 wrong=0), Phase-2 lane decide()
mirrors the dual path with the 3 categorical cases reclassified to their true
valid/invalid verdicts + 1 invalid case (n 27->28), report+SHA regenerated.
Honest wrinkles: irregular plurals ('fish') decline with unknown_morphology
(a reader limit, correctly not a wrong); surfaces read in singularized terms
('all whale are animal') -- the exact ids the engine reasoned over, no cosmetic
re-pluralization that could drift from the decision.
[Verification]: smoke 180 passed; cognition 122 passed/1 skipped; core test
--suite deductive 45 passed; test_categorical_decider 8 passed; practice
runner 5 bands all SERVE wrong=0; deduction_serve lane SHA regenerates to the
committed pin.
Deduction serving stops being a bare flag and becomes an EARNED capability
governed by the ADR-0175 reliability gate over the ADR-0199 learning arena
-- the arena's second concrete instance (GSM8K math is the first) and the
reliability substrate's first non-estimation serving consumer, a concrete
dent in that zone's standing 'designed, not wired' critique.
Non-circular thesis: the ROBDD engine is sound+complete (never wrong on the
problem it's handed), so the license does NOT certify it. It certifies the
full pipeline (reader -> projector -> engine) per argument shape -- the
FALLIBLE part is the template reader, which can misparse an argument and
hand the engine the wrong problem. The arena catches that as a 'wrong' by
comparing the pipeline's committed outcome to by-construction gold.
New: generate/proof_chain/shape.py (4 exhaustive structural shape-bands, the
capability axis, shared by arena + serving), evals/deduction_serve/practice/
(the deduction arena instance: deterministic synthetic corpus, by-construction
gold independent of the reader per ADR-0199 L-2, cross-checked against the
INDEPENDENT truth-table oracle before sealing), chat/data/deduction_serve_
ledger.json (committed SHA-sealed ledger, 4 bands x 720 correct/0 wrong),
chat/deduction_serve_license.py (tamper-evident serving reader, mirrors
estimation_license.py), tests/test_deduction_serve_license.py (13 tests),
docs/adr/ADR-0256 (+ resolves the ADR-0206 numbering collision in doc form).
Changed: chat/deduction_surface.py (composer consults the license: earned
band -> authoritative Phase-1 surface; unearned/stripped ledger -> same sound
answer served DISCLOSED/hedged -- authority now rests on committed evidence,
not a boolean), core/config.py (flag docstring: enables an EARNED path),
core/cli_test.py (deductive suite runs the license test).
All four structural bands earn SERVE at reliability 0.99087 (>= theta_SERVE
0.99) with wrong=0, so Phase-1 behavior is preserved byte-for-byte; the
gate's teeth are proven by injecting an empty ledger (the same sound answer
degrades to a disclosed hedge). Did NOT reuse the ADR-0206 govern_response
bridge (its STRICT/APPROXIMATE 'widen-past-strict' semantics are the opposite
shape from deduction's always-sound answer); did NOT rewrite report.json's
stale adr field (SHA-pinned bytes, documented in ADR-0256 s2a instead).
[Verification]: smoke 180 passed; cognition 122 passed/1 skipped;
core test --suite deductive 38 passed; architectural_invariants 75 passed;
practice runner 4 bands all SERVE wrong=0.
New evals/deduction_serve/ lane scores the PRODUCTION serving decider
(the exact comprehend -> to_deductive_logic -> evaluate_entailment_with_trace
pipeline chat/deduction_surface.py runs) end-to-end from raw text --
distinct from evals/deductive_logic (bare engine vs formula strings) and
evals/comprehension/propositional_runner.py (reader fidelity vs the
independent oracle, not the production engine). This is the only lane
proving the capability core chat actually serves.
27 hand-authored cases (gold computed by independent logical reasoning,
not copied from a first engine run), 4 classes: entailed/refuted/unknown/
declined. 27/27 correct, wrong=0. Wired into core test --suite deductive
(tests/test_deduction_serve_lane.py) and SHA-pinned in
scripts/verify_lane_shas.py (deduction_serve_v1).
Honesty check during authoring: a case intended as 'entailed'
(contraposition, 'Therefore if not q then not p') actually declined --
tracing why found a genuine reader-grammar boundary (negation cannot
nest inside an if/then clause; generate/meaning_graph/reader.py's _chunk
rejects it). Reclassified to declined/out_of_band_nested_negation
(documented in contract.md) rather than forcing an artificial pass, and
added a replacement entailed case (three-hop chain) to keep coverage.
Out-of-scope finding (documented, not fixed here): running
scripts/verify_lane_shas.py --update to compute this lane's pin also
re-executed every OTHER registered lane and surfaced two pre-existing,
unrelated problems on main -- miner_loop_closure/curriculum_loop_closure/
demo_composition regenerate non-deterministic content IDs (their
committed pins don't reproduce even on a clean checkout), and public_demo
errors outright (matches the known env-timeout flake in project memory).
Reverted all four lanes' results/*.json and PINNED_SHAS entries to their
original committed values -- this PR's only PINNED_SHAS change is the new
deduction_serve_v1 entry.
[Verification]: smoke 180 passed; cognition 122 passed/1 skipped;
core test --suite deductive 25 passed; evals.deduction_serve.runner
27/27 wrong=0; pinned SHA independently re-verified against the
committed report.json bytes.
Wires the verified ROBDD entailment engine (generate/proof_chain,
716/716 wrong=0) into core chat serving. A propositional argument
('P1. P2. ... Therefore C.') is now comprehended, projected, and
decided end-to-end -- the first real working basic-logic workflow:
user question -> CORE decides -> articulated deterministic answer.
New: chat/deduction_surface.py (DEDUCTION composer, sibling to the
existing oov/narrative/example composer pattern), generate/proof_chain/
render.py (deterministic EntailmentTrace -> surface templates, no LLM),
tests/test_deduction_surface.py (15 tests incl. full propositional gold
corpus decided end-to-end across a multi-turn session, wrong=0).
Changed: core/config.py (deduction_serving_enabled flag, default off),
generate/intent.py (IntentTag.DEDUCTION, observability-only -- the core
classify_intent rule table is untouched so flag-off stays byte-identical),
chat/runtime.py (one flag-gated block in the existing pack/teaching/
partial/oov dispatcher).
Design notes (full detail in docs/research/deduction-serve-arc-phase1-
turn-spine-2026-07-23.md): the deduction check runs BEFORE generic intent
classification (never touches classify_intent's rule table) and BEFORE
the empty-vault gate (a live multi-turn probe caught turn-12 silently
falling through once the vault warmed -- pack/teaching/oov are cold-start-
only by design, deduction serving isn't). grounding_source='deduction' is
NOT registered in the closed, Workbench-coupled GroundingSource Literal
(cross-stack TS contract, inert for this arc's core-chat-only consumer,
documented as a deferred follow-up). Band v1 stays propositional-only --
categorical/syllogism arguments commit but honestly decline as
out-of-band; evals.syllogism.oracle is never imported (INV-25 intact).
[Verification]: smoke 180 passed; cognition 122 passed/1 skipped;
test_deduction_surface.py 15 passed; test_dispatch_trace.py +
test_oov_surface.py 26 passed; test_intent*.py 145 passed.
Pins the pre-arc baseline for the basic-logic end-to-end workflow
(user logic question -> comprehend -> decide -> articulate).
Central finding, proven with a runnable probe: every organ the workflow
needs already exists and is verified wrong=0, but none are wired to each
other on the serving path. comprehend()+to_deductive_logic/to_syllogism
decide 'If p then q. p. Therefore q.' -> entailed and a Barbara syllogism
-> valid=True in isolation, while ChatRuntime.chat() on the SAME text
returns a pack token-gloss surface (grounding_source='pack',
refusal_reason='') — the deductive engine is bypassed entirely.
Baseline (all wrong=0): 7 comprehension lanes green on curated gold;
deductive engine 716/716 (dev 200 / holdout 500 / external 16);
proofwriter-owa 9 correct / 10 refused / 0 coverage-gaps; smoke 180 passed.
Band v1 = propositional + syllogism projectors. Input boundary defined
precisely: single-token propositional atoms in 'P1. P2. Therefore C.'
form, and single-word-term categorical syllogisms (declarative AND
interrogative). Multi-word English propositions ('the ground is wet')
refuse via reader _RESERVED guard -> deferred to Phase 4 widening.
Artifacts:
- docs/research/deduction-serve-arc-phase0-baseline-2026-07-23.md
- docs/research/deduction-serve-phase0-baseline.json (pinned numbers)
[Verification]: Smoke suite passed locally (133.47s, 180 passed)
2026-07-23 directive: instrument candidates-proposed-per-served-turn on
clean, post-reset traffic. Adds an additive-optional turn_count_baseline
manifest field (engine_state) stamping the turn_count at the last clean
reset of the discovery-candidate ledger, a pure compute_discovery_yield
function (teaching/discovery_yield.py), and a read-only `core teaching
discovery-yield` CLI surface. Fails closed (returns None / exits 1) when
no baseline has been stamped rather than fabricating a denominator.
scripts/ops/stamp_discovery_yield_baseline_20260723.py stamps the T11
reset epoch into the live store, mirroring reset_candidate_corpus_t11's
atomic generation-dir pattern. Dry-run verified against a throwaway copy
of the live store's gen-21703; not yet executed against the real store
(see PR body).
Weekly-audit 2026-07-22, T13 decision (2) ruling (Shay). The Shadow Coherence
Gate hard-refused open-geometry surfaces even when the surface was pack-grounded
and the openness was a geometric-coherence residual the pack never claimed to
certify — a false refusal (the warmed "What is doubt?" case:
"To doubt means to think maybe not. pack-grounded (en_core_meta_v1)." was
replaced by "I cannot certify an answer: ... contract is open (goldtether_residual).").
Fix the reading, not the question: route open-geometry-but-pack-grounded
surfaces to a hedge arm (authoritative=False), discriminated purely by grounding
provenance (structural), never by question type. The query-type classifier
bypass was rejected as a fail-open cue-table (ADR-0252 / INV-34).
- core/cognition/surface_resolution.py: the decision lives INSIDE the gate.
Predicate: pack_grounded (grounding_provenance in {pack, teaching}) AND every
open token in {versor_condition, goldtether_residual}. The ¬hazard clause is
enforced structurally by that residual allowlist — any unrecognized open token
(where a genuine safety/harm/imperative hazard lands), non-pack grounding, or a
None assessment falls through to the unchanged fail-closed refusal. New
authority tag grounded_open_hedge; hedged=True; walk/compose folds suppressed.
- core/cognition/pipeline.py: thread grounding provenance into resolve_surface
(read once at the gate seam, reused for the OOV telemetry).
- docs/adr/ADR-0254: the serving-physics decision + fail-closed argument.
- tests/test_grounded_open_hedge_arm.py: RED->GREEN unit suite + real-data
warmed-session integration.
Genuine safety/harm hazards are unaffected: they ride separate axes
(SafetyVerdict refusal, logos-morph override) that supersede this surface.
[Verification]: hedge suite 15 passed; warmed_session lane pin + architectural
invariants 85 passed (155.8s); GSM8K wrong=0 preserved (16 passed, 3 skipped);
smoke 180 passed (131.65s), exit 0. worktree core-wt-hedge @ base 19847f90.
Weekly-audit 2026-07-22 closeout — T13 decision (3) CI gate + T11 execution.
- scripts/hooks/pre-push + install.sh: a core.hooksPath pre-push gate that runs
the smoke suite PLUS the warmed_session consistency lane pin — the T13
telemetry-consistency regression class that smoke does NOT cover. The full
~12k fast-lane stays async CI by design; the gate is deliberately targeted so
it blocks the regression class without gridlocking pushes. Fail-closed on
empty stdin; skips deletion-only pushes; emergency bypass git push --no-verify.
- scripts/ops/reset_candidate_corpus_t11_20260722.py: provenance record of the
T11 live-store reset. The 465 discovery candidates predated the #100
isolation fix and carried no per-record test/prod discriminator, so a surgical
quarantine was impossible; ruling was to clear (not asterisk). Executed as an
ADR-0219 atomic generation reset (empty candidate ledger, keep=1) — candidates
465 -> 0, turn_count 14990 and identity lineage preserved, tainted generations
GC'd. Data lives in gitignored engine_state/; this script is the audit trail.
- AGENTS.md: document the automated pre-push gate under the Local-First CI
Validation Protocol.
- weekly-audit ledger: T10 merged (#100), T11 ruled+executed, T13 (2) hedge-arm
ruling + (3) pre-push gate recorded.
[Verification]: smoke 180 passed (132.9s) + warmed_session lane 10 passed
(140.5s), exit 0 — worktree core-wt-infra @ base 9a428d84.
CognitiveTurnPipeline emits TurnEvent inside runtime.chat() and only THEN
applies the #96 fail-closed surface resolution (resolve_surface) and the #97
logos-morph override — so an overridden turn's telemetry recorded a surface
the user never saw. warmed_session_consistency telemetry_consistency_rate was
0.9444 on main (fast-lane red since #96e0d1b475): an audit/replay trust break
(Absolute Provenance).
Add ChatRuntime.finalize_turn_surface, a sibling to finalize_turn_trace_hash:
the pipeline owns the final surface decision, so it back-stamps the resolved
surface onto turn_log[-1] immediately after the trace-hash back-stamp.
trace_hash byte-identity is preserved — it folds the pre-decoration surface,
not the served surface.
Scope: telemetry-consistency half of T13 only.
- Decision (2), the goldtether over-refusal, is orthogonal to the red and
OPEN pending ruling (ledger) — recommend AGAINST a query-type cue bypass
(fail-open / ADR-0252-retired cue-table pattern); prefer routing
open-geometry-but-pack-grounded surfaces to the existing hedge arm.
- Durable-sink deferral tracked as R7.
telemetry_consistency_rate 0.9444 -> 1.0 (10/10 warmed_session tests green).
[Verification]: uv run core test --suite smoke -q in core-wt-t13serve @ HEAD — 180 passed in 133.17s, exit 0; warmed_session lane 10/10 green.
Wire observed-HE morph constraint into CognitiveTurn answer path and
unify teaching store with the same pure decision function used by the
four-arm ablation. Typed LogosConstraint IR carries rule/morph/pack/span
provenance without free meaning dicts. Executable mode abstains on
plural-vs-singular exclusivity; metadata remains bit-identical to
canonical; adversarial OOV/missing fails closed. Residual dual-system
debt documented honestly (cue-table linguistic_pipeline still parallel).
Reconcile Master Blueprint ADR-0240–0253 titles with the live Accepted
registry without renumbering history. Claim ADR-0253 for the freeze policy
itself; reserve 0254–0261 for Blueprint gaps. Document packs/data vs
packs/he|grc dual boundary and pin serve isolation in smoke/packs suites.
[Verification]: Smoke 180 passed (includes 4 dual-pack boundary pins)
The inverted-seed P2 regex treated optional "times" as optional, so
additive "8 more" on multi-entity zoo case 0361 extracted as pure S1 and
the three-gate corridor emitted 1.125 vs gold 114.0 (wrong≠0).
- Require explicit multiplicative surface (times as many / twice the …)
- Fail-closed purity gate: multi-clause markers, multi-numeric, multi-mult
- Regression tests for 0361 and bare "N more"
- measure --mode full-holdout-wrong0 over all 500 holdout cases
[Verification]: full-holdout emit_ok=9 wrong=0 refused=491; unit 37 passed
Increment 2 for ADR-0252 Track B. Extends Increment 1's structure-mapping
slice with pure-family canonicals S2–S4, overlapping-waves selector, and a
structure-mapping-owned pure-S1 text extract that recovers four real
holdout cases the serving reader misses (0148, 0228, 0234, 0441).
Bar results (command-backed via scripts/measure_trackb_inc2.py):
- generalization ratio S1 = 9.0 (9 holdout cases / 1 template)
- coverage gain organ 5 → trackb 9, wrong=0
- selector routes S1/S2/S3; refuses empty; surface≠structure
Off-serving: no organ retirement, serving reader untouched. S2–S4 holdout
ratios are 0 (parser frontier). S3/S4 emit refuses at multi-register scope
without weakening the three-gate wrong=0 path.
[Verification]: Smoke suite passed locally (~132s, 176 passed);
trackb unit tests 35 passed; measure_trackb_inc2 all modes green.
Mapper required only that total include {a,b}; rebuild-from-binding then
dropped third entities and could emit a certified wrong answer (e.g. 15
instead of 115). Require exact total part-set {a,b}, refuse any contain
outside reference a, and agree with original-graph classical solve before
emit. Adds multi-entity refuse unit tests.
ADR-0252 §5 geometric SME is NO-GO; this is Track B Increment 1.
Adds role-predicate conversion from MathProblemGraph, S1 canonical skeleton,
blind symbolic mapper (match/refuse + binding), and solve via classical
verify plus multi-register certificate. Research report and holdout measures
included. Serving reader unchanged; no S2–S4 generalization.
Trace every module/class the six retired paradigm documents introduced or
proposed, plus a general zero-importer sweep of generate/, core/, evals/.
Every candidate is one of: never built, already removed in a prior pass, or
live (imported by the serving reader / a protected dependency chain, and/or
test-covered). No code removed; no behavior change.
Baseline == final (no code touched):
smoke: 176 passed
full: 13163 passed, 111 skipped, 1 xfailed, 0 failed
holdout_dev v1: correct=5 wrong=0 refused=495 (n=500)
Archive the six competing, unratified problem-solving-paradigm formulations
(semantic-substrate master plan, state-transition blueprint, binding-graph
proposal, capability roadmap v2, eight sprint lookbacks, and ADR-0174's
deprecated dispatch prescription) to docs/paradigm-archive/, each prefixed
with a SUPERSEDED header. ADR-0164 and ADR-0174 stay in place (Accepted,
permanent record) with a Superseded-by: ADR-0252 line added. ADR-0252 itself
is a decision record only — it records the retirement and reserves the
governing paradigm content for a separate, human-led step.
No code changes; no behavior changes.
Records the reader-arc recalibration: halt bespoke-per-case regex work,
verified clean-base reset to main@854ce023, and eradication of
feat/reader-inc2-caseband's (PR #80) debt (loosened _token_in, empty
proof file, bundled provenance) without merging that branch.
Preserves the genuinely-general knowledge before the branch is retired:
the has_numeric_token multiplier-blindness bug (root cause + minimal fix
idea, deliberately not landed) and case 0148's correct-for-the-right-reason
decomposition, both independently re-verified against source rather than
copied from the branch's own claims (which included a stale narrative
about a graph-dump proof file that was actually committed empty).
Adds a file:line-cited overfit inventory (31 bespoke/single-case surface
patterns) as inventory only -- nothing removed, the existing regex reader
keeps serving unmodified.
ADR-0251 proposes (not builds) a geometric surface->canonical
normalization spike using existing off-serving primitives
(conformal_procrustes, VocabManifold, the cognition pipeline's Phase C
anti-unification telemetry), wrong=0-gated against holdout_dev/v1, with
Smith-chart algebra and any Fibonacci "dimensional cascade" explicitly
disclaimed. Awaiting ruling -- no implementation started.
Records the increment-1 outcome straight (band-solve=0, ~30-band bet
falsified; foundations = banked byproduct not success) + the minimal-band
measurement: no small band converts; taxonomy not at bedrock (GSM8K statements
are per-sentence multi-capability); multi-compound highest-leverage (70/106
tractable needed-sets) but insufficient alone; q:complex biggest intractable
wall (~35 cases). Increment-2 recommendation = CASE-FIRST (target specific
closest cases, build their exact end-to-end needs) not capability-first, since
the taxonomy fragments. Foundations verified full-500 wrong=0, smoke 176.
The two reusable foundations of increment 1, both verified wrong=0:
- NEUTRAL_COUNT_VERBS (math_roundtrip): the shared positive-polarity ALLOWLIST
(production/acquisition/possession-count verbs), fail-closed. Extends ADD_VERBS
with curated neutral production verbs (score/write/teach/plant/harvest/produce/
draw/sing). Depletion/transfer (SUBTRACT/TRANSFER_VERBS) excluded by positive
determination — 'Alice lost twice as many' refuses, not via a blocklist.
- Seed reader: discrete_count matcher acquisition path aligned to the allowlist
(made/baked/grew/scored/wrote/taught now inject; depletion refuses).
- Comparison reader: _comparison_anchor_verb() allowlist extended to the same
NEUTRAL_COUNT_VERBS (keeps fail-closed; captures scored/caught/wrote frames).
Verified: production injects, depletion refuses; 71 pinned comparative tests
pass (polarity guards now via allowlist); tune wrong=0; smoke 176 green.
FINDING (measured 4 ways: yield harness, band map, band oracle, capability
histogram): band-1 as scoped {seed + forward-comparison + q:simple/summation}
converts 0 tune cases. Its otherwise-in-band cases STRAND on multi-compound (25)
and compare-additive (17) — capabilities outside the ~30 band. The conjunction
recurs at sub-capability granularity; the band-map taxonomy was too coarse.
Foundations validated + reuse-ready (done-when part 2); band-solve>0 (part 1)
needs the band to grow to +compare-additive(+multi-compound) — a scope/done-when
ruling for Josh. No merge until ruled.
Design-first plan, ruling before any build (same gate as #76). Band =
foundational spine core: seed-simple + comparison + question-arithmetic
(simple/summation/difference), on the shared #78 polarity substrate.
Bakes in Josh's four refinements: (1) deliberate ~30 smaller-band rationale
(first end-to-end reader conversion ever + two reusable foundations, zero new
hard capabilities beyond #78); (2) done-when reset to band-solve>0 wrong=0 +
foundations validated, ~40 bar cumulative across increments 1-3; (3) haircut
factor (real/upper-bound) as a named output; (4) TWO wrong=0 direction drivers
— #78 statement polarity AND q:difference question direction, both positively
determined + fail-closed. Grounds design on what EXISTS (question layer partly
built: summation works, q:difference partial). Schedules q:complex decomposition
next (may be corridor-tractable, could move the ~48% ceiling).
Reverts the c3aed13b parser change (frame-anchored comparative regexes +
verb-free connector + _COMPARE_POLARITY_BLOCK + 'as much'); the reader stays
on the _comparison_anchor_verb() whitelist (fail-closed). Increment 1 ships
the compiler tier + the funnel finding only.
Three reasons on record: (1) frame-anchoring converts 0 of 500 official cases
— zero measured benefit today; (2) its consumer is rate-frame admission, a
later increment — the next increment (seeding-sentence injection) is a shared
layer that doesn't touch it; (3) the polarity blocklist is fail-OPEN — an
unenumerated depletion verb in a complete frame is a coherent-but-inverted
graph = a wrong on the run-once sealed arbiter (round-trip guards parse
consistency, not gold-correctness), a trade the fail-closed doctrine never
takes for zero gain.
Verified before revert-merge: with the whitelist reader, the 5 real compare
parses are non-divergent — test_real_compare_cases_solved_wrong_zero still
sees 5, solves 5, wrong 0 (corridor real-reach 0->5/500 preserved).
Frame-anchoring recorded as an OPEN DESIGN TASK owned by the rate-frame
increment: frame + POSITIVE polarity determination (neutral-polarity allowlist
or small polarity classifier), not assume-safe-unless-blocklisted. Detail in
docs/research/compare-increment-funnel-2026-07-18.md.
Increment 1 reader half. Re-anchor the two multiplicative-comparison frame
regexes on the closed-class frame ('<factor> as many/much <unit> as <REF>')
instead of the _comparison_anchor_verb() whitelist: the verb slot becomes a
verb-free bounded connector, 'as much' joins 'as many'. Admission is now
open-class for safe verbs (scored/wrote/caught/taught/…), gated by the frame.
One closed class is NOT frame-neutral — polarity-inverting/depletion verbs
(lose/spend/give/sell/win/donate/pay/eat/drop/lend): in a compare frame they
make the compared quantity a loss, so actor=factor×reference reads backwards
→ wrong>0. _COMPARE_POLARITY_BLOCK excludes exactly that set (the sanctioned
fallback; the frame is the gate for every other verb). Pins the wrong=0
guards in test_adr_0131_G2a + test_recognizer_comparative_inject.
Develop-on-tune / measure-once held. Footprint over 500 official cases: tune
byte-identical (0 divergences, wrong=0, PARSED=3); measure 1 divergence
(0441 'Zig wrote four times as many books as Flo') which advances
frame-refusal -> question-refusal (sanctioned delta) but does not solve — the
binding wall is the shared summation-question layer. X=15 measured delta=0.
Ruling per the funnel-conditioned escalation rule: mass is in shared layers,
so the next increment is shared-layer (summation-question reader), not a
practice escalation and not more compare frames. wrong=0 held on tune (261),
measure (239), smoke (176).
Pre-registration before the shared-layer edit (PR ruling), all in the record:
1. Refusal-histogram baseline (tune, n=261): 'no injection' = 196/261 (75%) —
a SHARED bottleneck across every family, not a compare defect; 48 statement,
9 question, 3 PARSED (compare). Pinned; assert stable-or-explained after —
only sanctioned delta = compare 'no injection' -> PARSED, disclosed.
2. Funnel-conditioned escalation rule (pre-committed): X=15 miss read via the
funnel — compare-specific mass -> practice ruling; shared-layer mass ->
shared-layer design increment (NOT practice). Load-bearing: practice cannot
learn past a deterministic wall (better candidates still cross the same
pipeline), so shared bottlenecks are design work regardless of X.
3. Reframe recorded: compare is the first slice of a larger reader project
(reader parses ~1% of real GSM8K; 'no injection' 75% is a shared wall).
Not scope creep — the arc seeing its true size.