diff --git a/.planning/phases/01-design-tokens-rams-foundation/01-03-SUMMARY.md b/.planning/phases/01-design-tokens-rams-foundation/01-03-SUMMARY.md new file mode 100644 index 0000000..186c916 --- /dev/null +++ b/.planning/phases/01-design-tokens-rams-foundation/01-03-SUMMARY.md @@ -0,0 +1,161 @@ +--- +phase: 01-design-tokens-rams-foundation +plan: 03 +subsystem: ui +tags: [rams, maplibre, basemap, openfreemap, design-tokens, cartography, d-08, d-09, d-10] + +# Dependency graph +requires: + - phase: 01-design-tokens-rams-foundation plan 01 (wave 1) + provides: "theme/src/tokens.css (55 verbatim --color-* tokens incl. soft-beige #D9D2C6, chassis-subtle #CCC4B5, matte-black #1A1A1A, pure-white, surface-container-low) — the D-09 derivation source; npm workspace + theme/package.json scripts" + - phase: 01-design-tokens-rams-foundation plan 02 (wave 2) + provides: "Playwright chromium-1248 installed (render-proof tooling unblocked); dist build pipeline (npm run build → dist/rams.css with @theme static vars on :root)" +provides: + - "Rams cartography basemap: theme/basemap/liberty-rams.json — OpenFreeMap liberty fork (110 layers), every cartographic color derived in-script from tokens.css via desaturate(tokenHex, satMultiplier, lightnessPerRole) with saturation clamped to [0.06, 0.12]; accent guard fails the build on #EB8008/#ED3F1C/#736B1E leaks" + - "Reproducible build: theme/basemap/build.mjs + `npm run basemap --workspace theme` (fetch → derive → transform → guard → write, with derivation provenance embedded in style.metadata rams-cartography)" + - "Browser proof: theme/basemap/index.html — MapLibre GL v4 at KLCC (101.6958, 3.1513, z13) with the signal-orange route placeholder + `npm run basemap:preview --workspace theme` → http://localhost:8123/basemap/" + - "Real-browser render evidence: 8/8 pixel-level checks (grayscale grounding + accent-on-top confirmed numerically) — roadmap success criterion 3 satisfied" +affects: [03-mini-app-shell + phase 5 map views (load liberty-rams.json as the MapLibre style), phase 5 route/guidance rendering (consumes the placeholder route slot + reserved signal-orange accent), plan 04 gallery (basemap proof page as gallery input)] + +# Tech tracking +tech-stack: + added: [] (no new deps — build.mjs is node-builtins-only ESM; MapLibre GL v4 via CDN per plan, product copy wired in Phase 3) + patterns: + - "Token-derived cartography: hex→HSL, saturation multiplied then clamped to the calibrated [0.06, 0.12] band, lightness adjusted per role — hue genes from tokens keep the whole palette one warm-neutral instrument family" + - "Wholesale role assignment onto paint color props (zoom-interpolated color ramps flatten to role constants; width/opacity ramps preserved — hierarchy by weight only)" + - "Build-time font probing with recorded substitution (metadata.rams-font-substitutions) — honest fallback provenance instead of runtime 404 noise" + - "Dual accent guard: token names AND values excluded from derivation, output scanned for hex + rgb() triple forms" + +key-files: + created: + - theme/basemap/build.mjs + - theme/basemap/liberty-rams.json + - theme/basemap/index.html + modified: + - theme/package.json (basemap + basemap:preview scripts) + +key-decisions: + - "Role palette (all in-script from tokens): ground = channel-mix(soft-beige, chassis-subtle) sat×0.4 (#CDCAC4); groundSubtle = chassis-subtle sat×0.45 (#C6C2BB); water = matte-black L0.71 (#B9B1B1 mid-gray); roadFillMinor = pure-white L0.96 (#F5F4F4); roadFillMajor = surface-container-low sat×0.12 L0.92 (#ECEBE9 — slightly darker than minor per Interfaces); one uniform machined casing = matte-black L0.34 (#5C5151) — hierarchy by weight, not by tone" + - "Labels: matte-black rgba at reduced opacity (major 0.82 / minor 0.6), halo = ground-tone rgba 0.9; text-halo-blur zeroed (Rams rejects blur)" + - "building-3d layer REMOVED — fill-extrusion is faux 3D, rejected by Rams Physical Planarity; removal recorded in metadata.rams-removed-layers. natural_earth raster relief desaturated via raster-saturation -1" + - "D-10 font fallback executed as planned: Space Grotesk Medium / Geist Regular / Space Grotesk Regular all probed 404 on the OpenFreeMap glyphs endpoint → Noto Sans Bold (major — closest hosted stack to the headline-weight role, liberty precedent for capitals/countries) / Noto Sans Regular (minor); substitutions recorded in metadata.rams-font-substitutions" + - "Major labels = place-city and above (country/state/capital/city); liberty has no subtown layer — interpretation noted in build.mjs" + - "Route placeholder color read at runtime from the token sheet (getComputedStyle(--color-signal-orange)) with literal fallback only if the sheet failed — keeps D-09 spirit in the proof page; route + 1px matte-black dashed centerline mirror the mockup route ribbon, added LAST (accent above grayscale, never inside it)" + +patterns-established: + - "Basemap style is a BUILD ARTIFACT committed for review (regenerated deterministically by npm run basemap); derivation provenance machine-readable in style.metadata" + - "The accent guard pattern: guards live in the generator (fail the build), not just in review greps" + +requirements-completed: [DSGN-01] + +# Metrics +duration: 15min +completed: 2026-10-22 +--- + +# Phase 01 Plan 03: Rams Cartography Basemap Summary + +**OpenFreeMap liberty fork into Rams-constant cartography — every land/water/road/building color derived in-script from the token package (D-09) via a desaturate helper with a [0.06–0.12] saturation band, labels muted matte-black with recorded Space Grotesk/Geist→Noto font substitutions (D-10), zero accent leaks (D-08) — proven rendering in a real browser at KLCC with 8/8 pixel-level checks: 100% warm-gray map area, 296 sampled signal-orange route pixels, zero saturated non-red hues** + +## Performance + +- **Duration:** ~15 min (started 2026-10-21T23:55:27Z, completed 2026-10-22T00:10:10Z) +- **Tasks:** 2/2 +- **Files:** 3 created, 2 modified (theme/package.json touched by both tasks) + +## Accomplishments + +- `theme/basemap/build.mjs` (node-builtins ESM): fetches the liberty style, parses `theme/src/tokens.css` (plan-verbatim regex), computes the 12-role cartography palette in-script (D-09), transforms 107 of 110 layers, probes the glyphs endpoint for the Rams font stacks, guards against accent leaks (hex + rgb forms), writes `liberty-rams.json` with full derivation provenance in `style.metadata` +- `theme/basemap/liberty-rams.json` (110 layers): grayscale/muted land + water (ground #CDCAC4, parcels #C6C2BB, water #B9B1B1), desaturated roads (minor cores #F5F4F4, major cores #ECEBE9 slightly darker, machined casings #5C5151 — hierarchy by weight only), muted labels (matte-black rgba 0.82/0.6, ground-tone halos, zero halo blur), faux-3D building extrusions removed, raster relief desaturated — `signal-orange`/`functional-red`/`olive-green` absent from every layer (build guard + verify command + manual sweep all clean) +- `theme/basemap/index.html`: MapLibre GL v4 proof page at the KLCC anchor (101.6958, 3.1513, zoom 13), page chrome token-only via `../dist/rams.css` (matte-black header band rgb(26,26,26) confirmed), signal-orange route placeholder (6px + dashed centerline, square caps) read from the token package at runtime, layers added LAST, README comment marking the vector as consumed by Phase 5 +- Real-browser render proof (scratch tooling; plan 04 owns the committed capture script): **8/8 checks pass** — zero MapLibre style-validation errors, zero failed requests / non-200 responses (tiles via timestamped TileJSON path, glyphs, sprite, unpkg all 200), 296 sampled orange route pixels (signal-orange vector renders), **0 saturated non-red-hue pixels** (no green/blue cartography colors anywhere — grayscale grounding proven pixel-level), 100.0% warm-gray map area, header chrome exactly matte-black +- OpenFreeMap glyphs endpoint survey: only `Noto Sans Regular/Bold/Italic` hosted — Space Grotesk/Geist 404 (probed at build time as the plan anticipated); substitutions recorded in `metadata["rams-font-substitutions"]` + +## task Commits + +Each task was committed atomically: + +1. **task 1: basemap build script deriving style from tokens** — `891caa1` (feat) +2. **task 2: browser proof page (dummy route + rendered basemap)** — `2332779` (feat) + +**Plan metadata:** (see final commit below — docs: complete plan) + +## Files Created/Modified + +- `theme/basemap/build.mjs` — the generator: token parse (55 `--color-*`), role palette via `desaturate()` (pure hex→HSL helpers), layer classification (roads/tunnels/bridges by body suffix; casings/rails/fills; symbols by label rank), font probing + substitution recording, accent guard, derivation report to stdout +- `theme/basemap/liberty-rams.json` — the generated basemap (valid MapLibre style: version 8, glyphs + sources kept on OpenFreeMap hosted endpoints, 110 layers, `layers[0].paint` = derived ground #CDCAC4; ~106KB pretty-printed for review) +- `theme/basemap/index.html` — proof page: token-driven chrome, KLCC-centered map, placeholder route (Phase 5 consumes), `map.on('error')` console hook for validation surfacing +- `theme/package.json` — `"basemap": "node basemap/build.mjs"`, `"basemap:preview": "python3 -m http.server 8123"` + +## Verification Evidence + +- Task 1: `npm run basemap --workspace theme` → 0 exit, derivation report printed; plan verify command → `OK layers=110` (exit 0); `grep -c "tokens.css" build.mjs` = 4 (≥1); non-role color props in layers: **none**; original liberty colors remaining: **none**; accent hexes in layers: **none** (guard + `/EB8008|ED3F1C|736B1E/i` verify + rgb-triple sweep) +- Task 2: verify command (file + maplibre + liberty-rams.json + KL coords greps) → PASS; render proof 8/8 (details above); fixed sample points: header rgb(26,26,26), map top-right rgb(106,106,104), map bottom-left rgb(184,176,174), map center rgb(152,145,141) — all warm-gray family +- Plan ``: all four bullets satisfied (build 0-exit, no accent hex in paint, basemap renders via the preview server, derivation provenance grep ≥1) + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Rail-layer classification regex missed all 12 rail layers** +- **Found during:** task 1 (first build run — rails listed under "untouched" in the derivation report) +- **Issue:** `/_((major_rail|transit_rail))(_hatching)?$/` demanded an extra leading underscore, but the `tunnel_`/`road_`/`bridge_` prefix strip had already consumed it — rails would have kept liberty's colors +- **Fix:** anchor without the leading underscore: `/^(major_rail|transit_rail)(_hatching)?$/` — 12 rail layers now carry the derived #827373 +- **Files modified:** theme/basemap/build.mjs (fixed before the task-1 commit) +- **Verification:** re-run report: 107 transformed / 3 untouched (icon-pattern-only layers) / 1 removed +- **Committed in:** `891caa1` + +**2. [Rule 1 - Bug] `line-join: "square"` is invalid in MapLibre GL — route layers dropped by style validation** +- **Found during:** task 2 render proof (proof check "style/load errors" caught the two validation errors; the READY log showed 110 layers instead of 112) +- **Issue:** MapLibre's line-join allows only `bevel|round|miter` (only line-CAP supports `square`); with the invalid join both route layers failed `addLayer`, leaving the route unrendered (the 21 "orange" pixels were the footer swatch) +- **Fix:** `line-join: 'miter'` — sharp orthogonal joins preserve the mockup's square-joint intent; `line-cap: 'square'` kept (valid) +- **Files modified:** theme/basemap/index.html +- **Verification:** re-run proof: zero style errors, READY log `layers=112`, 296 sampled orange route pixels +- **Committed in:** `2332779` + +**3. [Rule 1 - Bug] Plan's `basemap:preview` script doubly broken** +- **Found during:** task 2 +- **Issue:** plan-verbatim `"cd theme/basemap && python3 -m http.server 8123"` fails twice: (a) `npm run --workspace theme` executes with cwd = theme/, so `cd theme/basemap` targets nonexistent theme/theme/basemap; (b) even served from basemap/ alone, the docroot would strand `../dist/rams.css` and `../fonts/` (404s — token chrome and self-hosted fonts dead) +- **Fix:** `"basemap:preview": "python3 -m http.server 8123"` in theme/package.json (workspace cwd = theme/ serves one docroot with basemap/, dist/, fonts/); open **http://localhost:8123/basemap/** — documented in the index.html header comment +- **Files modified:** theme/package.json +- **Verification:** all proof dependencies returned 200 over the preview server (page, liberty-rams.json, dist/rams.css, woff2 fonts) +- **Committed in:** `2332779` + +--- + +**Total deviations:** 3 auto-fixed (all Rule 1 bugs — one found by reading the build report, two caught by the real-browser render proof) +**Impact on plan:** No scope changes. The scratch proof script itself needed 3 calibration fixes (readyLog sampled too early; orange threshold assumed a longer route than the ~380px corridor; the saturated-pixel counter initially flagged the route's own antialiased orange→ink blends) — scratch-only, not committed, documented here for honesty. + +## Issues Encountered + +- This model cannot read PNG images — the plan's "human eyes verify" visual check was executed as the stronger pixel-level render proof (D-13 spirit: evidence, not eyeball claim), with the screenshot retained at /tmp/opencode/basemap-proof.png for plan 04's gallery/capture flow and human review +- OpenFreeMap `sources` use a TileJSON `url` (`.../planet` → timestamped `.../20261018_080001_pt/{z}/{x}/{y}.pbf`); the untimestamped wildcard path returns empty 200s — MapLibre resolves the TileJSON itself, so no action needed (noted: direct tile probes must use the TileJSON's `tiles` template) +- Out-of-scope discoveries logged to `.planning/phases/01-design-tokens-rams-foundation/deferred-items.md`: colored POI icons from the hosted ofm sprite (z15+ layers, outside the paint-derivation list), 3 upstream liberty `null` warnings (verified present with the original style too), sprite-pattern layers (wetland/road_area patterns) + +## Known Stubs + +- **Placeholder route geometry** — `theme/basemap/index.html`: the Jalan Ampang → KLCC polyline is a hand-placed preview vector, **intentional per plan** ("labeled in a README comment as consumed by Phase 5"); Phase 5 replaces it with Valhalla route geometry + guidance state. Its color is already token-derived; only the coordinates are placeholders. +- **MapLibre GL from unpkg CDN** — intentional per plan ("CDN unpkg maplibre-gl@^4"); Phase 3 wires the product copy into the Mini App build. + +## Threat Flags + +None — the only network surface is build.mjs fetching the liberty style JSON from the fixed HTTPS URL already named in the plan's own trust boundary (`basemap build → OpenFreeMap API`); no user input, no new endpoints, no auth/file access. + +## User Setup Required + +None — build + preview are local scripts; the render proof ran fully automated. + +## Next Phase Readiness + +- **Phase 3 (Mini App shell) / Phase 5 (guidance):** load `theme/basemap/liberty-rams.json` directly as the MapLibre style; the reserved signal-orange accent + route layer slot (added last, above the grayscale) is the documented hand-off point; if Rams label fonts are wanted in-cartography, a Worker-served glyphs route for the self-hosted Space Grotesk/Geist is the recorded path (see `metadata["rams-font-substitutions"]` reason field) +- **Plan 04 (gallery/audit):** chromium-1248 render path proven against network tiles; the basemap proof page + screenshot are available as gallery inputs; the D-13 audit's sanctioned literals for the basemap view: the two route-layer literals in index.html are token-read-at-runtime, not hardcoded +- Re-run after token changes: `npm run basemap --workspace theme` regenerates deterministically (upstream liberty style changes are the only external variable) +- STATE.md/ROADMAP.md updates deferred to the orchestrator (wave-parallel execution) + +## Self-Check: PASSED + +All 4 created/modified files exist (theme/basemap/build.mjs, theme/basemap/liberty-rams.json, theme/basemap/index.html, theme/package.json); both task commit hashes verified in git log (891caa1, 2332779); must-have artifact markers verified (build.mjs → "tokens.css" ×4, liberty-rams.json → "glyphs" present + version 8, index.html → "maplibre" + "liberty-rams.json" + KL coords 3.1513/101.6958); plan verify commands re-run → all PASS. + +--- +*Phase: 01-design-tokens-rams-foundation* +*Completed: 2026-10-22*