core/docs/handoff/ADR-0167-FOLLOWUPS.md
Shay 1cc05d9cce
docs: schema-defined proof obligations doctrine + convergence-site invariant + FOLLOWUPS §6 (#360)
Three small surgical anchors capturing the verified architectural
insight surfaced during the ADR-0167 wave (no new ADR — the gap claim
that prompted this resolved on verification; what remains is a sharper
residual question worth memorialising).

1. CLAUDE.md — new "Schema-Defined Proof Obligations" section between
   Documentation Discipline and Validation Through CLI.  Generalises
   the wrong=0 invariant pattern: schema types that name structural
   properties are real only when an executing test can meaningfully
   fail under the violations it is written to catch.  Three-step rule
   for treating a schema as load-bearing.

2. language_packs/compiler.py — ARCHITECTURAL INVARIANT comment on
   _apply_mounted_primary_domain_resonance naming it as the single
   convergence-decision site for DEPTH_ROOT/DEPTH_RELATION packs.
   Anchors the doctrine at the code site so any future modification
   trips on the reference to the holonomy proof's coverage gap.

3. docs/handoff/ADR-0167-FOLLOWUPS.md §6 — captures the structural-vs-
   blend convergence isolation question.  HolonomyAlignmentCase IS
   executed today (we verified), but the existing test doesn't
   distinguish structurally-derived convergence from blend-induced
   convergence.  Ablation test or reframed claim — both acceptable
   resolutions.

Verified before commit:
- All 13 architectural references in the Gemini analysis resolve
  exactly: triliteral 0.30, root 0.40, prefix 0.03/(idx+1), stem 0.24,
  _INFLECTION_PRIORITY case-near-last, _apply_mounted_primary_domain_resonance
  with 40% English-prototype blend, HolonomyAlignmentCase defined
  AND executed
- tests/test_alignment_graph.py: 8 passed (no behavioural change)
- Documentation discipline (#355) honoured: pure Markdown, no HTML

No code behaviour changes.  No runtime effect.  Drops the larger
ADR-0168-PROPOSAL idea — the gap claim that prompted it dissolved
under verification.
2026-05-27 07:52:20 -07:00

13 KiB

ADR-0167 — Follow-ups Queue

Date opened: 2026-05-27 (end of Wave 3) Parent: ADR-0167 Companion: SESSION-2026-05-27

The LexicalClaim slice landed clean (W1-A → W2-A/B/C/D → W3-A merged 2026-05-27). This file captures the named follow-ups that surfaced during the wave and were deliberately deferred so the slice could converge. The next operator picking up ADR-0167 work should walk this queue top-down and decide which item the project's current capability gate actually needs.

Each item: scope, why deferred, where the breadcrumbs live, and the acceptance criterion that would close it.


1. Frame-opener sub-types (FrameClaim / CompositionClaim / ReferenceClaim / SlotClaim)

Scope. Four additional ratification handlers, one per remaining sub-type in SUB_TYPE_FOR_OPERATOR:

Sub-type Maps from Ratification primitive
FrameClaim pre_frame_filler_sentence, multi_subject_sentence Verb-category reclassification
CompositionClaim multi_quantity_composition, quantity_extraction Frame-split rule
ReferenceClaim pronoun_resolution Anaphora-resolution entry
SlotClaim question_frame_slot, unit_binding, question_target_slot, descriptive_frame_question Slot-completion table entry

Why deferred. ADR-0167 §"Proposed sub-type set" explicitly chose LexicalClaim-first because it is the lowest-risk surface (drain_token additions cannot create wrong admissions without also passing graph completeness). The other four touch frame-opener decisions, anaphora, or slot bindings — each is a new admission path multiplying the wrong=0 surface area, and each needs its own scoping ADR with the six open questions from ADR-0167 §"Open questions" answered for that sub-type's mechanics.

Where breadcrumbs live.

  • teaching/math_evidence.py::SUB_TYPE_FOR_OPERATOR — already maps the operator labels; no schema change needed
  • teaching/math_lexical_ratification.py — the template for what a sub-type handler looks like (preconditions, receipt, idempotency, hazard pins)
  • tests/test_math_lexical_ratification.py::test_rejects_non_lexical_sub_type — pins that non-lexical claims currently raise WrongClaimSubType; each new handler retires its corresponding rejection

Acceptance. Each new sub-type ships as an ADR (likely ADR-0168, ADR-0169, ...) followed by a wave of PRs analogous to ADR-0167's W2-D. Each handler must:

  • declare its own SAFE_CATEGORIES allowlist analogous to W2-D's {"drain_token"}
  • preserve the case 0050 hazard pin (see feedback-wrong-zero-hazard-case-0050)
  • carry the same idempotency / evidence-tampering / unknown-category guards W2-D established
  • pass an e2e ratification → row-movement test analogous to test_lexical_ratification_advances_unknown_word_row

Priority hint. FrameClaim is the highest-leverage next sub-type (9 cases in the current taxonomy under pre_frame_filler_sentence), but also the riskiest — frame-opener miscategorisation is exactly the case 0050 hazard. CompositionClaim (8+11 cases) is the next-highest count, also high-risk. ReferenceClaim (3 cases) and SlotClaim (smaller buckets) are lower-leverage but structurally simpler.


2. Partition test architectural fix

Scope. Reframe or retire tests/test_candidate_domain_partition.py::test_existing_cognition_tests_untouched.

Why deferred. The current test uses git status --porcelain at test runtime to enumerate modified files, then asserts each name is in a named allowlist. Opus's W3-A report flagged this as structurally brittle: every future ADR-0167 PR has to edit the allowlist or the test fails, regardless of whether the PR actually touched cognition behaviour. Opus loosened from a single literal to a named set in W3-A as a tactical fix, but the architectural problem remains — a partition invariant doesn't belong as a git-state assertion in a pytest run.

Where breadcrumbs live.

  • tests/test_candidate_domain_partition.py::test_existing_cognition_tests_untouched — the current implementation
  • W3-A's PR description (#357) — the deeper brittleness flag in Opus's report-back

Acceptance. One of:

  • (a) Retire the test entirely — partition is already enforced structurally by the domain field default + type discriminator; the cognition test suite already runs against every PR and would break naturally if a PR modified cognition behaviour
  • (b) Move to CI — a GitHub Actions workflow that runs git diff --name-only origin/main...HEAD -- tests/ and fails on any cognition test path modification, with CODEOWNERS providing the human review layer
  • (c) Reframe as a diff-review constraint — drop the test, add a CODEOWNERS entry requiring a cognition reviewer for any PR touching tests/test_*cognition*.py or similar

Pick whichever fits the project's CI discipline. Option (a) is simplest if the assertion is genuinely redundant; (b) is most precise; (c) is most operator-friendly.


3. Pre-existing main test failures (unrelated to ADR-0167)

Scope. Two tests fail on clean main and on every branch that touches anything; W3-A's regression run surfaced them but they predate the LexicalClaim wave.

  • tests/test_math_candidate_graph.py::TestRefusals::test_unparseable_statement
  • tests/test_teaching_audit.py::test_audit_real_corpus_runs_clean

Why deferred. Out of scope for the LexicalClaim slice — these are not regressions caused by the wave. Confirmed by W3-A's report: both fail on origin/main with no ADR-0167 changes applied.

Where breadcrumbs live.

  • Run uv run pytest tests/test_math_candidate_graph.py -k test_unparseable_statement -v on clean main to reproduce
  • Run uv run pytest tests/test_teaching_audit.py -k test_audit_real_corpus_runs_clean -v on clean main to reproduce

Acceptance. Each test either:

  • gets fixed (root cause investigation + minimal patch), or
  • gets quarantined via the existing test-quarantine mechanism with a written reason that names what's actually broken

The "fix or quarantine, don't ignore" principle stands. They are distracting noise during regression runs and obscure real regressions.


4. Workbench v1 — math candidate rendering

Scope. Make the read-only operator UI (ADR-0160/0162) render MathReaderRefusalEvidence candidates alongside cognition DiscoveryCandidate records.

Why deferred. ADR-0167 §"Open questions Q4" explicitly out-of-scope. The LexicalClaim slice ships the ratification handler but no UI to trigger ratification through — today, an operator would call apply_lexical_claim() from a Python REPL.

Where breadcrumbs live.

  • ADR-0160 (Core Workbench v1)
  • ADR-0162 (Workbench Design System)
  • W-029 (proposal queue) — closest existing surface
  • W-031 (replay theater) — replay primitive that math evidence records inherit through ADR-0057

Acceptance. A workbench panel that lists pending MathReaderRefusalEvidence candidates with sub-type, claim signature, recognized terms, refusal context, and a ratify-action that calls apply_lexical_claim() (or its sub-type-specific successor) with an operator-supplied reviewer tag and category.


5. Cross-domain partition risks (from Gemini's W2-C audit)

Two specific code paths Gemini flagged in docs/handoff/ADR-0167-W2C-cross-domain-audit.md as needing partition discrimination:

5a. Contemplation pack indexing

Scope. teaching/contemplation.py::contemplate() uses hardcoded cognition pack and corpus indexes (_pack_index and _corpus_index). Future math-domain candidates would silently get cognition-domain lookups.

Acceptance. Pack and corpus indexes parameterised by candidate.domain — cognition candidates look up cognition packs, math candidates look up math packs (currently en_core_math_v1). Tests must exercise both paths.

5b. Replay gate default

Scope. teaching/proposals.py defaults its replay gate to cognition's. Proposing math/admissibility candidates requires passing run_admissibility_replay_gate explicitly to prevent false rejections.

Acceptance. Either the replay gate is selected by proposal.domain, or the cognition default is made explicit and math proposals are required to declare their gate. Decision goes in ADR-0168 (or wherever the first non-lexical sub-type ADR lands — that handler will be the first real exerciser of the proposals path for math).


6. HolonomyAlignmentCase — structural-vs-blend convergence isolation

Scope. Determine whether the existing tests/test_alignment_graph.py::test_holonomy_alignment_case_positive_closer_than_negative proves structurally-derived cross-language convergence or only proves endpoint similarity under the mount-time blend.

Why deferred. The proof obligation is executed today — the test asserts that an aligned Logos clause produces nearer holonomies across English/Hebrew/Greek than a misaligned negative triple. That clears the schema's nominal claim. But the test does not distinguish two possible explanations for the convergence:

  1. Structural. The Hebrew tri-consonantal root rotors and Greek case-last orientation rotations produce versors that genuinely land in the same regions of the manifold because the morphology operators encode equivalent semantic structure.
  2. Blend-induced. _apply_mounted_primary_domain_resonance (language_packs/compiler.py:558) nudges Hebrew/Greek versors toward an English prototype at 40% blend, and the test passes because both packs have been pulled close to the English anchor regardless of structural derivation.

If (2) is doing the work, the three-language architecture is a claim that English-anchored geometric averaging produces the right endpoints, not a proof that the depth packs are structurally independent operators converging coherently with the articulation surface.

Where breadcrumbs live.

  • language_packs/compiler.py::_apply_mounted_primary_domain_resonance — the architectural-invariant comment names this gap explicitly and references this section
  • tests/test_alignment_graph.py:73 — the existing positive-closer- than-negative assertion
  • language_packs/schema.py::HolonomyAlignmentCase — the schema type whose nominal contract is "proves structural divergence with coherent convergence"

Acceptance. One of:

  • (a) Ablation test. A test that runs the holonomy proof with _apply_mounted_primary_domain_resonance disabled (or with the blend factor set to 0.0) and asserts that the positive-closer-than- negative relation still holds. This would prove (1) and retire the concern.
  • (b) Reframe the claim. If the ablation fails, document explicitly that cross-language convergence depends on the mount-time blend, and update HolonomyAlignmentCase's contract to reflect what it actually proves (endpoint similarity under blend, not structural-derivation equivalence). Honest documentation of a weaker property beats a stronger claim that the test can't support.

Priority. Low-urgency, high-information. Not blocking any current capability gate. Worth picking up whenever someone next touches the language-pack architecture — the comment at the convergence-decision site is the trip-wire.

Per CLAUDE.md §"Schema-Defined Proof Obligations" — this is the prototypical example of a schema-defined obligation that is executed but where the test may not meaningfully fail under the violation it is written to catch.


Sequencing recommendation

For the operator picking this up next:

  1. First, decide which of the four frame-opener sub-types (§1) the next capability gate actually demands. ADR-0166 still gates this — the three-question test must pass for whichever sub-type is chosen.
  2. In parallel, do §2 (partition test fix) as a small docs/CI PR — it unblocks every future ADR-0167 PR's regression run.
  3. At convenience, handle §3 (pre-existing failures) — distracting but not blocking.
  4. Defer §4 + §5 until §1 actually ships a second sub-type — they only become load-bearing once a second domain candidate type exists.
  5. Pick up §6 opportunistically — whoever next modifies _apply_mounted_primary_domain_resonance or the holonomy test owns the ablation question.

No timelines. Order is by leverage, not calendar.


Cross-references