chore(planning): phase 5/6/7 planning artifacts + milestone-completion state sync
This commit is contained in:
@@ -0,0 +1,270 @@
|
||||
---
|
||||
phase: 05-routing-route-display
|
||||
plan: 05
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on: ["02", "04"]
|
||||
files_modified:
|
||||
- web/test/build-artifact.test.mjs
|
||||
- web/index.html
|
||||
- web/src/mock/route-profiles.ts
|
||||
- web/scripts/capture.mjs
|
||||
- bot/test/route-privacy.test.mjs
|
||||
autonomous: true
|
||||
requirements: [RTNG-01, RTNG-02]
|
||||
must_haves:
|
||||
decisions_implemented:
|
||||
- "D-11: the BA build-artifact bundle-budget gate — fixture geometry + route-model code ≤ ~200KB gzipped measured over production-shaped artifacts, printed with every run"
|
||||
- "D-05: the Worker gains no /api/route — a structural gate proves bot/src never registers a route proxy and never imports the routing workspace"
|
||||
- "D-15: capture signals are state tokens only — ARAHARAH-ROUTE state=drawn|pending, never coordinates; the capture legs pair the token with DOM text checks"
|
||||
- "D-12: the capture harness's ?fixpair knob delivers a PINNED PUBLIC origin from the committed fixture corpus — the harness introduces no user coordinate"
|
||||
- "DSGN-02: the route-gallery fidelity artifact extends the Phase 1/3 gallery pattern beside navigate_map_overview/screen.png"
|
||||
truths:
|
||||
- "BA9 measures the gzipped size of the fixture dataset + routing workspace sources + web route modules against a 204800-byte budget and PRINTS the measurement — the budget is visible, never discovered"
|
||||
- "BA10 proves every committed pairId rides the shipped production bundle (the BA7 id-parity analog — a stale bundle without the fixtures is unshippable)"
|
||||
- "The harness-only route-profiles module is provably excluded from web/dist (a named sentinel token the BA5-family scan greps for)"
|
||||
- "Five new capture legs exist and pass: shell-route-fixture, shell-route-alt (post-chip-tap), shell-route-pending, shell-route-dark, route-gallery-compare — all non-blank, all state-token-raced"
|
||||
- "bot/test/route-privacy.test.mjs proves the Worker stayed out of the routing path: zero /api/route tokens, zero @araharah/routing imports in bot/src"
|
||||
artifacts:
|
||||
- path: "web/test/build-artifact.test.mjs"
|
||||
provides: "BA9 (bundle budget, printed) + BA10 (pairId parity) — the production-build gates"
|
||||
contains: "BA9"
|
||||
- path: "web/src/mock/route-profiles.ts"
|
||||
provides: "the harness-only ?fixpair origin lookup (imports getPairOrigin from @araharah/routing; sentinel-guarded)"
|
||||
contains: "ROUTE_PROFILE_HARNESS_ONLY"
|
||||
- path: "web/index.html"
|
||||
provides: "the ?fixpair=<pair-id> knob — the mock's telegramFix becomes the pair's pinned public origin"
|
||||
contains: "fixpair"
|
||||
- path: "web/scripts/capture.mjs"
|
||||
provides: "the five route capture legs + the ARAHARAH-ROUTE signal race + DOM assertions"
|
||||
contains: "ARAHARAH-ROUTE"
|
||||
- path: "bot/test/route-privacy.test.mjs"
|
||||
provides: "RP1/RP2 — the Worker-side no-route-proxy gates (D-05)"
|
||||
contains: "RP1"
|
||||
key_links:
|
||||
- from: "web/index.html"
|
||||
to: "web/src/mock/route-profiles.ts"
|
||||
via: "the harness block resolves ?fixpair to a pinned origin and threads it as the mock's telegramFix"
|
||||
pattern: "getRouteProfileOrigin"
|
||||
- from: "web/src/mock/route-profiles.ts"
|
||||
to: "@araharah/routing/src/index.ts"
|
||||
via: "getPairOrigin(pairId) — the committed public fixture corpus is the ONLY origin source the harness can deliver"
|
||||
pattern: "getPairOrigin"
|
||||
- from: "web/scripts/capture.mjs"
|
||||
to: "web/src/route/route.ts"
|
||||
via: "the capture race waits on the ARAHARAH-ROUTE state token the controller emits"
|
||||
pattern: "ARAHARAH-ROUTE"
|
||||
- from: "bot/test/route-privacy.test.mjs"
|
||||
to: "bot/src"
|
||||
via: "the D-05 structural scan (no route proxy, no routing import) — the privacy-gate family pattern"
|
||||
pattern: "/api/route"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Close the phase with its verification gates: the BA9 bundle-budget + BA10 fixture-parity build-artifact gates over the production build, the five route capture legs driven by the new `?fixpair=` harness knob, and the Worker-side no-route-proxy privacy gates — the mechanical proof that the amended SC3 holds ("bundle-size and type gates holding").
|
||||
|
||||
Purpose: D-11's bundle budget is a build-artifact gate, not a hope; the UI-SPEC's five capture artifacts are the phase's visual evidence (and the DSGN-02 fidelity obligation); and D-05/D-12's privacy posture stays verifiable by reading gate output. The route legs run against the production-shaped harness bundle (the Phase 3 capture discipline — never against dev-mode).
|
||||
|
||||
Output: BA9/BA10 in `build-artifact.test.mjs`, `web/src/mock/route-profiles.ts` + the `?fixpair` knob in `web/index.html`, five new capture legs in `capture.mjs` (expected list 8 → 13), and `bot/test/route-privacy.test.mjs` (RP1/RP2).
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.config/opencode/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@AGENTS.md
|
||||
@.planning/phases/05-routing-route-display/05-CONTEXT.md
|
||||
@.planning/phases/05-routing-route-display/05-UI-SPEC.md
|
||||
@.planning/phases/05-routing-route-display/05-02-PLAN.md
|
||||
@.planning/phases/05-routing-route-display/05-04-PLAN.md
|
||||
@web/test/build-artifact.test.mjs
|
||||
@web/scripts/capture.mjs
|
||||
@web/index.html
|
||||
@web/src/mock/telegram-mock.ts
|
||||
@bot/test/privacy.test.mjs
|
||||
@routing/src/index.ts
|
||||
@routing/src/data/route-fixtures.json
|
||||
|
||||
<capture_contract>
|
||||
The five new legs (UI-SPEC §Capture Profiles — ALL existing legs unchanged; the expected list grows 8 → 13). The demo pair is selected AT RUNTIME from the committed corpus — never hardcoded: `const routePairs = JSON.parse(readFileSync(resolve(REPOROOT, "routing/src/data/route-fixtures.json", "utf8"))).pairs;` then `const demoPair = routePairs.find((p) => p.routes.length >= 2) ?? routePairs[0];` (RT10 guarantees ≥ 4 multi-route pairs, so the find succeeds; the fallback keeps the legs runnable in the degenerate case). Queries are then built from `demoPair.destinationId` / `demoPair.pairId`:
|
||||
|
||||
| Artifact | Query | Race + assertions |
|
||||
|----------|-------|-------------------|
|
||||
| `shell-route-fixture.png` | `mock=android&dest=${demoPair.destinationId}&fixpair=${demoPair.pairId}` (light) | race `ARAHARAH-ROUTE`; assert the line reads `state=drawn`; DOM: `waitForFunction` body text contains the destination's NAME (from the gazetteer entry read at capture time) AND `MIN` (the card's telemetry); 2000ms tile settle; shot |
|
||||
| `shell-route-alt.png` | same profile | after the drawn race: if `.route-chip` exists (multi-route pair — the RT10-guaranteed case), `page.click(".route-chip")`, wait 800ms (instant swap + 400ms refit + margin), assert body text matches `/ROUTE \d\/\d/`; ELSE (degenerate single-route fallback) assert the no-chip law: the page contains NO `.route-chip` element; shot |
|
||||
| `shell-route-pending.png` | `mock=android&dest=national-museum&fixpair=${demoPair.pairId}` | race `ARAHARAH-ROUTE`; assert `state=pending`; DOM: body contains `not wired up yet` (the pending headline, en); shot |
|
||||
| `shell-route-dark.png` | route-fixture profile + `scheme=dark` | same race + the PX-02 honesty check (`data-tg-theme` === "dark" before shooting); shot |
|
||||
| `route-gallery-compare.png` | — | the DSGN-02 composer (gallery-compare precedent): `shell-route-fixture.png` beside `stitch_minimalist_route_navigator/navigate_map_overview/screen.png`, columns labeled with source paths |
|
||||
|
||||
Harness mechanics:
|
||||
- `SIGNALS` gains `'ARAHARAH-ROUTE'` (the seen-map + diagnostics strings too).
|
||||
- New `waitMode: 'route'` — race the ARAHARAH-ROUTE console event (30s timeout, the SIGNAL_TIMEOUT_MS shape), then parse the captured line for `state=` and let the leg assert its expected state + DOM.
|
||||
- `web/index.html` harness block: `const fixpair = q.get("fixpair");` — when present, import the profile module and resolve the pinned origin; pass `telegramFix: { lat: <origin.lat>, lng: <origin.lng>, accuracyM: 12, headingDeg: null }` (fall back to the current default fix when the pairId is unknown — an unknown pairId must not wedge the boot).
|
||||
- `web/src/mock/route-profiles.ts` (HARNESS-ONLY, imported ONLY from the index.html harness block): imports `getPairOrigin` from `@araharah/routing/src/index.ts`; exports `getRouteProfileOrigin(pairId: string): { lat: number; lng: number } | null` and the sentinel `export const ROUTE_PROFILE_HARNESS_ONLY = true;`. Header comment: D-12 (the pinned origin is committed public fixture data — the harness delivers no user coordinate) + the BA-exclusion note.
|
||||
</capture_contract>
|
||||
|
||||
<ba_gate_contract>
|
||||
**BA9 — the bundle budget (D-11).** Appended to `build-artifact.test.mjs`:
|
||||
|
||||
- Inputs (all committed files, production-shaped by construction): `routing/src/data/route-fixtures.json`, every `routing/src/**/*.ts`, every `web/src/route/**/*.ts`.
|
||||
- Measurement: sum of `gzipSync(readFileSync(f)).length` over the inputs; assert `<= 204800` (200 KiB); the failure message and a `console.log` line BOTH print the measured bytes + budget (`BA9 route bundle budget: {n} / 204800 gz bytes`) — visible every run (D-11: "enforced as a BA build-artifact gate").
|
||||
- Comment in the gate: the measurement bounds the phase's ADDED payload (fixtures dominate; the minified route code's gz size is approximated by its source gz size — the approximation is conservative in the gate's favor at these sizes and the 200 KiB ceiling carries an order-of-magnitude headroom per 05-RESEARCH Finding 1).
|
||||
- The gate skips with a build-first message when `web/dist` is absent (the BA-suite convention) — run after `npm run build -w theme && npm run build -w web`.
|
||||
|
||||
**BA10 — fixture parity (the BA7 analog).** Every `pairId` from the committed `route-fixtures.json` must appear in the concatenated `web/dist/assets/*.js` text — a stale bundle that lost the fixtures could never serve a route (assert `pairs.length >= 10` first, the "scanned a real dataset" guard pattern).
|
||||
|
||||
**BA5-family extension.** Add `"ROUTE_PROFILE_HARNESS_ONLY"` to BA5's token list — the harness-only route-profiles module must be dead-code-eliminated from production (its import lives inside `if (__ARAHARAH_HARNESS__)` in index.html; the define-replaces-with-false mechanism strips the branch exactly like installMockWebApp).
|
||||
</ba_gate_contract>
|
||||
|
||||
<environment_facts>
|
||||
Verified in this session — do not re-derive:
|
||||
|
||||
- `capture.mjs` structure: `SIGNALS` array at line 166, `captureLeg({name, query, colorScheme, grantGeo, waitMode})`, the waitMode dispatch chain (`live`/`dest`/`denied`/`settle400`/`immediate`), and the final `expected` list of 8 artifacts with the missing/undersized/failures gate — the extension points are exactly these four.
|
||||
- `web/index.html`'s harness block already reads `?dest=` and threads it as `startParam`; `telegramFix` is hard-coded `{lat:3.1513,lng:101.6958}` today — the fixpair knob replaces its VALUE (never the shape).
|
||||
- `build-artifact.test.mjs` reads `search/src/data/kl-gazetteer.json` via `import … with { type: "json" }` (line 47) — the fixtures JSON imports identically; BA7's per-id loop is the BA10 template.
|
||||
- The mock's `telegramFix` delivery maps `{lat,lng,accuracyM,headingDeg}` → the Telegram wire shape (`telegram-mock.ts` lines 190–199) — the profile module returns the authoring shape.
|
||||
- Plan 05-02 pinned `national-museum` as the pending-probe destination (RT4: never a corpus destination) — the pending leg's carrier is valid by construction.
|
||||
- Plan 05-04's controller emits exactly two console tokens: `ARAHARAH-ROUTE state=drawn` / `ARAHARAH-ROUTE state=pending` (RS8-pinned).
|
||||
- `bot/test/privacy.test.mjs` is the bot-side grep-gate style reference (comment-stripping + banned-token scans).
|
||||
</environment_facts>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Harness query params → mock fix | `?fixpair=` selects which PUBLIC pinned origin the mock delivers. It must never accept arbitrary coordinates (a `?lat=&lng=` knob would reintroduce the coordinate-in-URL surface the fixture world exists to avoid). |
|
||||
| Committed artifacts → the measured budget | The budget gate reads repo files; a silent fixture bloat (someone re-fetching with `overview=full&steps=true`) must fail the build gate, not surface as a slow webview. |
|
||||
| Production build → users | Harness-only modules leaking into `web/dist` would ship mock wiring — the BA5-family sentinel scan is the tripwire. |
|
||||
| Worker surface | A route proxy appearing in bot/src would violate D-05's "the Worker gains no /api/route" — the structural gate keeps the pivot honest. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-05-12 | Denial of Service (availability budget) | Fixture/code growth pushing the webview bundle past the D-11 budget | mitigate | BA9 fails the suite above 204800 gz bytes and prints the measurement every run; RT9 (plan 05-02) is the fixture-level early-warning tier of the same law. |
|
||||
| T-05-14 | Information Disclosure | A coordinate-parameter harness knob or a coordinate-bearing capture signal reintroducing user-coordinate surfaces | mitigate | The knob accepts ONLY a pairId (resolved from the committed public corpus via `getPairOrigin` — arbitrary lat/lng params are never read); the capture legs assert the `state=` token form (no coordinates — RS8 pins the source; the legs pin the runtime); the pending leg proves an out-of-corpus pair renders the honest card, never a fabricated geometry. |
|
||||
| T-05-16 | Tampering (D-05 regression) | A route proxy growing into the Worker | mitigate | RP1: zero `/api/route` tokens in comment-stripped `bot/src`; RP2: zero `@araharah/routing` imports in `bot/src` — the Worker cannot route even accidentally. |
|
||||
| T-05-15 | Tampering (harness leakage) | The route-profiles harness module shipping in production | mitigate | The `ROUTE_PROFILE_HARNESS_ONLY` sentinel joins BA5's token scan — the module is provably dead-code-eliminated from `web/dist`. |
|
||||
</threat_model>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 1: BA9 bundle-budget + BA10 fixture-parity + BA5 sentinel extension</name>
|
||||
<files>web/test/build-artifact.test.mjs</files>
|
||||
<read_first>
|
||||
- `web/test/build-artifact.test.mjs` (BA1–BA8 — the gate file this task extends; BA7's loop is the BA10 template)
|
||||
- `routing/src/data/route-fixtures.json` (the committed pair set BA10 asserts)
|
||||
- `.planning/phases/05-routing-route-display/05-RESEARCH.md` (Finding 1 — the size math behind the budget)
|
||||
</read_first>
|
||||
<action>
|
||||
Extend `web/test/build-artifact.test.mjs`:
|
||||
- Import the fixtures dataset (`import routeFixtures from "../../routing/src/data/route-fixtures.json" with { type: "json"; }`) and `gzipSync` from `node:zlib`.
|
||||
- Define the input file list: `routing/src/data/route-fixtures.json` + a recursive walk of `routing/src` (`*.ts`) + `web/src/route` (`*.ts`) (resolve paths relative to the test file like `WEB`/`DIST` are).
|
||||
- `BA9` test: skip-with-build-first-message when `web/dist` is absent (the suite convention); compute the summed gz size; `console.log("BA9 route bundle budget: " + total + " / 204800 gz bytes")`; assert `<= 204800` with the measurement in the failure message.
|
||||
- `BA10` test: assert `routeFixtures.pairs.length >= 10` (a real dataset guard); concatenate `web/dist/assets/*.js`; assert EVERY `pairId` appears in the bundle text (failure names the missing pairId — the T-04-39 phrasing pattern).
|
||||
- BA5: add `"ROUTE_PROFILE_HARNESS_ONLY"` to the scanned token list (both the JS-assets and the HTML scans).
|
||||
- Update the file-header doc-comment inventory (BA9/BA10 lines).
|
||||
|
||||
Build first, then run: `npm run build -w theme && npm run build -w web && npm test -w web` — ALL BA gates green including the new ones (04-04's BA7/BA8 still pass — the route family adds no crypto, no CDN, and the gazetteer ids remain).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/C-Fu/araharah && npm run build -w theme >/dev/null 2>&1 && npm run build -w web >/dev/null 2>&1 ; npm test -w web 2>&1 | tail -15</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `npm test -w web` exits 0 and the output contains `PASS BA9` and `PASS BA10`
|
||||
- `grep -q 'BA9 route bundle budget' web/test/build-artifact.test.mjs` exits 0 (the printed measurement)
|
||||
- `grep -q '204800' web/test/build-artifact.test.mjs` exits 0
|
||||
- `grep -q 'ROUTE_PROFILE_HARNESS_ONLY' web/test/build-artifact.test.mjs` exits 0 (BA5 extension)
|
||||
- `grep -q 'pairId' web/test/build-artifact.test.mjs` exits 0 (BA10 parity loop)
|
||||
- `node -e "const{gzipSync}=require('node:zlib');const{readFileSync,readdirSync}=require('node:fs');const{join}=require('node:path');let t=0;const w=(d)=>{for(const f of readdirSync(d,{withFileTypes:true})){const p=join(d,f.name);if(f.isDirectory())w(p);else if(f.name.endsWith('.ts'))t+=gzipSync(readFileSync(p)).length}};w('routing/src');t+=gzipSync(readFileSync('routing/src/data/route-fixtures.json')).length;w('web/src/route');console.log('measured:',t);if(t>204800)throw new Error('over budget');console.log('BA9 preview OK')"` exits 0
|
||||
</acceptance_criteria>
|
||||
<done>The production build passes the budget gate (measured and printed), carries every committed pairId, and provably excludes the harness-only profile module.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 2: ?fixpair harness knob + five route capture legs</name>
|
||||
<files>web/src/mock/route-profiles.ts, web/index.html, web/scripts/capture.mjs</files>
|
||||
<read_first>
|
||||
- `web/index.html` (the harness block — where the knob threads)
|
||||
- `web/src/mock/telegram-mock.ts` (MockOptions.telegramFix — the authoring shape the profile feeds)
|
||||
- `web/scripts/capture.mjs` (SIGNALS, captureLeg waitModes, the expected list, the gallery composer)
|
||||
- `.planning/phases/05-routing-route-display/05-UI-SPEC.md` §Capture Profiles (the binding artifact table)
|
||||
</read_first>
|
||||
<action>
|
||||
**`web/src/mock/route-profiles.ts`** — per the `<capture_contract>` block: sentinel export + `getRouteProfileOrigin` wrapping `getPairOrigin` from `@araharah/routing/src/index.ts`; header comment citing D-12 and the BA5 sentinel.
|
||||
|
||||
**`web/index.html`** — inside the harness block: read `const fixpair = q.get("fixpair");`; after the existing mock-import, when `fixpair` is truthy dynamically import the profile module (`await import("/src/mock/route-profiles.ts")` — the `/src/` absolute form is the PROVEN harness-import path), resolve the origin, and use it as the mock's `telegramFix` lat/lng (accuracy 12, heading null); unknown pairId → keep the current default fix (log nothing). Add `fixpair` to the profile comment line listing the knobs.
|
||||
|
||||
**`web/scripts/capture.mjs`**:
|
||||
- `SIGNALS` gains `'ARAHARAH-ROUTE'`; the seen-map + `signalDiagnostics` gain the same key.
|
||||
- At the top of the leg block, load the committed corpus and pick the demo pair (`routePairs` / `demoPair` per the `<capture_contract>` block — the legs are data-driven from the committed dataset, never hardcoded pair literals).
|
||||
- New `waitMode: "route"` branch: race the `ARAHARAH-ROUTE` console event (`SIGNAL_TIMEOUT_MS`); on timeout → hard failure with diagnostics; parse `state=` from the captured line; the leg's expected state + DOM assertions are leg parameters (`expectState`, `expectText: […]` — `waitForFunction` over `document.body.textContent`, 15s, the dest-leg polling shape); 2000ms settle before the shot (tile render).
|
||||
- Add the five legs per the `<capture_contract>` table (fixture → alt (chip click when present) → pending → dark → gallery-compare, reusing the existing composer with `shell-route-fixture.png` in the implemented column).
|
||||
- Extend `expected` to the 13-artifact list. Update the header doc-comment's Outputs inventory.
|
||||
|
||||
The alt leg's chip click: `page.click(".route-chip")` (first chip), then assert `/ROUTE \d\/\d/.test(bodyText)` — the `{n}/{N}` register the swapped card renders; the degenerate no-chip fallback asserts the single-route rendering law instead (UI-SPEC: single-route draws render NO chip row).
|
||||
|
||||
**Acceptance:** `npm run web:capture` exits 0 with 13/13 non-blank captures (`CAPTURE OK — 13/13`), including the five new legs; no existing leg regressed.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/C-Fu/araharah && npm run web:capture 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `npm run web:capture` exits 0 and its output contains `CAPTURE OK — 13/13`
|
||||
- `test -f web/screenshots/shell-route-fixture.png && test -f web/screenshots/shell-route-alt.png && test -f web/screenshots/shell-route-pending.png && test -f web/screenshots/shell-route-dark.png && test -f web/screenshots/route-gallery-compare.png` exits 0
|
||||
- `grep -q 'ARAHARAH-ROUTE' web/scripts/capture.mjs` exits 0 AND `grep -q 'fixpair' web/index.html` exits 0
|
||||
- `grep -q 'ROUTE_PROFILE_HARNESS_ONLY' web/src/mock/route-profiles.ts` exits 0
|
||||
- `grep -q 'getPairOrigin' web/src/mock/route-profiles.ts` exits 0 (the pinned-corpus-only origin law)
|
||||
- the runner-page profile comment lists the fixpair knob: `grep -q '?fixpair' web/index.html` exits 0
|
||||
</acceptance_criteria>
|
||||
<done>Five route capture legs pass against the production-shaped harness bundle — drawn, alt-swapped, pending, dark, and the DSGN-02 gallery artifact — all raced on state tokens with DOM text assertions.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 3: Worker-side no-route-proxy gates (D-05)</name>
|
||||
<files>bot/test/route-privacy.test.mjs</files>
|
||||
<read_first>
|
||||
- `bot/test/privacy.test.mjs` (the bot-side grep-gate style: comment stripping, banned-token scans, named cases)
|
||||
- `bot/src` tree (the scanned surface)
|
||||
- `.planning/phases/05-routing-route-display/05-CONTEXT.md` D-05 (the law being gated)
|
||||
</read_first>
|
||||
<action>
|
||||
**`bot/test/route-privacy.test.mjs`** — RED-first-then-green (pure gates over the unmodified Worker tree):
|
||||
- `RP1 no route proxy` — strip comments from every `bot/src/**/*.ts` (the privacy.test.mjs pattern) and assert ZERO occurrences of `/api/route` and `route` handler registrations matching `/app\.route\(|on\("message:url"|\.route\("route/`… keep it PRECISE: assert zero `/api/route` tokens AND zero `registerRoute|routeProxy` tokens (D-05: the Worker gains no route endpoint this phase).
|
||||
- `RP2 no routing import` — zero `@araharah/routing` specifiers anywhere in `bot/src` (routing lives in the webview bundle only; the Worker imports only `@araharah/search`).
|
||||
|
||||
Header comment: the D-05 citation + "the pivot's Worker boundary, gated".
|
||||
|
||||
**Acceptance:** `npm test -w bot` exits 0 with `PASS RP1` and `PASS RP2` (all existing bot suites + GE gates still green).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/C-Fu/araharah && npm test -w bot 2>&1 | tail -10</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `npm test -w bot` exits 0 and the output contains `PASS RP1` and `PASS RP2`
|
||||
- `grep -rc '/api/route' bot/src --include='*.ts' | grep -v ':0' | wc -l | tr -d ' '` returns 0 (the gate's premise holds in the tree itself)
|
||||
- `grep -rc '@araharah/routing' bot/src --include='*.ts' | grep -v ':0' | wc -l | tr -d ' '` returns 0
|
||||
</acceptance_criteria>
|
||||
<done>The Worker's routing abstinence is mechanically gated: no route endpoint, no routing-workspace import — the pivot's D-05 boundary holds by test, not by memory.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. `npm run build -w theme && npm run build -w web && npm test -w web` — BA1–BA10 all green (BA9 prints the measurement).
|
||||
2. `npm run web:capture` — CAPTURE OK — 13/13 (the five new legs non-blank, state-raced, DOM-asserted).
|
||||
3. `npm test -w bot` — all suites green including GE1–GE7 and RP1/RP2.
|
||||
4. Full suite: `npm run routing:test && npm run routing:corpus && npm test -w web && npm test -w bot` — green.
|
||||
5. The amended SC3 evidence chain: RT1–RT10 (corpus routes cleanly) + BA9/BA10 (bundle-size gate) + the 05-01 C2 type gate + the pending capture (deliberate, designed) — all four clauses have a green instrument.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- BA9/BA10 pass over the production build with the measurement printed.
|
||||
- 13/13 captures pass including the five route legs driven by ?fixpair.
|
||||
- RP1/RP2 gate the Worker out of the routing path.
|
||||
- The phase's full verification story is green across all four workspaces.
|
||||
</success_criteria>
|
||||
Reference in New Issue
Block a user