- docs/SEARCH-QUALITY.md: the bake-off close record — corpus v1 (75 cases) x
gazetteer v1 (30 entries), measured 2026-10-23T17:02Z via npm run search:corpus
- top-1 66/68 (97.1%), top-3 68/68 (100.0%) >= 90% threshold
- negatives refused 7/7, refusal margin 0.643 < 0.72 admission line
- per-category table (landmark/abbreviation/glued/typo/alias all 100%)
- escalation lever: None — met on first run; no abbreviations.ts row, no
alias, no corpus edit; the fixed ladder was never climbed
- coordinate verification: 10 seed entries spot-checked against Nominatim
(identifying UA, 1 req/s): 8/10 within 0.01°; bukit-bintang outlier is an
area-vs-point discrepancy (no change); pavilion-kl was a real ~2 km error
- both top-1 misses documented as expected disjoint-tier behaviour
- kl-gazetteer.json: pavilion-kl coordinate corrected (3.1478,101.695) ->
(3.149,101.7132) — Rule 1 data bug found by the prescribed Nominatim
verification; post-fix delta 0.0002°; corpus is coordinate-blind, all suites green
- kl-gazetteer.json: 30 hand-curated KL-metro landmarks (10 required + 20
more) with locals' aliases as native data (D-08) — canonical OSM names
as name, shorthand ('KLCC', 'DUKE', 'Sg Wang') as aliases — plus the
ODbL-1.0 attribution header traveling inside the JSON (D-06/D-07)
- gazetteer.ts: the three laws — lazy memoized index never at module
scope (Workers startup, T-04-04), static JSON import surviving
wrangler/Vite/bare-Node (D-07), zero logging/storage (D-15); aliases
normalized once at index-build (D-09 symmetry)
- source.ts: the D-03 SearchSource — async search() + synchronous byId()
(slug-validated, honest null); tiered admission (fuzzy only at sim
>= 0.72) so 'Kuching'/'Penang' refuse as [] — never a fabricated pin
- index.ts: the named-export public API (no default, subpath+extension
imports per the @araharah/theme precedent)
- LICENSE: the verbatim GPL-3.0 text (D-04); LICENSES.md: the dual-licence
note (code GPL-3.0, data ODbL-1.0 + OSM attribution); README: corrected
licence section + search/ in the layout, four-package table, commands
G1-G11 + L1-L3 green (39/39); typecheck clean; esbuild bundle carries the
suria-klcc marker; wrangler dry-run exits 0; bot + web suites unaffected.
- distance.ts: hand-rolled 3-band OSA, adjacent transposition = 1 edit,
zero imports (D-07); the row rotation is pinned by the D1-D9 vectors
- score.ts: TIER bands strictly disjoint descending — EXACT 1.00 /
ALIAS_EXACT 0.90 / PREFIX 0.75 / TOKEN_SET 0.65 / FUZZY as the 0.60
CEILING (a fuzzy score is fuzzySim * 0.60, never a 0.00 multiplier);
FUZZY_ADMIT_SIMILARITY = 0.72 exported for source.ts + the corpus
runner; token-level residual lets 'Sentrl' reach 'KL Sentral' while
'Jln Tun Razak' cannot reach 'Jalan Ampang'
- scoreCandidate returns the RAW matched alias (corpus provenance);
an exact ALIAS hit rides ALIAS_EXACT so a name-exact outranks it
- score.test.mjs S7 corrected during GREEN: admission belongs to
source.ts — scoreCandidate asserts the sub-floor similarity instead
D1-D9 + S1-S7 green; typecheck clean.