feat(tests,assessment): G-23 — the domain/pack binding is stated twice and checked one way

core/capability/domains.py's DOMAIN_PACKS declares domain -> pack membership
for the capability ledger. Each packs/data/<pack>/manifest.json independently
declares domain_id. Two statements of one fact, and only one direction was
ever checked: domain_contract_predicates P3 validates domain_id -> a known
ledger domain. Nothing validated DOMAIN_PACKS -> the manifest agrees.

P3 passes VACUOUSLY on a manifest with no domain_id at all — the same shape as
its own test_pack_without_contract_reports_absent. So a pack that DOMAIN_PACKS
places in a domain, but whose manifest never says so, is invisible to every
check in the repository.

Measured: 7 of 9 bound, 0 contradictory, 2 absent — en_core_cognition_v1 and
en_core_meta_v1, both placed in philosophy_theology, which is one of the four
bands queued to earn a SERVE license behind R-8. The domain whose license is
next to be earned is the one whose binding exists in only one place.

Why nothing caught it: tests/test_domain_contract_predicates.py exercises
P1-P9 entirely against synthetic tmp_path manifests, never against the real
nine, and is itself in no curated suite. A predicate proven correct on
fabricated input and never run on the real input is this arc's defect class
stated precisely — its success state and "it never ran here" are
indistinguishable.

tests/test_domain_pack_binding.py, registered in smoke (on the gate):
  - every pack DOMAIN_PACKS names has a manifest on disk
  - a manifest that CONTRADICTS the table always fails
  - a manifest with NO domain_id fails unless the pack is in UNBOUND_BASELINE
  - the reverse direction: a pack may not self-assign a ledger domain the
    table does not grant (clean across all 30 packs today, pinned because a
    new manifest copying domain_id from a sibling is the obvious mistake and
    would silently widen a licensed domain)
  - both directions on the baseline: a pack that gains domain_id must leave it
  - the parse is guarded against vacuity (N-1 has produced three false
    conclusions in this repo)
  - the count is pinned so the gap moving is a reviewed decision

Four sabotages, each observed red, each caught by its intended pin.

PINNED, NOT FIXED, deliberately. Whether en_core_cognition_v1 is a
philosophy_theology domain pack or merely grouped under that domain for ledger
accounting decides what the license actually covers. That is
ratification-adjacent content, not engineering, and it is routed to R-8 /
PR-14 where that band's license is decided. Sealing that ledger without
answering it licenses a domain whose membership is asserted in one place and
denied by silence in the other.

How it was found, recorded because the method is the point. An external
assessment proposed collapsing the pack/domain machinery, stating its method
up front: "I have enough from the directory and CLAIMS.md to reason
seriously." That is the method AGENTS.md protocol rule 1 forbids, and five of
its six proposals are falsified by opening the file — recorded in full in
31-hindrance-audit.md, in short:

  - conftest.py "almost certainly" holds suite-membership logic: it holds two
    autouse fixtures and two data registries, and its collection hook stamps
    markers and never skips. Zero membership logic.
  - cli_test.py "almost certainly a nested conditional or a big dict": a flat
    dict, 17 keys, zero conditionals in the module body.
  - field/algebra/morphology as a peer triad needing a single serve(): 72, 15
    and 3 importers respectively, morphology is 97 lines, and no def serve(
    exists anywhere in core/, chat/ or generate/.
  - the grade space is richer than the serve layer consumes: all 15
    EpistemicState members have non-test consumers.
  - constraints: blocks in 314 ADRs: that is 314 new places that must be kept
    true, inside ratified immutable records, in the repository whose dominant
    defect class is records drifting from reality. The existing design puts
    the machine-readable half in the checker that reads the ADR, which is
    better on purpose.
  - a root contracts.toml as "the highest-leverage single change": the
    principle is already ADR-0263 Rule 5 and is implemented in the very file
    the assessment cites. Centralizing four tables with four authorities adds
    a fifth copy that must agree with four generators.

The seam it pointed at was real even though every specific was wrong. Bad
method, useful direction; both halves recorded, and the triage rule earns a
second demonstration — every rejected proposal failed on a file its author did
not open.

AGENTS.md — the standing philosophy revised to eleven, per Shay's review.
  - "be quick to be humble" -> "investigate yourself first". Speed is a
    consequence of reflex, not a criterion; writing it in makes velocity sound
    like the point. Examples replaced with three that are verifiable in-repo:
    the H-13 refcount claim, and the two rulings that were about to be
    recorded stronger than they were made.
  - the defect-class principle gains its prescriptive half as its own
    sentence, and gains this commit's instance: a predicate that passes
    vacuously on the input it was written for.
  - NEW #2: don't build a ratchet to guard a ratchet. Placed adjacent to
    "less is more" and named as its corollary so it does not read as
    duplication. The arc paid for it — a membership ratchet needed a
    reachability ratchet to mean anything, and what actually shrank the gap
    was deleting four suites nobody called.
  - NEW #10: a closed issue is not evidence the problem is gone. G-7 was
    CLOSED and REOPENED-PARTLY inside one arc; H-13 was closed with a fix that
    did not work.

Two review points were already satisfied in the file and are recorded as such
rather than re-applied: the defect-class principle already stood alone on its
first line, and already sat at position 3, not 8. The review was reading a
chat summary, not AGENTS.md — which is, exactly, reviewing the record instead
of the reality.

PR-5 additionally owes one page: an index of which declared tables exist and
what pin makes each true. That is the defensible residue of the contracts.toml
proposal, and explicitly not a fifth copy of four tables.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
This commit is contained in:
Claude 2026-07-28 06:13:10 +00:00
parent e6a3038335
commit ca5e614ec8
No known key found for this signature in database
7 changed files with 290 additions and 27 deletions

View file

@ -227,54 +227,76 @@ alias, flag, suite, and document is something that has to be kept true forever.
Prefer removing to adding, and prefer shrinking a gap by deletion (costs Prefer removing to adding, and prefer shrinking a gap by deletion (costs
nothing) over closing it by addition (costs forever). nothing) over closing it by addition (costs forever).
**2. Master the little details and the big ones handle themselves.** **2. Don't build a ratchet to guard a ratchet.**
This is not a metaphor about care — it is a claim about causation. Large The corollary of 1, and the one this repository paid for: when the fix for an
architectural failures in this repo have consistently decomposed into small, enforcement mechanism is another enforcement mechanism, the smell is not
insufficient enforcement — it is that the thing being policed should not exist.
A membership ratchet needed a reachability ratchet to mean anything; what
actually shrank the gap was deleting four suites nobody called, which cost
nothing and needed no pin at all. Look downstream of where the pain is felt.
**3. Master the little details and the big ones handle themselves.**
Not a metaphor about care — a claim about causation. Large architectural
failures in this repo have consistently decomposed into small,
individually-boring divergences that nobody closed. Get the small ones exactly individually-boring divergences that nobody closed. Get the small ones exactly
right and the large ones do not form. right and the large ones do not form.
**3. A mechanism whose failure state is indistinguishable from its success **4. A mechanism whose failure state is indistinguishable from its success
state is not a guarantee.** state is not a guarantee.**
The dominant defect class here: a curated suite no gate invokes, a pin never The dominant defect class in this repository: a curated suite no gate invokes,
observed failing, a digest nothing re-derives, a verdict on an unmerged a pin never observed failing, a digest nothing re-derives, a verdict on an
branch, a decision recorded only in conversation. All of them look exactly unmerged branch, a predicate that passes vacuously on the input it was written
like working. **Red before green** — if you cannot make a new pin fail on for, a decision recorded only in conversation. Every one of them looks exactly
purpose, you have not shipped a guarantee, you have shipped a story about one. like working.
**So design the failure to look different, and prove that it does.** Red before
green: if you cannot make a new pin fail on purpose, you have not shipped a
guarantee, you have shipped a story about one.
**4. Prefer a record that contradicts nothing to a record that impresses.** **5. Prefer a record that contradicts nothing to a record that impresses.**
A document, docstring, banner, or status field that disagrees with the code at A document, docstring, banner, or status field that disagrees with the code at
a load-bearing point is worse than its absence, because it converts *"I should a load-bearing point is worse than its absence, because it converts *"I should
check"* into *"I already checked."* Recorded prose with no re-derivable check"* into *"I already checked."* Recorded prose with no re-derivable
artifact is testimony, not evidence. When a record and reality diverge, that artifact is testimony, not evidence. When a record and reality diverge, that
is a defect with the same severity as a wrong answer. is a defect with the same severity as a wrong answer.
**5. Consecutive unclear problems are a signal to stop and look wider.** **6. Consecutive unclear problems are a signal to stop and look wider.**
One surprising failure is a bug. Several in a row that are not individually One surprising failure is a bug. Several in a row that are not individually
obvious means the frame is wrong. Stop patching, step back, and ask what the obvious means the frame is wrong. Stop patching, step back, and ask what the
failures have in common before writing another fix. Almost always the answer failures have in common before writing another fix. Almost always the answer
is that there is too much going on, not too little. is that there is too much going on, not too little.
**6. Be quick to be humble about mistakes, and quick to investigate yourself **7. Investigate yourself first.**
for them. Always.** When a result is challenged, turn the standard inward before defending outward
When a result is challenged, check your own work before defending it. When you — not because it is polite, but because it is where the error usually is. The
were wrong, say so plainly, fix it, and **record the error next to the fix** H-13 refcount fix was wrong on a claim stated confidently, and its tests passed
rather than overwriting the history so that the tree looks clean. An arc's only because they mirrored a call pattern production no longer used. Two
self-correction chain is the strongest evidence it has; deleting a mistake adopted rulings were about to be recorded stronger than they were made: R-11 B
deletes that evidence. authorizes no gate, and R-8 C's floor had no number. All three were caught by
re-reading my own work against the source, none by anyone disagreeing with me.
Then say so plainly, fix it, and **record the error next to the fix** rather
than tidying the history. An arc's self-correction chain is its strongest
evidence; deleting a mistake deletes that evidence.
**7. Do not cut corners because the plan is large.** **8. Do not cut corners because the plan is large.**
Volume of work is not a licence for lower diligence — it is the condition that Volume of work is not a licence for lower diligence — it is the condition that
makes diligence load-bearing. Meticulousness is cheapest exactly when it feels makes diligence load-bearing. Meticulousness is cheapest exactly when it feels
most expensive. most expensive.
**8. Fix what you find, when you find it.** **9. Fix what you find, when you find it.**
Do not leave a discovered defect unfixed and unrecorded on the grounds that it Do not leave a discovered defect unfixed and unrecorded on the grounds that it
is off-topic. The two exceptions, both explicit: work a later planned item is is off-topic. Two exceptions, both explicit: work a later planned item is
*expected* to fix (say so, and name the item), and work that requires an *expected* to fix (say so, and name the item), and work requiring an authority
authority you do not hold (say so, and prepare it so the decision costs one you do not hold (say so, and prepare it so the decision costs one word).
word).
**9. Let the tree settle, then run the gate once.** **10. A closed issue is not evidence the problem is gone.**
It is evidence that someone decided to stop tracking it. The closure is a
governance artifact; the condition it describes is a fact about the system, and
the two drift. G-7 was CLOSED and REOPENED-PARTLY inside a single arc — the
closure was accurate about *membership* and manufactured confidence about
*execution*. H-13 was closed with a fix that did not work. Before trusting a
closed item, a merged PR, or a passing suite, ask what it actually observed.
**11. Let the tree settle, then run the gate once.**
Never mutate the working tree while a validation run is in flight — the result Never mutate the working tree while a validation run is in flight — the result
describes a tree that no longer exists, and you will not notice. Land all describes a tree that no longer exists, and you will not notice. Land all
edits, let it settle, run once, read the whole output. edits, let it settle, run once, read the whole output.

View file

@ -129,6 +129,16 @@ TEST_SUITES: dict[str, tuple[str, ...]] = {
# It deliberately does NOT decide which of the 19 belong on the gate — # It deliberately does NOT decide which of the 19 belong on the gate —
# that costs gate time and is Shay's call. <1s. # that costs gate time and is Shay's call. <1s.
"tests/test_suite_reachability.py", "tests/test_suite_reachability.py",
# G-23 — DOMAIN_PACKS and the pack manifests state one fact twice, and
# only one direction was checked. domain_contract_predicates P3 validates
# domain_id -> a known domain; NOTHING validated DOMAIN_PACKS -> the
# manifest agrees, and P3 passes VACUOUSLY on a manifest with no
# domain_id at all. Measured 7 of 9 bound, 0 contradictory, 2 absent —
# both in philosophy_theology, a band queued to earn SERVE behind R-8.
# The existing predicate tests run only on synthetic tmp_path manifests
# and sit in no curated suite, so they could not have caught it.
# Reads 30 small JSON files, <1s.
"tests/test_domain_pack_binding.py",
# G-22 — CLAIMS.md is a PUBLISHED capability artifact, deterministically # G-22 — CLAIMS.md is a PUBLISHED capability artifact, deterministically
# regenerable from the capability ledger + PINNED_SHAS. It published a # regenerable from the capability ledger + PINNED_SHAS. It published a
# superseded evidence digest for deduction_serve_v1 for two days after # superseded evidence digest for deduction_serve_v1 for two days after

View file

@ -165,6 +165,13 @@ Own `use_salience`, the two underived constants, the self-narrowing budget feedb
**The mechanism, and why it is G-7's story again:** one commit updated the lane, its verifier and its test, and missed one downstream artifact. The pin that guards that artifact — `tests/test_claims_md_is_current.py`**exists and is an orphan in no curated suite**, so nothing surfaced it until this arc ran the full tree once. Registered on the gate as part of the fix. **The mechanism, and why it is G-7's story again:** one commit updated the lane, its verifier and its test, and missed one downstream artifact. The pin that guards that artifact — `tests/test_claims_md_is_current.py`**exists and is an orphan in no curated suite**, so nothing surfaced it until this arc ran the full tree once. Registered on the gate as part of the fix.
- **G-21 · The math reader decides 1.0% of `holdout_dev/v1`** *(new, 2026-07-28)* — measured while building the §5 corpus: `parse_and_solve` returns a selected graph for **5 of 500** held-out cases, all carrying the same relational skeleton (`compare_multiplicative`); on the public lane, 24/150. This is a sharper measurement of the comprehension frontier than G-3's construction count, on the corpus the project already treats as its held-out standard, and it is why ADR-0252 §5.1's four-structure corpus is not extractable today. Distinct from G-3 (which counts *constructions* the general reader admits); this counts *cases decided* on a standing eval corpus. **Authority:** the widening program (G-3) + a ruling on whether holdout decision-rate becomes a tracked lane metric. - **G-21 · The math reader decides 1.0% of `holdout_dev/v1`** *(new, 2026-07-28)* — measured while building the §5 corpus: `parse_and_solve` returns a selected graph for **5 of 500** held-out cases, all carrying the same relational skeleton (`compare_multiplicative`); on the public lane, 24/150. This is a sharper measurement of the comprehension frontier than G-3's construction count, on the corpus the project already treats as its held-out standard, and it is why ADR-0252 §5.1's four-structure corpus is not extractable today. Distinct from G-3 (which counts *constructions* the general reader admits); this counts *cases decided* on a standing eval corpus. **Authority:** the widening program (G-3) + a ruling on whether holdout decision-rate becomes a tracked lane metric.
- **G-20 · The `refusal_reason` materialisation** — typed refusal evidence exists and is discarded at the public `str` boundary; the plumbing for materialisation already landed. Cross-listed as H-3. **Authority:** small ADR (anticipated by the ADR-0024 chain). - **G-20 · The `refusal_reason` materialisation** — typed refusal evidence exists and is discarded at the public `str` boundary; the plumbing for materialisation already landed. Cross-listed as H-3. **Authority:** small ADR (anticipated by the ADR-0024 chain).
- **G-23 · The domain↔pack binding is stated twice and checked in one direction** *(new, 2026-07-28; **pinned** the same day)* — `core/capability/domains.py`'s `DOMAIN_PACKS` declares domain→pack membership for the capability ledger; each `packs/data/<pack>/manifest.json` independently declares `domain_id`. `domain_contract_predicates.py` **P3** validates `domain_id → a known ledger domain` and is the only binding check that existed. Nothing validated the reverse, and **P3 passes vacuously on a manifest with no `domain_id` at all** — the same shape as its own `test_pack_without_contract_reports_absent`. **Measured: 7 of 9 bound, 0 contradictory, 2 absent**`en_core_cognition_v1` and `en_core_meta_v1`, both placed in `philosophy_theology`, which is one of the four bands queued to earn a SERVE license behind R-8. The domain whose license is next to be earned is the one whose binding exists in only one place.
*Why nothing caught it:* `tests/test_domain_contract_predicates.py` exercises P1P9 entirely against synthetic `tmp_path` manifests — never against the real nine — and is itself in **no curated suite**. A predicate proven correct on fabricated input and never run on the real input is this arc's defect class stated precisely: its success state and *"it never ran here"* are indistinguishable.
*How it was found, recorded because the method matters:* an external assessment proposed collapsing the pack/domain machinery, reasoning explicitly from a directory listing. Every specific it gave was falsified by opening the files (see the 2026-07-28 triage in `31-hindrance-audit.md`) — but the seam it pointed at was real. Bad method, useful direction; both halves are worth recording.
**Pinned, not fixed**`tests/test_domain_pack_binding.py`, on the gate. Freezes the 2-pack gap, fails on contradiction always and on absence unless baselined, enforces both directions, pins the reverse direction (a pack may not self-assign a ledger domain — clean across all 30 packs today), and guards its own parse against vacuity. **Four sabotages observed red.** The fix is deliberately *not* here: whether `en_core_cognition_v1` is *a philosophy_theology domain pack* or merely *grouped under that domain for ledger accounting* is a ratification-adjacent content question. **Authority:** R-8 / PR-14, where that band's license is decided.
--- ---

View file

@ -163,3 +163,12 @@ For symmetry with the Candidate Register's "considered and not registered" — h
- **The five unreconciled articulations** — dissolved by the taxonomy (D1), not a standing hindrance; the residue is one stale Draft banner (folded into H-8's amendment batch). - **The five unreconciled articulations** — dissolved by the taxonomy (D1), not a standing hindrance; the residue is one stale Draft banner (folded into H-8's amendment batch).
- **Flag-gated conservatism itself** — seventeen dark flags is not inherently hindrance; *unregistered* darkness is (G-8). The posture may be exactly right; the register exists so that judgment can be made deliberately. - **Flag-gated conservatism itself** — seventeen dark flags is not inherently hindrance; *unregistered* darkness is (G-8). The posture may be exactly right; the register exists so that judgment can be made deliberately.
- **Six proposals from two external assessments (2026-07-28)** — each verified against code and cleared, with the refuting file named: **EpistemicState compression** (the "unused" states are the vault's refuse-forcing states and the serving path's `UNVERIFIED_POSSIBLE``vault/store.py:99-103`, `chat/runtime.py:1329`; ADR-0206 already partitions the taxonomy per-state in `core/response_governance/policy.py:64-100`); **an EpistemicState×NormativeClearance product type** (forbidding DECODED+SUPPRESSED at the type level erases the refusal's evidence — H-3's failure rebuilt as a type constraint; the coupling already exists as policy, where the legitimate combinations are decided); **PID attestation on `lived_life.json`** (a reboot-resumed life's writer PID is dead by definition, so the check refuses every legitimate resume, and PID reuse defeats attestation — the same pid-file pitfalls the lock's own comment derives against); **path-based cycle skipping in the inductive closure** (would refuse sound transitive derivations; see H-8e — the docstring, not the code, is the wrong side); **a depth-enrichment session cache** (the cost it targets is already `@lru_cache`d at the pack layer, `chat/pack_resolver.py:19`; measure first — the 0.22% Rust precedent); **a unified reasoning engine presented as refactor** (changes `operator_invocation` bytes → `trace_hash`, silently widens capability by letting compose see closure-derived triples, and reduces entailment to set membership one merge after ADR-0265 ratified denial representation — the underlying facts are real and are recorded as design considerations under H-4/H-14). Triage rule kept from this round: **no external lead folds in without the one-more-file check** — every rejected proposal above failed on a file its author did not open. - **Six proposals from two external assessments (2026-07-28)** — each verified against code and cleared, with the refuting file named: **EpistemicState compression** (the "unused" states are the vault's refuse-forcing states and the serving path's `UNVERIFIED_POSSIBLE``vault/store.py:99-103`, `chat/runtime.py:1329`; ADR-0206 already partitions the taxonomy per-state in `core/response_governance/policy.py:64-100`); **an EpistemicState×NormativeClearance product type** (forbidding DECODED+SUPPRESSED at the type level erases the refusal's evidence — H-3's failure rebuilt as a type constraint; the coupling already exists as policy, where the legitimate combinations are decided); **PID attestation on `lived_life.json`** (a reboot-resumed life's writer PID is dead by definition, so the check refuses every legitimate resume, and PID reuse defeats attestation — the same pid-file pitfalls the lock's own comment derives against); **path-based cycle skipping in the inductive closure** (would refuse sound transitive derivations; see H-8e — the docstring, not the code, is the wrong side); **a depth-enrichment session cache** (the cost it targets is already `@lru_cache`d at the pack layer, `chat/pack_resolver.py:19`; measure first — the 0.22% Rust precedent); **a unified reasoning engine presented as refactor** (changes `operator_invocation` bytes → `trace_hash`, silently widens capability by letting compose see closure-derived triples, and reduces entailment to set membership one merge after ADR-0265 ratified denial representation — the underlying facts are real and are recorded as design considerations under H-4/H-14). Triage rule kept from this round: **no external lead folds in without the one-more-file check** — every rejected proposal above failed on a file its author did not open.
- **Six architectural proposals from a third external assessment (2026-07-28), whose author states the method up front: *"I have enough from the directory and CLAIMS.md to reason seriously."*** That is the method `AGENTS.md` protocol rule 1 forbids, and the hedges mark where it broke — *"almost certainly a nested conditional or a big dict," "likely doing both," "may be operating at a resolution."* **One folded in (G-23); five falsified by opening the file:**
1. ***`conftest.py` is "almost certainly the most dangerous file in the repo"; suite-membership logic has accumulated in it.*** **False.** It holds two autouse engine-state isolation fixtures, a QUARANTINE nodeid registry, a SLOW_FILES/SLOW_TESTS registry, `_is_slow`, and `pytest_collection_modifyitems` — which *stamps markers and never skips* (`"classification ONLY, never skipped here"`). Zero suite membership, zero gate-tier assignment, zero conditional inclusion. The 12KB is two data tables with per-entry justifications: already the declared-table pattern the proposal asks for.
2. ***`cli_test.py` manages suite topology "through what is almost certainly a nested conditional or a big dict."*** **A flat dict — 17 keys, all tuple values, zero conditionals in the module body.** 463 lines, 171 of them comments (37%). The *surviving* half of the point — gate tier is a property of which suites the shell invokes, not of the suite — is real; but it is already **declared and verified**: `test_suite_reachability.py` declares `GATE_SUITES` and asserts it against `pre-push`/`local-ci.sh` precisely so the pin cannot go vacuous. The claim that this "makes pin 3's ratchet trivially implementable" describes a pin that shipped this arc.
3. ***`field/` + `algebra/` + `morphology/` are a triad a caller must compose correctly or bypass the ratification contract; give them a single `serve()`.*** **Not a triad.** `algebra/` has 72 non-test importers (a foundational library), `field/` 15, **`morphology/` 3 — and `morphology/` is 2 files, 97 lines total.** No `def serve(` exists anywhere in `core/`, `chat/` or `generate/`; the blessed path is `ChatRuntime`. Top-level directories look like peers regardless of size, which is the entire content of this proposal.
4. ***The epistemic grade space is richer than the serve layer consumes; collapse it.*** **All 15 `EpistemicState` members have non-test consumers** (min 1, max 13). Nothing is unconsumed. Compression was already examined and cleared in the round above, on different evidence.
5. ***Give each of the 260+ ADRs a machine-readable `constraints:` block.*** The count is **314**, and the class already exists in the better direction: `test_adr_status_governance.py` parses `core/config.py`'s bool flags *with their cited ADR numbers* and fails when a default-ON flag is governed by a `Proposed` ADR, with a non-vacuity guard. **The proposal inverts this the wrong way** — 314 ratified, immutable decision records each carrying live assertions is 314 new places that must be kept true, in the repository whose dominant defect class is records drifting from reality. Either the ADR is edited when code moves (breaking ratification) or the assertion goes stale (H-8, at 314×). Machine-readability belongs in the **checker that reads the ADR**, not in the ADR.
6. ***The unifying proposal — a root `contracts.toml` holding every declared relationship — is "the highest-leverage single change."*** The *principle* is already this repository's (ADR-0263 Rule 5: declared in the table, not the call site) and is implemented in the very file the assessment cites: `core/epistemic_state.py` declares `GroundingSource` as a closed `Literal` plus a runtime-iterable `GROUNDING_SOURCES` frozenset, single-sourced across the runtime, the lane validator, the Workbench API coercion, and a build-time enum-coverage test under `workbench-ui/` — and its docstring records the bug that pattern caught (unregistered `deduction`/`curriculum` labels floored to `"none"`, *"recording a proved answer as ungrounded in a durable audit artifact"*). Centralizing four tables with four different authorities and lifecycles into one file adds a fifth copy that must agree with four generators; it is a ratchet to guard ratchets (standing philosophy 2). **Cleared as a proposal, kept as an observation:** nobody can currently enumerate *"which declared tables exist and what enforces each"* — that is one page, folded into PR-5, not a new mechanism.
**What it got right, and it is worth saying:** proposal 1 pointed at the pack/domain seam. Every specific was wrong, and reading the code there anyway found **G-23** — a real, load-bearing, one-directional check on the band whose license is next to be earned. Bad method, useful direction. The triage rule survives unchanged and earned a second demonstration: *every rejected proposal above failed on a file its author did not open.*

View file

@ -289,6 +289,8 @@ Promoted out of a parenthetical in PR-4, because a parenthetical is how fifteen
### PR-5 · The flag-default register · **M** · G-8/H-6 ### PR-5 · The flag-default register · **M** · G-8/H-6
`docs/specs/flag_register.md`: all **28** default-off flags plus the 4 default-on — current default, deliberate-posture vs accumulated-hesitancy, **what evidence flips it**, and named profiles (one-shot / eval / continuous-life) per R-4. Declared in the table, not the call site (ADR-0263 Rule 5). A pin asserts the register lists exactly the flags `core/config.py` defines — so a new flag cannot land unregistered. Includes the F-6 accrual resolution from R-3 and the N-6 docstring correction. `docs/specs/flag_register.md`: all **28** default-off flags plus the 4 default-on — current default, deliberate-posture vs accumulated-hesitancy, **what evidence flips it**, and named profiles (one-shot / eval / continuous-life) per R-4. Declared in the table, not the call site (ADR-0263 Rule 5). A pin asserts the register lists exactly the flags `core/config.py` defines — so a new flag cannot land unregistered. Includes the F-6 accrual resolution from R-3 and the N-6 docstring correction.
**Plus one page this PR is the natural home for (from the 2026-07-28 triage, proposal 6).** The repository's governing pattern is *declared in the table, not the call site* (ADR-0263 Rule 5) and it is implemented in many places — `GROUNDING_SOURCES`, `DOMAIN_PACKS`, `TEST_SUITES`, `GATE_SUITES`, `CONTINUOUS_LIFE_CONFIG_FLAGS`, the quarantine/slow registries, `PINNED_SHAS`. **Nobody can currently enumerate them, or say what enforces each.** That is a one-page index in the flag register's own document — *table → where declared → what pin makes it true* — and explicitly **not** a `contracts.toml` that centralizes them into a fifth copy needing agreement with four generators. The index is a reader's aid; the tables stay where their authority lives.
### PR-6 · Failing pins for three doctrine-level prohibitions · **M** · G-9 — **LANDED 2026-07-28** ### PR-6 · Failing pins for three doctrine-level prohibitions · **M** · G-9 — **LANDED 2026-07-28**
(a) no-approximate-recall — a pin that fails if a cosine/ANN ranker is substituted on the recall path; (b) a bypass pin — governance not merely working when called, but *unbypassable*; (c) safety-pack non-swappability mechanically enforced. Each demonstrated red-then-green; a law with a pin that cannot fail is a hollow gate. (a) no-approximate-recall — a pin that fails if a cosine/ANN ranker is substituted on the recall path; (b) a bypass pin — governance not merely working when called, but *unbypassable*; (c) safety-pack non-swappability mechanically enforced. Each demonstrated red-then-green; a law with a pin that cannot fail is a hollow gate.
@ -334,6 +336,8 @@ Shipped as ADR-0264 R5, discharged 2026-07-26. The 16-premise cap does not exist
**Under R-8 C the deliverable is now specific:** two entries in `CAPABILITY_LEDGERS``unknown`-serving and `entailed`-serving licensed as **different capabilities on different evidence** — the mix rule declared in the table rather than at the call site (ADR-0263 Rule 5), an **ADR-0264 §5 amendment** recording the ruling, then the seal. The four bands license the thing they have actually demonstrated at 660 decisions (correct refusal); nothing licenses entailment on a maximum of 9 cases. **Under R-8 C the deliverable is now specific:** two entries in `CAPABILITY_LEDGERS``unknown`-serving and `entailed`-serving licensed as **different capabilities on different evidence** — the mix rule declared in the table rather than at the call site (ADR-0263 Rule 5), an **ADR-0264 §5 amendment** recording the ruling, then the seal. The four bands license the thing they have actually demonstrated at 660 decisions (correct refusal); nothing licenses entailment on a maximum of 9 cases.
**It also carries G-23's decision, because it is the same band.** `DOMAIN_PACKS` places `en_core_cognition_v1` and `en_core_meta_v1` in `philosophy_theology`; neither manifest declares a `domain_id`, so `domain_contract_predicates` P3 has never run on them. The gap is pinned (`tests/test_domain_pack_binding.py`, on the gate, four sabotages red) and deliberately not fixed: whether those two are *philosophy_theology domain packs* or merely *grouped under it for ledger accounting* decides what the license actually covers. Sealing that band's ledger without answering it licenses a domain whose membership is asserted in one place and denied by silence in the other.
**One sub-question the ruling leaves open, stated rather than assumed.** The packet's recommendation was *"C, with a floor from A applied to the entailed capability only"* — and **N was never named.** Adopting C adopts the floor in principle; the number is not derivable from the evidence, because the whole point is that no band is near it (max entailed volume = 9). This PR must therefore either carry a proposed N with its consequence measured, or land the two capabilities with the entailed one explicitly unlicensable-pending-N. It may not quietly ship without a floor — that would license entailment on 9 cases through an omission, which is the exact outcome C exists to prevent. **One sub-question the ruling leaves open, stated rather than assumed.** The packet's recommendation was *"C, with a floor from A applied to the entailed capability only"* — and **N was never named.** Adopting C adopts the floor in principle; the number is not derivable from the evidence, because the whole point is that no band is near it (max entailed volume = 9). This PR must therefore either carry a proposed N with its consequence measured, or land the two capabilities with the entailed one explicitly unlicensable-pending-N. It may not quietly ship without a floor — that would license entailment on 9 cases through an omission, which is the exact outcome C exists to prevent.
### Track B · The fabrications and the widening — *gated, see §6* ### Track B · The fabrications and the widening — *gated, see §6*

View file

@ -14,8 +14,8 @@ A read-only, evidence-bearing assessment of CORE's cognitive-cycle design versus
| [`04-phase2-findings.md`](04-phase2-findings.md) | 2 | Stage-coverage audit; corrections to Phase 0; findings F-1…F-5 | | [`04-phase2-findings.md`](04-phase2-findings.md) | 2 | Stage-coverage audit; corrections to Phase 0; findings F-1…F-5 |
| [`20-component-cards/`](20-component-cards/) | 3 | Eight component cards: the four zero-subsystem zones + always-on, derivation organs, surface selection, attention | | [`20-component-cards/`](20-component-cards/) | 3 | Eight component cards: the four zero-subsystem zones + always-on, derivation organs, surface selection, attention |
| [`05-phase3-findings.md`](05-phase3-findings.md) | 3 | Corrections C-1…C-5; findings F-6…F-10; the consolidated Phase-4 seed list | | [`05-phase3-findings.md`](05-phase3-findings.md) | 3 | Corrections C-1…C-5; findings F-6…F-10; the consolidated Phase-4 seed list |
| [`30-gap-register.md`](30-gap-register.md) | 4 | **The live gap register** — 22 entries (G-21, G-22 added during execution), 4 tiers, each with evidence + deciding authority; **supersedes `docs/gaps.md` and the substrate-liveness ratchet** per R-7 | | [`30-gap-register.md`](30-gap-register.md) | 4 | **The live gap register** — 23 entries (G-21, G-22, G-23 added during execution), 4 tiers, each with evidence + deciding authority; **supersedes `docs/gaps.md` and the substrate-liveness ratchet** per R-7 |
| [`31-hindrance-audit.md`](31-hindrance-audit.md) | 4 | Fourteen hindrances with fitness verdicts and better homes; eleven candidates examined and cleared (five in Phase 4, six in the 2026-07-28 external-assessment triage) | | [`31-hindrance-audit.md`](31-hindrance-audit.md) | 4 | Fourteen hindrances with fitness verdicts and better homes; **seventeen candidates examined and cleared** — five in Phase 4, six in the first 2026-07-28 external-assessment triage, six in the second (of which one, the pack/domain seam, survived as G-23) |
| [`40-assessment.md`](40-assessment.md) | 5 | The synthesis | | [`40-assessment.md`](40-assessment.md) | 5 | The synthesis |
| [`50-execution-plan.md`](50-execution-plan.md) | 6 | **The execution plan** — five waves + five frontier tracks over every G/H entry, with the dependency gates and the risks. §0 carries **nine** corrections to the assessment found while sizing and executing it; **§2.1 carries the adopted ruling docket and its execution order** | | [`50-execution-plan.md`](50-execution-plan.md) | 6 | **The execution plan** — five waves + five frontier tracks over every G/H entry, with the dependency gates and the risks. §0 carries **nine** corrections to the assessment found while sizing and executing it; **§2.1 carries the adopted ruling docket and its execution order** |
| [`50-rulings.md`](50-rulings.md) | 6 | **The ruling packet** — R-1…R-14, each with evidence, options, a recommendation, and the exact diff that follows from each choice. **RULED 2026-07-28: twelve adopted, R-10 and R-14 stricken, §5 NO-GO ratified.** Wave 0's deliverable, and Wave 0 is closed | | [`50-rulings.md`](50-rulings.md) | 6 | **The ruling packet** — R-1…R-14, each with evidence, options, a recommendation, and the exact diff that follows from each choice. **RULED 2026-07-28: twelve adopted, R-10 and R-14 stricken, §5 NO-GO ratified.** Wave 0's deliverable, and Wave 0 is closed |

View file

@ -0,0 +1,211 @@
"""G-23 — `DOMAIN_PACKS` and the pack manifests state one fact in two places.
**How this was found.** An external assessment proposed collapsing the
pack/domain machinery, reasoning from a directory listing. Every specific it
gave was wrong ``core/protocol/`` is the trace protocol, not governance;
``core/capability`` is reporting surfaces; ``packs/domain_contract.py`` says of
itself *"intentionally read-only … does not promote capability status."* But it
pointed at the right seam, and reading the code there found a real one.
**The seam.** ``core/capability/domains.py`` declares ``DOMAIN_PACKS`` the
domainpack membership the capability ledger reasons over. Each pack's
``packs/data/<pack>/manifest.json`` independently declares ``domain_id``. Two
statements of one fact, and only one of the two directions is checked:
* ``domain_contract_predicates.py`` **P3** validates ``domain_id`` *a known
ledger domain*. It is the only binding check that exists.
* Nothing validates ``DOMAIN_PACKS`` *the manifest agrees*.
**P3 passes vacuously on a manifest with no ``domain_id`` at all** the same
shape as its own ``test_pack_without_contract_reports_absent``. So a pack that
``DOMAIN_PACKS`` places in a domain, but whose manifest never says so, is
invisible to every check in the repository. Measured 2026-07-28: **7 of 9 bound,
0 contradictory, 2 absent** ``en_core_cognition_v1`` and ``en_core_meta_v1``,
both placed in ``philosophy_theology``, which is one of the four bands queued to
earn a SERVE license behind R-8. The domain whose license is about to be earned
is the one whose binding exists in only one place.
**And the existing tests could not have caught it.**
``tests/test_domain_contract_predicates.py`` exercises P1P9 entirely against
synthetic ``tmp_path`` manifests never against the real nine and is itself
in no curated suite. A predicate proven correct on fabricated input, never run
on the real input, is this arc's defect class stated precisely: *its success
state and "it never ran here" are indistinguishable.*
**What this pin does not do.** It does not add ``domain_id`` to the two
manifests. Whether ``en_core_cognition_v1`` is *a philosophy_theology domain
pack* or merely *grouped under that domain for ledger accounting* is a
ratification-adjacent content question, not an engineering one, and it is
routed to R-8 / PR-14 where that band's license is decided. This pin freezes
the state, fails on growth, and enforces both directions so the gap is
visible and cannot widen while the decision is pending.
"""
from __future__ import annotations
import ast
import json
from pathlib import Path
from typing import Any
_ROOT = Path(__file__).resolve().parents[1]
_DOMAINS_PY = _ROOT / "core" / "capability" / "domains.py"
_PACK_DATA = _ROOT / "packs" / "data"
#: Packs that ``DOMAIN_PACKS`` places in a domain whose manifest declares no
#: ``domain_id``. This list may only SHRINK. A pack leaves it by gaining the
#: field, not by being deleted from here.
UNBOUND_BASELINE: frozenset[str] = frozenset(
{
"en_core_cognition_v1",
"en_core_meta_v1",
}
)
def _domain_packs() -> dict[str, tuple[str, ...]]:
"""Read ``DOMAIN_PACKS`` without importing the package.
``core.capability`` imports ``chat`` imports ``generate`` imports numpy, so
a plain import drags the whole runtime in to read one dict. Executing the
module body is safe here: ``domains.py`` is pure data with no imports of
its own beyond ``__future__``.
"""
namespace: dict[str, Any] = {}
source = _DOMAINS_PY.read_text(encoding="utf-8")
exec(compile(ast.parse(source), str(_DOMAINS_PY), "exec"), namespace) # noqa: S102
packs = namespace["DOMAIN_PACKS"]
assert isinstance(packs, dict), "DOMAIN_PACKS is not a dict"
return packs
def _manifest_domain_id(pack: str) -> str | None:
path = _PACK_DATA / pack / "manifest.json"
if not path.exists():
return None
return json.loads(path.read_text(encoding="utf-8")).get("domain_id")
def test_parse_is_not_vacuous() -> None:
"""A parse that silently matched nothing would make every pin below pass.
That failure mode (N-1) has produced three false conclusions in this repo,
so it is asserted rather than assumed.
"""
packs = _domain_packs()
members = [p for members in packs.values() for p in members]
assert len(packs) >= 5, f"parsed only {len(packs)} domains from DOMAIN_PACKS"
assert len(members) >= 9, f"parsed only {len(members)} pack memberships"
assert list(_PACK_DATA.glob("*/manifest.json")), "no pack manifests found on disk"
def test_every_declared_pack_has_a_manifest() -> None:
"""``DOMAIN_PACKS`` may not name a pack that does not exist on disk."""
missing = sorted(
pack
for members in _domain_packs().values()
for pack in members
if not (_PACK_DATA / pack / "manifest.json").exists()
)
assert not missing, (
"DOMAIN_PACKS names packs with no manifest under packs/data: " f"{missing}"
)
def test_manifest_domain_id_agrees_with_domain_packs() -> None:
"""The forward direction P3 does not check: the table must match the pack.
A *contradiction* is always a failure. An *absence* is a failure unless the
pack is in ``UNBOUND_BASELINE`` which is a recorded gap with a named
owner (R-8 / PR-14), not a default.
"""
contradictions: list[str] = []
undeclared_absences: list[str] = []
for domain, members in _domain_packs().items():
for pack in members:
declared = _manifest_domain_id(pack)
if declared is None:
if pack not in UNBOUND_BASELINE:
undeclared_absences.append(f"{pack} (DOMAIN_PACKS: {domain})")
elif declared != domain:
contradictions.append(
f"{pack}: manifest says {declared!r}, DOMAIN_PACKS says {domain!r}"
)
assert not contradictions, (
"pack manifests contradict DOMAIN_PACKS — one of the two is wrong and "
"the capability ledger reasons over the second:\n "
+ "\n ".join(contradictions)
)
assert not undeclared_absences, (
"these packs are placed in a domain by DOMAIN_PACKS and declare no "
"domain_id, so P3 passes vacuously on them. Add domain_id to the "
"manifest, or add the pack to UNBOUND_BASELINE with the reason in your "
"commit message:\n " + "\n ".join(undeclared_absences)
)
def test_no_manifest_claims_a_domain_the_table_does_not_grant() -> None:
"""The reverse direction: a pack may not self-assign a ledger domain.
Measured clean at 2026-07-28 across all 30 packs under ``packs/data``. It is
pinned because it is the direction a *content* edit would break a new pack
manifest copying ``domain_id`` from a sibling is the obvious mistake, and it
would silently widen a domain the ledger licenses.
"""
granted = {
pack: domain for domain, members in _domain_packs().items() for pack in members
}
offenders: list[str] = []
for manifest in sorted(_PACK_DATA.glob("*/manifest.json")):
declared = json.loads(manifest.read_text(encoding="utf-8")).get("domain_id")
if declared is None:
continue
pack = manifest.parent.name
if pack not in granted:
offenders.append(f"{pack}: claims {declared!r}, not in DOMAIN_PACKS at all")
elif granted[pack] != declared:
offenders.append(
f"{pack}: claims {declared!r}, DOMAIN_PACKS grants {granted[pack]!r}"
)
assert not offenders, (
"pack manifests claim ledger domains DOMAIN_PACKS does not grant:\n "
+ "\n ".join(offenders)
)
def test_unbound_baseline_has_no_stale_entries() -> None:
"""Both directions — a pack that gains ``domain_id`` must leave the list.
Same discipline as the suite-membership and reachability ratchets, for the
same reason: a baseline that keeps entries it no longer needs stops being a
measurement and becomes a story about one. That discipline has caught this
file's author twice.
"""
declared_packs = {
pack for members in _domain_packs().values() for pack in members
}
vanished = sorted(p for p in UNBOUND_BASELINE if p not in declared_packs)
assert not vanished, (
f"UNBOUND_BASELINE names packs DOMAIN_PACKS no longer places: {vanished}"
)
now_bound = sorted(p for p in UNBOUND_BASELINE if _manifest_domain_id(p) is not None)
assert not now_bound, (
"these packs now declare domain_id (good) but are still listed as "
f"unbound — remove them from UNBOUND_BASELINE: {now_bound}"
)
def test_recorded_gap_matches_the_measurement() -> None:
"""Pin the number so the gap moving is a reviewed decision, not a drift.
2 of 9 at 2026-07-28. Shrinking it is the point, and it shrinks by a
ratification-adjacent content decision (R-8 / PR-14), not by an edit here.
"""
assert len(UNBOUND_BASELINE) <= 2, (
f"unbound domain packs grew to {len(UNBOUND_BASELINE)} (was 2). The "
"ratchet only turns one way."
)