Files

10 KiB
Raw Permalink Blame History

phase, slug, status, nyquist_compliant, wave_0_complete, created, updated
phase slug status nyquist_compliant wave_0_complete created updated
4 destination-search-kl-gazetteer final true false 2026-10-23 2026-10-23

Phase 4 — Validation Strategy

Per-phase validation contract for feedback sampling during execution.


Test Infrastructure

Property Value
Framework Existing hand-rolled harnesses: bot/test/runner.mjs, web/test/runner.mjs; plan 04-01 adds the fourth workspace's identical pair (search/test/runner.mjs + search/test/harness.mjs, copied verbatim) — no new framework; the corpus runner is both a script (search/scripts/gazetteer-corpus.mjs) and an in-process suite (search/test/corpus.test.mjs)
Config file None — suites are discovered by filename convention (*.test.mjs) per workspace
Quick run command npm run search:test / npm test -w bot / npm test -w web
Full suite command npm run search:test && npm test -w bot && npm test -w web && npm run audit -w theme + both typechecks
Corpus gate command npm run search:corpus (the D-14 bake-off close; also asserted in-process by search/test/corpus.test.mjs so npm run search:test fails on a corpus regression)
Estimated runtime ~15 seconds (the hand-rolled runners are sub-second; the corpus run is ~2500 score comparisons; the capture harness is the slow leg and is plan-scoped)

Sampling Rate

  • After every task commit: Run the touched workspace's suite (npm run search:test, or npm test -w bot / npm test -w web) plus that workspace's typecheck.
  • After every plan wave: Run the full suite (npm run search:test && npm test -w bot && npm test -w web) plus npm run search:corpus — the corpus must be ≥ threshold at every wave merge, not just at the end (this is what makes the D-14 escalation ladder actionable rather than a last-minute scramble).
  • Before /gsd-verify-work: Full suite green + corpus gate green.
  • Max feedback latency: 15 seconds (excluding the plan-scoped capture harness and the wrangler dry-run proof, which are per-plan gates, not per-commit gates).

Per-task Verification Map

task ID Plan Wave Requirement Threat Ref Secure Behavior Test Type Automated Command File Exists Status
04-01-T1 (workspace + types + JUPEM table + normalizer) 01 1 SRCH-02, D-09 T-04-01/02/08 no logging/storage in the module; table is DATA; symmetric normalization unit npm run search:test ❌ W0 ⬜ pending
04-01-T2 (OSA distance + tiered scorer) 01 1 SRCH-02 T-04-03/05 min-length guard; disjoint tiers; fuzzy admission ≥ 0.72 unit npm run search:test ❌ W0 ⬜ pending
04-01-T3 (gazetteer + loader + source + LICENSE/ODbL) 01 1 SRCH-01, D-04/06/07/15 T-04-04/06/07/09/11 honest refusal ([]); dataset invariants; GPL+ODbL notices; lazy index; 3-environment loader proof (esbuild search-module bundle marker at wave 1; wrangler dry-run = exit-0 build guard) unit + bundle npm run search:test + node_modules/.bin/esbuild search/src/index.ts --bundle marker grep + npx wrangler deploy --dry-run exit 0 (in bot/) ❌ W0 ⬜ pending
04-02-T1 (golden corpus + D-14 runner + Nyquist gate) 02 2 SRCH-01/02, D-13/14 T-04-12/13/19 pinned corpus; threshold 0.9 un-editable; zero negative leaks corpus npm run search:corpus + npm run search:test ❌ W0 ⬜ pending
04-02-T2 (Overpass top-up + JSON→SQLite converter) 02 2 D-05/06 T-04-14/15/16/17/20 repo tools never runtime deps; UA mandatory; soft-exit on busy; id set preserved fixture-driven npm run search:test ❌ W0 ⬜ pending
04-02-T3 (bake-off close + SEARCH-QUALITY.md + coord verification) 02 2 D-14 T-04-13 measured, recorded, honest decision; one named lever record npm run search:corpus ❌ ⬜ pending
04-03-T1 (/cari + venue card + OPEN deep link + MINI_APP_URL) 03 2 SRCH-03, D-01/02/10/11/15 T-04-21/22/24/30 128-char cap; category-only logs; configured origin source-gate + typecheck npm run bot:typecheck + npm test -w bot ❌ W0 ⬜ pending
04-03-T2 (alternates callback handler + bot factory wiring) 03 2 SRCH-03, D-02 T-04-23/27/28 id validated before lookup; every callback answered; no editMessageText; Worker-bundle marker grep (deferred from 04-01-T3 — bot.ts now imports the source) integration npm test -w bot + npx wrangler deploy --dry-run marker grep (in bot/) ❌ W0 ⬜ pending
04-03-T3 (commands.test.mjs landmines + cari.test.mjs gates) 03 2 SRCH-03, D-11/15 T-04-25/26/29 no crypto in deep-link path; no-match refusal honest; no query in logs gates npm test -w bot ❌ W0 ⬜ pending
04-04-T1 (two-carrier destination resolver + mock + strings) 04 3 ONBD-02, D-11 T-04-32/33/34/35 both carriers one validation; honest null; memory-only; no crypto unit npm test -w web ❌ W0 ⬜ pending
04-04-T2 (ONBD-03 hooks + boot wiring + ?dest= profile) 04 3 ONBD-03, D-12 T-04-36/37/38 Bot API 8.0 guard; "unknown" ≠ error; no browser install APIs unit npm test -w web ❌ W0 ⬜ pending
04-04-T3 (BA7/BA8 drift gates + destination-preload capture) 04 3 ONBD-02 T-04-39/40/41/42 gazetteer inlined in bundle; no crypto shipped; capture asserts state=resolved + card text integration + capture npm test -w web + npm run capture -w web ✅ extend ⬜ pending
04-04-T4 (device gate — BLOCKING checkpoint) 04 3 ONBD-02/03 — real-device deep-link + home-screen verification; honest UNVERIFIED if no device manual (checkpoint) automated half: npm test -w bot && npm test -w web && npm run search:corpus ❌ exit gate ⬜ pending

Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky


Wave 0 Requirements

All new this phase (the repo has no search infrastructure today). The owning tasks are marked; a task's <verify> may reference a suite created in the SAME task (RED-first per the repo's TDD convention).

  • search/test/runner.mjs + search/test/harness.mjs — the fourth workspace's harness pair, copied verbatim from bot/test/ (04-01-T1)
  • search/test/normalizer.test.mjs — per-rule vectors (D-09: every table rule), symmetry, whole-token-only, glued split, Presint/Prsn, idempotence (04-01-T1)
  • search/test/distance.test.mjs — hand-computed OSA vectors (04-01-T2)
  • search/test/score.test.mjs — tier disjointness, min-length guard, exact-outranks-fuzzy, admission boundary (04-01-T2)
  • search/test/gazetteer.test.mjs — entry schema, unique slug ids, KL bbox, ODbL header, LICENSE presence, no-logging/no-storage, lazy loader (04-01-T3)
  • search/test/loader-env.test.mjs — bare-Node import + static-import assertion + esbuild search-module bundle marker (suria-klcc); the wrangler deploy --dry-run Worker-bundle marker grep lands in 04-03-T2 once bot.ts imports the source (04-01-T3 + 04-03-T2)
  • search/test/fixtures/golden-corpus.json — the pinned D-13 corpus, 60+ hand-written cases (04-02-T1)
  • search/scripts/gazetteer-corpus.mjs + search/test/corpus.test.mjs — the D-14 runner and its in-suite Nyquist gate (04-02-T1)
  • search/scripts/overpass-topup.mjs + search/test/topup.test.mjs + search/test/fixtures/overpass-response.json — the D-06 tool, proven offline (04-02-T2)
  • search/scripts/json-to-sqlite.mjs + search/test/converter.test.mjs — the D-05 converter (04-02-T2)
  • bot/test/cari.test.mjs — CR1–CR15 payload/structural gates (04-03-T3)
  • web/test/destination-preload.test.mjs — DP1–DP11 two-carrier gates (04-04-T1)
  • web/test/homescreen.test.mjs — HS1–HS9 capability-guard gates (04-04-T2)
  • Extend (not new): bot/test/commands.test.mjs (CS4 carve, CS5 menu, CS5b narrowed stub ban, CS6 alias set), web/test/build-artifact.test.mjs (BA7 inlining, BA8 no-crypto), web/test/no-storage.test.mjs (explicit web/src/search/ case)

The corpus runner is the phase's primary acceptance instrument (D-14 thresholds) — it is both a script and an in-suite gate, so npm run search:test fails whenever the corpus regresses.


Manual-Only Verifications

Behavior Requirement Why Manual Test Instructions
Venue card renders + OPEN IN ARAHARAH launches Mini App preloaded (ONBD-02) SRCH-03, ONBD-02 Requires real Telegram client + deployed Worker (Bot API cannot be simulated locally end-to-end) /cari KLCC in chat → venue card appears → tap OPEN → webview opens aimed at the destination (04-04-T4, legs 1-3)
Home-screen launch lands search-ready (ONBD-03) ONBD-03 PWA install is a device-OS interaction Add to home screen → launch → app boots into search-ready map (04-04-T4, leg 4)
Deep-link carrier behavior on real clients ONBD-02 The carrier mechanism (URL query primary, start_param secondary) must be observed on live Telegram Android/iOS Open the web_app button from the inline keyboard on both platforms; verify destination preload (04-04-T4)
Honest unknown-id + honest refusal on device D-13 Client-rendering truth ?dest=not-a-real-place → "Destination unavailable"; /cari Kuching → text refusal, no pin (04-04-T4, legs 5-6)

All four are consolidated in plan 04-04's blocking checkpoint (task 4), which records explicit PASS/FAIL/UNVERIFIED verdicts in 04-DEVICE-GATE.md — an UNVERIFIED device leg holds the phase open with the blocker in .planning/STATE.md, never a silent close (the Phase 3 precedent).


Validation Sign-Off

  • All tasks have <automated> verify or Wave 0 dependencies
  • Sampling continuity: no 3 consecutive tasks without automated verify (every task carries an automated verify; the single checkpoint pairs it with the automated half)
  • Wave 0 covers all MISSING references
  • No watch-mode flags
  • Feedback latency < 15s (excluding plan-scoped capture/dry-run gates)
  • nyquist_compliant: true set in frontmatter

Approval: planner-complete (2026-10-23) — statuses flip to ✅ during execution