Commit graph

970 commits

Author SHA1 Message Date
Claude
5f18b7e58f
feat(tests): three doctrine prohibitions enforced by test, not review (PR-6, G-9)
G-9's charge: "law-enforced-by-review is weaker than law-enforced-by-test." Its
stated method — verification pass first, then mechanical PRs — is what made this
worth doing, because the three prohibitions turned out to be in three different
states and only one of them was the state the register assumed.

(a) EXACT RECALL — the real gap, and worse than recorded. "Would a cosine
ranker actually fail a test?" answers NO across nearly the whole surface. The
only import ban in the repository covers exactly one file
(core/physics/wave_manifold.py, via test_third_door_cohesion.py — itself in no
curated suite). Meanwhile generate/realize/recall.py, which IS the recall path,
states the prohibition in its module docstring — "an exact, deterministic
equality scan (no cosine / HNSW / ANN)" — and nothing enforced it. Law asserted
where it executes, enforced nowhere: H-8's failure mode applied to a
prohibition rather than a record.

Now scanned across vault/, generate/realize/, generate/meaning_graph/, field/,
recognition/ for banned ANN libraries AND for hand-rolled cosine* definitions.
The second half matters: an import ban only stops the convenient version, and
ten lines of numpy is the realistic way a well-meaning contributor violates
this. A third pin ties the docstring claim to the scan, so the sentence and its
enforcement cannot drift apart.

(b) GOVERNANCE BYPASS — now exact. INV-07 proves governance works WHEN CALLED;
nothing proved it could not be skipped. Mechanism: no function in
chat/runtime.py may construct a TurnVerdicts without invoking
safety_check.check, and every such call must pass self.safety_pack — because a
bypass need not skip the call, it can substitute the pack. Measured at close:
two verdict-constructing functions, both governed.

(c) SAFETY-PACK NON-SWAPPABILITY — already well pinned; the gap was REACH, not
coverage. tests/test_safety_pack.py pins unratified-pack-refused-in-production,
missing-companion-report-refused, seal-failure-refused, path-traversal-rejected,
missing-pack-fails-closed — and ran in NO curated suite, so a fail-closed safety
contract was verified only after merge. Promoted onto the gate; the new pin
guards that placement rather than duplicating the substance.

FOUR SABOTAGES, EACH OBSERVED RED, each caught by its own pin: a banned library
imported on a recall path, a hand-rolled cosine ranker, a serving path
assembling verdicts without governing, the safety pack demoted off the gate.

Both new files registered in TEST_SUITES["smoke"] on creation (#136). Source
scans only — no runtime behavior, no flag, no serving path touched.

Registers: G-9 CLOSED with the per-item findings; PR-6 marked LANDED.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 04:13:42 +00:00
Claude
68e68589a2
feat(tests): curated-suite membership ratchet — and PR-4's parity pin withdrawn (PR-4, G-7, N-9)
N-9 is the finding, and it PREVENTS work rather than re-scoping it.

WHAT THE PLAN GOT WRONG. N-3 concluded the 23-vs-13 smoke delta was unintended
drift, on two in-repo comments asserting parity, and called that "the strongest
evidence available that the drift was never intended". H-12 repeated it; R-14
built three options on it recommending "raise CI, +46s measured"; PR-4
specified a bidirectional parity pin as deliverable one.

A third artifact answers all of it, and none of them read it.
tests/test_cli_test_suites.py::test_cli_smoke_suite_covers_ci_smoke_gate has
pinned smoke.yml SUBSET-OF TEST_SUITES["smoke"] since before this assessment —
which is why every measurement finds CI-only == 0; a pin, not luck — beneath a
comment headed "DELIBERATELY ONE-DIRECTIONAL — do not 'complete' this by also
asserting local_paths <= ci_paths". Its history is 50fa287d (2026-07-25):
"revert(tests): drop the local-to-CI smoke parity assertion — CI is not a
gate." An earlier agent read the same comment, drew the same inference, made
the assertion symmetric, and reverted it hours later: "That was wrong, and
AGENTS.md says so in a line I had already read."

AGENTS.md:280 — GitHub is "a mirror only; its Actions are billing-locked and
produce dead signals — never chase them." §277 — the workflows are "secondary
observability only." So the local suite is the SOURCE and is deliberately
broader; the delta is the design.

  - PR-4 pin 1: WITHDRAWN. It exists, in the direction that protects something.
  - R-14: premise corrected in the packet. Option A spends 46s on a workflow
    that does not gate; option B would delete real protection for a fiction;
    option C's live half is a two-line comment fix, done here.
  - N-3's exposure claim was too GENEROUS to CI: for a push that skipped the
    local gate there is no automatic check at all. Larger than stated, and not
    closable by editing smoke.yml.

WHAT LANDED. Measured: 749 of 877 test files are in no curated suite. The
plan's mechanism ("assign every orphan, or 749 exclusion reasons") is hollow at
that scale — glob topic-suites ("adr": tests/test_adr_*.py absorbs 172) satisfy
the assertion while changing nothing about what executes, which is precisely the
Third-Door objection the plan levels at G-7's own first formulation. All four
demonstrated incidents (#113, #136, negation-survives-articulation,
speculative-subject-lifecycle) were caused by a NEWLY LANDED orphan; none by a
legacy one.

So: a ratchet. tests/test_suite_membership.py + tests/full_only_baseline.txt.
New orphans fail. The baseline is enforced in BOTH directions — a promoted or
deleted file must leave it, so the list can only shrink — and its count is
pinned so bulk movement is a reviewed decision, in the discipline
test_volume_honesty.py uses.

Also: the gate-guarding pin now runs on the gate. test_cli_test_suites.py lived
in `fast`; the pre-push gate runs smoke + deductive. The pin guarding the gate
did not run on the gate.

Three sabotages, each OBSERVED RED: a new unregistered test file, a stale
baseline entry, the parity pin demoted out of smoke.

Note on the delta: now 12 (local 27, CI 15) because this session added three
pins locally. Under the corrected reading that is the design, not widening
drift — the invariant is CI-only == 0, and that is pinned. The registers now
say so; "ten files" was a measurement, never a target.

Process note: `git checkout --` during sabotage cleanup destroyed uncommitted
edits to core/cli_test.py, which were redone. Backups, not checkout, on files
with unstaged work.

Registers: G-7 CLOSED, H-12 amended, R-14 dissolved in the packet, PR-4
re-specified, N-9 added to plan §0.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 04:03:51 +00:00
Claude
98a6e8b439
feat(cognition): count the swallow — three silent backstops made visible (PR-9, H-11)
Wave 2's PR-9, whose authority is "mechanical PR" and which is gated on no
ruling. Widened by the 2026-07-28 triage from the accrual guard alone to all
three broad `except` blocks on the turn spine.

Each guard is DELIBERATE and stays: accrual is additive and must never crash a
turn; a pack/catalog failure must not take down logos authority; a probe
failure must not touch the served surface. Narrowing them to ImportError/
OSError — as an external assessment proposed — would convert a malformed
decision object into a turn-spine crash, trading silent failure for
served-surface failure. What was wrong is not the tolerance, it is that each
wrote a value INDISTINGUISHABLE FROM ITS HEALTHY CASE:

  - a raising read->realize->determine chain left the same None accrual a quiet
    turn leaves;
  - a logos decision that raised looked exactly like a turn where logos was
    never consulted (both: logos_decision_kind == "");
  - a crashed geometric probe recorded the same empty neighborhood as a probe
    that ran and found nothing — in the block whose entire job is producing the
    data that prices the anti-unification roadmap.

Silence, in the one layer whose constitution is "failures are typed, never
silent" (INV-34).

  chat/runtime.py         _accrual_swallows / _last_accrual_error, read via
                          accrual_swallow_telemetry(). _last_turn_accrual is
                          still None on failure, so _maybe_surface_determination
                          returns byte-identically — ADDITIVE BY CONSTRUCTION,
                          and pinned as such.
  cognition/result.py     logos_error on CognitiveTurnResult.
  cognition/pipeline.py   logos_error recorded; probe_error,
                          neighbor_scan_errors, graph_anti_unify_error on the
                          OOV telemetry.

tests/test_accrual_swallow_telemetry.py — 7 pins. The two behavioral ones were
OBSERVED RED against a build carrying the fields with the recording stripped,
so they discriminate rather than merely pass. Two test bugs of mine were fixed
along the way (wrong logos module path; CognitiveTurnResult has required
fields) — the tests were wrong, not the code.

Registered in BOTH TEST_SUITES["smoke"] and smoke.yml on creation, per #136.
Local/CI delta verified unchanged at exactly ten (local 25, CI 15, CI-only 0),
so this settles nothing R-14 has to rule.

Cleanup as found: three dead imports removed from chat/runtime.py
(_COGNITION_PACK_ID, _TEACHING_CORPUS_ID, engine_identity_for_config), each
confirmed to have no re-export dependence first — workbench/readers.py imports
engine_identity_for_config straight from core.engine_identity. ruff 4 -> 1 on
these paths; the survivor is a pre-existing structural E402.

Registers: H-11 CLOSED; PR-9 marked LANDED in the plan.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 03:50:42 +00:00
Claude
2cfc6ad251
fix(cognition): claimant-tracked speculative markers — the refcount fix was wrong (H-13)
71542e61 fixed only half of H-13 and asserted a premise that is false. This
replaces it, and records why, because the failure mode is the one this arc
exists to catch.

WHAT WAS WRONG. The refcount rested on "seeding and eviction walk the same
source list, so the counts balance." They do not. The call site's arms are
MUTUALLY EXCLUSIVE (pipeline.py §10b): a proposal is seeded when SPECULATIVE or
released when COHERENT, never both. So a release never balances a claim the
same proposal made, and a COHERENT proposal ALWAYS releases claims it never
held. Refcounting therefore repaired only the sub-case where both proposals had
been taught speculatively. The ordinary case — an unreviewed `wisdom`, then a
separate correction about `practical wisdom` that passes review on its own turn
— still stripped the marker. Measured against the landed refcount:
`sibling marked? False`.

The first attempt's tests passed because their helpers split tokens themselves,
mirroring a call pattern production no longer used — tests written to the fix
instead of to the defect. That is the same "asserted past the evidence" failure
diagnosed in two external assessments earlier today, committed here one commit
later, and catchable by reading fifteen lines at the call site.

WHAT IS CORRECT. _speculative_subjects maps token -> SET OF CLAIMING SUBJECTS.
A token is evicted exactly when the subject that actually claimed it is
reviewed; discarding an absent claimant is a no-op, so a neighbour's marker
survives. Token derivation moved out of the call site into
_speculative_index_tokens, because ownership is what the call site cannot see —
doing the split out there made every token its own claimant, which was the bug.

Tests rewritten to call the cache exactly as §10b does. The lead pin
(test_coherent_proposal_does_not_unmark_an_unrelated_subject) was confirmed to
FAIL against the refcount design before this landed, so it discriminates rather
than merely passing. 13 pins green; the 8 pre-existing lifecycle pins unchanged.

SUITE MEMBERSHIP, resolved rather than deferred. The file pinning this behavior
sat in NO curated suite, reachable only through `full` — which is why the
regression was invisible (G-7's mechanism, on the pin that would have caught
it). Registered now in BOTH TEST_SUITES["smoke"] and smoke.yml, per the #136
precedent that an unregistered pin runs nowhere. Verified the local/CI delta is
unchanged at exactly ten files (local 24, CI 14, CI-only 0), so this is
compatible with all three R-14 options and settles none of them; PR-4 still
owns the standing ten.

Cleanup as found: three unambiguously dead imports removed from pipeline.py
(hashlib, RatificationOutcome, RatifiedIntent). ruff 4 -> 1; the survivor is a
pre-existing structural E402, left alone.

Ratification note unchanged: this changes served surfaces — markers appear on
turns where they previously vanished. Direction is toward disclosure and it
restores ADR-0021 §Articulation's stated intent rather than extending it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 03:38:53 +00:00
Claude
71542e61ee
fix(cognition): a promoted proposal must not un-mark a sibling's speculative material (H-13)
The speculative-marker cache seeds a subject AND each >=4-char token split from
it, so two independent proposals can claim the same token ("wisdom" from both
`wisdom` and `practical wisdom`). `_forget_speculative_subject` popped the token
outright on COHERENT promotion — regardless of which proposal had seeded it — so
promoting one proposal stripped the marker from another that was still
unreviewed. Unreviewed material was then served WITHOUT the "(speculative, not
yet reviewed)" prefix: the expensive failure direction, on the surface whose
whole job is telling reviewed knowledge from unreviewed.

_speculative_subjects becomes OrderedDict[str, int] — the value is a reference
count. Seeding increments and refreshes LRU position; promotion decrements and
evicts at zero. Seeding and eviction walk the same source list so counts
balance; where they disagree the count stays positive and the subject keeps its
marker, which is the honest direction to fail. An unmatched release floors at
removal and never goes negative, so it cannot borrow against a later proposal's
claim. The LRU cap survives unchanged as a size valve.

Four new pins in tests/test_speculative_subject_lifecycle.py, ALL FOUR OBSERVED
RED BEFORE GREEN — sibling token evicted, marker lost at the served surface,
repeated teaching cleared by one promotion, and count underflow. The eight
pre-existing lifecycle pins pass unchanged.

Also H-8e — expand_relation_closure's docstring claimed "Cycle: if path would
revisit a node, skip", describing a check the code does not perform. The code
was right: termination is structural (monotone growth over a finite triple set),
and a path-revisit skip would REFUSE SOUND transitive derivations, since a
witness path that revisits a node still proves a true fact. The docstring now
says that, and records that `path` is the witnessing step rather than full
ancestry — extending it would move operator_invocation and therefore trace_hash,
so it is deliberately not done here.

Registers updated: H-13 marked FIXED with the ratification note (this changes
served surfaces toward disclosure, restoring ADR-0021 §Articulation's stated
intent rather than extending it); H-8e marked CORRECTED.

Found while fixing it, recorded not acted on: tests/test_speculative_subject_
lifecycle.py — the file pinning this exact behavior — is in NO curated suite,
reachable only through `full`. G-7's mechanism, caught on the pin that would
have caught the defect. Assignment left to PR-4; adding it to TEST_SUITES
["smoke"] alone would widen N-3's ten-file delta and prejudge R-14.

Pre-existing ruff findings in pipeline.py (3 unused imports, 1 E402) confirmed
present before this change and left alone — module-top, not adjacent to the edit.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 03:24:20 +00:00
Claude
e025de7ad4
docs(assessment): fold in the external-assessment triage — H-13, H-14, H-8e; six proposals cleared
Two external first-principles assessments of CORE were verified against code
line-by-line at 8f0ccfe3. What survived is entered; what failed is recorded in
the audit's negative space with the refuting file named, per the register's own
discipline.

Entered:

  H-13 — the speculative-marker cache over-evicts shared tokens: promoting one
  proposal to COHERENT pops tokens regardless of owner (pipeline.py:736-739,
  :1045-1055), so a sibling proposal's still-SPECULATIVE material is served
  without its marker. Honesty regression, served direction. Remedy: refcounted
  cache, failing test first. (The source assessment described this seam with
  the direction inverted — under-eviction; the code over-evicts. The check
  that caught the inversion is what confirmed the bug.)

  H-14 — _run_turn is 759 lines (pipeline.py:189-947) under a thin-wrapper
  docstring, with finalize_turn_trace_hash x2 / finalize_turn_surface x1
  back-stamps as the measured symptom. H-4's sibling: refactor ADR, PR-10's
  byte-identical-trace bar, and the learn stage named stateful.

  H-8e — inductive_closure.py's docstring claims a path-based cycle skip the
  code does not perform (:106 vs :189-193) — and the code is the right side:
  the monotone fixed point needs no path check, and adding one would refuse
  sound derivations. path records only (a,b,c) for longer chains (:198), so
  trace provenance is incomplete. Docstring fix is mechanical; extending path
  changes operator_invocation bytes -> trace_hash, held to the trace-change bar.

  PR-9 widened — the logos-authority bare except (pipeline.py:555) and the
  three OOV-probe swallows (:605-657) join the count-the-swallow scope; same
  remedy shape (record repr(e), keep the backstop), explicitly not the
  narrow-catch alternative.

  R-4 packet addition — first collapse candidate: composed_surface +
  transitive_surface + transitive_max_depth -> chain_depth:int, justified by
  ADR-0083's own docstring ladder; ruled in-lineage, not ad-hoc.

  R-12a note — lived_life.json staleness recorded as a question; the proposed
  PID-attestation fix rejected (refuses every reboot resume; PID reuse).

Cleared, with the refuting file named: EpistemicState compression
(vault/store.py, policy.py's ADR-0206 partition), the EpistemicState x
NormativeClearance product type (erases refusal evidence — H-3 rebuilt as a
type), PID attestation, path-based cycle skipping, the depth-enrichment cache
(pack_resolver.py:19 already lru_caches the cost), and the unified reasoning
engine as-refactor (trace_hash change + capability widening + discards the
ROBDD engine one merge after ADR-0265 ratified denial).

Register ranges updated to G-1..G-21 / H-1..H-14. Docs only; no runtime
behavior, no flag, no test changed.

[Verification]: uv run pytest tests/test_adr_index.py
tests/test_adr_status_governance.py tests/test_adr_0252_s5_blindness.py -q —
327 passed at this tree.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 03:17:54 +00:00
Claude
8f0ccfe317
docs(assessment): re-anchor the §5 provenance SHAs to the commits that landed
The branch was re-created with SSH signatures before landing, which changed my
commit SHAs. Four citations of the pre-registration commit — the verdict's
header and closing note, G-1, and the plan's Track A section — pointed at
dfc394d2, which is not in main's history. They now point at 299c92be, which is.

Same tree, same message, same ordering, signature added. The claim the verdict
rests on is unchanged and checkable at 299c92be: that commit contains the
thresholds and no results. Recorded as a note in the verdict document rather
than swapped silently, because a document citing a commit absent from its own
repository's history is precisely the failure mode its §1 catalogues — and this
one would have been self-inflicted.

Docs only.

[Verification]: uv run pytest tests/test_adr_index.py
tests/test_adr_status_governance.py tests/test_adr_0252_s5_blindness.py -q —
327 passed.
2026-07-28 03:00:46 +00:00
Claude
7728a25dee
feat(rnd): ADR-0252 §5 run to a verdict — NO-GO, and the two prior GOs are void
Track A of docs/assessment/50-execution-plan.md §6, executed under its own
protocol. Criterion pre-registered at dfc394d2 (that commit carries the
thresholds as importable constants and NO results); this commit carries the run.

VERDICT: NO-GO. Full credit by ADR-0252 §5.4's own terms.
  docs/research/sme-experiment-verdict-797ebad5.md
  evals/structure_mapping/adr0252_s5/results/report-797ebad5.json
  deterministic_digest b3d9d27592e213104c51bcace7415fd819d722a1b06f7d0a5ca65bd5a234e4ec

53 cases, 1378 pairs, 0.0% non-convergence — no exception contributed to any
number. RS-A (structure-only) separates perfectly, AUC 1.0000, margin +0.0867,
and fails structure-sensitivity. RS-B (attribute-bearing) fails all three.

The mechanism, measured rather than argued: the similarity quotient that would
deliver attribute-invariance is the same quotient that annihilates structural
contrast. An add-vs-subtract minimal pair — one entity, identical numbers, one
relation kind changed — aligns at residual exactly 0.0, its two configurations
being related by a PROPER rotation about e1. Sweeping the attribute weight
(committed as diagnostic_sweep), structure-sensitivity fails at every setting,
and every regime where the SME property survives is a regime where attributes
contribute nothing: AUC 1.00 -> 0.97 -> 0.83 -> 0.69 as they begin to matter.

Scope stated narrowly: this refutes H1 for embeddings that encode role-structure
as point positions aligned by conformal Procrustes under similarity. The
argument is about the quotient, so it generalises across that class; it does not
refute every Cl(4,1) representation, and it does not touch the symbolic
structure-mapping lane already in evals/structure_mapping/.

Two findings that were on nobody's list:

N-8 — the experiment was NOT unrun. rnd/sme-experiment-v2 @ 96e5f468 is titled
"Verdict: GO" and rnd/structure-mapping-experiment @ fc9d0c14 carries an earlier
one. Neither survives inspection: attempt 1 leaked the S1-S4 label into the
embedding; attempt 2 was blind and then took its separability from
`except ValueError: res = 1000.0` — a solver exception counted as a distance —
on a corpus 46/51 of which is outside holdout_dev/v1 with nothing marked, with
duplicate graphs, colliding ids, and an extractor that was never committed. The
register, the plan and the ADR all read the same absence and inherited the same
error for nine days. A branch tip is not a record.

G-21 — the math reader returns a selected graph for 5 of 500 holdout_dev/v1
cases (1.0%), all one skeleton; 24/150 on the public lane. That is why §5.1's
four-structure corpus is not extractable, and it is a sharper measurement of the
comprehension frontier than G-3's construction count.

Registers updated: G-1 carries the verdict, H-10 confirmed and discharged, G-21
added, plan §0 gains N-8, Track A closed, synthesis frontier 2 rewritten.

Pins: tests/test_adr_0252_s5_blindness.py, each observed red before green
(label leak and duplicate-graph sabotage both caught).

Off-serving — evals/structure_mapping/adr0252_s5/ is imported by no serving
path, emits no answers, and changes no flag.

[Verification]: 797ebad5 + this branch — `uv run core test --suite smoke -q`
641 passed in 199.76s; `uv run ruff check` clean; report regenerated after lint
with an unchanged digest.
2026-07-28 02:55:31 +00:00
Claude
299c92be68
test(rnd): pre-register the ADR-0252 §5 criterion, before the run
Track A, per 50-execution-plan.md §6: "state the GO/NO-GO criterion in writing
BEFORE running (the ADR's §5 conformance bar, not a criterion chosen after
seeing results)." This commit carries the criterion and the code and NO results.

Why the protocol is load-bearing here rather than ceremonial: the §5 experiment
is not unrun. It has been run twice and returned GO twice, on unmerged branches,
and neither run's rule was binding.

  - rnd/structure-mapping-experiment @ fc9d0c14 — disowned by its own successor:
    the S1-S4 label entered the embedding, so Procrustes ran on identical
    matrices per structure.
  - rnd/sme-experiment-v2 @ 96e5f468 — blind, and then measured separability
    with `except ValueError: res = 1000.0`. Its GO test cross_mean > 2*same_mean
    is satisfied by that sentinel alone. A solver exception is not a distance.

Instrument characterised before use, on dummy clouds: conformal_procrustes has
two branches. The (5,K) Kabsch+Umeyama path never raises and is invariant to
rotation, translation AND uniform scale (identical 1.8e-16, rotated 7.8e-16,
translated 3.9e-15, scaled x2.5 1.4e-14) while separating non-similar
deformation (5.9e-1) and unrelated clouds (1.1e0). The mixed versor/point
sequence path performs field conjugacy and raises. Attempt 2 used the second;
this uses the first, and treats a raise as non-convergence — excluded, reported
as a rate, never counted as separation.

Corpus fact that forced the design, measured at 797ebad5: the serving reader
returns a selected graph for 5 of the 500 holdout_dev/v1 cases (1.0%), all with
the same skeleton. §5.1's four-structure corpus is not extractable from
holdout_dev/v1 today, so §5.1's own escape hatch is used and MARKED — 5 real
cases, 48 synthetic from four declared templates. A verdict from this corpus
speaks about the geometry, not about coverage; §5.5's compression estimate is
not answerable at 1.0% coverage and is not attempted.

Off-serving. Imported by no serving path; no answers emitted.
2026-07-28 02:55:30 +00:00
Claude
635f248834
docs(assessment): re-verify the Wave 0 amendments at main tip, and correct H-8d
PR-1's amendments were derived at ed06dd64. main has since moved (#140, #138),
so every amended claim was re-measured against code at 797ebad5 before landing:

  - TEST_SUITES["full"] == ("tests/",)                        (N-1) holds
  - 5 tests/test_l10_*.py, none marked quarantine or slow      (N-2) holds
  - smoke suite 23 files vs smoke.yml 13, same ten-file delta,
    and zero CI-only files (local is strictly the superset)    (N-3) holds
  - RuntimeConfig: 32 booleans, 28 off, the same 4 on          (N-7) holds
  - CONTINUOUS_LIFE_CONFIG_FLAGS still omits accrual           (N-6) holds
  - ADR-0264 §4.1 self-supersession banner in place            (N-5) holds
  - 18 resolve_promotable_* organs in generate/derivation/     (H-8b) holds

One correction to the amendment itself. H-8d said CONTINUOUS_LIFE_CONFIG_FLAGS
"contains neither claim's subject." It contains one of them: consolidate_
determinations IS forced. The false clause in both docstrings is the one naming
accrue_realized_knowledge as co-enabled, and a third comment on persist_session_
state makes the same shape of claim and is true. The entry now says that, and
says it was caught on re-verification — the same standard §0 holds itself to.

Docs only. No runtime behavior, no flag, no test changed.
2026-07-28 02:55:30 +00:00
Claude
0c11b53083
Merge the Wave 0 record amendments (PR-1) into the assessment docs branch 2026-07-28 02:55:30 +00:00
9f29f1aa10 Merge branch 'main' into feat/construction-inventory 2026-07-28 01:09:40 +00:00
Shay
0641e211e6 docs(assessment): H-12 — the pre-push hook asserts the parity too
scripts/hooks/pre-push describes its own step 1 as "the smoke suite — exact
CI-gate parity" while running 23 files against the CI gate's 13. That is the
second in-repo assertion of the parity H-12 falsifies, and it is inside the
tooling that enforces the protocol.
2026-07-27 17:38:13 -07:00
Shay
9efd5a6143 docs: apply the Wave 0 record amendments that need no ruling (N-1…N-7, H-12)
The corrections derived in 50-execution-plan.md §0, applied where the wrong
claims live. Every amendment is marked [AMENDED N-x] inline so the register
carries its own correction history rather than quietly reading correct.

Gap register — four entries amended:
- G-5  the soak RAN and passed (5000 beats, reboot at 2500, all four gates,
       aed273b1). Retitled: what is owed is the committed artifact, the pinned
       deterministic_digest, and a cadence — not the run. Both prior claims
       withdrawn with their causes: "no suite contains any l10 test" was a
       scan artifact (TEST_SUITES["full"] is a directory), and the pins
       execute twice a day (post-merge + nightly cron).
- G-7  respecified. The proposed meta-check was already satisfied and would
       have shipped green proving nothing — a hollow gate in the entry
       proposing to abolish hollow gates. Now two pins: gate parity, and
       curated-suite membership EXCLUDING full.
- G-8  28 default-off flags of 32 RuntimeConfig booleans, not seventeen.
- G-10 the 16-premise cap was removed by ADR-0264 R5 on 2026-07-26; the ADR
       carries a self-supersession banner directly under the heading Phase 4
       quoted. No engineering prerequisite remains — R-8 alone gates it.
- G-19 note: the 21/25 exposure is already pinned in test_volume_honesty.py,
       so the work is applying the demotions, not discovering them.

Hindrance audit:
- H-12 added — the local smoke suite (23) and smoke.yml (13) have diverged by
       ten files. Counter-evidence stated first: AGENTS.md makes the local run
       the merge gate and it is the superset, so nothing is unguarded. What is
       real is one comment claiming an equality that does not hold, and the
       push that skipped the local gate.
- H-8  gains instance (d), inside the code: two flag docstrings assert a
       production profile CONTINUOUS_LIFE_CONFIG_FLAGS does not have.
- H-1  gains the already-pinned note.

Also: correction note on the 2026-07-25 verification doc (a corrective
document that introduced the error at H-8c), and a staleness banner on
MIND-PHYSICS-BLUEPRINT.md pointing at the taxonomy that dissolved it.

The two ratified-ADR amendments (ADR-0146, ADR-0252) are NOT here — they are
drafted in full in 50-rulings.md R-12 and land on that ruling, because editing
a ratified ADR is Shay's authority even when the edit changes no decision.

Docs only. No runtime behavior, no flag, no test changed.
2026-07-27 17:38:12 -07:00
Shay
c3f1ddb643 docs(assessment): the pre-push hook makes the same parity claim (R-14, N-3)
scripts/hooks/pre-push — the automation of the AGENTS.md local-first protocol
— opens by describing its own step 1 as "the smoke suite — exact CI-gate
parity". It runs 23 files; the CI gate runs 13. That is the second in-repo
assertion of a parity that does not hold, the first being core/cli_test.py's
audio block, and it is the strongest evidence available that the drift was
never intended rather than a deliberate narrowing.
2026-07-27 17:37:58 -07:00
Shay
372bdf987d docs(assessment): Wave 0 — the execution plan and the ruling packet (R-1…R-14)
Phase 6 of the holistic assessment: what follows from it, and what it needs
ruled before anything else can be sequenced.

`50-execution-plan.md` — five waves (scrub/rule → delete → simplify/enforce →
accelerate evidence loops → automate last) plus five frontier tracks, covering
every G-1…G-20 and H-1…H-12 entry, with four hard dependency gates and nine
risks stated before they are incurred.

`50-rulings.md` — R-1…R-14. One decision per section: the evidence verified at
ed06dd64, the options enumerated, a recommendation with its reasoning, and the
exact diff that follows from each choice, so ruling costs one word. Nothing in
it recommends reversing a ratified decision; R-12 amends two ADRs' records
without touching either decision.

§0 of the plan carries SEVEN corrections to the assessment, all found by
reading code, workflows and ADR supersession banners — none by re-reading
documents. Four change planned work:

- N-3 (new) the local smoke suite (23 files) and smoke.yml (13) have diverged
  by 10 files. Severity is LOWER than this plan's first draft stated: AGENTS.md
  makes the in-worktree run the merge gate and CI secondary observability, and
  the local gate is the superset — so nothing is unguarded. What is real is one
  in-code comment claiming an equality that does not hold, and the push that
  skipped the local gate, for which CI is the only automatic check.
- N-4 the L10 soak HAS been run — 5000 beats, reboot at 2500, all four gates
  pass (contract.md, landed aed273b1). G-5's "never produced an artifact" is
  wrong. The debt is the ceremony: no committed report, no pinned
  deterministic_digest (the contract's own unmet closing instruction), no
  cadence. Prose with no pinned digest is testimony, not evidence.
- N-5 G-10's blocker does not exist. ADR-0264 §4.1 carries a self-supersession
  banner: R5 removed the 16-premise cap on 2026-07-26, and four bands would
  earn SERVE the moment a ledger is sealed. PR-13 is WITHDRAWN and the whole
  throughput frontier reduces to one ruling (R-8).
- N-7 the flag count is 28 of 32 RuntimeConfig booleans, not seventeen.

Plus N-1 (the orphan scan artifact: TEST_SUITES["full"] is a directory),
N-2 (the L10 pins run twice a day, post-merge and nightly), and N-6 (H-8 has a
fourth instance inside the code — two flag docstrings assert a production
profile the production profile does not have, which moves R-3 decisively
toward "incomplete flag set").

Docs only. No runtime behavior, no flag, no test changed.
2026-07-27 17:18:43 -07:00
Shay
ee481c1c92 docs(assessment): Phase 5 — the synthesis, and the directory index
40-assessment.md — the verdict in one paragraph (skeleton real, discipline
exceptional, the two deepest commitments unproven in opposite directions);
the cognitive cycle stage-by-stage with the honest sentence for each; what
is excellent, stated as findings; the macro picture (built-and-dark,
capability outruns proof, the record decays faster than the code); THE
FIVE FRONTIERS (the reading, the verdict, the chooser, the proof of life,
the throughput — everything else on the registers is hygiene, enforcement,
or ceremony); the recommended attack order in five waves sequenced by the
mastery algorithm (scrub/rule -> delete -> simplify/enforce -> accelerate
evidence loops -> automate last, with the warning that an always-on
process consolidating an empty set is "garbage at high speed" and CORE
came within one flag of it); the charter's four questions answered
directly; and the maintenance contract that keeps this directory from
becoming the next dead instrument.

README.md — the directory index and reading order.

The assessment is complete: 21 documents, four phases, three
self-corrections, every claim SHA-stamped at 8927c563.
2026-07-27 16:08:21 -07:00
Shay
4c342cdc02 docs(assessment): Phase 4 — the gap register and the hindrance audit
30-gap-register.md — CORE's first LIVE gap register since docs/gaps.md
closed its 26th entry (supersession proposed for ruling). 20 entries in
four tiers, each with evidence, deciding authority, and leverage rank:

- Tier A (frontier-blocking): G-1 the unrun ADR-0252 §5 experiment
  (leverage 1 — authorized, scaffolded, verdict-less; the paradigm governs
  ALL future comprehension); G-2 the #138 fabrications (pre-labeled
  measured-and-pinned, held for ADR + ratification); G-3 reader 19 vs
  writer 1739; G-4 CR-2 no-chooser (the AGI-grade conceptual absence);
  G-5 L10 proof debt (no artifact, no suite, no cadence); G-6 the
  half-gated lived learning loop.
- Tier B (enforcement debt): G-7 orphaned-pin meta-check (the highest-
  leverage single mechanical change); G-8 flag-default register; G-9 three
  doctrine laws without verified failing pins; G-10 curriculum scoping +
  absent ledger; G-11 identity authorization bar unstated.
- Tier C (one-line rulings): CR-3 efferent, CR-4 temporal stance, CR-1
  attention ADR, the daemon's owning ADR.
- Tier D (latent/carried): aspect-arm defect class (generate/templates.py
  :79, unreachable today), sensorium entry criterion, curriculum-formation
  bypass (unverified carry), Wilson evidence debt, refusal materialisation.
- Plus what is NOT in the register and why (deferred-with-ruling is the
  scripture model; benchmarks excluded by the completeness criterion).

31-hindrance-audit.md — 11 entries ranked by leverage, each with fitness
verdict, evidence, proposed better home, deciding authority; decides
nothing. Headliners: H-1 Wilson/replay counting basis (wrong-solution in
the counting, not the gating — 21/25 bands short; the re-count may demote
licenses and that is the mechanism working); H-2 decoration as testimony
(DriveGradientMap, InhibitionMask — deletion per mastery step 2); H-3 the
typed refusal discarded at the public boundary; H-4 extend the resolver's
declared-precedence pattern upstream; H-8 three record/code contradictions
(each one paragraph to fix); H-9 dead instruments standing as if live.
Plus five examined-and-CLEARED candidates (the 18 organs' continued
service is governance working; pure-Python-by-default is measured-correct;
flag-gated conservatism is not hindrance — unregistered darkness is).
2026-07-27 16:04:22 -07:00
Shay
901f028f32 docs(assessment): Phase 3 — component descent; the correction chain continues into code
Eight component cards in 20-component-cards/: the four zero-subsystem zones
(comprehend-organ, determine-phase, realize-phase, sensorium-falsification)
plus always-on-process, derivation-organs, surface-selection,
attention-allocation. Plus 05-phase3-findings.md and correction banners on
the M6 and M4 layer cards.

CORRECTIONS (C-1..C-5):
- THREE more map live-serving labels demoted to live-internal:
  comprehend-organ (a MATH SETUP ROUTER, not chat comprehension — imported
  by neither serving entrypoint), determine-phase and realize-phase (both
  behind accrue_realized_knowledge=False / idle flags; NO default-config
  serving turn touches any of the three).
- Phase 2's M6 card overstated ephemerality: Shape B+ persistence exists
  (persist_session_state, daemon-forced, "restored bit-exactly").
- Phase 2's M4 card understated the resolver: surface_resolution.py IS a
  declared-precedence resolver for the pipeline seam; only the upstream
  composer arms remain ordered branches. Third Door refines to "extend the
  existing pattern upstream."
- 34-vs-18 RESOLVED: 18 resolve_promotable_* organs existed AT the ADR-0252
  ratification commit; "34" counted modules (~32-33). No consolidation has
  occurred; the governing ADR's headline number has no stated basis.
- The 2026-07-25 verification doc's claim that accrual "is enabled by the
  production L10 process" is contradicted: CONTINUOUS_LIFE_CONFIG_FLAGS is
  exactly {persist_session_state, consolidate_determinations,
  strict_identity_continuity}.

NEW FINDINGS (F-6..F-10):
- F-6 THE CONTINUOUS LIFE MAY CONSOLIDATE AN EMPTY SET: the daemon forces
  the consolidator ON but not the accruer, and realize_comprehension — the
  only turn-path writer of realized facts — sits behind the accrual flag.
  The two halves of the lived learning loop are gated by different flags
  and only one is forced. Undocumented either way.
- F-7 ADR-0008's Allocation Physics LANDED, mutated, ungoverned: curvature
  kernel -> salience adapter -> attention threshold plan -> candidate
  intersection, with budget feedback making attention SELF-NARROWING
  across a walk. InhibitionMask is decoration. top_k=16/threshold=0.3 have
  no recorded derivation. CR-1 closes with a one-page ADR.
- F-8 two load-bearing naming traps (comprehend-organ; realize vs realizer).
- F-9 REALIZE is sound; its ceiling is the reader that feeds it (the #138
  fabrication locus — measured/pinned/held, recorded not re-discovered).
- F-10 _accrue_in_turn swallows exceptions into a no-op with no telemetry.

Phase 4 seed list consolidated: 8 hindrance candidates, 10 gap candidates,
each with its deciding authority. Method note recorded: three phases of
corrections, and nothing in the chain was caught by re-reading documents.
2026-07-27 15:57:36 -07:00
Shay
36cb29a693 docs(assessment): Phase 2 — nine layer cards, verified against code not documents
Fills the taxonomy with evidence. Every liveness label re-verified at
8927c563 rather than inherited from the 2026-06-09 system map. Three
inherited claims did not survive, and every one of them came from a
document while every falsification came from reading code.

CORRECTIONS:

- Phase 0 Finding 0-C was WRONG. The L11 always-on process IS BUILT:
  chat/always_on.py::run_continuous, chat/always_on_daemon.py::run_daemon
  (single-instance lock, SIGINT/SIGTERM stop, load-time identity guard),
  CLI `core always-on`. Landed 2026-06-14 — five days AFTER the system-map
  snapshot that declared no forever entrypoint exists. The real finding is
  sharper: built, with a complete falsifiable soak harness, never run to a
  recorded artifact, and enforced by NO SUITE AT ALL.
- CR-1 attention/allocation is not a missing layer but a live undocumented
  one: generate/stream.py:255-263 runs SalienceOperator -> AttentionOperator
  and gates every generation step; use_salience defaults True. It owns the
  ~73%-of-turn hot path and has no ADR, no card, no layer in any ratified
  articulation. (generate/salience.py COMPOSES core.physics.salience — not
  a duplicate; that false lead is recorded, not propagated.)
- CR-2's components exist: DriveGradientMap and ExertionMeter are both
  constructed in chat/runtime.py. DriveGradientMap is NEVER READ — textbook
  decoration by the sabotage test. ExertionMeter runs but feeds telemetry
  only; fatigue gates no decision. The claim survives and sharpens: CORE has
  drive objects and no chooser.

STAGE-COVERAGE AUDIT: 7 of 9 covered; non-text ingest uncovered (59
sensorium modules reach no serving path); the cycle's runner claimed-only.

CROSS-CUTTING: F-1 built-and-off is the dominant pattern (17 flags default
False, only deduction_serving ratified ON) with no register of the set or
what would flip it. F-2 enforcement lags capability — orphaned pins are
undetectable because suite tuples are hand-curated. F-3 formation's
six-boundary trust standard is not applied at M2, the surface facing
untrusted user text. F-4 ADR/code contradictions are load-bearing (the
daemon has no ADR while ADR-0146 rejected the daemon shape; ADR-0252's
"34 surface organs" does not reproduce — 18 found). F-5 what is excellent,
stated plainly: the typed learning boundary, selection-not-rewrite, the
non-hardening invariant, fail-closed lane shapes.

No layer is wrong-solution. Two candidates deferred to Phase 4 with
evidence: Wilson/replay independence in licensing, DriveGradientMap.
2026-07-27 15:45:52 -07:00
Shay
8f2e238dc7 docs(assessment): Phase 1 — layer taxonomy and card schema (the keystone)
Fixes the decomposition Phases 2-3 fill and Phase 4 audits against, per
Pillar I: criteria before judgment. Five decisions recorded explicitly:

- D1 The five unreconciled articulations answer five DIFFERENT questions
  (what for / what happens today / by what mechanism / judged how / built
  where) and become axes of one taxonomy rather than competitors. Functional
  axis = the AGENTS.md north star; structural axis = 7 macro layers (M0
  Substrate, M1 Knowledge & Memory, M2 Afferent, M3 Comprehension &
  Reasoning, M4 Expression & Serving, M5 Learning & Growth, M6 Continuity &
  Process) + 2 cross-cuts (MG Governance, MV Verification), grouping all 33
  system-map zones with straddles recorded honestly. Mind-physics blueprint
  dispositioned element-by-element (identity/compositional/renderer =
  superseded by stronger implementations; Allocation Physics and
  DriveGradientMap = orphans -> Candidate Register).
- D2 System-map schema EXTENDED, not replaced: 7-value liveness kept;
  four missing dimensions added as orthogonal fields — evidence (with
  would-fail-if-absent sabotage bit), capacity (designed/measured/ceilings),
  design-vs-build split, fitness (6-value: fit/strained/misplaced/
  wrong-solution/superseded-in-place/undetermined). Liveness ⊥ fitness:
  the 34 organs are live-serving + superseded-in-place, a state no merged
  enum could express.
- D3 Completeness criteria fixed: coverage = evidence-bearing ownership of
  a functional stage; deferred-with-ruling ≠ missing; pins must run.
- D4 Candidate Register: CR-1 attention/allocation (the ~73%-of-turn-time
  hot path has no owner), CR-2 agenda/drive (mechanisms without a chooser;
  L10 risks landing as a heartbeat with nothing to want), CR-3 efferent
  action (needs an explicit deferred-vs-out-of-telos ruling), CR-4 temporal
  self-location (determinism vs lived time, undesigned). Considered-and-not-
  registered recorded (sociality, affect, embodiment, self-governance-as-
  layer) so the boundary is visibly examined.
- D5 Assessment output is COMMITTED (reviewable, versioned, visible);
  system map stays local as a regeneratable navigation index. Staleness
  handled by verified_at SHA stamps on every card.

Phase 2 work order: nine layer cards, priority M6 > M3 > M4 > M5; re-verify
every map liveness label; run the stage-coverage audit with evidence; the
four zero-subsystem zones scoped honestly and queued first for Phase 3.
2026-07-27 15:28:42 -07:00
Shay
9267506a42 docs(assessment): Phase 0 — ground truth, corpus triage, and the Phase 1 handoff
Opens the holistic macro→micro assessment of CORE's cognitive-cycle design
and implementation fulfillment. Read-only; no runtime behavior changes.

Method is governed by docs/conceptualizing_engineering_mastery.md (committed
here because the assessment cites it as its method): Semantic Rigor,
Mechanical Sympathy, Third Door; delete before optimizing; automate last.

Phase 0 findings:

- 0-A CORE carries FIVE unreconciled macro articulations of its own cognitive
  cycle (AGENTS.md north star; AGENTS.md live path; the 2026-05-12 draft
  three-physics-layer blueprint; ratified ADR-0252's five paradigm stages; the
  system map's 10-band L0-L11 layering). The blueprint's allocation/attention
  layer (Salience/Attention/Inhibition) exists in no other articulation.
- 0-B .system-map/ is the best existing macro→micro artifact and is 48 days
  stale, gitignored/local-only, and carries four zero-subsystem zones — three
  of which sit on the serving path.
- 0-C ADR-0252 §5 — the experiment gating the governing paradigm's build — has
  never returned a verdict (two unmerged rnd/ worktrees). The 34 surface organs
  the ADR diagnoses as the error keep serving. L10 blocks all 7 OPEN W-* items;
  field + T1 vault are discarded on exit, so "one continuous life" is today
  many short lives sharing a checkpoint.
- 0-D No live gap register: docs/gaps.md is 26/26 closed, the liveness ratchet
  is L10-blocked and stale, docs/analysis/ is a chronological archive.
- 0-E 333 ADRs, flat numbering, status not reliably machine-extractable;
  INDEX-by-domain is 21 files behind; the Blueprint reserved 0254-0261 for
  intents never materialised while those numbers were minted for other work.
- 0-F Measured: CGA is ~73% of turn time via cga_inner/geometric_product
  (~34k calls/turn), NOT versor_condition (0.22%). Rust backend off by default,
  parity question open and blocked. MLX in no runtime path.
- 0-G The 2026-07-25 verification precedent falsified ~1/3 of an external
  blueprint's items by reading code — the bar for every downstream phase.

Phase 1 receives the system map's 17-field proto schema and 7-value liveness
vocabulary, plus the four dimensions it lacks (evidence pointer, capacity,
design-vs-built, fitness judgment) and five decisions to make explicitly.

PR #138's fabrication findings are recorded as measured-and-pinned, held for
ADR + ratification — never re-discovered, never fixed here.
2026-07-27 15:19:54 -07:00
Shay
c69f99485d feat(evals): measure the reader/writer construction inventories against each other
Phase 5 item 1 asked for the overlap to be sized "by measuring the reader's
construction set against the writer's rather than by growing corpora blindly".
New lane `evals/construction_inventory` does that: it sweeps the writer's whole
parameter space (every RhetoricalMove x IntentTag x predicate x quantifier x
tense x aspect, both public entry points, 32292 cells), quotients it by the
reader's OWN function-word skeleton, and comprehends each construction under
three vocabularies.

  writer constructions            1739
  reader constructions              19   (mint-site AST-guarded)
  overlap, faithful                  6
  accepts but MIS-READS             22
  refuses                         1711
  vocabulary-dependent               0

Faithfulness, not acceptance, is the criterion — and that reverses two claims
in the plan's §6 RESULT, using a metric that was already on the page:

* g_read_rate is 1/293 but **g_args_rate is 0.0**. The one surface that "reads",
  `all molecules are defined as compounds`, is comprehended as
  `subset(molecule, defined_as_compound)` — the reader chunked the writer's verb
  phrase into a class name. It accepted; it did not comprehend.
* "one construction wide" was wrong both ways: zero on that corpus, six over the
  writer's actual output space. A corpus cannot report an inventory's size.

Three findings re-order Phase 5 item 1:

1. The reader FABRICATES on 22 constructions — neither reads nor refuses.
   `every dog is a mammal` -> member(every_dog, mammal);
   `furthermore, all dogs are mammals` -> asserted(furthermore).
   Both are ordinary user English, not writer artefacts. Root cause: _RESERVED
   lacks the function words the writer emits, and _parse_propositional accepts
   any single token as a fact.
2. It reaches SERVED output. deduction_surface recites
   `Given: furthermore; p implies q; p.` — a premise the user never stated — and
   chat/runtime.py realizes declarative turns into the held self, so a
   fabricated atom is vault-writable. Widening the inventory first would widen
   the fabrication surface with it.
3. ADR-0265's defect class survives inside ADR-0265's designated owner of clause
   grammar: the four aspect arms of _inflect_predicate bind `negated` to a
   wildcard and never read it, so `dog has been defined as mammal` serves both
   the assertion and the denial (10530/16146 points). Unreachable today — no
   producer sets aspect — so a loaded gun, not a casualty. It survived because
   ADR-0265's invariant is structural (is `negated` threaded?) and cannot see an
   arm that receives the flag and ignores it. A behavioural sweep can.

The two fixes are written already, as the mutations that turn the defect pins
red. They are NOT applied here: they change what CORE comprehends from user
input, which is a serving change on the truth path — authorization-gated, ADR
first, on the ADR-0261 §5.1 refuse-don't-drop precedent.

Guards, so the tables cannot rot: reader constructions are pinned to an AST
count of the reader's 10 mint sites; the declared tense/aspect axes are pinned
to _inflect_predicate's match arms; the committed corpus is pinned to the
fillers in use. 14 pins, 13 mutations observed RED.

[Verification]: in-worktree, CPython 3.12.13, uv sync --locked.
  deductive 517 (was 503, +14 — count moved, registration confirmed)
  smoke     641 (unchanged; the file is registered in `deductive`)
  lane SHAs 11/11 match, no pin edited
  pyright   0 errors on all new files
  mutations 13/13 red, including both fabrication fixes
2026-07-27 14:11:07 -07:00
Shay
892e503347 docs(adr): ratify ADR-0265 — Accepted, Joshua Shay 2026-07-27
Serving-path authorization for the negation fix in this PR. The ADR is
Accepted rather than Proposed because the defect it removes was LIVE behind a
shipped flag (realizer_grounded_authority), not latent: CORE served the
affirmative of propositions users denied.
2026-07-27 13:17:57 -07:00
Shay
dccce6a7b9 fix(generate): a proposition graph must be able to represent denial (ADR-0265)
CORE served the affirmative of propositions the user denied. Measured live on
main @ 536d6e55 with realizer_grounded_authority=True:

    "evidence does not support truth" -> 'Evidence is verified: what supports truth.'
    "evidence supports truth"         -> 'Evidence is verified: what supports truth.'

Byte-identical.

Phase 4 (#136) pinned "render_semantic has no negated parameter" as a defect.
Sizing the exposure before acting on it -- the step §7 requires -- showed the
pin was correct and INCOMPLETE. The defect is two drops in series, and the
first is in the graph:

  intent.negated      parsed from the user's words, always has been
  GraphNode           NO FIELD FOR IT  <-- first drop
  ground_graph        rebuilds nodes field-by-field; nothing to carry
  depth enrichment    rebuilds nodes field-by-field; nothing to carry
  plan_articulation   cannot carry what the node does not hold
  realize_semantic    never read step.negated
  render_semantic     no parameter for it                <-- second drop

Five separate constructors, each silently defaulting to the affirmative.

Why every gate was green
------------------------
On the default config the ungrounded realizer emits "...", _is_useful_surface
rejects it, and the runtime echo wins the resolver -- and the echo contains the
user's own "does not". The truth path was correct BY ACCIDENT. ADR-0088 Phase B
grounds the graph first, which is exactly when the realizer's surface becomes
useful enough to win. So the defect sat behind a shipped flag, invisible to
every property-of-one-surface test, because nothing compared a denial to its
assertion.

Delivered
---------
- GraphNode.negated, threaded intent -> graph -> ground -> enrich -> step ->
  surface. Serialized ONLY when True, so every pre-existing as_dict and every
  trace_hash folded from one stays byte-identical -- which is why no lane pin
  moves.
- Clause grammar delegated to its one owner. Four of the eight intent
  "templates" were never frames; they were plain clauses. They now call
  render_step. Writing a second negation implementation in semantic_templates
  was the tempting fix and is rejected: Phase 2A spent a unit giving every
  linguistic fact one owner, and that design rebuilds the disease one level up.
- Frames that keep a finite verb (VERIFICATION, PROCEDURE, COMPARISON) get an
  explicit negated form. RECALL is a speech act with no proposition to deny, so
  it falls back to the clause path rather than drop the denial (ADR-0261 §5.1).

Measured
--------
    delegation on affirmatives          192/192 byte-identical
    serving realizer, all corpora       85/347 -> 109/347   (+24 = the denials)
    feature-bearing bucket              49/214 -> 73/214
    CONTROL (nothing droppable)         33/33  -> 33/33     unchanged
    multi-node (clause joining)         3/100  -> 3/100     unchanged, out of scope
    lane pins                           11/11 byte-identical, none edited

The control staying 33/33 and multi-node staying 3/100 is the evidence that
this moved the denials and nothing else.

The exhaustive control earned its keep immediately
--------------------------------------------------
It found FIVE more intents serving a denial as its own assertion --
TRANSITIVE_QUERY, FRAME_TRANSFER, NARRATIVE, EXAMPLE, DEDUCTION -- because an
intent with no frame fell back to the UNKNOWN *template* (which cannot say
"not") rather than the UNKNOWN *clause* (which can). The default is now the
capable path, so the next intent added inherits correctness. Found by the
control, not by inspection.

Mutation -- every link reverted individually
--------------------------------------------
    baseline                                        19 pass
    graph_from_intent drops intent.negated           8 FAIL
    plan_articulation drops node.negated             6 FAIL
    ground_graph drops it on rebuild                 2 FAIL
    realize_semantic stops passing step.negated      6 FAIL
    unframed intents fall back to the template       1 FAIL
    pipeline depth-enrichment drops it               1 FAIL

The last row was GREEN on the first run -- my fix there was unguarded. That is
what added the structural invariant: every GraphNode(...) built on the serving
path must NAME `negated` or be recorded in an allowlist with a reason. The
defect was five constructors; a per-site test must be written per site, and a
site added without one is invisible. Only recognition/connector.py is exempt
(an EpistemicNode has no polarity to carry).

Four Phase 4 pins are revised, not relaxed -- #136's M2 mutation ("render_semantic
GAINS a negated parameter -> FAIL") has now happened for real, and forced the
deliberate revision it was built to force.

Registered in `smoke` in the same PR that creates it, per the #136 finding that
an unregistered pin runs nowhere.

Still unexpressed, deliberately: quantifier, tense, aspect. NO PRODUCER sets
them anywhere on the serving path, so threading them would be machinery with no
caller. render_step already handles all three the moment a producer exists.

[Verification]: in-worktree on CPython 3.12.13 with `uv sync --locked` --
smoke 641 (was 621), deductive 503, lane pins 11/11 with no pin edited.
2026-07-27 13:08:17 -07:00
Shay
7d6760ae5b feat(evals): the fluency lanes report the writer that actually ships (Phase 4)
Phase 4 of the grammar-unification arc, resolved by option (b) of the plan.

The problem: 149 green fluency cases scored `realize_target`, and
`core/cognition/pipeline.py` never calls it -- it calls `realize_semantic`.
`english_fluency_ood` reported 117/117 + 39/39 for a function that does not
speak, for the whole life of the lane.

The plan framed this as "which realizer is better". Reading the source says it
is not a quality question. `render_semantic`'s signature is
`(intent, subject, predicate, obj, secondary, language, root)` -- no `negated`,
no `quantifier`, no `tense`, no `aspect` -- and `realize_semantic` never reads
them off the step. So the serving writer cannot express content the
ArticulationStep is carrying:

    negated=False -> 'Knowledge is defined as opinion.'
    negated=True  -> 'Knowledge is defined as opinion.'

It serves the AFFIRMATIVE of a negated proposition. That is the ADR-0261 §5.1
family, not a fluency defect. Pinned here as a defect; NOT fixed, because
fixing it changes served output and belongs to Shay.

Measured -- identical contract, all seven scored corpora
--------------------------------------------------------
    bucket                                n     realize_target   realize_semantic
    feature-bearing, single node        214          207               49
    no features, multi-node             100          100                3
    no features, single node (CONTROL)   33           33               33
    total                               347          340               85

The control is what makes the rest mean anything. Every corpus hardcodes
IntentTag.UNKNOWN, so "the serving writer scores badly" could have been an
artifact of never giving it a real intent. On the 33 cases carrying nothing it
cannot express, the two are IDENTICAL -- so the gap on the other 314 is the
dropped features and the clause joining, not the intent.

Delivered
---------
- `grammatical_coverage/runner.py` reports `serving_accuracy` beside
  `accuracy`; `english_fluency_ood` delegates to that run_lane and gains it
  for free. The realizer is a parameter now instead of a hardcoded import.
- `tests/test_phase4_realizer_resolution.py` -- the control, the decomposition,
  the negation defect pin, and the §6 evidence.
- The claim is restated at both places it was made: the lane docstring and
  `grammar_roundtrip/contract.md`.

Zero served bytes change. No serving authorization needed.

§6 -- the plan's pre-commitment was wrong, and is corrected rather than
quietly edited
------------------------------------------------------------------------
§6 forks on read_rate: risen => Phase 5 diversity; near-zero => §1.8 graph-model
mismatch => ADR. It pre-committed to the second. Neither is what the
measurement says.

g_read_rate went to 1/293. The unblocked case was blocked by a one-line WRITER
defect (predicate-nominal object agreement, #135), not by §1.8. The other 292:

    no_template_match     289   reader has no SUBJ-VERB-OBJ template at all
    unknown_morphology      2   prepositional objects (reserved_word_in_np)
    unsupported_negation    1   reader has no negated-categorical template

Every one is the reader declining a CONSTRUCTION, not a projection disagreeing
about a graph it parsed. Where a construction is in both inventories the round
trip closes exactly. So the barrier is the OVERLAP of the two construction
inventories, currently one construction wide -- Phase 5's item 1, tractable,
not an ADR-scale model decision.

§1.6 read "uniform no_template_match" as evidence FOR the type mismatch. It is
not: no_template_match is a coverage fact, and the corpus was 289/293 bare
transitives, a construction the reader never claimed to read. The measurement
was mostly reporting the corpus's composition.

And a finding in my own stack
------------------------------
`tests/test_realizer_quantifier_agreement.py` lived ONLY in the `cognition`
suite, which is not on the AGENTS.md pre-push gate. So every pin Phases 3 and 4
added to it -- including the invariant covering all twelve inflection branches
-- ran in NO gate. That is why smoke stayed at 621 across two PRs that added 13
tests between them, and it is the same silent-red shape the smoke list already
calls out for test_adr_index.py.

Registered into `deductive`, which now runs 504 instead of 406.

Mutation
--------
    baseline                                             12 pass
    serving metric computed with realize_target           4 FAIL
    render_semantic GAINS a `negated` parameter           2 FAIL
    realize_semantic delegated to realize_target          6 FAIL

Row 2 matters most: if someone FIXES the negation defect, the pin forces a
deliberate revision instead of passing silently.

Still open, still Shay's: option (a), promoting realize_target to the serving
path. It now has a price tag -- 340/347 over 85/347 and the ability to say
"not", against a move in every surface hash and whatever the Shadow Coherence
Gate ruling in core/cognition/surface_resolution.py was protecting.

[Verification]: in-worktree on CPython 3.12.13 with `uv sync --locked` --
smoke 621, deductive 504 (was 406; +12 new Phase 4 pins, +86 previously
ungated), lane pins 11/11 unchanged, no pin edited.
2026-07-27 11:57:48 -07:00
Shay
997a6eb043 fix(generate): plural-subject agreement, 26/26 (Phase 3)
_inflect_predicate applied base_form — a SINGLE-VERB function — to whole
humanized predicate phrases, stripping the final word's last character
class, and its plural branch never consulted `copular`. Nine of the 26 seed
predicates came out wrong and every multi-word one did:

  is defined as         -> "is defined a"     want "are defined as"
  has the following ... -> "...step"          want "have the following steps"
  belongs to            -> "belongs to"       want "belong to"
  causes                -> "caus"             want "cause"

Root causes, both single-point as §1.4 predicted:

1. morphology.agree_plural_phrase inflects the HEAD (the finite verb is the
   first token of every humanized predicate) and carries the rest through.
   base_form stays single-verb and is PINNED as still wrong on a phrase, so
   nobody "fixes" the symptom in the wrong place.
2. The plural branch now agrees the phrase. The plural NEGATED branch had the
   same defect ("do not is defined a") and is fixed with it: a plural copula
   takes a bare "not", everything else takes do-support.

Two further defects found while writing the eval cases, both fixed here:

  base_form("causes") -> "caus". The -es sibilant rule fired on a stem
  ending in a single "s"; it must require a doubled "ss" ("passes"->"pass"),
  because a single "s" is nearly always a stem ending in "e" that took a
  plain -s ("causes"->"cause").

  pluralize("proof") -> "prooves". f/fe -> ves is NOT productive in English
  (proof->proofs, chief->chiefs, roof->roofs); it is a closed set. Now
  derived as lexicon.VES_PLURAL_SINGULARS from the ves-rows of
  IRREGULAR_SINGULARS, so the rule cannot claim a word the table does not
  know. Phase 2B had put pluralize on the SERVING path, so this one was live
  — and the surface-hashing pin from #133 confirms no served surface moved,
  which is that guard's first real use.

New construction C14 quantified_copular, 13 cases: the combination that was
broken was the one never tested. Measured before: 152 corpus cases carry a
quantifier and ZERO combine it with a copular predicate.

MY FIRST DRAFT OF THOSE CASES COULD NOT FAIL. must_contain/word_order listed
quantifier, subject and object but NOT THE VERB, so "all molecules is
defined a compound" passed and reverting the fix left 47/47 green. Rewritten
with the agreed verb in both constraints plus reject_surfaces carrying the
ACTUAL pre-fix output, computed by running the reverted code rather than
guessed. Mutation now:

  baseline                      13/13 C14
  revert phrase-head agreement   4/13
  revert -es stem rule          12/13
  revert closed ves set         11/13

The 4 survivors of the first mutation are the mass-noun controls, which is
correct — "all evidence is grounded in truth" must NOT pluralize.

Same lesson as the lane pins in #133: a pin that cannot fail guards nothing,
and the only way to know is to break the thing on purpose.

grammar_roundtrip's graph corpus is harvested from the committed case files,
so it grew 280 -> 293. Updated exactly rather than loosened to an inequality:
a corpus that grows or shrinks should require a deliberate edit.

[Verification]: in-worktree on CPython 3.12.13, uv sync --locked —
agreement 26/26 (from 17/26); english_fluency_ood 117/117 + 39/39 + dev
13/13 unchanged; grammatical_coverage v1 49/49; smoke 621 unchanged;
deductive 405; scripts/verify_lane_shas.py 11/11, no pin edited.
2026-07-27 10:11:22 -07:00
Shay
f9e9cc0c6a fix(evals): the deduction lane hashes the prose it serves
The pinned artifact carried verdict counts only. The runner's docstring
justified that: prose is "presentation, not decision", so the pinned bytes
"stay stable against wording-only changes; wording is covered by
tests/test_deduction_surface.py."

Measured, that justification was false. With render._display_noun sabotaged
so every clause reads "all SABOTAGE_dogs are SABOTAGE_animals":

  11 lane SHA pins                     -> 11/11 byte-identical, blind
  test_deduction_serve_lane + _license -> 20 passed, blind
  tests/test_deduction_surface.py      -> 41 passed, blind
                                          (the named wording guard)
  evals/grammar_roundtrip              -> RED, the only witness

So CORE's user-visible output was unguarded by its own hash pins, which is
how the ratified v1b band served "all dog are mammal" for the entire arc
with wrong=0 intact.

build_report now emits surface_sha256 + per-case surfaces from the real
deduction_grounded_surface — the same call chat serving makes, so what is
hashed is what a user reads. Note build_combined_report re-projects five
named fields per split, so a field added to build_report alone never reaches
the pinned bytes; both had to change. The payload is not the report.

Surfaces are recorded, not just digested, so a moved pin shows the exact
sentence that changed in review instead of an opaque hash to go re-derive.
2,766 -> 37,280 bytes.

  deduction_serve_v1 under sabotage: BEFORE byte-identical (blind)
                                     AFTER  52370b73 vs c855d55c (RED)

Re-pinned surgically, one line, old hash recorded beside it, never --update.
Verdicts untouched: 166/166 correct, wrong=0. The hash moved because the
payload grew.

test_surface_hash_moves_when_the_renderer_is_sabotaged makes it permanent:
it corrupts the renderer, requires a digest to move, AND asserts the
aggregate counts are unchanged — proving the digest tracks PROSE rather than
smuggling in a decision change. A pin that cannot fail guards nothing.

Accepted cost: wording-only changes now move this pin. That is the intent —
a wording change IS a user-visible change and should require a deliberate
re-pin. The other 10 lanes are untouched; deduction_serve was fixed because
it is the one demonstrably serving prose to users.

[Verification]: in-worktree on CPython 3.12.13, uv sync --locked —
smoke 621 unchanged; deductive 405 (403 + 2 new);
scripts/verify_lane_shas.py 11/11 with the new pin, and 10/11 (RED on
deduction_serve_v1) under the sabotage it previously could not see.
2026-07-26 19:45:48 -07:00
Shay
5ba1359a79 fix(generate): CORE writes plural nouns in categorical clauses (Phase 2B)
The ratified, flag-ON v1b categorical band served "all dog are mammal" for
every noun: the A/E/I/O templates supply all/no/some + are, which demand a
plural, and the slots were filled with singular canonical entity ids. Four
of the 47 ratified corpus cases were affected, and wrong=0 never noticed
because every verdict was correct — only the prose was wrong.

Two fixes that compose into a closed round trip:

1. generate/morphology.py becomes the single owner of the number RULES
   (Phase 2A gave the TABLES one owner). Both directions live there:
   pluralize + singularize, over lexicon.IRREGULAR_PLURALS /
   IRREGULAR_SINGULARS, with mass-noun and compound-head handling.
   templates.pluralize and reader._singularize were two independent copies
   of the regular suffix rules that disagreed about which irregulars they
   knew; both now delegate.

2. render._display_noun re-inflects at render time and swaps _ for a space,
   so compound ids read "guard dogs" rather than "guards dog" or
   "guard_dog".

Result: reader gives wolves -> wolf, renderer gives wolf -> wolves. The
round trip closes.

  0 malformed of 47 (from 4)
  reader-vs-reader singularization 20/20 (from 8/20), 0 silently wrong
  s_surface_match_rate 0.0 -> 0.625, now EQUAL to s_renderable_rate, so the
    only remaining losses are surfaces the renderer cannot express at all

NEAR-MISS — widening a reader broke soundness, caught by the lane:

Routing the reader through the full 29-entry table first produced wrong=1 on
band v6-EX. ds-ex-0012 ("No fish are mammals. Therefore some fish are
mammals.") answered invalid where gold is refuted.

_singularize returning None makes the reader REFUSE, and the serving
composer tries the categorical band FIRST, falling through to more capable
bands. "fish" previously returned None (it matches no suffix rule), so v6-EX
got the case and decided it correctly. Resolving "fish" made v1b accept a
sentence it cannot decide.

Fixed by a principle, not a patch: a number-INVARIANT form is ambiguous in
number — "fish are mammals" is plural, "a fish is a mammal" is singular, and
the token cannot tell you which — so a reader that must not guess number
declines it. lexicon.INVARIANT_NUMBER is derived from the singularizer's own
key == value rows, so the rule cannot drift from the table.

  => Coverage and correctness are different axes. Fixing a corrupted VALUE is
     safe; widening ACCEPTANCE changes which band answers, and in a
     first-match composer that turns a right answer into a wrong one. Same
     family as ADR-0261 5.1 refuse-don't-drop.

Also fixed, found by testing the inverse law: the two number tables were not
mutual inverses. The pluralizer lacked cactus->cacti, fungus->fungi,
die->dice and the invariants aircraft/means/offspring, so CORE could read
"cacti" but wrote "cactuses", and would have written "aircrafts", "meanses",
"offsprings". No round trip can close across a non-invertible table.

THE LANE SHA PINS ARE BLIND TO SERVED ENGLISH:

2B changed 4 served surfaces and the pins came back 11/11 byte-identical.
The deduction_serve_v1 hashed report holds only n/counts/by_gold/
correct_by_gold/all_cases_correct/mismatch_examples — no prose at all.
Confirmed by sabotage: with _display_noun returning "SABOTAGE_" + ..., so
every clause reads "all SABOTAGE_dogs are SABOTAGE_animals",

  11 lane SHA pins                        -> 11/11 byte-identical, blind
  test_deduction_serve_lane + _license    -> 20 passed, blind
  Phase 1 grammar_roundtrip               -> 3 tests RED, caught it

This corrects #129: byte-identity of the pins is the arbiter for values and
verdicts, NOT for surface text, so the 2A/2B split is not a partition of
"changes users can see." Phase 2A's 11/11 conclusion still stands on its own
independent evidence (24/24 table-equality checks vs the pre-migration
literals), but the justification was thinner than stated.

Before Phase 1 no test in the tree would have noticed CORE's served prose
turning into word salad. That is exactly how "all dog are mammal" survived
on a ratified flag-ON band with wrong=0 intact.

Deliberately not fixed: mass-noun verb agreement ("all evidence ARE truth"
should be "is"). The copula is fixed text inside the templates, so agreeing
it changes the template shape rather than a slot, and no mass noun reaches a
categorical clause in any serve corpus. Recorded in render.py.

No lane pin edited — none moved.

[Verification]: in-worktree on CPython 3.12.13, uv sync --locked —
smoke 621 unchanged; deductive 403 passed (383 + 20 new/rewritten);
scripts/verify_lane_shas.py 11/11 (see blindness note above — gate on
grammar_roundtrip for surface work, not on these pins).
2026-07-26 18:42:42 -07:00
Shay
df6332fcb5 refactor(generate): one owner per linguistic fact (Phase 2A)
Collapses the duplicated closed English tables into generate/lexicon.py.
Measured on main @ 0948f7cd: the same facts were encoded up to five times
across the reading and writing paths, each copy written independently as a
successive deduction band landed.

The 2A/2B split is decided empirically per #129 — make the change, run the
11 pinned lanes, let byte-identity rule. Result: 11/11 byte-identical, so
this entire unit is 2A and nothing spilled into the authorization-gated 2B.

Collapsed to one object:
  - connectives    3 identical copies (member, verb, cond_member)
  - predicate display  2 identical 26-entry copies (semantic_templates,
                       templates)
  - be-form inventory  5 copies, not the 2 §1.3 counted. The structural
                       test found realizer_guard._BE_AUX and
                       chat/runtime._BE_FORMS, which a COPULA-shaped grep
                       could never see.

Derived rather than duplicated, so they can no longer drift:
  - STRUCTURAL = CONNECTIVES | {therefore}
  - QUANTIFIER_TOKENS = QUANTIFIER_LEAD | QUANTIFIER_NON_LEAD
  - NEGATION_BEARING_WITH_NOT = NEGATION_BEARING | {not}
  - READER_IRREGULAR_SINGULARS = IRREGULAR_SINGULARS restricted to 8 keys

§1.3 re-measured while doing this, and it overstated the disease (plan
§1.9). Almost nothing contradicts:
  - the "3 divergent plural tables" are 1 pluralizer + 2 singularizers,
    i.e. inverse directions; comparing them as copies is a category error
  - reader's 8 singulars are a STRICT SUBSET of member's 29 with the
    difference in reader's favour empty, so its values are all correct and
    only its coverage is short
  - the "3 divergent quantifier sets" are 3 distinct facts; every/each lead
    a clause but take singular nouns, so their absence from
    PLURAL_QUANTIFIERS is correct
  - english's negation set is a subset of member's, and the difference is
    principled: v2-EN normalizes "<copula> not", v3-MEM refuses it

Exactly one genuine contradiction exists in the whole inventory:
discourse_planner maps is_defined_as -> "is" where the others map it to
"is defined as". Preserved as a register choice, pinned by test.

Found while reading, pinned as current behaviour for 2B to flip:
reader._singularize does NOT refuse uncovered plurals despite a comment
claiming it does — it falls through to a bare -s strip, so news -> new and
species -> specy, exactly the corruptions member.py's table comment says
its table exists to prevent.

Two 2A exit criteria were unmeasurable as written and are replaced:
  - "Jaccard -> 1.00" cannot work. measure_grammar_seam.py scans source
    literals, so unifying a fact removes it from those files and Jaccard
    FELL, 0.083 -> 0.023 — the success direction reported by a metric
    shaped to read like failure. Replaced with an object-identity count:
    3 distinct objects behind 8 names, from 8-behind-8. Value comparison
    cannot tell a shared object from two equal copies.
  - "one table per fact" presumed §1.3's inventory was right. The report now
    separates must-be-one-object (all UNIFIED) from related-but-distinct
    (6 relations, all HOLD).

ADR-0258 §5 already decided morphology's destination is a ratified pack,
triggered by a grc/he member band. No such band exists, so the promotion is
correctly deferred and lexicon.py is a staging area, not a rival home — no
new ADR. Destination measured for whoever picks it up: packs/en/morphology
.jsonl has 9 records and zero irregular plurals, features.number has no
consumer, and _pack_morph_roots_for reads a top-level "root" key that 0 of
31 records across all four packs define, so it returns {} every call.

[Verification]: in-worktree on CPython 3.12.13, uv sync --locked —
smoke 621 unchanged; deductive 383 (364 + 19 new);
scripts/verify_lane_shas.py 11/11 byte-identical.
2026-07-26 17:45:27 -07:00
Shay
3c5084ad2d docs(plans): the 2A/2B split is a measured result, not a prediction
Third correction, same session, again from measurement rather than
argument.

I specified the Phase 2A/2B split as "a table is off-serving only when
every caller is proven eval-only," then computed the actual serving import
closure: 227 first-party modules from the six real serving entries, and
ALL 13 table-owning modules are in it — 13 of 13, including
generate/templates.py via pipeline.py -> realizer.py -> templates.py. So
the static test I had just written returns "everything is serving" and
discriminates nothing.

Import-reachable is not call-reachable. Importing a module executes its
table literals (pure data) but not its functions. The chain
realize_target -> render_step -> _inflect_predicate -> base_form is closed
and small enough to verify exhaustively, and realize_target has zero
non-eval non-test callers, so §1.2 and §1.4 stand as written.

But Python call-reachability is not statically decidable in general.
So the arbiter for this arc is EMPIRICAL: make the change, run the 11
pinned lanes, let byte-identity decide. That is stronger than a static
claim because it tests behaviour instead of predicting it, and it is what
the lane pins already encode as doctrine.

Consequence: 2A and 2B are now defined as a RESULT. A change is 2A if the
pins come back byte-identical and 2B if they move. Work the changes in
order, run the pins, let the split fall out — do not argue a change into
2A.
2026-07-26 15:55:03 -07:00
Shay
7acd6b9722 docs(plans): a ratified band serves malformed English; correct Phase 2 risk
Two additions found while designing Phase 1's projection.

1. NEW §1.7 — band v1b serves malformed surfaces today, user-visible:

     in : All dogs are mammals. All mammals are animals.
          Therefore all dogs are animals.
     out: Given: all dog are mammal; all mammal are animal.
          That's valid — all dog are animal follows.

   render.py::_CATEGORICAL_PHRASE uses plural syntax ("all {s} are {p}")
   but interpolates the reader's SINGULARIZED entity ids, with no
   re-pluralization step — because reader and renderer sit on opposite
   sides of the §1.3 seam. Affects every plural noun, regular or
   irregular; 4 of 47 ratified corpus cases that serve a categorical
   clause (ds-v1-0023/0024/0026/0028). wrong=0 is NOT threatened — every
   verdict is correct; this is purely surface.

   Compounding cause: the reader's plural table has 8 entries where the
   v3-MEM band's has 29, so wolves->wolve, knives->knive, leaves->leave,
   lives->live, halves->halve, loaves->loave, thieves->thieve,
   elves->elve. CORE's two readers disagree on 12 of 20 plurals. The
   wrong singulars do not break soundness (uniform deterministic
   bijection) but they are why cause 2 needs a SHARED table.

2. CORRECTED §4 — the earlier draft claimed "Phases 1-3 cannot change
   serving behaviour." That was wrong, and it is the same shape as the
   mistakes this arc exists to fix: I classified a table as off-serving
   without asking which callers reach it. reader.py::_IRREGULAR_PLURALS
   IS serving-reachable (comprehend -> to_syllogism -> v1b ->
   deduction_grounded_surface).

   New rule: a table is off-serving only when EVERY caller is proven
   eval-only. Phase 2 splits on that test into 2A (off-serving,
   byte-identical) and 2B (serving tables + the render fix,
   authorization-gated). Phase 2A now ships a caller-provenance test so
   this check lives in the tree, not in a reviewer's head.

Refs: ADR-0256 (band v1b), ADR-0258 (v3-MEM plural table)
2026-07-26 15:37:26 -07:00
Shay
05c60bb606 docs(plans): grammar unification plan of record
CORE cannot read its own writing. Measured on main @ 9696443a:

- round-trip is 0/280 (0.0%) — all 280 writer-produced surfaces refuse
  with no_template_match; all six readers refuse all writer output,
  including CORE's own live served surfaces (24 refusals, 0 comprehensions)
- 35 word-tables on the reading path vs 9 on the writing path,
  Jaccard 0.083; three divergent _IRREGULAR_PLURALS tables, two of them
  both on the reading side
- 9 of 26 predicates produce wrong plural agreement
  ("all wolves is defined a mammal"); two single-point root causes
- the fluency instrument is decorative: "banana does the." passes all
  five content predicates, and 0 of 280 eval cases exercise the
  quantifier x copular combination that breaks
- the good realizer (render_step, 232 lines) is eval-only; 149 green
  fluency cases score a function that never serves

Central design decision: the instrument IS the fix. Grammaticality cannot
be measured without a grammar, so round-trip forces one artifact to both
parse and generate. Positive AND negative corpora are both load-bearing —
round-trip proves mutual intelligibility, not English quality.

Phases 1-3 cannot change serving; Phase 4 is authorization-gated. Phase 5
is deliberately unestimated until Phase 1 reports. Section 6 makes the
next direction a measured fork rather than an argument, and pre-commits to
the reading that the graph-model mismatch (MeaningGraph vs
PropositionGraph are not type-compatible) is the likelier barrier.

Refs: ADR-0264, ADR-0071/0072 (seeded variation reused for diversity)
2026-07-26 15:28:27 -07:00
Shay
b970054226 docs(adr): ratify ADR-0264 — Accepted, and §4.1 self-superseded by its own R5
Ratified by Joshua Shay 2026-07-26. All nine rules are implemented and merged to
main @ 803703fc: R5-R7 (#120), R9 (#117), R1-R4/R8 (#125). The status line names
the enforcement pins so the record points at what holds it up.

Also discharges a staleness the ratification would otherwise have blessed. §4.1's
heading — "No curriculum band can earn a SERVE license under the current
architecture" — is NO LONGER TRUE of the running system, and it was falsified by
this ADR's own R5: the finding rested on "the premise cap holds a family to <=16
chains", and query-scoped compilation removed that cap. Post-R5, four bands would
earn SERVE the moment a ledger is sealed. Leaving that heading unqualified in an
Accepted ADR is precisely the ADR-0256 shape — a governance record asserting
something false about live code.

The §4.1 CONCLUSION survives, for a different and stronger reason, and the note
says so: reliability is commitment precision and a correct UNKNOWN is a
commitment, so a band clears theta_SERVE on non-commitments alone
(conservative_floor(660,660)=0.990046). Licensable evidence is 99.0-99.98%
non-entailed; max entailed volume in any band is 9. So §4.1's "≥97.6%
non-entailed" arithmetic was directionally right and its CAUSE was wrong — it is
not the premise cap that makes a curriculum license dishonest, it is the absence
of a mix rule. The binding constraint is now the outcome-mix ruling (§5, still
open), and nothing is licensed while it is unmade: the ledger is absent and
`core proposal-queue reseal` refuses to grant a license without
--allow-new-licenses.

[Verification]: in-worktree on canonical CPython 3.12.13 with `uv sync --locked`:
smoke 621 (unchanged), and 335 passed across test_adr_status_governance.py /
test_adr_index.py / test_volume_honesty.py. Status parses as "Accepted …" under
the governance regex and the banned `ratify-on-merge` predicate is absent. No
flag moved; no code changed.
2026-07-26 14:39:56 -07:00
803703fcbf Merge pull request 'Arc reconciliation + arc-close brief — five corrections to my own records' (#126) from docs/arc-reconciliation into main 2026-07-26 20:20:27 +00:00
9d9fa340df Merge pull request 'Rust parity: cargo green, lane parity unfalsifiable — plus core rust build was broken (plan §6A)' (#124) from fix/rust-build-env-and-parity-report into main 2026-07-26 20:20:16 +00:00
be1271af9e Merge pull request 'Audit-ledger R7 — the durable stream carries the post-override turn event (E1 code half)' (#123) from feat/audit-ledger-r7 into main 2026-07-26 20:19:57 +00:00
Shay
923e60c136 docs(arc): reconcile the arc's records with what the units measured
Every correction here is to something I wrote earlier in this arc. Original text
is preserved in place; nothing was rewritten to look right in hindsight.

ADR-0264 §4.2 — CORRECTED block. I sized the eleven band ceilings from per-TERM
exclusivity; `resolve_domain`'s predicate is per-PAIR, which is looser, so every
ceiling was understated and one verdict was wrong: systems_software_causal is
720, not 630, i.e. reachable rather than impossible. Seven of eleven bands
cannot reach 657, not eight. Downstream conclusions survive — physics·modal (480)
is still impossible and philosophy_theology·modal is still the target.

ADR-0264 R5 + DIVISION-OF-WORK ×2 + compile_premises docstring — the
"verdict-identical" claim is exact only where the full family was READABLE. Over
MAX_PREMISE_SENTENCES, full-family compilation refuses outright, so narrowing is
verdict-IMPROVING there. Read literally the unqualified phrasing implies a
17-chain family keeps declining, which is backwards — the opposite of the
unblock.

E2 rename staleness, 4 sites: ADR-0256 (Accepted) named
`gold.py::assert_corpus_sound`, which #119 deleted; the plan's E2 item and
DIVISION-OF-WORK's §2 row, §4 heading and "renaming after that is strictly more
work" reasoning were all still written as pending.

DIVISION-OF-WORK §0b — new status table: which PR each unit is, what stacks on
what, and the four things the units found that the plan did not predict.

Adds the arc-close brief from ARC-CLOSE-TEMPLATE.md. Its §3 (falsified
assumptions) is the section that cannot be recovered from the diff: nine
entries, five of them corrections to my own claims. Records that the
articulation trigger is not merely unmet but STRUCTURALLY unmeetable — the
previous arc left it unrecorded, which is what kept it re-triggerable — and
names four pieces of stale reasoning still in the tree, with files, since that
is the class of staleness nobody greps for.

[Verification]: in-worktree on canonical CPython 3.12.13 with `uv sync
--locked`: smoke 569, deductive 291 — both baseline, since the only non-doc
change is one docstring.
2026-07-26 12:33:30 -07:00
Shay
808f3d32c4 fix(cli): core rust build was broken; report Rust parity as unfalsifiable
Plan §6A, report-only per DIVISION-OF-WORK §4. Both halves ran. The cargo half
is real evidence; the LANE half is vacuous, and saying so is most of the point.

cargo test (PYO3_PYTHON=/opt/homebrew/bin/python3.12, --offline): 43 passed,
0 failed across 7 suites including test_crdt_hash_parity.rs. The plan's read
was right — cargo 1.97.1 and the registry cache are present locally and the
cloud session was blocked only by sandbox network policy.

Lane parity: 11/11 pinned SHAs match under both backends. That number means
nothing. Sabotaging EVERY core_rs callable to raise and re-running under
CORE_BACKEND=rust still gives `lanes: 11/11 match pinned SHAs`, exit 0. Three
sufficient reasons:

- The hot path bypasses the dispatch. algebra.cga.cga_inner is NOT
  algebra.backend.cga_inner; 69 call sites import the hot functions from
  algebra.cga/algebra.cl41 directly vs 24 through algebra.backend. The ~34k
  cga_inner->geometric_product calls/turn that plan §6C calls ~73% of turn
  time never reach the dispatch.
- The dtype gates reject the runtime workload. versor_condition IS imported
  from algebra.backend by pipeline.py:24, and still falls through: the Rust
  arm requires f32 and the wave-field workload is f64.
- Every dispatch arm ends in `except (..., Exception): pass`, so a broken,
  garbage-returning, or absent kernel falls back to Python SILENTLY. That is
  what makes the first two unfalsifiable by lane hashes.

Instrumented under CORE_BACKEND=rust: zero Rust calls in the whole
deductive_logic lane (pure ROBDD, no CGA), and exactly ONE
(versor_apply_with_closure_f64) in a full pipeline turn. CORE_BACKEND=rust is
today a one-call switch.

So §6B ("if parity holds, consider Rust default") rests on a measurement that
cannot fail, and §6C is the real blocker and is upstream of the backend. NOT
acted on — §6.4 says a parity/determinism finding is escalated, not fixed.
Recorded three follow-ups in dependency order: make the dispatch fail loudly,
add a parity test that fails when the Rust path is not TAKEN, only then route
the hot path.

The one thing fixed here is a plain defect blocking the measurement.
`core rust build` — the activation path print_rust_status() itself recommends —
raised `TypeError: _run() got an unexpected keyword argument 'env'` on every
invocation. core/cli.py::_run is injected into cli_rust.cmd_rust_build, which
passes env=rust_build_env() to carry PYO3_USE_ABI3_FORWARD_COMPATIBILITY, and
_run lacked the keyword the CommandRunner Protocol declares. One keyword, plus
tests/test_cli_runner_contract.py deriving the expected keywords FROM the
Protocol so the two cannot drift apart again. Verified end-to-end: `core rust
build` now builds and installs.

[Verification]: in-worktree on canonical CPython 3.12.13 with `uv sync
--locked`: smoke 575 (569 + 6), deductive 291 (unchanged). New tests fail
under mutation (revert the env keyword -> 3 red). Registered in `fast` AND
`smoke`. Installing core_rs does not activate it (_ALLOW_RUST requires
CORE_BACKEND), verified: extension present + no env var -> "rust backend:
inactive", so no default behaviour moves.
2026-07-26 11:50:32 -07:00
Shay
bb95dfbe67 fix(runtime): defer the turn-event sink emission to the serve boundary (R7)
Audit-ledger R7, implementing docs/handoff/curriculum-license-loop-2026-07/
E1-R7-ASSERTION-SPEC.md. `ChatRuntime.chat` seals a TurnEvent before the
surface authority is known; `CognitiveTurnPipeline` back-stamps the resolved
surface and canonical trace_hash onto `turn_log[-1]` after `chat` returns. So
the in-memory log ended up correct and the DURABLE stream carried the
pre-override record — the thing that persists was the wrong one.

Mechanism, per the spec's M1-M3:

- M1 — stage an INDEX into `turn_log`, never the TurnEvent. The flush re-reads
  the log, so every back-stamp is picked up by construction and the staged
  record cannot go stale. Capturing the event object would be the original
  defect with extra steps.
- M2 — `attach_telemetry_sink` does NOT flush a staged turn event, the
  opposite of what it correctly does for `_pending_reboot_payload`. The reboot
  payload is final when buffered; a staged turn event is mid-flight. Flushing
  on attach would silently restore the defect.
- M3 — deferral is opt-in. The pipeline wraps the runtime rather than the
  reverse, so the runtime cannot detect whether a pipeline will run. A runtime
  used directly (eval and demo paths) emits inline, byte-identically and at
  the same moment as before.

The `finally` at the serve boundary is load-bearing (I5): a pipeline that
raises after `chat` returns must still flush, or an error path silently
swallows a telemetry record — worse than the staleness being fixed.

TWO CORRECTIONS TO MY OWN SPEC, reported rather than quietly absorbed:

1. I2 bullet 3 and I3 assert on an emitted `trace_hash`. There is no such
   field — `chat/telemetry.py` never serializes `TurnEvent.trace_hash`, so the
   sink never carried a trace_hash, stale or otherwise. R7 repairs the SURFACE
   only. Pinned by `test_trace_hash_is_not_in_the_wire_format_at_all` so the
   claim cannot be re-asserted; the replay-not-recomputation ruling is kept on
   the object that does carry the hash.
2. The surface divergence is unreachable under the DEFAULT config:
   `finalize_turn_surface` is a no-op on all 36 turns probed, because the
   runtime's own surface already wins the resolver. It fires under
   `realizer_grounded_authority` — 24 divergences over 84 turns. A test on the
   default config would have passed while proving nothing, exactly as the spec
   warned, so the fixture pins that flag and asserts the pre-fix divergence
   exists before asserting it is gone.

[Verification]: in-worktree on canonical CPython 3.12.13 with `uv sync
--locked`: smoke 591 (569 + 22), deductive 291 (unchanged). All seven existing
sink test files pass UNCHANGED (80 tests). warmed_session_consistency
telemetry_consistency_rate = 1.0. Mutation-checked all three mechanisms:
staging the event instead of the index -> 3 I2 tests red; flushing on attach ->
the M2 test red; dropping the `finally` -> the I5 test red. Registered in
`smoke` and `cognition`.
2026-07-26 10:56:03 -07:00
Shay
c1d08ff9d5 feat(curriculum): sealed-practice producer for curriculum serving (Phase C)
Builds the producer `chat/curriculum_serve_license.py` already named as the
only writer of the ledger it reads — `evals.curriculum_serve.practice.runner
.seal_ledger` — which did not exist. Until now, authoring curriculum could
not earn a license at any volume.

A band's case space is ENUMERATED, not authored: every exam question that
routes to that subject (resolve_domain) whose relation is in that family, one
committed case per distinct query atom. So `committed == distinct` is not a
property the generator maintains, it is the same fact twice — the structural
form of ADR-0264 R9, and why this producer cannot develop the deduction
producer's exposure (CASES_PER_BAND=720 filled from spaces as small as 28).
CASES_PER_BAND=660 follows `evals/determination_estimation` per
DIVISION-OF-WORK §4: a ceiling on committed volume, not a quota.

11 bands, wrong=0, inflation 1.0 everywhere.

THE LEDGER IS DELIBERATELY NOT COMMITTED. The plan's Phase-1 exit criterion
asked for "a real (still-unearned) ledger"; that state is unreachable.
Reliability is commitment precision and a correct UNKNOWN is a commitment, so
conservative_floor(660,660)=0.990046 clears theta_SERVE on non-commitments
alone. Sealing today licenses FOUR bands on evidence that is 99.0%-99.98%
correct non-commitment — which ADR-0262 §5.1 rules unacceptable and which
ClassTally structurally cannot see (no verdict axis). Writing the artifact is
therefore a ratification and belongs to Phase D's explicit human-run verb.
The open outcome-mix ruling is now blocking, not academic.

Two corrections to my own earlier records, both measured:

- ADR-0264 §4.2 sized bands from per-TERM exclusivity; the router's predicate
  is per-PAIR, which is looser. Every ceiling was understated, and
  systems_software_causal is 720 not 630 — flipping it from "cannot reach
  657" to "can". 4 of 11 bands can reach the floor, not 3. Now pinned as
  measured values in BAND_ATOM_SPACE.
- `then` and `therefore` are taught philosophy_theology lemmas AND control
  words of the argument reader (`therefore` is the conclusion marker). 164
  routable atoms refuse compiled_premises_unreadable — coverage misses, never
  wrongs — in the band Phase F retargets to. The vocabulary boundary is not
  screened against the reader's grammar; a pack teaching and/or/not/if would
  open a much larger hole the same way. Recorded, not fixed (§6.5), pinned as
  a two-lemma bound.

Registers curriculum_serve in AUDIT_SOURCES, retiring the declared `None`
that was Phase C's forcing function, and adds the producer to `deductive`.

[Verification]: in-worktree on canonical CPython 3.12.13 with `uv sync
--locked`: smoke 571 (569 + 1 test + 1 parametrization), deductive 310
(291 + 19). Mutation-checked: injecting the deduction padding pattern into
band_cases turns 5 tests red including the R9 audit and the seal guard.
curriculum_serve lane unchanged at n=32 correct=32 wrong=0 anti_recall=5;
all 11 lane SHA pins match; no ledger written, so no serving change.
2026-07-26 09:52:54 -07:00
bc8276d693 Merge pull request 'E1/R7 assertion spec + arc status after Phases A and B (closes the Opus share)' (#118) from docs/e1-r7-assertion-spec into main
Reviewed-on: #118
2026-07-25 23:29:20 +00:00
Shay
e58e377e06 docs(handoff): E1/R7 assertion spec + arc status after Phases A and B
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).
2026-07-25 16:11:33 -07:00
Shay
13a4b05d62 test(reliability): volume-honesty invariant + distinct-evidence audit
Phase B of the curriculum-license-loop arc. Stacked on the ADR-0264 branch.

The invariant: conservative_floor is a one-sided Wilson lower bound and Wilson
assumes INDEPENDENT trials. CORE's pipeline is deterministic, so replaying an
identical case is not a second trial -- it is the same trial with a guaranteed
outcome. A ledger's `committed` count is an upper bound on its evidence; the
defensible figure is its distinct-decision count.

Phase B was scoped as a precaution for a curriculum producer that does not
exist yet. Running the instrument against the producers that DO exist found it
already violated:

  21 of the 25 ratified deduction_serve bands do not clear theta_SERVE=0.99 on
  distinct evidence. Three inflate 28 distinct cases into 720 committed
  (honest floor 0.8084 vs claimed 0.9909). deduction_serving_enabled was
  ratified ON 2026-07-24 and is live.

The estimation producer (ADR-0175) is clean -- 660 committed, 660 distinct,
zero repeats, evidently chosen just above the 657 a perfect record needs. So
this is a regression from a standard already established in the repo, not an
architectural gap. `claimed` is identical for all 25 deduction bands because
every band commits 720 with a perfect record: the gate sees 25
indistinguishable passes while honest floors span 0.8084..0.9909.

NOT REPAIRED. The ledger is SHA-sealed, ratified, and gating a live flag, so
re-sealing it is Shay's ratification, not a test's. wrong=0 still holds -- no
band answered anything incorrectly. What is not established is reliability at
the volume claimed. Measured, pinned in both directions, written down.

Added:
- core/reliability_gate/evidence.py -- pure measurement, imported by no serving
  path. volume_for_theta() DERIVES 657 from conservative_floor rather than
  restating it, so a WILSON_Z change cannot leave a stale literal behind.
- tests/test_volume_honesty.py -- 13 tests. AUDIT_SOURCES is checked against
  CAPABILITY_LEDGERS, so a new licensed capability cannot ship unaudited; the
  curriculum entry is a declared None that fails the moment its ledger exists.
  That is Phase C's forcing function against repeating the pattern.
- docs/research/distinct-evidence-audit-2026-07-25.md -- full audit + the open
  outcome-mix ruling for Shay.
- ADR-0264 R9 amended with the measured exposure.

Decision key is case TEXT deliberately: a tighter key (normalized atom) would
collapse spelling variants and report MORE inflation, so text-identity
under-reports and every number is a floor on the real gap.

Outcome mix is deliberately NOT pinned. ClassTally has no verdict axis, the
deduction producer already balances 240/240/240 by construction, and imposing
a per-verdict-class floor would retroactively fail all 25 bands on a criterion
no ADR has ratified (smallest per-band class count is 120). Recorded for a
ruling with numbers instead.

[Verification]: uv sync --locked on canonical CPython 3.12.13; in-worktree
smoke 569 passed (556 baseline + 13), deductive 285 passed. Registered in the
`smoke` curated suite -- not left to `full`, which gates nothing. Mutation-
checked: doubling CASES_PER_BAND -> 2 failed; reporting committed as distinct
-> 4 failed; gating on `claimed` instead of `honest` -> 3 failed; tree restores
to 13 passed.
2026-07-25 16:03:10 -07:00
Shay
a35f6f7bd7 docs(adr): ADR-0264 — negative curriculum, premise scope, and band ceilings
Phase A of the curriculum-license-loop arc. Design only; no production code,
no flag, no default changed.

Rules R1-R9: polarity is a row-level field (absent => affirmative), negatives
compile to the sentential-not form and MUST reuse the affirmative connective
(atom identity), contradiction is rejected at ratification, premise
compilation becomes query-scoped, an empty scope is UNKNOWN, premise_count
keeps reporting family size, the oracle learns polarity independently, and a
band's honest ceiling must be reported before anyone authors for it.

Three findings, each measured in-tree and each reversing something:

1. `polarity` is read by nothing. A row authored to refute a question today
   serves a confident wrong "Yes" -- and the independent oracle ignores the
   field for the same reason, so gold agrees and wrong=0 stays green. This is
   the silent-failure class Phase A existed to prevent, now demonstrated.

2. The 16-premise honesty cap holds a family to <=16 chains; at 17 the band
   declines everything, including the taught positive that works today. So
   ADR-0262 §5.1's own remedy (author ~219 relations) destroys the capability
   at row 17, tripping the fix §6 deferred as a future contingency. Jointly:
   a band has <=16 entailed cases against a 657-committed threshold, so no
   curriculum band can earn SERVE until scoping lands. The blocker is
   engineering, not content -- inverting the conclusion two arcs closed on.

3. Honest volume is bounded by taught vocabulary, not corpus size. 8 of 11
   bands cannot reach 657 at all, including the plan's chosen first target
   physics·modal (ceiling 480, counting every possible question). Retargets
   Phase F to philosophy_theology·modal (ceiling 44,104, same 8-chain start).

R5 is the only rule that could change an existing answer, so it is the only
one carrying evidence: 8,520 routable questions, 0 verdict mismatches for both
candidate scopes. Verdict-identity holds for any scope that is a superset of
the query-atom rows, since each premise mints one independent atom. That is
stronger than ADR-0262 §6's soundness argument, and it is why this narrowing
is not the ADR-0261 §5.1 premise-dropping failure.

ADR-0262 §5.1/§5.2/§6 carry forward-pointers; original text preserved.

[Verification]: uv sync --locked on canonical CPython 3.12.13; in-worktree
`core test --suite smoke -q` 556 passed (555 baseline + 1: the derived
ratify-on-merge pin picked up ADR-0264 and discharged it), `--suite
deductive -q` 285 passed. Four probes reproducible from
docs/research/curriculum-premise-scope-2026-07-25.md; the corpus-mutating one
restores the file and asserts byte-identity.
2026-07-25 15:44:25 -07:00
Shay
55a3f79f18 docs(arc): curriculum-license-loop plan of record + tier division of work
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.
2026-07-25 15:09:09 -07:00
Shay
efd3bee9ce chore(governance): stamp nine merged ADRs Accepted + pin the invariant
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.
2026-07-25 14:38:02 -07:00
Claude
bbca86b72f
docs(register-matrix): 801 passed, 0 failed — clears the DO NOT MERGE on bf17f42
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
2026-07-25 14:45:51 +00:00
Claude
50fa287d17
revert(tests): drop the local-to-CI smoke parity assertion — CI is not a gate
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
2026-07-25 13:44:30 +00:00
Claude
71bf04fb44
feat(provenance,teaching): close the lateral gaps the assessment actually found
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
2026-07-25 04:51:15 +00:00