docs(04): plan revision 2 — 04-03 contract wording fixes (inline-card sequencing, async alternatesFor)

This commit is contained in:
araharah-executor
2026-10-24 00:14:02 +08:00
parent 4a83ec6871
commit c9458add47
2 changed files with 11 additions and 9 deletions
@@ -110,7 +110,7 @@ From `search/src/index.ts` (plan 04-01's output — the surface every script in
```typescript
export type { GazetteerEntry, SearchQuery, SearchHit, SearchSource } from "./types.ts";
export { normalize } from "./normalize.ts";
export { TIER } from "./score.ts";
export { TIER, FUZZY_ADMIT_SIMILARITY } from "./score.ts";
export { damerau } from "./distance.ts";
export { getEntries, getEntryById, getIndex } from "./gazetteer.ts";
export { createGazetteerSource, search } from "./source.ts";
@@ -316,7 +316,7 @@ Review and hand-merge into the curated dataset file — this script NEVER writes
corpus before it ships.
```
**Acceptance:** `npm run search:corpus` exits 0 with top-3 >= 90% and zero negative leaks; `npm run search:test` green; `docs/SEARCH-QUALITY.md` records the measured numbers and the lever.
**Acceptance:** `npm run search:corpus` exits 0 with top-3 >= 90% and zero negative leaks; `npm run search:test` green; `docs/SEARCH-QUALITY.md` records the measured numbers and the lever. If lever 1 cannot reach threshold, the runner exits 1 and the acceptance is met by the recorded lever-2/3 outcome in `docs/SEARCH-QUALITY.md` per the escape-hatch clause (D-14's honest close) — never by fabricating a pass.
</context>
<tasks>
@@ -363,14 +363,14 @@ and to the root `package.json`:
The suite imports the shared module and the corpus directly (in-process), so `npm run search:test` fails on a corpus regression.
**Acceptance:** `npm run search:corpus` exits 0; `npm run search:test` green with `PASS CO1`…`PASS CO8`.
**Acceptance:** `npm run search:corpus` exits 0 (threshold met) OR exits 1 with the lever-2/3 outcome recorded in `docs/SEARCH-QUALITY.md` (D-14's honest close); `npm run search:test` green with `PASS CO1`…`PASS CO8`.
</action>
<verify>
<automated>cd /home/C-Fu/araharah && npm run search:corpus 2>&1 | tail -30 ; npm run search:test 2>&1 | grep -E 'PASS CO|runner' ; node -e "const c=require('./search/test/fixtures/golden-corpus.json'); if(c.cases.length<60) throw new Error('corpus too small: '+c.cases.length); console.log('corpus OK '+c.cases.length+' cases')"</automated>
</verify>
<acceptance_criteria>
- `npm run search:corpus` exits 0
- `npm run search:corpus` output contains a `top-3` line with a percentage >= 90.0% and a `negatives refused` line showing all negatives refused
- `npm run search:corpus` exits 0 (threshold met) OR exits 1 with `docs/SEARCH-QUALITY.md` recording the lever-2/3 outcome per the escape-hatch clause
- `npm run search:corpus` output contains a `top-3` line with a percentage >= 90.0% and a `negatives refused` line showing all negatives refused (threshold-met branch; the threshold-unmet branch prints `D-14 threshold not met` + the escalation ladder instead)
- `npm run search:corpus` output contains one `PASS`/`FAIL` threshold line naming the 90.0% gate
- `npm run search:test` exits 0 and the output contains `PASS CO1` through `PASS CO8`
- `node -e "const c=require('./search/test/fixtures/golden-corpus.json'); const g=require('./search/src/data/kl-gazetteer.json'); const ids=new Set(g.entries.map(e=>e.id)); const kinds={}; let bad=0; for(const x of c.cases){ kinds[x.kind]=(kinds[x.kind]||0)+1; if(x.expectId!==null && !ids.has(x.expectId)) bad++; if(x.expectId!==null && !/^[A-Za-z0-9_-]{1,64}$/.test(x.expectId)) bad++; } if(c.cases.length<60) throw new Error('size'); if(bad) throw new Error('dangling ids'); console.log(JSON.stringify(kinds))"` exits 0 and prints all six `kind` keys with landmark>=12, abbreviation>=15, glued>=6, typo>=12, alias>=12, negative>=6