heavymap

Current coordination: WORKING-AGREEMENT.md. This dated review is preserved as historical evidence. Before merging, verify whether the changed paths trigger publication and whether jurisdiction approval applies. Its split sequence does not resume paused PR #80. Follow the living handoff for current scope and decisions.

Reference for HANDOFF-2026-10-18.md. Snapshot as written by HM Front on 2026-10-18 (SHAs as of that review: #71 head 1d74db5, main b524f3e); not a living doc.

PR #71 review: HeavyMap v0 front end (HM Front, 2026-10-18)

Read-only review. Nothing was pushed, commented, merged or changed in Linear. Sources: local clone of niagaraassembly/heavymap with all PR heads fetched (gh, authed as peers8862), gh api for Pages, Linear (read), Railway (read).

1. PR state

Item Value
PR #71 “Draft: Hamilton v0 frontend spec and synthetic local prototype”, draft, open
Head codex/heavymap-v0-frontend @ 1d74db5
Merge base d1afda4 (main after PR #75)
Current main b524f3e (PR #79, 2026-10-18)
Ahead / behind 5 commits ahead, 6 behind (#76, #77, #78, #79 landed since the sync)
Mergeable No. GitHub reports CONFLICTING / DIRTY. “Synced with main at 1d74db5” in the brief is out of date: 1d74db5 is the head commit, and it was synced to d1afda4
Conflicts (git merge-tree) Spreadsheets/00-PLANNING-INDEX.csv, Spreadsheets/19-process-insights.csv (both append-only, easy), Spreadsheets/02-dataset-inventory-v4.xlsx (binary)
Size 33 files, +10,237 lines. web/ 19 files, mockups 10 files (~740 KB of PNG), V0-SPEC.md, PRIOR-ART-GLEAN.md note, 2 sheet CSVs, 2 xlsx
Local checks I ran on the head npm ci ok; npm test 3/3 pass; vite build ok. Playwright was not run (no browser run on the box)
Measured build (gzip -9) index.html 1.5 KB, CSS 13.3 KB, main JS 298.8 KB, MapLibre worker 145.5 KB, PMTiles 0.5 KB → ~459 KB compressed first load
PI ids PI-0070..0077 match the reservation. No clash with main (main has 0065–0069 and 0078–0090; PR #80 starts at 0091)

2. What #71 is

Overall: good craft and the right instincts (refusals visible, no vacancy assertion, textContent-only, failed fetch ≠ empty evidence, Range verified locally, no external calls, no deploy script). It is a synthetic interaction prototype. It does not consume the dossier v1 contract yet, so most contract items are backlog, not merge blockers.

3. Verdict

Land before the split (option E) after a short fix list. Do not merge as-is. Two items block. They are the merge conflict and the fact that a merge to main publishes to heavymap.com today. A few cheap doc and fixture fixes should ride along, so that #71 doesn’t become stale front-end canon the moment the split rewrites the tree.

3.1 Must fix before merge (blockers)

# Item Where Fix
B1 Merge conflicts with main Spreadsheets/00-PLANNING-INDEX.csv, Spreadsheets/19-process-insights.csv, Spreadsheets/02-dataset-inventory-v4.xlsx Merge main again. Keep main’s rows and append #71’s (PI-0070..0077 unchanged). Take main’s 02-…xlsx (main added publish_visibility to the CSV and rebuilt the xlsx). Also drop #71’s change to Spreadsheets/09-per-jurisdiction-dataset-catalog.xlsx: it has the same byte size and #71 changes no CSV behind it, so it is a spurious rebuild (xlsx is rebuilt from CSV, never hand-edited)
B2 Merging = public publish today. GitHub Pages is the legacy branch build from main / (gh api …/pages: build_type: legacy, source: main /, last build at b524f3e 2026-10-18 10:27 EDT). The whole tree is live, e.g. https://heavymap.com/Spreadsheets/20-data-gaps-ledger.csv → 200. Merging #71 would serve web/ source, the synthetic PMTiles and the mockup PNGs at heavymap.com/web/… with no sign-off. The page would also be broken, because index.html loads /src/app.js, a Vite source path. This is the HARD-RULES 3 / NA-269 concern in practice, even though #71 adds no workflow repo Pages setting, plus optionally a root _config.yml Needs Morgen’s decision D1 before the merge. My recommendation: add a root _config.yml exclude: for web/ and heavymap-planning/frontend/mockups/, landed in #71 or just before it. Then switch Pages to “GitHub Actions” when the gated deploy workflow lands

3.2 Should fix in #71 (cheap; prevents canon drift)

# Item Where Fix
S1 Budgets contradict the accepted beta-gate budgets (BETA-GATE §6, 2026-10-18: startup ≤ 50 KB compressed, shard ≤ 50 KB compressed p95, each .pmtiles ≤ 100 MB, site ≤ 1 GB) V0-SPEC.md l.9, l.112 (“<300MB”), l.116 (“≤1.5MB compressed”), l.121 (“≤250KB” per response, 40 MB dossier allocation) Cite §6 as the accepted ceiling. Keep the finer allocations only as planning targets. Mark “what counts as startup payload” as open (decision D2). The measured build is ~459 KB compressed
S2 Gate terminology out of date: “G6 remains closed”, “no deployment tooling” V0-SPEC.md l.21, l.143, l.145; web/README.md l.5 & l.51; web/VALIDATION.md l.3 Replace with “Hamilton beta gate (NA-269), Stage B not signed off”. Note that HARD-RULES 3 now allows building and testing deploy tooling. Publishing still needs sign-off
S3 Tax-class fixture contradicts what Hamilton actually has. NPCA has no tax class (GAP-0001). PR #75 moved to neutral wording, and the dossier ships MPAC property code + family range (conf low, stale 2020, GAP-0002/0003). The vacant lens does not depend on tax class web/src/fixtures.js l.120 ("Tax class confirmed"), l.213, l.269; V0-SPEC l.7, l.68 Model the fixture as Property code (MPAC, 2020 snapshot): "590" · 5xx Industrial range with its stale/sparse gap labels. Leave V0-SPEC Q5 (tax-class wording) open for a future tax-class source
S4 The tile key name differs from the contract and from the pending rename. The fixture tile property is id, but the PR #80 §4.1 tile contract names heavymap_id web/scripts/make-fixture.mjs l.33; web/src/map.js l.265, l.384; fixtures.js featureCollection Put one exported constant (ID_FIELD = "heavymap_id") and the id prefix in one place. Regenerate the fixture. The pending Industrial Atlas rename (package / hm: prefix / heavymap_id) then becomes a one-line change
S5 Share unit mismatch with the v1 record. Fixtures use percent 0–100, while the record share is a fraction 0–1 (4 dp). share.toFixed(1)% would render slivers as “0.0%” web/src/dossier.js l.47–55 Note in V0-SPEC “Area shares”: record shares are fractions. Render share==0 && m2>0 as “below 0.01%” and < 0.001 as “below 0.1%”, never “0.0%”

3.3 Not blockers (correct for a synthetic prototype; tracked as backlog)

3.4 Split-plan checks

Check Result
Build outputs committed? No. web/.gitignore ignores dist/, node_modules/ and the test reports
Data committed? No real data. 768-byte synthetic PMTiles plus inline invented GeoJSON. Mockup PNGs (~740 KB) are design assets, not data
Owner names / mailing / Legal_Txt shown or shipped? None. All rendering is via textContent
Deploy workflow? None. No .github/workflows on main or #71. vite.config.js has no deploy config. But see B2: the legacy Pages build publishes on merge anyway
Railway Service heavymap (project heavy-map-v1-railway) auto-deploys main. Latest deploy FAILED 2026-10-18 10:26 EDT. No domain, so not public, but it rebuilds on every merge (decision D6)
Paths the split breaks (fix in the split’s HM delete/rewrite commit, not in #71) V0-SPEC relative links to docs that move to IA: l.23 (claim-refusal-contract.md, context-band-ladder.md, parcel-dossier.md, ../focus/hamilton/* ×4, first-attempts/niagara-atlas/README.md), l.45, l.64, l.124 (parcel-dossier.md). PCDP.md and PRIOR-ART-GLEAN.md stay. web/README.md l.47–48 tells readers to run root python3 -m unittest / scripts/check_index_paths.py, which both move to IA. #71’s 00-PLANNING-INDEX.csv rows (V0-SPEC, mockups) sit in Spreadsheets/ → inventory/ on IA
Pending naming switch Only S4 above (hm:ca:on:hamilton:parcel:SYN-00n at fixtures.js l.89, l.183, l.242; tile key id). There are no Python imports or heavymap.* module paths in web/
Pin to a screened IA release Not applicable yet. #71 has no artifact consumption. → HM-14

4. Principles scorecard

Principle #71
Transparent view mode Partial: refusals visible, “loading/failed ≠ refusal” ✓; no gap types
Withheld data shown as applying-but-held ✗
Signal held identifying info ✗ (copy belongs to purchase funnel)
No load drag / budgets Data side ✓ in spirit (lazy shards specified); first load ~459 KB compressed vs 50 KB accepted startup budget (D2)
Experience profiles NA-310 ✗ (palette switch is the only style-swap primitive)
Browser notice NA-305 ✗
Address-no-parcel list NA-309 ✗
Run records NA-308 ✗
Slivers flag-never-hide Spec ✓, fixtures don’t exercise it
No vacancy assertion / source-quoted labels ✓
OSM deferred (NA-304) ✓ (no live calls; options documented)