docs(post-rat1): four parallel dispatch briefs (B/C/D/E)

After RAT-1's architecture audit exposed five systematically
underbuilt components, A (the 4 missing injectors) is being taken by
this agent. The other four are parallel-safe and each gets its own
dispatch brief:

  B (Opus)   — contemplation produces ratifiable claims (medium)
  C (Sonnet) — comprehension reader audit + decision (small→medium)
  D (Sonnet) — core teaching coverage CLI (small)
  E (Codex)  — lexical ratification auto-compile (tiny)

Each brief carries: operator profile, branch name, base, dispatch
line, reads required FIRST, outcome inventory, hard requirements,
tests, truth test. Anti-regression invariants enumerated. All
parallel-safe — no shared file conflicts.

The dispatch DAG names ALL FIVE pieces with A in-flight as a peer.
This commit is contained in:
Shay 2026-05-27 20:24:39 -07:00
parent 3e196df097
commit 85b8f0cd11

View file

@ -0,0 +1,304 @@
# Post-RAT-1 Parallel Briefs (B, C, D, E)
**Date:** 2026-05-27
**Author:** Shay
**Context:** Following the architecture audit (RAT-1 / PR #406), five components are structurally underbuilt. **A (the 4 missing injectors) is in flight as its own PR by Opus**. The remaining four are parallel-safe and each can be dispatched to its own operator.
Each brief below is self-contained, copy-paste-runnable.
---
## Brief B — Make math contemplation produce **ratifiable** claims
**Operator profile:** Opus (load-bearing — the claim shape is what an operator ratifies; getting it wrong is operator-burden risk)
**Branch:** `feat/contemplation-ratifiable-claims`
**Base:** `origin/main`
**Estimated effort:** medium
### Why
Currently `core eval math-contemplation` produces proposals whose `proposed_change_payload` is:
```json
{"evidence_count": 8, "group_key": {...}, "modal_sub_type": "composition"}
```
This is evidence aggregation, not a ratifiable claim. The operator has to **design the claim from scratch** (pick a `surface_pattern`, pick a `composition_category`, pick a `polarity`) before calling `apply_composition_claim()`. The "operator just reviews" framing is misleading.
### Outcome
Make `teaching/math_contemplation.py::decompose_audit` (and the dispatcher in `teaching/math_contemplation_proposal.py`) emit proposals where `proposed_change_payload` carries:
```json
{
"surface_pattern": "bound(count) × bound(unit_cost)",
"composition_category": "multiplicative_composition",
"polarity": "affirms",
"evidence_count": 8,
"group_key": {...},
"modal_sub_type": "composition"
}
```
— directly ratifiable by `apply_composition_claim()` with no operator-side design step.
For `proposed_change_kind == "composition_reclassification"`, dispatch by `missing_operator`:
- `quantity_extraction``multiplicative_composition` + `bound(count) × bound(unit_cost)` (currency-per-unit shape)
- `multi_quantity_composition``additive_composition` + `bound(qty_a) + bound(qty_b)` (default; operator may edit)
For `frame_reclassification`, `matcher_extension`, `injector_sub_shape` — keep current payload (those are still upstream of ratifiable claims).
### Reads required FIRST
- `teaching/math_contemplation.py::decompose_audit`
- `teaching/math_contemplation_proposal.py` (proposal schema)
- `teaching/math_composition_proposal.py::SAFE_COMPOSITION_CATEGORIES`
- `teaching/math_composition_ratification.py::apply_composition_claim` signature
### Hard requirements
- Backward-compatible JSONL: existing tests that read evidence_count + group_key + modal_sub_type must still pass
- Only `composition_reclassification` proposals get the enriched payload in v1 (frame/matcher/injector deferred)
- `polarity` is always `"affirms"` (the audit row signals a real refusal — the operator can override to `"falsifies"` if needed)
- `surface_pattern` must be in the operator's expected vocabulary (mirror the three SAFE patterns)
- An end-to-end test that runs `core eval math-contemplation` then immediately feeds the first composition proposal into `apply_composition_claim()` without any field synthesis
### Tests
- `tests/test_contemplation_ratifiable_payload.py`:
- 5+ test cases: each refusal pair yields a payload whose fields satisfy `apply_composition_claim`'s preconditions
- Round-trip: proposal payload → ratification → no exception
- Schema regression: existing fields still present
- `tests/test_adr_0172_w2_decomposer.py` — update existing assertions
### Truth test
After this PR + a fresh `core eval math-contemplation`, the operator workflow becomes:
```
core eval math-contemplation
core teaching review <composition-proposal-id> --accept --review-date YYYY-MM-DD
```
**without manually constructing a `MathReaderRefusalEvidence` or picking a category.**
---
## Brief C — Comprehension reader audit + decision
**Operator profile:** Sonnet (investigation + documentation; minor wiring if needed)
**Branch:** `docs/comprehension-reader-audit`
**Base:** `origin/main`
**Estimated effort:** small (investigation) — could escalate to medium if "operationalize" is chosen
### Why
The comprehension reader (`generate/comprehension/lifecycle.py` — `begin_sentence`, `apply_word`, `end_sentence`, `finalize`, `ProblemReadingState`, `EntityRef`, Phase 1/2 of ADR-0164) is substantial code that **admits zero cases in the math eval**.
Direct measurement:
```
core eval gsm8k_math --split public --use-reader → 150/150 wrong=0 (same as without)
train_sample --use-reader → 3/47/0 (same as without)
```
The reader exists but contributes nothing observable. Two possible truths:
1. **It's load-bearing for something we don't measure** (cognition lane? semantic recall? answer rendering?)
2. **It's a parallel R&D track that needs honest naming** as not-yet-operational on math
### Outcome (investigation phase)
Produce `docs/handoff/COMPREHENSION-READER-AUDIT.md` answering:
1. Where in the live code path does `_try_comprehension_reader` actually run? Trace every caller.
2. When `comprehension_reader_questions=True`, what specifically does the reader admit on the cognition eval lane (not just math)?
3. Is the all-or-nothing discipline (one refusing sentence kills the whole reader path) the bottleneck on math? Or is the reader itself refusing on simple shapes?
4. Are there ADR-0164 Phase 1/2 promises that aren't being honored?
5. List 3 options:
- **Operationalize**: change all-or-nothing → per-sentence so reader can contribute partial admissions
- **Relabel**: honest doc update naming reader as "cognition track, not math substrate" (if true)
- **Retire**: if the reader path duplicates capability that the regex/recognizer paths already provide
### Hard requirements
- No code changes in the investigation phase — pure read + doc
- Audit must distinguish reader-on-math vs reader-on-cognition usage
- Recommendation must be falsifiable (provide a measurable test for each option)
- If "operationalize" is chosen, ship as separate PR after operator approval
### Tests
- None in audit phase. Implementation phase (if approved): operationalize path requires its own test plan.
### Truth test
After this brief: the project has a deliberate answer to "what does the comprehension reader do today, and what should it do?" Right now nobody knows. That's the bug being closed.
---
## Brief D — `core teaching coverage` CLI
**Operator profile:** Sonnet (tight-scope CLI; mechanical aggregation)
**Branch:** `feat/teaching-coverage-cli`
**Base:** `origin/main`
**Estimated effort:** small
### Why
There's no automated way to answer "given the current ratified state, what % of train_sample admits / refuses / wrong-counts by ShapeCategory?" We only see deltas by running the eval manually and eyeballing report.json. Flying blind on operator dispatch decisions.
### Outcome
New CLI: `core teaching coverage [--lane gsm8k_math] [--split train_sample] [--use-reader] [--json]`
Behavior:
1. Run the lane's runner if its report.json is stale (or always, if `--run`)
2. Read the per-case verdict + refusal reasons
3. Bin by:
- `correct / refused / wrong`
- Within refused: by `(refusal_mode, ShapeCategory)` — using the same categorization the position paper §4 table uses
4. Emit a clean histogram with deltas vs the **last committed** report.json
Example output:
```
Lane: gsm8k_math/train_sample/v1 (use_reader=true)
Counts: correct=3 refused=47 wrong=0 (Δ from prior: 0 / 0 / 0)
Refusal taxonomy:
21 recognizer_empty_injection(discrete_count_statement)
10 no_admissible_candidate
5 recognizer_empty_injection(multiplicative_aggregation)
4 recognizer_empty_injection(currency_amount)
3 recognizer_empty_injection(rate_with_currency)
2 recognizer_empty_injection(temporal_aggregation)
2 recognizer_empty_injection(descriptive_setup_no_quantity)
Wrong=0: ✓
Case 0050 hazard pin: refused ✓
```
### Reads required FIRST
- `evals/gsm8k_math/train_sample/v1/runner.py`
- `evals/gsm8k_math/train_sample/v1/report.json` schema
- `core/cli.py` existing teaching subcommands
- `evals/refusal_taxonomy/shape_categories.py`
### Hard requirements
- Read-only (no eval lane mutation)
- Delta comparison against the most recent **committed** report.json (uses `git show HEAD:evals/.../report.json` — if absent, no delta)
- `--json` for CI integration
- `--lane` defaults to `gsm8k_math`; `--split` defaults to `train_sample`
- Refusal taxonomy is regex-pulled from `report.json[per_case][].reason` — no hardcoded category list
- Exit code 0 on success regardless of counts (it's a report, not a gate)
### Tests
- `tests/test_teaching_coverage_cli.py`:
- Fixture report.json with known counts → expected histogram
- Delta path: stage old + new report → expected delta
- `--json` schema
- Empty/malformed report.json → clear error
### Truth test
After dispatch: every operator can run `core teaching coverage` after any ratification to see exactly which refusal modes their work moved (or didn't).
---
## Brief E — Lexical ratification auto-compile
**Operator profile:** Codex (tiny mechanical; mirror RAT-1's pattern)
**Branch:** `feat/lexical-ratification-auto-compile`
**Base:** `origin/main`
**Estimated effort:** tiny
### Why
RAT-1 (PR #406) added `compile_pack()` auto-call at the end of `apply_frame_claim` + `apply_composition_claim` so source-file writes immediately reach the runtime. **`apply_lexical_claim` was deliberately skipped** because the existing `language_packs/compiler.py` already compiles `lexicon.jsonl`. But the lexicon compiler runs at pack-build time, not after a runtime ratification.
So today: `core teaching` ratifies a LexicalClaim → writes `lexicon/{category}.jsonl` → the **next runtime turn doesn't see it** because nothing triggers re-compile + manifest update.
### Outcome
Extend `teaching/math_lexical_ratification.py::apply_lexical_claim` to call `compile_pack()` at the end of a successful ratification — same pattern RAT-1 used for frame + composition.
Plus: ensure `compile_pack()` regenerates the lexicon compiled artifact `lexicon.jsonl` AND updates `manifest.checksum`. Currently RAT-1's `compile_pack` only handles frames + compositions; this brief extends it.
### Reads required FIRST
- `teaching/math_lexical_ratification.py::apply_lexical_claim`
- `language_packs/compile_pack.py` (the RAT-1 helper)
- `language_packs/compiler.py::_load_pack_cached` (existing lexicon compile)
- `generate/comprehension/lexicon.py::load_lexicon` (the runtime consumer)
### Hard requirements
- `wrong == 0` preserved (no test moves wrong)
- The existing lexicon checksum SCHEME stays the same — just regenerated more frequently
- Mirror RAT-1's `tests/test_math_{frame,composition}_ratification.py` update — `test_lexicon_checksum_preserved_by_lexical_ratification` (manifest may change; lexicon checksum re-derives from compiled bytes)
- Idempotent: running ratification twice doesn't bump checksum unless source bytes changed
- Existing `core teaching compile-pack` command should pick up lexical changes too — extend the receipt to include `lexicon_checksum` + `lexicon_bytes_written`
### Tests
- `tests/test_lexical_ratification_auto_compile.py`:
- Ratify a LexicalClaim → compile fires → lexicon registry reload sees the new entry
- Idempotent: second ratify with same evidence → no compile mutation
- Lexicon-checksum-preserved-across-ratify (with new bytes)
### Truth test
After this PR: a LexicalClaim ratification reaches the runtime within one turn, matching the frame + composition discipline RAT-1 established.
---
## Dispatch DAG
```
RAT-1 (PR #406) — base for all four briefs
├──── A (Opus, in-flight) — 4 missing injectors
├──── B (Opus) — contemplation ratifiable claims
├──── C (Sonnet) — comprehension reader audit
├──── D (Sonnet) — coverage CLI
└──── E (Codex) — lexical auto-compile (tiny)
```
All four briefs are parallel-safe — no shared file conflicts. Each touches different modules.
## Anti-regression invariants (all four)
- `wrong == 0` on `core eval gsm8k_math --split public` preserved (150/150)
- Case 0050 hazard pin holds
- `engine_state/*` never committed
- ADR-0166 — no new eval lanes
## Memory pointers
- [[milestone-me1-me5-matcher-extensions-complete]] — the wave that exposed the gaps
- [[project-ratification-consumption-gap-2026-05-27]] — the original finding
- [[feedback-ratify-vs-consume-loop-closure]] — the general pattern
---
## Copy-paste dispatch (per brief)
```text
# Brief B
Read docs/handoff/POST-RAT1-PARALLEL-BRIEFS.md §"Brief B".
git fetch origin main && git worktree add /tmp/wt-brief-b origin/main && cd /tmp/wt-brief-b && git checkout -b feat/contemplation-ratifiable-claims
# Brief C
Read docs/handoff/POST-RAT1-PARALLEL-BRIEFS.md §"Brief C".
git fetch origin main && git worktree add /tmp/wt-brief-c origin/main && cd /tmp/wt-brief-c && git checkout -b docs/comprehension-reader-audit
# Brief D
Read docs/handoff/POST-RAT1-PARALLEL-BRIEFS.md §"Brief D".
git fetch origin main && git worktree add /tmp/wt-brief-d origin/main && cd /tmp/wt-brief-d && git checkout -b feat/teaching-coverage-cli
# Brief E
Read docs/handoff/POST-RAT1-PARALLEL-BRIEFS.md §"Brief E".
git fetch origin main && git worktree add /tmp/wt-brief-e origin/main && cd /tmp/wt-brief-e && git checkout -b feat/lexical-ratification-auto-compile
```