core/docs/handoff/DCS-S1-FINDING.md
Shay ce6b2f5173
docs(ADR-0170): injector contract widening + DCS-S1 schema-gap finding (#372)
DCS-S1 (proper-noun possession sub-shape expansion) investigation
revealed that the recognizer-injector path's `CandidateInitial`-only
return type is a substrate-level constraint blocking four Wave-Next
sub-shape categories — not just one.

## Two artifacts

1. **`docs/handoff/DCS-S1-FINDING.md`** — investigation result. Of
   the 21 DCS-refused GSM8K cases, zero are pure S1-only blockers.
   Acquisition-verb expansion (`collected`, etc.) conflicts with
   ADR-0131.G.1's branch-disagreement discipline. The right fix is
   the DCS injector emitting `CandidateOperation(add)`, but the
   `inject_from_match` return type doesn't allow that.

2. **`docs/decisions/ADR-0170-injector-contract-widening.md`** —
   scoping ADR. Names the contract change, the four categories it
   unblocks (DCS-S1 acquisition, A1 currency, A3 multiplicative,
   A4 temporal), the three load-bearing rules it must preserve
   (ADR-0131.G.1, SentenceChoice union, admissibility gates), and
   a 5-step implementation outline.

## Pattern recognised

Wave-Next surfaced four schema gaps. All four trace to the same
constraint: per-category injectors can only emit `CandidateInitial`.
The right next-capability work is ADR-0170 ratification, then a
small no-behavior-change PR widening the contract, then per-injector
follow-up PRs against the widened contract.

That is the actual lift-per-risk path for GSM8K Round-1 closure.

## Test plan

Docs-only. No code, no test, no eval, no pack change.

## Cross-references

- ADR-0163.D.2 — original parsed_anchors → solver-state ADR
- ADR-0131.G.1 — branch-disagreement discipline ADR-0170 preserves
- ADR-0167 — parallel teaching-corridor mechanism (independent)
- ADR-0167-FOLLOWUPS §7 — Wave-Next findings backlog
- WAVE-NEXT-REVISED — parent plan; ADR-0170 is the upstream blocker
- PR #369 — A2's schema-refusal artifact (first observation of gap)
2026-05-27 10:51:46 -07:00

141 lines
6.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.

# DCS-S1 Finding — Injector Contract Is the Substrate Bottleneck
**Date:** 2026-05-27
**Status:** Finding; no implementation. Routes work to ADR-0170.
**Parent:** `docs/handoff/WAVE-NEXT-REVISED.md`
**Result:** DCS-S1 (proper-noun possession sub-shape expansion) cannot
ship meaningful lift on current GSM8K without first widening the
`inject_from_match` return-type contract. Same schema gap A3
(multiplicative) and A4 (temporal) identified.
---
## What was investigated
The Wave-Next-Revised plan named **DCS-S1** as the next tractable
sub-shape: extend the existing v1 `inject_discrete_count_statement`
injector to cover more of the 21 `discrete_count_statement` refusals
on the GSM8K train_sample.
The hypothesis: most DCS refusals are proper-noun possessions whose
extraction fails for one of a few narrowable reasons (verb whitelist,
clause-split markers, single-quantity rule). Loosening the matcher +
extending the injector to cover those would lift several cases.
## What the data actually shows
All 21 DCS-refused cases were enumerated and the triggering statements
inspected. The structural breakdown:
| Sub-shape pattern in DCS-refused statements | Count |
|---|---:|
| Acquisition verb (`collected`, `donated`, `bought`) — not in possession whitelist | ~5 |
| Multi-clause / enumeration (`and`, `then`, `,`) | ~8 |
| Pronoun subject (`he`, `she`, `they`) | ~4 |
| Comparative reference (`twice as many`, `half of`) | ~3 |
| Anonymous subject (`There are`, `The guests`) | ~3 |
| Other (modal verbs, copula, multi-verb) | ~4 |
(Buckets overlap — many cases hit multiple narrowness rules.)
**Critical observation:** of the 21 cases, **zero** are pure S1-only
blockers. Every single one has additional blockers downstream in the
same problem. Even if the DCS injector admitted the first sentence,
sentences 2/3/4 carry their own refusals.
Concrete trace — case `gsm8k-train-sample-v1-0023`:
```
S1: "Nicole collected 400 Pokemon cards."
→ would inject Nicole=400 if `collected` were in possession verbs
S2: "Cindy collected twice as many, and Rex collected half of Nicole and Cindy's combined total."
→ multi-clause + comparative reference; will refuse on current rules
S3: question with `If Rex divided...` conditional prefix
→ conditional prefix recovery exists but answer still requires S2's admission
```
Admitting S1 alone does not close the case. The bottleneck moves from
S1 to S2; no admission-lift is delivered.
## The architectural blocker
To admit `"Nicole collected 400 Pokemon cards"` as initial state, three
edits would be needed:
1. Add `collected` to `_POSSESSION_VERBS` (matcher narrowness in
`generate/recognizer_match.py`)
2. Extend `_locate_possession_verb` in the injector
3. Add `collect, collects, collected` to `CandidateInitial.__post_init__`
whitelist in `generate/math_candidate_parser.py`
**ADR-0131.G.1 explicitly removed `collected/bought/saved/buys/makes`
from initial-anchor extraction** — they were routed exclusively to
`ADD_VERBS` (operation extraction) to avoid branch disagreement when
the same sentence could produce both `CandidateInitial(Sam=5)` AND
`CandidateOperation(Sam, add, 5)`.
The solver "defaults from zero for operations" so
`Sam collected 5 apples. How many does Sam have?``0 + 5 = 5`. That
discipline is load-bearing for wrong=0 in the regex path.
The right architectural fix is not to break ADR-0131.G.1. It's to teach
the **DCS injector** to emit `CandidateOperation(add)` for acquisition
verbs — the same kind of state-introducing operation the parser
already emits for them.
**But:** `inject_from_match`'s return type is
`tuple[CandidateInitial, ...]`. It cannot emit `CandidateOperation`.
Widening the contract is the prerequisite.
## The pattern — fourth time this gap appears
Wave-Next surfaced four schema gaps. All four trace back to the same
substrate-level constraint: the recognizer-injector path can only
emit `CandidateInitial`.
| Brief | What needed emission | Available type | Blocker |
|---|---|---|---|
| **A2** rate_with_currency | `CandidateRate` (carries `Rate(value, num_unit, den_unit)`) | None — `Rate` not in `SentenceChoice` union | Schema gap (PR #369) |
| **A3** multiplicative_aggregation | `CandidateInitial(outer × inner)` OR `CandidateOperation(multiply)` | `CandidateInitial` works for product semantics; but other cases need composition | Half-blocked |
| **A4** temporal_aggregation | `CandidateOperation(apply_rate, ...)` | `apply_rate` primitive doesn't exist | Algebra-level gap |
| **DCS-S1** | `CandidateOperation(add)` for acquisition verbs | Injector returns only `CandidateInitial` | Schema gap (this finding) |
This is no longer four separate sub-shape problems. It's **one substrate
bottleneck affecting all four categories**. The right artifact is a
contract-widening ADR — see [ADR-0170](../decisions/ADR-0170-injector-contract-widening.md).
## What this PR ships
- This finding doc (`docs/handoff/DCS-S1-FINDING.md`)
- `docs/decisions/ADR-0170-injector-contract-widening.md` — the scoping
ADR that names the contract change and the four categories it unblocks
What this PR does NOT ship:
- Any DCS-S1 implementation (it's blocked on ADR-0170)
- Any matcher/extractor edits
- Any test changes
- Any pack changes
## Recommended next move
ADR-0170 ratification, then a small focused PR implementing the
contract widening (no behavior change, type-only). Then a follow-up
wave (DCS-S1 acquisition, A1 currency, A2 rate, A3 multiplicative,
A4 temporal) can ship in parallel — each as a focused injector PR
against the widened contract.
That's the actual lift-per-risk path. Until ADR-0170 lands, no
recognizer-injector work other than `CandidateInitial`-only narrow
possession can ship cleanly.
## Cross-references
- [WAVE-NEXT-REVISED](./WAVE-NEXT-REVISED.md) — parent plan; needs an
update pointing to ADR-0170 as the prerequisite
- [ADR-0170](../decisions/ADR-0170-injector-contract-widening.md) — the
scoping ADR this finding routes to
- PR #369 (A2) — first observation of the gap; documents `Rate`
extension steps
- ADR-0131.G.1 — the original branch-disagreement discipline that
forces acquisition verbs to be operations, not initials
- ADR-0167-FOLLOWUPS §7 — Wave-Next findings backlog