# REPORT Part C2: meaning-based matching through a precomputed word map (18 Oct 2026, CET)

## Blocked on you
Nothing to decide. Deploy for the lead (not done): publish `search/word_map.json` next to `index.json` (gex44 `/srv/forge-assets/repo/search/word_map.json`, same Caddy route as the Engine CLI uses), copy `omg/factory/library_search.py` + `games_library.py` to the Factory, refresh the Engine pack file `library_search_cli.py`. Without the map file everything behaves as in Part C.

## What changed
- `tools/build_word_map.py` (re-runnable publish step): vocabulary = taxonomy classes + aliases + asset name/tag words + `taxonomy/vocab.txt` (3,419 everyday nouns/phrases from Gemini 4, skimmed, filtered to 1-2 words) = 3,967 entries. Each is embedded once with the same TEXT model as the description vectors (gemini-embedding-001, 768d, as "a 3D model of <word>", RETRIEVAL_QUERY), cached under `vectors/words/` (256 shards, 7.5 MB). Cosine vs each asset's description vector, up to 8 kept above 0.68 and within 0.04 of the word's best. Output `search/word_map.json`: 1,230 words, 192 KB, byte-stable (checked: 3 runs, same md5). Re-run cost: 0 calls if nothing new; 1 call after adding 19 words. After the 30 creatures join: run `index` build, `enrich_library.py`, then this script.
- `library_search.py`: a word (or two-word phrase) looks the map up first, then BM25; map hits lead, ranked by stored score, ties on id. Map ids not in the cleared set (reuse flag, status, setting) are dropped. `games_library.load_word_map()` reads `repo/search/word_map.json`, cached until the file changes; missing file = Part C behaviour. `pick(..., word_map=)`; `library_attachments` passes it.
- Engine CLI kept equal (head of the file is the Factory file, checked) and fetches `/repo/search/word_map.json` (optional, 10 min cache, any failure = BM25 only). Map hits score cosine x10 beside BM25.
- Laws unchanged: class named in the brief with no asset = GAP (those words are never searched); reuse.omg / reuse.omu; floor; cap 6 (Factory) / limit (CLI).

## Evidence
- Calibration (`test/word_map_cases.json`, 18 positives + 12 negatives; table in `test/word_map_calibration.txt`): at 0.68 / 0.04 all 12 negatives map to nothing (cow, elephant, toaster, dragon, giraffe, piano, submarine, pizza, volcano, penguin, microwave, trampoline), recall 12/18 = 0.67, precision 0.76 (hits on an expected class / all hits). 0.71 gives precision 0.95 but recall 0.50; below 0.66 negatives start to map.
- Hits: settee, couch, loveseat -> store-sofa-3; closet -> wardrobe; bookcase, lorry -> trucks, automobile -> cars, chopper -> helicopters, rowing/fishing boat, lamp post. Misses: jetty, bathtub, bike, motorbike, stool (no hit above the floor), cupboard/dresser (nearest is a shelf partition, not the cabinet/drawers).
- `integrations/run_factory_tests.sh`: 149 passed (145 before + 4 new; the settee test fails on old code, `pick` has no `word_map`). Engine tests via throwaway layout: 7 passed (1 new).
- `integrations/search-wiring-evidence.txt`: 3 columns (old / BM25 / word map) on 10 briefs + 10 synonym briefs. "a cosy room with a settee and a closet": old 0, BM25 1 (a lighthouse), word map 5 (2 sofas, wardrobe, shelf partition, lighthouse). "bookcase and dresser": 4 (bookcases, chest of drawers). "bathtub": +bath vanity. "supermarket shelving aisles": +checkout, 2 gondola racks.

## What would break / limits
- Lookalike noise at 0.68: "well" -> crane, "pillars" -> chimney/rock/ruin column, "closet" -> a shelf partition as 2nd hit. Capped at 6, after real picks. Raise `THRESHOLD` and re-run to trade recall for precision.
- Briefs whose word hits an old alias still follow old rules: "lorries" -> alias `vehicle.utility.truck` (class does not exist, so nothing is picked and no GAP), "rowing boat" -> alias `boat` -> sea vehicles not picked for games. Alias fix is outside this part.
- Jetty still misses: the library has no jetty and the nearest (buoy, mooring) scores 0.656, the same as a toaster's best score. Not fixable by threshold.
- Map is built from the current index: new assets (30 creatures) need a re-run, otherwise they are only reachable by BM25.
- The Engine file duplicates the Factory file (head equal, checked by hand since the throwaway layout cannot see the forge-assets tree).

## NOT done
No deploy, no publish, no gex44/ax42 edit, no real build. `vectors/words/` and `taxonomy/vocab.txt` committed; `.scratch` removed.
