Closes the Opus share of the curriculum-license-loop arc. Three deliverables,
all docs -- no production code.
1. E1-R7-ASSERTION-SPEC.md -- the Opus half of Phase E1. The code move was
already specified by runtime_contracts.md ("defer the single sink emission
to the pipeline serve boundary"); what to ASSERT was not, and the default
sink is None, so nothing exercises the path. Eight invariants (I1-I8) plus
three mechanism rulings (M1-M3), each with the silent failure it prevents.
The three mechanism rulings are the value. M1: stage a turn_log INDEX, not
a captured TurnEvent -- a captured copy is the pre-override snapshot, i.e.
the defect with extra steps. M2: attach_telemetry_sink must NOT flush a
staged turn event, which is the OPPOSITE of what it correctly does for
_pending_reboot_payload; the reboot payload is final when buffered, a turn
event is mid-flight, and flushing on attach would silently restore R7. M3:
the runtime cannot detect whether a pipeline will run (the pipeline wraps
the runtime), so deferral must be explicit opt-in and the no-pipeline path
must stay byte-identical.
All ten source citations verified against HEAD.
2. Plan of record AMENDED with four falsified premises from Phases A/B, with
the original text preserved:
- physics·modal was the wrong first target -- its ceiling is 480 against a
657 threshold, and 8 of 11 bands cannot reach 657 at all. Vocabulary is
the constraint, not chain count. Retarget: philosophy_theology·modal.
- MAX_PREMISE_SENTENCES=16 caps a family at 16 chains, so ADR-0262 §5.1's
own remedy (~219 relations) collapses the band at row 17. No curriculum
band can earn SERVE until premise compilation is query-scoped. New
implementation phase precedes ALL content work.
- authoring a negative today serves a confident wrong "Yes", not UNKNOWN.
- 21 of 25 ratified deduction bands are short on distinct evidence.
3. DIVISION-OF-WORK updated for the handoff: §0 status table with branch names
and merge order, the revised order (E2 -> R5-R7 -> C -> D -> E1(code) ->
parity), the new R5-R7 unit specified to gate-level detail, and a standing
"do not fix" on the deduction ledger exposure.
One correction to my own earlier text: §4 told Phase C to mirror the
deduction producer "exactly". That would replicate the padding -- a flat
CASES_PER_BAND=720 cycling a 28-instance space. Phase C now points at the
`estimation` producer instead (660 distinct, zero repeats), and must
register in AUDIT_SOURCES.
[Verification]: uv sync --locked on canonical CPython 3.12.13; in-worktree
smoke 555 passed, deductive 285 passed (this branch is off main and carries
neither ADR-0264 nor the Phase B tests, so 555 is the correct baseline here,
not the 569 that holds on the stacked A+B branches).
Two prior arcs closed on "the binding constraint is ratified curriculum volume,
not machinery." Verified against code 2026-07-25: the machinery half is wrong in
a load-bearing way, and the correction is the reason this arc exists.
evals/curriculum_serve/practice/ does not exist. seal_ledger exists only for
deduction (evals/deduction_serve/practice/runner.py:96), yet
chat/curriculum_serve_license.py's own docstring names
evals.curriculum_serve.practice.runner.seal_ledger as "the only writer" of the
artifact it reads -- that writer was never built, and
chat/data/curriculum_serve_ledger.json does not exist. And the ceremony stops
short: RatificationReceipt.pending_stages = (arena_queue_entry, ledger_reseal).
So the pipeline is author -> ratify -> ??? -> ??? -> license -> serve. Authoring
curriculum today cannot earn a license at ANY volume. Content is necessary and
currently insufficient. The build is still modest, because oracle.py is already
an independent decision procedure usable as the gold tether and ADR-0263 already
extracted the sealers for exactly a third instance.
Two measured facts shape the content work. `refuted` has NEVER been produced --
the physics lane gold is entailed 14 / unknown 12 / declined 6, and no corpus row
can express it. And bands key on (domain, operator_family), so negative content
must live INSIDE operator_family "modal" with a row-level polarity marker; a
separate modal_negative family would create a new band at n=0 instead of adding
refuted volume to the target band -- the easiest available way to waste the whole
authoring effort.
DIVISION-OF-WORK.md records the tiering criterion and why it is not difficulty.
The evidence does not support a prestige split: in the same 48h window Sonnet 5
found the DEFAULT_SINK bug that wrote outside the repository and did the
six-worktree public_demo archaeology, while Opus 5 mis-diagnosed the CGA hot path
by multiplying a microbenchmark by a call count. Model tier was not the variable;
verification discipline was.
The split is therefore: would an error here be caught by a gate, or is it silent?
Two units are silent-failure BECAUSE THEY ARE THE GATE -- the negative-curriculum
epistemology (wrong => corpus encodes falsehoods and wrong=0 still passes, since
lane gold and the oracle both read the same rows) and the volume-honesty invariant
(wrong => conservative_floor cannot tell 657 independent facts from 16 asked 41
times, so the ledger clears theta=0.99 having earned nothing). Those get Opus.
Everything loud goes to the cheapest competent tier.
Also records the ordering correction: the epistemology ADR must precede the
generator, or the generator gets built twice.
[Verification]: docs-only; no code paths touched. Every referenced path confirmed
to exist and all five line citations confirmed accurate at HEAD (gold.py:1163,
curriculum_serve/runner.py:71, discovery_yield.py:67-70, entail.py:125,
curriculum_surface.py:184). Pre-push gate runs on push.
Nine ADRs (0254, 0256-0263) were merged into main and left stamped Proposed.
Two carried an explicit ratify-on-merge predicate their own merge had already
discharged, and ADR-0256 governs deduction_serving_enabled, which was ratified
True on 2026-07-24 and is serving live traffic. So the governance record
asserted "not yet decided" about a decision already in force.
That is the asymmetry the assessment arc (#113) found one file over in
workbench/api.py: the honest path degrades, the stale record lies. An unwritten
ADR is a visible gap; a Proposed one that is actually in force is a false
statement.
Each stamp records the ACTUAL ratifying act -- "Accepted, ratified by Joshua
Shay via <merge> (<sha>, <date>)" -- derived from the commit that added the file
and verified an ancestor of main, not assumed. Merge authority is Shay's alone
(AGENTS.md: no merge automation), so the merge IS the ratifying act.
No ADR content changed. No flag changed. ADR-0262's stamp says so explicitly:
accepting it does NOT enable curriculum_serving_enabled, which stays False
pending ratified volume -- eleven bands re-measured today, still 24x-73x short.
tests/test_adr_status_governance.py pins two independent invariants in the
smoke (pre-push) suite:
1. A default-ON flag is not governed by a Proposed ADR. The flag -> ADR mapping
is DERIVED by walking core/config.py for `<name>: bool = True` and reading
ADR refs from the preceding comment block -- not a hand-written table, which
would be the same second-copy-of-a-closed-set defect ADR-0256's arc fixed.
2. A ratify-on-merge predicate cannot coexist with Proposed. Self-discharging:
the file being on main IS the merge having happened.
Plus a vacuity guard, because a derivation that parses zero flags would make
every other assertion pass on an empty set.
Registered two orphans found in passing: test_adr_index.py (5) and
test_ratification_ceremony.py (14) landed in #113 in NO curated suite, so 19
tests -- including the one mechanism that can move curriculum volume -- ran
only under `full`, which gates nothing. Fifth instance of this shape.
Deliberately NOT fixed, recorded in the research doc: the 312-file corpus has
27 unparseable status lines and draft/ratified/active variants. A closed-vocab
assertion would fail on ~35 pre-existing files and get muted, and a muted gate
reads as coverage.
[Verification]: smoke 555 passed in 137.73s (236 baseline + 314 + 5, +1.2s);
governance pin 314 passed in 1.18s standalone and MUTATION-CHECKED -- reverting
ADR-0256 to Proposed fails both invariants independently (2 failed/312 passed);
orphans 19 passed; ruff clean. Canonical Python 3.12.13, uv sync --locked.
The full 99-register run is green: 801 passed, 0 failed, up from 99 failures on
clean main. bf17f42's "DO NOT MERGE until green" is hereby satisfied; the fix in
bf17f42 + bec76d4 is verified.
Records the whole thing, including what was NOT broken: the load-bearing
ADR-0072 claim (test_register_matrix_trace_hash_invariant) passed throughout, on
main and after. trace_hash folds hash_surface, so a real truth-path leak would
have moved it, and it never did. The invariant was intact the entire time — it
just was not observable, because hash_surface was pipeline-local, which is why
the test drifted onto `surface` when Phase 0 changed what that field means.
Also records the wrong turn: the first patch aimed at
evals/cognition/runner.py::CaseResult, while the test consumes
evals/metrics.py::CaseResult via run_cognition_eval. Thirteen files here define
a class called CaseResult. Follow the import, not the name.
[Verification]: register matrix 801 passed / 0 failed (17m50s), including the
trace_hash invariant across all 99 registers; capability index + eval harness 23
passed. evals/metrics.py::CaseResult gained a DEFAULTED field and its only other
consumers (calibration/tune.py, calibration/replay.py) never construct it.
NON-CANONICAL: Python 3.12.11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
Reverts the bidirectional half of the smoke parity pin added earlier today,
and records why at the assertion site so it is not "completed" again.
I read the suite list's comment ("so the local-first pre-push gate equals the
CI gate") as promising equality, made the assertion symmetric, and reported
tests/test_pack_draft_serve_boundary.py as drift because it sits in the local
gate and not in .github/workflows/smoke.yml.
That was wrong, and AGENTS.md says so in a line I had already read: GitHub
Actions are billing-locked and produce dead signals, never chase them. The
Forgejo host (e2-micro) cannot run workflows either. CI here is run MANUALLY
in-worktree — usually faster anyway on UMA hardware with real RAM — which is
what scripts/ci/local-ci.sh now front-ends. smoke.yml is secondary
observability, not a gate.
So the local suite is the SOURCE and is free to be broader. Asserting the
reverse converts a file nobody executes into a maintenance obligation, and one
agents cannot discharge at that — pushing workflow changes needs an OAuth scope
the push credential lacks. The three "owed" lines were never owed.
The surviving direction (CI subset-of local) still protects something real: if
smoke.yml ever names a path the local gate lacks, the local gate is narrower
than a published claim about it.
Arc-close brief updated to match: the "three files owed to smoke.yml" open item
is replaced by a recorded non-finding, so the next reader does not re-derive it.
Its shipped-work table now lists what actually landed in its place — the local
CI runner, the lane-roster fix, and the proposal-sink test-isolation fix.
[Verification]: 21 passed across test_cli_test_suites, test_lane_sha_verifier,
test_adr_index; ruff clean on the changed test.
NON-CANONICAL: Python 3.12.11, not the pinned 3.12.13 (unavailable for
linux-x86_64). Hash-pinned tests reproduce committed values regardless.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
Squashes the arc's work into one commit; the workflow-file edit it originally
carried is excluded (see the end of this message).
## Lane 1 — Workbench recorded a proved answer as ungrounded
With deduction_serving_enabled ratified ON (ADR-0256), workbench/api.py's live
chat route builds a bare ChatRuntime(), so the deduction composer decides
Workbench turns and stamps grounding_source="deduction" — but
_coerce_grounding_source carried a hand-copied whitelist of the six pre-arc
labels and silently rewrote anything else to "none". The runtime comment
reasoned this was inert because "REPL turns do not flow through Workbench's
CognitivePipelineRecord path". True, and irrelevant: the traffic flows the
other way. Stale since 2026-07-24.
Scope is one field. workbench/api.py:818 prefers TurnEvent.epistemic_state,
which read epistemic_state_needed — honest. So the UNregistered path degraded
honestly while the hand-copied whitelist asserted a falsehood; a second copy of
a closed enum was worse than no copy. Hence registration AND derivation:
GROUNDING_SOURCES exposes the Literal's members, and the coercion reads it.
workbench-ui badges/tokens/snapshot follow; enumCoverage.test.ts forces atomicity.
## Lane 2 — the ratification ceremony
The discovery loop was instrumented but not closed. teaching/ratification.py
turns a reviewed decision into a chain record, a corpus commit, and a receipt.
Its design turns on one observation: _ratified_rows DROPS unadmissible rows
silently — correct when serving, a trap when ratifying, because the file grows,
the commit lands, and the band count does not move. So the ceremony refuses to
call an append a ratification until it has re-read the curriculum through the
real loader and seen the chain arrive; a non-admitted append is rolled back.
Validation is a pre-flight courtesy, admission is the proof.
Arena queue entry and ledger reseal are deliberately NOT performed (bridge rule
1); the receipt names them. Front door: `core proposal-queue ratify`, a sibling
of `review` rather than a flag on it.
## Lane 3 — structural closures
- ADR-0263 gains rule 5: absence policy is DECLARED in CAPABILITY_LEDGERS, not
passed at the call site. An AST-matched test fails if a serving path passes
missing_ok again.
- Deductive suite added WHOLE to the pre-push gate: 285 tests in 29s against
smoke's 216 in 62s, so no coverage trade was needed.
- Smoke/CI parity assertion made bidirectional. It was one-directional, and had
drifted.
- test_prior_surface_deduction_binding.py pins correction binding on the
deduction path. The review's diagnosis did NOT reproduce — hash_surface moves
in lockstep — so it pins what is there. Mutation-checked.
- Domain-keyed ADR index over 312 flat-numbered files, explicitly partial.
- Arc-close brief template, plus this arc's own brief filled in against it.
## Lanes 4 and 5 — two premises falsified by measurement, one of them mine
Math 4.2: baseline reproduced (correct=5 wrong=0 refused=495); all four named
cases traced to one seam with each gap isolated by one-variable probes. Then the
number that changes the recommendation: the gap blocking case 0000 affects 1
case in 500, the 'than' gap blocking 0001 affects 2. ADR-0251's prohibition on
per-case growth now rests on a count. No reader change made.
CGA: versor_condition is 0.22% of a turn, not the "~10x proof latency" I claimed
— that multiplied an isolated microbenchmark by a call count and compared it to
a single verdict's latency. The real cost is geometric_product at 33,986
calls/turn (~73%) via cga_inner in search paths. The obvious closed form is NOT
bit-exact (954/4000 in f32); backend.vault_recall's serial fold IS (3000/3000,
worst-rel 0) and is the correct target. cargo test could not run —
static.crates.io is denied by the sandbox network policy — so the Rust parity
question stays open and the typestate lane is carried forward, not shipped
uncompiled.
## Not landed: three lines owed to .github/workflows/smoke.yml
The CI smoke gate is narrower than the local one —
test_pack_draft_serve_boundary.py (ADR-0253 INV-33) has been local-only, unseen
because the parity pin checked one direction. The edit was authored and rejected
at push for lacking the `workflow` OAuth scope, so it is recorded as a named,
dated PENDING_IN_CI exception rather than dropped: the assertion still fires on
any new divergence, and a second guard fires once the three land.
[Verification]: pre-push gates all green — smoke 236 passed, warmed_session 10
passed, deductive 285 passed. Ratification 14, ADR index 5, CLI suites 10.
Grounding/epistemic sweep 741 passed 1 skipped. workbench-ui 598 passed across
73 files, tsc -b clean. capability index 11 passed, digest unchanged. Math
holdout correct=5 wrong=0 refused=495. Committed chain corpora byte-unchanged
after the tests that write to them.
Environment caveat: the repo pins requires-python ==3.12.13, which uv cannot
fetch for linux-x86_64, so `uv sync --locked` fails. All Python runs used a
scratch venv on 3.12.11 with declared deps — not the locked universe, not the
full ~12k suite. The pin was left untouched. Re-run on a 3.12.13 host before
treating this as merge evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
Re-checks every load-bearing claim of an external architectural assessment
(18 observations + a five-pillar blueprint) against the tree, by reading the
implicated code and, where possible, executing it.
Headline: Workbench records a proved deduction answer as ungrounded. With
deduction_serving_enabled ratified ON, chat/runtime.py stamps
TurnEvent.grounding_source="deduction", and workbench/api.py's
_coerce_grounding_source whitelist silently rewrites it to "none". Verified by
running workbench.api._run_chat_turn, not by reading. Scope is one field:
epistemic_state still reads epistemic_state_needed (honest) because
workbench/api.py:818 prefers the TurnEvent's own value. The unregistered path
degrades honestly; the hand-copied whitelist asserts a falsehood.
Falsified items, with evidence:
- parents[n] audit — run: 168 sites, 0 outside the repo root.
- realizer-guard exemption — documented at both guard sites.
- T12 — present in the weekly-audit stragglers TODO.
- assert_corpus_sound — already domain-parameterized and called unconditionally.
- promotion oracle — already the wrong=0 lane gate, per tier-s-housekeeping §3.
- accrue_realized_knowledge — a session-memory deployment profile, not a debt clock.
- cross-subject proof — resolve_domain fails closed on ambiguity.
Also corrects the blueprint's flagship item: EntailmentTrace carries BDD node
keys, not derivation steps, so multi-step articulation is engine work
(proof-term extraction), not a rendering-layer gap. And records that the
"second subject arena" premise is falsified by the curriculum-volume
measurement — 11 bands across 4 subjects already route, all 24x-73x short.
Companion Apple Silicon/MLX blueprint assessed: both premises contradicted by
the repo's own benchmark report (no MLX in any runtime path; Rust backend
opt-in and off). One of five items adopted (typestate), one rejected on
numerics (bf16 against a 1e-6 gate).
[Verification]: docs-only change; no code paths touched. Findings themselves
were executed against a scratch venv — the repo pins ==3.12.13, which uv
cannot fetch for linux-x86_64, so the pin was left untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
- Promote tests/test_register_substantive_consumption.py into the smoke
suite (local core/cli_test.py + mirrored .github/workflows/smoke.yml):
the falsifiable register-axis (ADR-0069/0071/0077) contract was red on
main outside every gate for the 2026-07-22..24 window, masked by an
unrelated flake label. 36 tests, ~4.6s.
- Found in passing: test_core_test_deductive_suite_expands_to_entailment_lane
was already red on clean main (an exact-tuple pin from when the
`deductive` suite had one file; it has since grown to 12). Fixed to a
contains-style assertion so it can't go stale the same way again.
- Two new capability-index adapters: deduction_serve_existential_result
(Band v6-EX alone — the one band among six that changes the decision
procedure itself) and curriculum_serve_result. Breadth 11 -> 13; baseline
re-frozen (a deliberate re-freeze per its own docstring).
- Promotion sweep: confirmed via the existing wrong=0 lane gate (run
repeatedly this session across 166/166 deduction-serve cases) that no
split beyond the already-applied ds-mem-0020 has a stale declined-gold
case a current band now decides differently.
`core proposal-queue {status,list,show,review}`: a read + review-state-only
CLI over the two proposal sinks that had a Python API but no operator
surface — teaching/proposals/comprehension_failures/ (N5 contemplation)
and teaching/proposals/derived_close_facts/ (idle_tick PR-2 bridge).
"review" appends to a sidecar log (teaching/proposals/review_log.jsonl)
recording that a human looked at an artifact; it never mutates the
artifact, never ratifies, never mounts, never flips a flag. The existing
ratification corridor (teaching/proposals.py, `core teaching
proposals`/`review`, a different sink) is untouched.
Found and fixed a real bug while building the second sink's reader:
generate/determine/derived_close_proposals.py's DEFAULT_SINK used
parents[3] instead of parents[2], resolving one directory ABOVE the repo
instead of inside it. Silent because the feature is default-off and no
test asserted the path — confirmed live via a stray artifact sitting
outside the repository since 2026-06-16. Pinned by
test_derived_close_default_sink_resolves_inside_the_repo.
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.
Implements the measurable test from COMPREHENSION-READER-AUDIT.md's Q5,
generalized to the deduction and curriculum composers: a deterministic
refusal histogram split mechanism/coverage/engine_refused, plus an
admissions-per-batch counter for measuring a future lexicon or curriculum
expansion's effect as a before/after delta instead of asserting it.
Found and fixed a real classification bug while building it: an argument
successfully read by a band but declined by the ROBDD engine for
inconsistent premises was being misattributed to whichever reader band
happened to run last in the fallback cascade. That case is neither a
shape gap (mechanism) nor a vocabulary gap (coverage), so it gets its own
"engine_refused" bucket rather than being folded into either.
Baseline measurement: deduction refusals are 100% mechanism-class today
(the bands read a closed connective grammar, not open vocabulary) —
curriculum refusals are a real mechanism/coverage mix, confirming the
volume ceiling S6 quantifies independently from the ratified-chain side.
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)