core/docs/adr/ADR-0258-member-chain-band.md
Shay efd3bee9ce chore(governance): stamp nine merged ADRs Accepted + pin the invariant
Nine ADRs (0254, 0256-0263) were merged into main and left stamped Proposed.
Two carried an explicit ratify-on-merge predicate their own merge had already
discharged, and ADR-0256 governs deduction_serving_enabled, which was ratified
True on 2026-07-24 and is serving live traffic. So the governance record
asserted "not yet decided" about a decision already in force.

That is the asymmetry the assessment arc (#113) found one file over in
workbench/api.py: the honest path degrades, the stale record lies. An unwritten
ADR is a visible gap; a Proposed one that is actually in force is a false
statement.

Each stamp records the ACTUAL ratifying act -- "Accepted, ratified by Joshua
Shay via <merge> (<sha>, <date>)" -- derived from the commit that added the file
and verified an ancestor of main, not assumed. Merge authority is Shay's alone
(AGENTS.md: no merge automation), so the merge IS the ratifying act.

No ADR content changed. No flag changed. ADR-0262's stamp says so explicitly:
accepting it does NOT enable curriculum_serving_enabled, which stays False
pending ratified volume -- eleven bands re-measured today, still 24x-73x short.

tests/test_adr_status_governance.py pins two independent invariants in the
smoke (pre-push) suite:

1. A default-ON flag is not governed by a Proposed ADR. The flag -> ADR mapping
   is DERIVED by walking core/config.py for `<name>: bool = True` and reading
   ADR refs from the preceding comment block -- not a hand-written table, which
   would be the same second-copy-of-a-closed-set defect ADR-0256's arc fixed.
2. A ratify-on-merge predicate cannot coexist with Proposed. Self-discharging:
   the file being on main IS the merge having happened.

Plus a vacuity guard, because a derivation that parses zero flags would make
every other assertion pass on an empty set.

Registered two orphans found in passing: test_adr_index.py (5) and
test_ratification_ceremony.py (14) landed in #113 in NO curated suite, so 19
tests -- including the one mechanism that can move curriculum volume -- ran
only under `full`, which gates nothing. Fifth instance of this shape.

Deliberately NOT fixed, recorded in the research doc: the 312-file corpus has
27 unparseable status lines and draft/ratified/active variants. A closed-vocab
assertion would fail on ~35 pre-existing files and get muted, and a muted gate
reads as coverage.

[Verification]: smoke 555 passed in 137.73s (236 baseline + 314 + 5, +1.2s);
governance pin 314 passed in 1.18s standalone and MUTATION-CHECKED -- reverting
ADR-0256 to Proposed fails both invariants independently (2 failed/312 passed);
orphans 19 passed; ruff clean. Canonical Python 3.12.13, uv sync --locked.
2026-07-25 14:38:02 -07:00

8.7 KiB
Raw Blame History

ADR-0258 — Member-chain band (Band v3-MEM): singular membership + universal premises

  • Status: Accepted — ratified by Joshua Shay via the PR #108 merge (dd2245a7, 2026-07-24)
  • Date: 2026-07-23
  • Relates to: ADR-0257 (Band v2-EN — this is its scope-out #1), ADR-0256 (earned license), ADR-0201 (ROBDD keystone), ADR-0175/0199 (calibrated learning / arena), ADR-0253 (dual-pack serve boundary — grc/he)

1. Context

ADR-0257 §6.1 reserved the classic instantiation syllogism as the next band:

"Socrates is a man. All men are mortal. Therefore Socrates is mortal."

Band v2-EN refuses it (membership_shape_out_of_band — an is a|an clause announces internal structure the opaque band must not flatten), Band v1b's categorical decider has no notion of an individual (its terms are classes), and the v1 propositional reader declines multi-word names. The eval lane pins the gap: ds-en-0022 is gold-declined purely for want of a reading band. The engine underneath was never the limit.

2. Decision

Add a dedicated member-argument reader + per-individual propositional lowering (generate/proof_chain/member.py), used ONLY by the deduction serving path as a fallback tier strictly AFTER Bands v1 / v1b / v2-EN (so every previously-served argument is byte-identical; this band only widens):

  • Closed sentence grammar (function-word dispatch, refusal-first):
    • Singular fact — <NAME> is [not] [a|an] <CLASS> (contractions expanded, plus the sentential it is not the case that <singular> prefix). <NAME> and <CLASS> are opaque token runs; is only (no tense in v1).
    • Universal — all|every|each <C1> are|is [a|an] <C2> (affirmative A-form) and no <C1> are|is [a|an] <C2> (negative E-form).
    • Conclusion — final sentence, sole therefore-led, singular form only.
  • Per-individual propositional lowering: collect the named individuals (subjects of singular sentences, conclusion included); mint one opaque atom per (individual, class) pair; each universal instantiates at EVERY named individual — A(C1,C2)mem(i,C1) -> mem(i,C2), E(C1,C2)mem(i,C1) -> ~mem(i,C2) for each i; singular facts are literals. The same verified ROBDD engine decides; deterministic templates render the verdict over the user's own sentences (generate/proof_chain/render.py::render_entailment_member).
  • Number linking (the one semantic identification this band performs): a universal's class word is usually plural ("all men") while the singular fact uses the singular ("a man"). Two attested class tokens are identified iff related by a closed morphology table — a curated irregular/invariant table (men↔man, people↔person, children↔child, …; invariants like sheep, species, news map only to themselves) consulted FIRST, then the three regular suffix rules (+s, +es after sibilants, y↔ies). Table priority kills the over-link hazards (specie/species, new/news); anything the closed relation cannot relate stays distinct — under-linking costs coverage (honest UNKNOWN), never soundness. Linking is per-token, so multi-token classes ("guard dog"↔"guard dogs") link on the head noun.
  • Four new shape-bandsen_member_single (exactly 1 universal), en_member_chain (≥2 universals), en_member_negative (any E-form), en_member_atomic (0 universals); priority negative > chain > single > atomic — each earning its own SERVE license through the ADR-0199 arena at n=720/band wrong=0, sealed into the same ratified ledger (unlicensed ⇒ automatically hedged, per ADR-0256).

3. Why the lowering is sound (the load-bearing argument)

  1. Universal instantiation is sound. Every first-order model of ∀x (C1(x) → C2(x)) satisfies its instantiation at each named individual, so any model of the premises is a model of the lowered premise set. An ENTAILED/REFUTED/inconsistent verdict over the lowered set therefore holds in every model of the original premises — served at full strength. Both verdicts survive even if two distinct names co-refer: adding equality facts only ADDS premises, and entailment is monotone.
  2. For this closed fragment the lowering is also complete. With premises restricted to singular literals and A/E universals (Boolean reading, no existential import) and a singular conclusion, a propositional countermodel lifts to a first-order countermodel whose domain is exactly the named individuals — every universal holds because every element is a named individual whose instantiation holds. So UNKNOWN means genuinely not forced within the disclosed reading — it is never an artifact of instantiating too little.
  3. UNKNOWN is still the one guarded verdict. Two readings could settle what the lowering leaves open: distinct names might co-refer, and class words the closed relation didn't link might be the same class. The UNKNOWN surface therefore scopes itself — "Reading each name as one individual and each class word at face value…" — same discipline as v2-EN's indivisible-clause scoping (ADR-0257 §3).
  4. Existential forms refuse out of band. some/most/any/…-led sentences assert anonymous witnesses that per-individual lowering cannot represent — quantifier_out_of_band, typed. Pure categorical arguments stay with Band v1b (tried first, unchanged).

Structural guards (closed refusal vocabulary, all typed): quantifier_out_of_band, bare_plural_out_of_band ("Dogs are loyal" — implicit genericity is a reading this band refuses to guess), definite_description_out_of_band ("is the …" is identity, not membership), relative_clause_out_of_band (that/which/who inside a name or class), universal_conclusion_out_of_band (categorical conclusions belong to v1b), mixed_structure_out_of_band (if/or/either/and — conditional-membership fusion is a future band), internal_negation_unread, structural_leak, sentence_shape_out_of_band, plus the v2-EN sentence discipline (question_sentence, no_conclusion, multiple_conclusions, conclusion_not_last, no_premises, empty_side, empty) and the honesty caps (too_many_premises at 16, too_many_atoms at 24 minted pairs — refuse, never truncate).

4. What the earned licenses certify

Same posture as ADR-0257 §4: the license certifies READER fidelity per shape (the engine cannot be wrong; the reader can hand it the wrong problem). The arena corpus is synthetic names × a class lexicon chosen to exercise every row-type of the morphology table (irregular, invariant, +s, +es, y↔ies); gold is authored by construction and cross-checked against the truth-table oracle over the template's INTENDED instantiated formulas — no reader in the loop (INV-25). The hand-authored real-English eval lane (evals/deduction_serve/v2_member/, content disjoint from the synthetic lexicon) keeps the structural-fidelity claim honest against natural prose, including the promoted ds-en-0022 (declined → entailed: this band's designed acceptance case).

5. Tri-language posture

Unchanged from ADR-0257 §5 — with one addition: the number-linking table is CORE's first morphology table, and it is exactly the artifact class the tri-language doctrine says must eventually enter through pack ratification (ADR-0253). English needs ~30 closed rows; Greek/Hebrew declension is where a curated morphology pack stops being optional. When a grc_*/he_* member band is built, the table moves from module constants to a ratified pack; no premature parameterization now.

6. Scope-outs (deliberate)

  1. Conditionalmembership fusion ("If Socrates is a man then …") — refuses mixed_structure_out_of_band; a future band composes the v2-EN connective grammar with this band's sentence readings.
  2. Existential premises/conclusions (some …) — needs witness semantics; pure categorical some stays with Band v1b.
  3. Tense (was/were) and verb predicates ("Socrates runs") — refuse; verb morphology is ADR-0257 scope-out #2's territory.
  4. Identity / definite descriptions / co-reference — refuse (definite_description_out_of_band); an equality-aware band is future work.
  5. Flag posture unchanged: rides deduction_serving_enabled (default off); no new flag.

7. Verification

  • uv run core test --suite deductive -q — reader contract, surfaces, license, all three lane splits, e2e through ChatRuntime.
  • Practice arena: 13 bands × 720 = 9,360 cases, wrong=0 required for every band; ledger re-sealed (chat/data/deduction_serve_ledger.json).
  • Eval lane: v1 28/28, v2_en 26/26 (ds-en-0022 promoted), v2_member new — wrong=0; report re-pinned (scripts/verify_lane_shas.py).