core/docs/handoffs/DCS-S1-FINDING.md
Shay 310aed9ff0
chore: Refactor CLI and Governance Anchors (#926)
* docs: consolidate governance anchors and clean up test registries

* refactor(cli): decompose cli into dedicated modules

* test: fix broken test baselines and formatting

* docs: add domain boundary READMEs for governance anchors

* test: update baseline for determination lane

* test: fix capability_pass expectation

* test: fix CORE_SHOWCASE_SKIP_BUDGET enforcement

* chore: cleanup CLI extraction and unreachable code
2026-07-03 12:34:56 -07:00

141 lines
6.1 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](../adr/ADR-0170-injector-contract-widening.md).
## What this PR ships
- This finding doc (`docs/handoff/DCS-S1-FINDING.md`)
- `docs/adr/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](../adr/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