PR-6 promoted tests/test_safety_pack.py onto the gate and left it listed in
tests/full_only_baseline.txt. test_baseline_has_no_stale_entries failed the very
next smoke run, naming the file and the direction.
That is the both-directions half of the ratchet doing exactly the work it was
written for, roughly an hour after it landed, against the person who wrote it.
A baseline that silently keeps entries it no longer needs stops being a
measurement and becomes a story about one — so the failure is the feature.
Recorded in the pin's own docstring and in G-7, because a mechanism catching its
author is better evidence than a sabotage test: the sabotage was contrived, this
was not.
Baseline: 749 -> 748. The ratchet only turns down.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
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
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
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
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
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
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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
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.
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.
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.
Phase 3 fixed two of the twelve branches of `_inflect_predicate` by hand and
pinned them with a hand-written oracle. The other ten kept handing whole
predicate phrases to single-verb functions, so the same root cause was still
live on **57 of 96** (branch x multi-word predicate) pairs, 9 of 12 branches:
"belongs to" --perfective--> "has belongs toed"
"belongs to" --imperfective--> "is belongs toing"
"belongs to" --past--> "belongs toed"
"is defined as" --future--> "will is defined a"
"contrasts with" --negated--> "does not contrasts with"
Every branch now routes through `morphology.inflect_phrase_head`, which applies
a single-verb inflection to the finite verb and carries tokens 2..n through
byte-identically. Two closed tables were needed because `_base_form` is a
suffix stripper and the head of every copular predicate is a form of BE:
`base_form("is")` returned "i" and `present_participle("is")` returned "iing".
Also fixes predicate-nominal object agreement. `render_step` pluralized the
subject and never the object, so it wrote "all dogs are a mammal". The object
agrees only for a predicate nominal -- a closed set of two -- because a
prepositional object carries its own number and a productive rule would write
"all claims are grounded in evidences".
Measured
--------
tail-mangling pairs 57/96 -> 0/96
g_read_rate (round-trip) 0.0 -> 0.003413 first non-zero in the arc
grammatical_coverage v1 49/49 (2 cases corrected, see below)
english_fluency_ood 117/117 + 39/39 + 13/13 unchanged
discourse_paragraph 12/12 + 6/6 + 5/5 + 1/1 unchanged
zero_code_domain_acquisition 18/18 + 30/30 + 21/21 unchanged
`gram_C14_p01` and `gram_C14_p10` expected "...defined as compound", which is
not English. Corrected, and the old string moved into `reject_surfaces` so the
case now actively rejects what it used to accept. The correction is not my
judgment: the reader independently READS "all molecules are defined as
compounds" and REFUSES the singular form, and that is precisely what took
g_read_rate off zero.
Why the invariant instead of a bigger oracle
--------------------------------------------
A per-branch oracle has to be extended by hand for each new branch, and a
branch added without one is invisible -- which is how eight survived Phase 3.
The pin is structural instead: English marks tense, number and aspect on the
finite verb, so inflection must leave tokens 2..n byte-identical. It needs no
oracle and covers branches nobody has written yet.
It is necessary, not sufficient: "does not contrasts with" preserves its tail
perfectly and is still wrong. `test_do_support_puts_the_head_in_the_bare_
infinitive` is the sufficiency half.
Mutation
--------
baseline 84 pass
inflect_phrase_head applied to the whole phrase 39 FAIL
_IRREGULAR_BASE emptied 2 FAIL
_IRREGULAR_PARTICIPLE auxiliaries removed 2 FAIL
PREDICATIVE_NOMINAL emptied 3 FAIL
PREDICATIVE_NOMINAL widened to every copular predicate 2 FAIL
The last row is the one that matters: the plausible-but-wrong fix -- "pluralize
the object under a plural subject" -- is caught by the mass-noun control.
Not fixed here, deliberately: "has not the following steps" is archaic rather
than wrong, and modernizing it to do-support is a separate judgment call.
[Verification]: smoke 621, deductive 406, lane pins 11/11 unchanged
(grammatical_coverage is not a pinned lane; no pin was edited).
_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.
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.
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).
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.
The measurement foundation for docs/plans/grammar-unification-2026-07-26.md.
WHY: evals/deterministic_fluency reports 1.00 on all six predicates and
still passes "banana does the.", "wet ground rains the is." and
"is is is is." — it checks terminal punctuation, presence of a verb-shaped
token, and two anti-shape regexes. Heuristic predicates will always have
that failure mode, because grammaticality cannot be measured without a
grammar. So this lane measures agreement between the two halves of CORE
that already encode grammar, and requires the measurement to FAIL on salad.
Two directions, reported separately because they fail for different
reasons and have different remedies:
G-round-trip graph -> realize_target -> surface -> comprehend -> graph
S-round-trip surface -> comprehend -> graph -> categorical renderer -> surface
v1 baseline on main @ 9696443a:
graph_cases 280 surface_cases 8
g_write_rate 1.000 s_read_rate 1.000
g_read_rate 0.000 s_renderable_rate 0.625
g_exact_rate 0.000 s_surface_match_rate 0.000
negative_cases 16
reject_rate 1.000
g_read_rate and s_surface_match_rate are pins on measured DEFECTS, not
goals; they may be revised upward only. s_surface_match_rate = 0 is the
§1.7 categorical render defect caught by construction — the lane found it
without being told to look.
g_args_rate and g_predicates_rate are deliberately separate: high argument
agreement with low predicate agreement would mean the grammars align and
only the vocabulary is split, a materially different remedy from both
being low. That distinction decides the arc's direction (plan §6).
Design notes:
- The committed cases.jsonl is the SINGLE source for authored surfaces —
no in-module duplicate, since a second copy of a corpus is the defect
this arc exists to remove. Negative shuffles are DERIVED at run time so
they cannot drift from the positives.
- The shuffles are lexically identical to positives (same vocabulary, same
length, order destroyed) so the lane cannot pass by vocabulary-checking.
- Fixed rotation, not a PRNG, so reject_rate is byte-reproducible.
- The lane keeps a local copy of the reader's quantifier map ON PURPOSE so
it never becomes a consumer of what it measures;
test_quantifier_map_matches_reader fails loudly if the reader changes.
- _render_categorical deliberately reaches a private serving function: a
lane measuring a private copy would measure what users never see.
Every guarantee is paired with a mutation test. The load-bearing one is
test_reject_rate_goes_red_when_the_reader_accepts_everything: an
accept-everything reader must drive reject_rate to 0.0. Without it,
reject_rate == 1.0 would be unfalsifiable — precisely the defect that
makes the existing fluency lane decoration.
Also documents plainly what round-trip does NOT prove: it measures mutual
intelligibility, not English quality. english_fluency_ood accepts "river
flows valley" and round-trip would be happy with it. No metric here may be
cited as evidence of prose quality.
scripts/measure_grammar_seam.py reproduces every number in the plan's §1
so a reader can check them instead of trusting them.
[Verification]: in-worktree on CPython 3.12.13, uv sync --locked —
smoke 621 (unchanged), deductive 364 (349 + 15 new). Lane SHA pins
verified separately. No serving code touched; new files plus one suite
registration line.
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.
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)
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)