docs: composition-wall execution scope (ADR-0207 §5 step 2)
Three reviewed, non-serving analysis/handoff docs for the COMPOSITION lever. Read-only; no serving code; train_sample 6/44/0 untouched. - analysis/wiring-promotion-gate-brief: product_bridge is the only serving derivation tendril (two-token target whitelist); resolve_pooled refuses all ten R1/R4/R5/R6 positives and wrong-commits 0016 -> 510. Wiring is trivial; the gate is the work. - analysis/composition-wall-execution-plan: per-case stage taxonomy (A/B/C/D) for all 15 corpus positives, reproduced live; R4-first scope (new goal-residual production, verified by code-read of accumulate.py, NOT a target tweak); promotion gate generalizes product_bridge, never disagreement alone; gates inherit ADR-0207 §6 (corpus + sealed 1,319). - handoff/stage-c-composition-investigation: dispatch task for the execution lane to pin the per-case bail for the production-wrong shapes (the real grouping/op-order wall), read-only, in-tree.
This commit is contained in:
parent
b84ca33548
commit
5ae377065e
3 changed files with 407 additions and 0 deletions
172
docs/analysis/composition-wall-execution-plan-2026-06-03.md
Normal file
172
docs/analysis/composition-wall-execution-plan-2026-06-03.md
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
<!-- CANONICAL | composition-wall-execution-plan-2026-06-03.md | 2026-06-03 | spec-and-review (Opus) lane | read-only analysis / execution plan for ADR-0207 §5 step 2 | verified against origin/main d9fc7f9 | NOT a new design -->
|
||||
|
||||
# Composition Wall — Execution Plan (R1/R4 first)
|
||||
|
||||
This is the execution scope for **ADR-0207 §5 step 2 (COMPOSITION)** — *not* a new design. It
|
||||
takes the ratified substrate as given and answers the only question that matters next: *what,
|
||||
concretely, must change in the built `generate/derivation/` composer to move the live metric,
|
||||
and where is each target shape actually blocked?* Every per-case claim below was reproduced
|
||||
read-only against the tree on 2026-06-03 by running the actual composer on the corpus cases.
|
||||
|
||||
## 1. The wall, restated from reproduction (folds in the wiring brief)
|
||||
|
||||
- `resolve_pooled` (the general pooled composer, `pool.py:62`) **refuses all ten R1/R4/R5/R6
|
||||
corpus lift-targets**, so wiring it to serving yields **+0 correct**; and it **wrong-commits
|
||||
`0016 → 510`** (gold 2), so wiring it wholesale is a live `wrong=0` regression.
|
||||
- The only derivation symbol on serving is `product_bridge.resolve_promotable_product`
|
||||
(`math_candidate_graph.py:530`), and its safety is a **two-token target near-whitelist**
|
||||
(`money`+`make/earn`, `weight`+`total/move`) — it does not generalize.
|
||||
- Therefore **WIRING is trivial and already done for the safe shapes**; the actual work is
|
||||
making the composer *build the right chain for a new shape and certify it*. The wire is the
|
||||
last, one-line step (mirror the `:530` block) once a shape builds-and-certifies.
|
||||
|
||||
## 2. Grounded per-shape diagnosis (all 15 corpus positives)
|
||||
|
||||
Ran `extract_quantities` → `extract_target` → the three composers
|
||||
(`accumulation_candidates` / `multiplicative_candidates` / `candidate_chains`) on each. "Stage"
|
||||
is where the shape is blocked (see §3). Golds are dataset-sourced (corpus invariant 6).
|
||||
|
||||
| case | comp | gold | #qty | target-agg | acc/mul/chn | result | **stage** |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| cv-0001 | R1 | 64 | 1 | None | 0/0/0 | none | **A** extraction (`$16` not extracted) |
|
||||
| cv-0003 | R1 | 9 | 6 | None | 0/0/0 | none | **B** target |
|
||||
| cv-0002 | R1 | 400 | 3 | total | 0/0/4 | wrong-only | **C** production |
|
||||
| cv-0004 | R1 | 3840 | 4 | None | 0/1/4 | wrong-only | **C** production |
|
||||
| cv-0005 | R4 | 3 | 3 | None | 0/0/0 | none | **B** target |
|
||||
| cv-0021 | R4 | 4 | 4 | None | 0/0/0 | none | **B** target |
|
||||
| cv-0006 | R5 | 14 | 2 | None | 3/0/4 | wrong-only | **C** production |
|
||||
| cv-0022 | R5 | 38 | 2 | total | 0/1/4 | wrong-only | **C** production |
|
||||
| cv-0007 | R6 | 21 | 3 | None | 0/0/0 | none | **B** target |
|
||||
| cv-0008 | R6 | 15 | 1 | None | 0/0/0 | none | **A**+**B** extraction+target |
|
||||
| cv-0009 | compare_mult | 60 | 1 | altogether | 0/0/0 | none | **A** extraction |
|
||||
| cv-0018 | compare_mult | 28 | 2 | None | 0/0/2 | wrong-only | **C** production |
|
||||
| cv-0019 | additive | 1200 | 2 | None | 0/0/0 | none | **B** target |
|
||||
| cv-0017 | *(control)* | 438 | 5 | None | 0/0/2 | wrong-only | control — solves on serving |
|
||||
| cv-0020 | *(control)* | 450 | 3 | total | 0/1/2 | **GOLD-BUILT** | control — **D** gate exemplar |
|
||||
|
||||
Controls (cv-0017=case 0024, cv-0020=case 0021) already solve on serving via other paths; they
|
||||
are regression anchors, not lift targets. cv-0020 is the **proof the build-then-gate pattern
|
||||
works**: the pool *builds the gold* (450) but only commits it on serving because
|
||||
`product_bridge`'s gate promotes it.
|
||||
|
||||
## 3. The four-stage failure taxonomy (and the `wrong=0` firewall at each)
|
||||
|
||||
The composer is a pipeline: **extract → characterize target → build production → certify (gate)**.
|
||||
A shape can be blocked at any stage; each stage refuses (never commits) when incomplete, so
|
||||
`wrong=0` holds throughout.
|
||||
|
||||
- **Stage A — extraction-thin.** The composer is starved of a needed quantity, so no chain is
|
||||
possible. *cv-0001* (currency literal `$16` not extracted — the derivation reader has no `$N`
|
||||
production; this is the gap I earlier *overstated as closed*), *cv-0008*, *cv-0009*. Firewall:
|
||||
no quantity → no candidate → refuse.
|
||||
- **Stage B — target-uncharacterized.** Quantities are present but `extract_target` returns
|
||||
`aggregation=None`, so the composer doesn't know *what* to build. *cv-0003, cv-0005, cv-0007,
|
||||
cv-0019, cv-0021*. **Both R4 cases live here.** Firewall: no target → no admissible chain → refuse.
|
||||
- **Stage C — production-wrong.** The composer builds chains, but the *wrong* ones (none equals
|
||||
gold), and the disagreement rule correctly refuses them. *cv-0002, cv-0004, cv-0006, cv-0018,
|
||||
cv-0022.* This is the hard core — "which quantities group, via which ops, in what order."
|
||||
Firewall: disagreement among wrong candidates → refuse.
|
||||
- **Stage D — gold-built, needs gate.** The composer builds the gold (possibly among rivals); a
|
||||
*structural* promotion gate certifies and commits it. Exemplar: *cv-0020* via `product_bridge`.
|
||||
Firewall: the gate must prove the reading; a lone wrong chain with no rival (the *0016* pattern)
|
||||
is exactly what disagreement cannot catch, so **the gate must be structural, never
|
||||
disagreement alone.**
|
||||
|
||||
## 4. Execution sequence — one shape, end-to-end, instrumented
|
||||
|
||||
Each shape is a **hypothesis to test against the gates**, not a guaranteed win. Drive *one* shape
|
||||
through its blocked stages to green on the corpus, then check the sealed 1,319, before starting
|
||||
the next.
|
||||
|
||||
1. **R4 first — but it is NEW PRODUCTION, not a target tweak** (verified 2026-06-03 by code-read
|
||||
of `accumulate.py`: `_build_accumulation` is single-referent gain/loss running-total with **no
|
||||
goal/target/residual concept** — target characterization alone does **not** unblock it). cv-0005
|
||||
(single-referent `goal − Σchanges`) is the nearest extension of GB-3b.1 and the first build;
|
||||
cv-0021 (multi-referent give-away to a remainder) is two layers harder — separate, do not bundle.
|
||||
Goal-recognition is a new `wrong=0` surface (must refuse on ambiguous goal-language), and beware
|
||||
cv-0005 passing for the wrong reason (`10−3−4=3` also falls out of treating the goal as a start);
|
||||
the gate must consume the goal *as a goal*, and the real test is the sealed set, not cv-0005.
|
||||
Detailed first-build scope: **§10**.
|
||||
2. **R1 next (cv-0001) — Stage A (extraction).** Add currency-literal (`$N`) extraction to the
|
||||
derivation reader, then test whether the comparative-multiplicative production (multiplier ×
|
||||
base) fires and builds `4×16=64`. Extraction-first; different stage from R4, so it exercises a
|
||||
different pipeline leg.
|
||||
3. **Then Stage C shapes (cv-0002/0004/0006/0022)** — the genuine wall: building the correct
|
||||
grouping/op-order. Highest risk, do last, with the most instrumentation. Investigation task:
|
||||
`docs/handoff/stage-c-composition-investigation-2026-06-03.md`.
|
||||
|
||||
## 5. The promotion gate (generalize `product_bridge`, do not relax disagreement)
|
||||
|
||||
Every new production needs a co-designed structural gate, modeled on `product_bridge` but real:
|
||||
|
||||
- Use `extract_target` + `target_units` to prove the built chain answers *the asked question in
|
||||
the asked units* (op-class + target + unit agreement), replacing the two-token whitelist with a
|
||||
general op-class/target proof.
|
||||
- A chain may commit only if it is the unique reading that *structurally* certifies; otherwise
|
||||
refuse. **Disagreement is not the defense** — *cv-0016/0016* shows a lone wrong chain commits
|
||||
with no rival to disagree. The gate, not the pool, is the `wrong=0` firewall for committed
|
||||
compositions.
|
||||
|
||||
## 6. Acceptance gates (inherit ADR-0207 §6 verbatim)
|
||||
|
||||
train_sample stays `wrong=0` (6/44/0 or better); the no-reference `<N> times` hazard stays
|
||||
refused; no partial chain commits with an unbound quantity; **progress measured on the 22-case
|
||||
corpus AND the sealed 1,319** — a train_sample/corpus gain that does not move held-out does not
|
||||
count; each gain lands as composer/extractor enrichment behind a gate, audited to confirm it is
|
||||
**not** a new serving recognizer/injector branch (the §4 freeze).
|
||||
|
||||
## 7. Honest uncertainty
|
||||
|
||||
This is the research wall, not engineering. The payoff against the **sealed 1,319** is genuinely
|
||||
unknown: the composer building gold on a corpus case does not mean the same production fires on
|
||||
held-out paraphrases, and Stage C (correct grouping/op-order) is an open research problem, not a
|
||||
known build. I can scope the work and the gates; I cannot promise R1/R4 transfer until the
|
||||
productions exist and the sealed number actually moves. The gates (§6) are what keep that honest
|
||||
— they will tell us the truth either way.
|
||||
|
||||
## 8. Correction recorded
|
||||
|
||||
I earlier asserted "extraction is substantially done; only EX-3 deferred; extraction is not the
|
||||
open lever." Stage A (cv-0001: `$16` present in text, not extracted) shows that was overstated —
|
||||
the ADR-0179 lever was largely shipped *for its four cases*, but per-shape extraction gaps remain
|
||||
(currency literals at minimum). This does not unwind ADR-0207; the per-shape gaps are execution
|
||||
detail and live here, not in the ratification ADR.
|
||||
|
||||
## 9. Reproduction-methodology correction
|
||||
|
||||
The `target-agg` column was first computed by passing the **full problem text** to
|
||||
`extract_target`; serving passes only the **question clause**. For cv-0004 and cv-0006 the
|
||||
aggregation word is in the body, not the question, so full-text wrongly read `combined`/`total`
|
||||
where the serving-faithful call returns `None` (now corrected above). The other agg cells are
|
||||
unaffected (their agg word is in the question). This changes no stage (both are Stage C) and no
|
||||
claim. Methodology fix carried forward: reproduce serving behaviour with serving's inputs —
|
||||
`extract_target` gets the question clause, the composers get the full problem.
|
||||
|
||||
## 10. R4 first-build scope (from code-read of `accumulate.py`)
|
||||
|
||||
R4 is **new production, not a target tweak** — confirmed by reading the source (not a trace):
|
||||
`compose_accumulation` / `_build_accumulation` (GB-3b.1) computes single-referent gain/loss
|
||||
*running-total* `start ± changes` and consults **no goal/target/residual concept**. Hard bails:
|
||||
the anchor must establish exactly one quantity (`accumulate.py:70`); a new named actor refuses
|
||||
(`continues_anchor_referent`, `:76`/`:143`); no unambiguous licensed change cue refuses
|
||||
(`:87`/`:149`). So R4's `goal − accumulated` cannot be expressed regardless of target — both R4
|
||||
cases build **0 candidates** at `d9fc7f9e` (latent, nothing passes for any reason yet).
|
||||
|
||||
- **cv-0005 first (single-referent, nearest extension of GB-3b.1).** A new production recognizing
|
||||
(a) a **goal** quantity as a distinct anchor type (*not* a running total), (b) accumulated
|
||||
same-referent changes, (c) a **residual** question target, computing `goal − Σchanges`. Single
|
||||
referent (Michael), so it reuses GB-3b.1's referent/cue guards.
|
||||
- **cv-0021 second (harder).** Multi-referent give-away to a remainder —
|
||||
`(start − desired_remainder) − Σgiven`. Outside GB-3b.1's single-referent scope (refused at
|
||||
`continues_anchor_referent`); needs multi-referent accumulation **and** residual handling. Two
|
||||
layers beyond cv-0005; do not bundle.
|
||||
- **Gate (mandatory, co-designed).** The residual chain commits only if `extract_target`
|
||||
structurally proves the residual question + op-class + unit agreement, and completeness consumes
|
||||
the **goal** quantity. Never disagreement alone (the 0016 firewall).
|
||||
- **`wrong=0` hazard (latent, build-time guard).** `10 − 3 − 4 = 3` also falls out of
|
||||
*mis-anchoring* the goal as a running-total start — coincidental correctness. The production must
|
||||
consume the goal *as a goal*; the real test is the **sealed set**, not cv-0005's arithmetic.
|
||||
- **Build-time discipline.** Whoever builds instruments the exact `_build_accumulation` interaction
|
||||
and the new production's path through `self_verifies` (completeness must require the goal
|
||||
consumed) **in-tree** — the code-read scopes *what* to build; the runtime path confirms *that it
|
||||
fires* before the one-line wire.
|
||||
166
docs/analysis/wiring-promotion-gate-brief-2026-06-03.md
Normal file
166
docs/analysis/wiring-promotion-gate-brief-2026-06-03.md
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
<!-- analysis | wiring-promotion-gate-brief | 2026-06-03 | read-only, no code | ADR-0207 §5 step 1 execution brief -->
|
||||
|
||||
# WIRING + Promotion-Gate Brief — feeding the derivation composer into serving
|
||||
|
||||
**Status:** Analysis only. No serving, eval, or code edits. Every claim reproduced
|
||||
read-only against `origin/main` @ `2cb0922` (corpus + branch state per PR
|
||||
[core#536](https://github.com/AssetOverflow/core/pull/536)).
|
||||
|
||||
**Scope.** ADR-0207 §5 step 1 (WIRING) is the named next lever: connect the disjoint
|
||||
`generate/derivation/` composer to the serving candidate-graph so its output counts
|
||||
toward 6/44/0. This brief reads the actual integration surface — `product_bridge.py`,
|
||||
`pool.py`, `verify.py`, `target.py`, the `math_candidate_graph.py:530` boundary — and
|
||||
defines what the wiring *and its promotion gate* must be. It also reports an empirical
|
||||
finding that **partly corrects ADR-0207 §5's sequencing claim**; see §5.
|
||||
|
||||
---
|
||||
|
||||
## 1. Bottom line
|
||||
|
||||
"The wiring is trivial, the gate is the work" is correct — but incomplete, and the
|
||||
honest version is sharper:
|
||||
|
||||
1. **The general composition entry already exists** — it is `resolve_pooled(text)`, not a
|
||||
thing to build. (The brief's earlier "there is no `resolve_composition`" was wrong.)
|
||||
2. **The general self-verify gate already exists and is composition-agnostic**
|
||||
(`select_self_verified`: grounding ∧ cue ∧ unit ∧ completeness ∧ uniqueness).
|
||||
3. **`product_bridge` is already the promotion gate for one shape (pure products) and is
|
||||
already wired** at `math_candidate_graph.py:530`. The wiring lever, for what the
|
||||
composer can safely certify *today*, is **already done**.
|
||||
4. **The composer refuses every R1/R4/R5/R6 target in the validation corpus** (§3). So
|
||||
wiring `resolve_pooled` yields **zero lift** on the target shapes — there is nothing
|
||||
correct yet to promote.
|
||||
5. **Wiring `resolve_pooled` *wholesale* is a live `wrong=0` regression** — it commits
|
||||
`0016 → 510` (gold 2) (§3). The promotion gate is mandatory from the first wire.
|
||||
|
||||
**Therefore:** WIRING and COMPOSITION (§5 steps 1–2) are **not separable**, and WIRING is
|
||||
**not the lowest-risk first step** as ADR-0207 §5 states. The safe wire already exists
|
||||
(`product_bridge`); extending it to a new shape requires the composer to *build that
|
||||
shape's chain* (it currently refuses) **and** a promotion gate to *certify it*, in
|
||||
lockstep. The gate and the composition capability are the same problem (§6).
|
||||
|
||||
## 2. The verified integration surface
|
||||
|
||||
| Component | What it is | Serving-wired? |
|
||||
|---|---|---|
|
||||
| `math_candidate_graph.py:524–540` | The ADR-0195 boundary: `r = resolve_promotable_product(text); if r is not None: return CandidateGraphResult(answer=r.answer, branches_enumerated=1, branches_admissible=1)` | **yes** (the one tendril) |
|
||||
| `derivation.pool.resolve_pooled(text) → Resolution \| None` | General composition resolver: pools accumulation + multiplicative + chain candidates, classifies each `complete`/`exempt`/`None`, refuses on disagreement or exempt-only, guards `asks_prior_state` | no (sealed) |
|
||||
| `derivation.verify.select_self_verified(derivs, text, target_units=…)` | Composition-agnostic gate: grounding ∧ cue ∧ unit ∧ completeness, then uniqueness/disagreement; `target_units` drops chains answering the wrong dimension | no (sealed) |
|
||||
| `derivation.verify.classify_derivation` | ADR-0182 commit-eligibility: `complete` (resolves) / `exempt` (pool-only, forces disagreement) / `None` (fails a clause) | no |
|
||||
| `derivation.target.extract_target(question, known_units) → Target` | Question-target extraction (a general analogue of product_bridge's hardcoded target check) | no |
|
||||
| `product_bridge.resolve_promotable_product` | The promotion gate for **one shape** (pure products) — see §4 | **yes** |
|
||||
|
||||
**Wiring shape (mechanically trivial, confirmed).** A second block at the same `:530`
|
||||
boundary: `c = resolve_promotable_composition(text); if c is not None: return
|
||||
CandidateGraphResult(answer=c.answer, branches_enumerated=1, branches_admissible=1)`.
|
||||
The work is entirely inside `resolve_promotable_composition` — the gate.
|
||||
|
||||
## 3. The empirical crux (live `resolve_pooled`, all corpus positives)
|
||||
|
||||
Ran `resolve_pooled` on the validation corpus's composition positives + diagnostics:
|
||||
|
||||
| Cases | Composition | `resolve_pooled` result |
|
||||
|---|---|---|
|
||||
| 0029, 0038, 0008, 0027 | R1 | **refuse** (×4) |
|
||||
| 0037, 0035 | R4 | **refuse** (×2) |
|
||||
| 0030, 0015 | R5 | **refuse** (×2) |
|
||||
| 0005, 0046 | R6 | **refuse** (×2) |
|
||||
| 0033 | downstream | refuse |
|
||||
| 0024 | extraction (EX-3) | refuse |
|
||||
| 0040 | world-knowledge | refuse |
|
||||
| **0016** | hazard-both | **COMMIT 510 — WRONG (gold 2)** |
|
||||
|
||||
Two facts fall out, both load-bearing:
|
||||
|
||||
- **Zero lift from wiring.** The pool commits *nothing correct* on the 10 R1/R4/R5/R6
|
||||
positives — it refuses all of them. The composers cannot yet assemble a committable
|
||||
complete-unique chain for these shapes. Wiring the pool moves 6/44/0 by **+0 correct**.
|
||||
- **A live wrong commit.** `0016` is a unique complete self-verifying reading that is
|
||||
*wrong* (the chain `… → 510`, no rival to trigger the disagreement rule). This is
|
||||
exactly the "known wrong commits" `product_bridge`'s docstring warns of. Wiring the pool
|
||||
*ungated* makes serving **6/43/1** — a `wrong=0` violation. `product_bridge`'s
|
||||
`_has_hazard_surface` blocks `0016` today via its `{less, more, per, …}` cues.
|
||||
|
||||
## 4. What `product_bridge` actually proves (the gate to generalize)
|
||||
|
||||
`resolve_promotable_product` is a **four-layer** promotion gate over `resolve_pooled`:
|
||||
|
||||
1. `_has_hazard_surface` — a refuse-preferring **blocklist**: comma-thousands, `%`/percent,
|
||||
text blockers `{left, more, less, gave, spent, profit, remaining, …}`, question
|
||||
blockers `{per, after, before, remaining, profit, …}`. (This is what stops `0016`.)
|
||||
2. `resolve_pooled` — the pooled candidate + disagreement rule.
|
||||
3. `_is_complete_pure_product` — every step `multiply`, non-comparative, `classify == "complete"`.
|
||||
4. `_has_product_target` — a **near-whitelist of two target shapes**: `money … make/earn`,
|
||||
or `weight … total/move` with a mass unit.
|
||||
|
||||
**The tell:** layer 4 is almost hardcoded to 0003 (revenue) and 0021 (weight). Its safety
|
||||
comes from its narrowness — it admits essentially two target forms. **This does not
|
||||
generalize.** A composition gate for R4/R5/R6 cannot copy a two-entry target whitelist; it
|
||||
needs a *general* op-class/target proof (the natural tool is `extract_target` +
|
||||
`select_self_verified(target_units=…)`), and that proof is the hard part.
|
||||
|
||||
## 5. Correction to ADR-0207 §5 (flagged, not buried)
|
||||
|
||||
ADR-0207 §5 step 1 calls WIRING "the crux … the lowest-risk of the execution steps … it's
|
||||
wiring, with `product_bridge` already proving the pattern … the central task, not a
|
||||
preliminary." The verified surface refines this:
|
||||
|
||||
- **The safe wire is already done** (`product_bridge` at :530). What remains to wire is, by
|
||||
shape, gated on composition capability that **does not exist yet** (the pool refuses every
|
||||
R4/R5/R6 target).
|
||||
- **WIRING is not lower-risk than COMPOSITION** — they are the same step. The risk lives in
|
||||
the promotion gate, which is where "which composed readings are safe to promote" = "which
|
||||
readings the composer got right" = the §5 step-2 composition wall.
|
||||
- **The ungated pool is `wrong=0`-negative** (0016), so "just wire it in" is not a safe
|
||||
preliminary.
|
||||
|
||||
**This does not unwind the ratification.** ADR-0207's core — freeze the regex serving path,
|
||||
design-of-record, the `wrong=0` gates, measure on the sealed set — all stand. The
|
||||
correction is to §5's *sequencing optimism*. Recommended: amend §5 step 1 to "extend the
|
||||
`product_bridge` promotion-gate pattern to one new composition shape, in lockstep with the
|
||||
composer capability for it; never wire the pool ungated," or carry this brief as the §5
|
||||
execution refinement. Operator's call which.
|
||||
|
||||
## 6. The promotion-gate contract (what to build, no code)
|
||||
|
||||
`resolve_promotable_composition(text) → Resolution | None`, mirroring `product_bridge` but
|
||||
**per chosen shape**, must prove all of:
|
||||
|
||||
1. **Candidate source.** Obtain the shape's candidate (via `resolve_pooled`, or a
|
||||
shape-scoped composer call) — only if the composer actually *builds* it (today: none of
|
||||
R4/R5/R6, so this gate is empty until composition lands).
|
||||
2. **General self-verify** (reuse, don't reinvent): `select_self_verified` —
|
||||
grounding ∧ cue ∧ unit ∧ completeness ∧ uniqueness.
|
||||
3. **Target proof** (generalize layer 4): the question's target op-class matches the
|
||||
chain's, via `extract_target` + `target_units` — not a per-shape whitelist.
|
||||
4. **Hazard firewall** (generalize layer 1): the residual `wrong=0` hazards that survive
|
||||
self-verify (the `0016`-class: a unique complete reading that is still wrong) must be
|
||||
refused. The `0016 → 510` commit is the **mandatory first firewall test** — the gate is
|
||||
not done until `resolve_promotable_composition` refuses it.
|
||||
|
||||
**`wrong=0` boundary contract:** every corpus case + every sealed case either **refuses or
|
||||
returns exactly gold**. Proven on the 22-case `composition_validation/v1` corpus (the
|
||||
instrument) **and** the sealed 1,319 (the real bar, ADR-0207 §6). A train_sample/corpus
|
||||
gain that does not move the sealed set is overfitting and does not count.
|
||||
|
||||
## 7. Recommended first slice + open questions
|
||||
|
||||
- **First slice:** pick the *one* shape whose composer is closest to building a correct
|
||||
committable chain (candidate: R4 accumulation/residual — `accumulation_candidates` already
|
||||
exists and `compose_accumulation` is wired into the pool). Confirm whether the composer
|
||||
can build `0037`/`0035` at all (today: pool refuses both — so step 0 is *composer* work,
|
||||
not gate work). Only once a correct chain exists does the gate become the lever.
|
||||
- **Open Q1:** *why* does the pool refuse the R4/R5/R6 positives — is it a missing composer
|
||||
production (no chain built) or a gate over-refusal (chain built but fails completeness/
|
||||
uniqueness)? This split decides whether step 1 is composer or gate work, and it needs a
|
||||
per-case `pooled_candidates` + `classify_derivation` trace read (the next brief).
|
||||
- **Open Q2:** can `extract_target` express the R4/R5/R6 target op-classes (residual,
|
||||
duration-sum, percent-of) precisely enough to replace product_bridge's whitelist without
|
||||
admitting a `0016`-class wrong? This is the generalization risk.
|
||||
- **Open Q3:** does any composer *currently* produce a unique-complete wrong on a sealed
|
||||
case (not just 0016)? The hazard firewall must be characterized on the sealed set, not
|
||||
only the corpus, before any pool output is wired.
|
||||
|
||||
**Invariants observed by this brief:** read-only; `train_sample` 6/44/0 reproduced live;
|
||||
no serving code touched; the no-ref `<N> times` hazard and 0016 remain refused on the
|
||||
serving path (product_bridge blocks 0016).
|
||||
69
docs/handoff/stage-c-composition-investigation-2026-06-03.md
Normal file
69
docs/handoff/stage-c-composition-investigation-2026-06-03.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<!-- handoff | stage-c-composition-investigation | 2026-06-03 | execution-lane dispatch task | read-only investigation, no code | input: composition-wall-execution-plan-2026-06-03.md -->
|
||||
|
||||
# Stage-C Composition Investigation — dispatch task (execution lane)
|
||||
|
||||
**Type:** Read-only, in-tree investigation. **No code changes.** Output is a per-case fix-spec.
|
||||
**Dispatch:** execution lane (this is the fine-grained runtime tracing the spec-and-review lane
|
||||
deliberately does *not* do from a sandbox — it must run the real code paths in-tree).
|
||||
**Input:** [composition-wall-execution-plan-2026-06-03.md](../analysis/composition-wall-execution-plan-2026-06-03.md).
|
||||
**Base:** current `origin/main` (`git fetch && git reset --hard origin/main` first — the tree
|
||||
moves constantly; verify the SHA you run against and record it).
|
||||
|
||||
## Why this is an execution-lane task, not a sandbox one
|
||||
|
||||
The execution plan's stage taxonomy is a verified **symptom-map** (what's blocked, by candidate
|
||||
count). It is **not** a fix-spec. Pinning *why* a Stage-C production builds the wrong chain needs
|
||||
instrumenting the real production internals — the kind of fine-grained trace that has been
|
||||
unreliable from the spec lane's sandbox. Run the actual functions, in-tree, faithfully.
|
||||
|
||||
## The targets (Stage C — production-wrong)
|
||||
|
||||
The composer builds chains but only **wrong** ones (none equals gold); the disagreement rule then
|
||||
correctly refuses. Five cases, from the plan's §2 table:
|
||||
|
||||
| case | comp | gold | acc/mul/chn | what it builds instead (plan) |
|
||||
|---|---|---|---|---|
|
||||
| cv-0002 | R1 | 400 | 0/0/4 | wrong chains only |
|
||||
| cv-0004 | R1 | 3840 | 0/1/4 | wrong chains only |
|
||||
| cv-0006 | R5 | 14 | 3/0/4 | wrong (e.g. accumulation + chain rivals) |
|
||||
| cv-0022 | R5 | 38 | 0/1/4 | (plan: builds 80/160/18/36, none = 38) |
|
||||
| cv-0018 | compare_mult | 28 | 0/0/2 | wrong chains only |
|
||||
|
||||
## What to produce, per case
|
||||
|
||||
For each of the five, using the **serving-faithful inputs** (`extract_target` gets the *question
|
||||
clause*; the composers get the *full problem* — the §9 methodology fix):
|
||||
|
||||
1. **Enumerate every candidate** each composer builds (`accumulation_candidates`,
|
||||
`multiplicative_candidates`, `candidate_chains`): the exact step list (op, operand, unit) and
|
||||
answer of each, not just the count.
|
||||
2. **Locate the gold chain.** Is the correct grouping/op-order *expressible* by the current
|
||||
composer primitives at all? If a sequence of existing ops yields gold, the gap is **search/
|
||||
ranking** (the production can build it but doesn't, or builds it and a wrong rival outvotes it).
|
||||
If no sequence of existing ops yields gold, the gap is a **missing primitive/production**.
|
||||
3. **Pin the divergence.** Where does the built (wrong) chain diverge from the gold reading — wrong
|
||||
clause segmentation, wrong referent binding, wrong op for a relational cue, wrong grouping order?
|
||||
Name the exact function and the exact decision (cite `file:line`).
|
||||
4. **Minimal production fix.** State the smallest change that would make the gold chain *buildable
|
||||
and uniquely certifiable*, and the **co-designed structural gate** that admits it without
|
||||
admitting the wrong rivals (per plan §5 — `extract_target` + `target_units`, never disagreement
|
||||
alone).
|
||||
|
||||
## Hard constraints (non-negotiable — from ADR-0207 §6 / plan §6)
|
||||
|
||||
- **Read-only.** No edits to `generate/` in this task. The output is a spec; building is a later,
|
||||
separately-gated task.
|
||||
- **`wrong=0` is the governor.** Any proposed fix must be argued to preserve: train_sample
|
||||
`wrong=0` (6/44/0+), the no-reference `<N> times` refusal, and the 0016 firewall (a lone wrong
|
||||
chain with no rival must still refuse — the gate, not disagreement, is the defense).
|
||||
- **Sealed set is the real bar.** Note explicitly that building gold on a corpus case proves
|
||||
nothing about transfer; the fix-spec must say what would be measured on the sealed 1,319.
|
||||
- **Serving-faithful reproduction.** `extract_target(question_clause)`; composers get full text.
|
||||
Record the `origin/main` SHA you ran against.
|
||||
|
||||
## Output
|
||||
|
||||
A markdown report, one section per case, with the four items above, ending in a ranked list:
|
||||
which Stage-C case has the **smallest buildable gap** (search/ranking) vs which needs a **new
|
||||
primitive** — so the build lane starts with the cheapest real lift. This report becomes the
|
||||
fix-spec; it does not authorize a build.
|
||||
Loading…
Reference in a new issue