core/docs/adr/ADR-0260-verb-predicate-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

131 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-0260 — Band v5-VP: verb-predicate arguments, decided
- **Status:** Accepted — ratified by Joshua Shay via the PR #111 merge (`2a82c8a3`, 2026-07-24)
- **Date:** 2026-07-24
- **Arc:** deduction-serve (ADR-0256 → 0257 → 0258 → 0259 → this)
- **Governs:** `generate/proof_chain/verb.py`, the `en_verb_*` shape-bands,
their arena templates, the `v2_verb` lane split, and the
`_verb_band_surface` serving tier.
## 1. Context
ADR-0258 §6.3 scoped verb predicates out of the member band: v3-MEM reads
only copula sentences, so "All philosophers teach. Socrates is a
philosopher. Therefore Socrates teaches." refused — despite being the plain
English shape in which most non-logic subject matter states its rules
(physics, biology, and every other domain speak in verb sentences, not is-a
chains). The generalization arc (docs/plans/generalization-arc-2026-07-24.md
Phase 1.1) makes this the single highest-leverage reading unlock: Phase 2's
curriculum-entailment serving is gated on it.
The earlier bands each guard this territory out rather than misreading it:
v2-EN refuses quantifier-led sentences (`categorical_shape_out_of_band`),
is-a clauses (`membership_shape_out_of_band`), and unnormalized negation
(`internal_negation_unread`); v3-MEM/v4-CM refuse copula-free sentences
(`sentence_shape_out_of_band`). Pure verb-fact restatements with none of
those triggers are ALREADY decided soundly by v2-EN as opaque clause-atoms —
so this band's genuine territory is exactly the fall-through: arguments
mixing verb sentences with quantified rules, membership facts, or `does not`
negation.
## 2. Decision
Add `generate.proof_chain.verb.read_verb_argument` as the next fallback tier
(after v1 / v1b / v2-EN / v3-MEM / v4-CM), extending v3-MEM's
per-individual propositional lowering with a SECOND atom family in one
shared space:
- **Membership atoms** *(individual, class-group)* — minted by v3-MEM's own
`_parse_singular` / `_parse_universal`, reused verbatim (copula sentences
keep their exact v3-MEM reading, refusals included).
- **Verb atoms** *(individual, verb-lemma-group, object-term)* — minted from
verb facts (`<name> <verb3sg> [<object>]`, `does not` negation, the
sentential-not prefix) and verb universals
(`all|every|each|no [the] <class> <verb> [<object>]`).
A verb universal instantiates at every named individual as
`mem(i, C) -> [~]verb(i)` — so a membership fact minted by a copula sentence
discharges a verb rule. The same verified ROBDD engine decides.
Bands (priority order): `en_verb_negative` (any negative universal, either
kind) > `en_verb_chain` (≥2 universals) > `en_verb_universal` (exactly 1) >
`en_verb_fact` (facts only). Rendered by `render_entailment_verb` — the
member surface with the UNKNOWN scoping extended to the verb reading. Rides
`deduction_serving_enabled` (default off); no new flag.
## 3. Why this is sound
1. **Instantiation** — identical to ADR-0258 §3: universal instantiation at
named individuals is truth-preserving in every first-order model, and
each *(verb-group, object-term)* pair is one opaque unary predicate over
the same named-individual domain. ENTAILED / REFUTED / inconsistent
verdicts over the lowered premises hold in every model of the original
argument; completeness within the fragment lifts the same way (a
propositional countermodel's domain is exactly the named individuals).
2. **Agreement is the ONE new identification** — a universal's base form
("teach") and a singular fact's 3sg form ("teaches") are identified iff
related by a CLOSED relation: a verb-specific irregular table (has/have,
goes/go, does/do; consulted first, sole authority for its tokens) else
the three regular suffix rules (+s, +es after sibilants, y↔ies). The noun
table is deliberately NOT reused — it would misroute verb forms like
"lives" (noun: life; verb: live). Under-linking costs coverage, never
soundness. Object terms link by exact token identity only.
3. **Scope-tight segmentation** — without a lexicon, an arbitrary
copula-free token run cannot be soundly split into subject/verb/object;
the band reads single-token name/class/verb/object shapes ONLY, and
refuses everything longer, typed. A three-token sentence read as
(subject, verb, object) is a disclosed reading, restated verbatim in the
surface's "Given:" line.
4. **Cascade honesty** — every text this band decides was verified to be
REFUSED by all four earlier bands (the arena templates and lane cases
are constructed on the fall-through path serving actually uses), so this
tier is pure widening: every previously-served argument is served
byte-identically.
## 4. License and render
The four `en_verb_*` bands earned SERVE through the ADR-0199 arena
(720/band committed, wrong=0, θ_SERVE=0.99; ledger resealed at 21 bands).
The arena's by-construction gold is cross-checked template-by-template
against the independent truth-table oracle over each template's INTENDED
per-individual lowering, with no reader in the loop (INV-25). Unearned or
ledger-stripped deployments serve the same sound answer hedged
(`_UNVERIFIED_SHAPE_DISCLOSURE`), exactly as every earlier band.
## 5. Scope-outs (typed refusals today, future bands' territory)
1. **Connective × verb composition** — "If Socrates teaches then …"
refuses `mixed_structure_out_of_band`; fusing v4-CM's connective grammar
over verb leaves is the natural next composition.
2. **Multi-token subjects/objects, PPs, adverbs, ditransitives**
`sentence_shape_out_of_band`. Note one disclosed misreading inside the
3-token shape: an adverb in object position ("Lena runs quickly") is
read as an object TERM — never unsound (worst case an over-scoped
UNKNOWN), documented here rather than guessed at.
3. **Tense**`did not` refuses `tense_out_of_band`; bare past forms
("taught") are indistinguishable from lemmas without a lexicon and read
as distinct opaque lemmas (under-linking, sound).
4. **Arity/subsumption** — "teaches" does not entail "teaches logic" (nor
vice versa) under the face-value reading; both directions stay UNKNOWN,
scoped. Same posture v2-EN already takes for opaque clauses.
5. **Plural/bare subjects in verb facts** ("dogs bark") — read as an
individual named "dogs" (disclosed in the UNKNOWN scoping); the copula
analogue is refused by v3-MEM's bare-plural guard, but no copula means no
plural signal in the closed grammar.
6. Existential (`some`) witnesses, identity/co-reference — unchanged
scope-outs inherited from ADR-0258 §6 (existentials are the
generalization arc's Phase 1.2, assigned to the Opus tier).
## 6. Verification
- `tests/test_verb_argument_reader.py` — 34 tests: gap-is-real cross-check,
flagship lowering, every agreement rule, object-in-atom-key, chain
discharge, cross-individual non-leakage, family separation
(teacher/teaches), 14 typed refusals incl. delegated v3-MEM reasons,
honesty caps, determinism.
- Surface/e2e/lane: `tests/test_deduction_surface.py` (+5),
`tests/test_deduction_serve_e2e.py` (+1),
`tests/test_deduction_serve_lane.py` (+1 over the 28-case `v2_verb`
split) — full lane 134/134, wrong=0, all five splits.
- Arena: 4 × 720 committed, wrong=0, first seal run; corpus soundness
asserted against the independent oracle before sealing (15,120 cases).