Files
arah2/.planning/phases/01-design-tokens-rams-foundation/01-03-SUMMARY.md
T
araharah-executor 1544b92b31 docs(01-design-tokens-rams-foundation-03): complete Rams cartography basemap plan
- 2/2 tasks: build script + token-derived style JSON, browser proof page with signal-orange route placeholder
- real-browser render proof 8/8: grayscale grounding pixel-verified, zero accent leaks, route accent on top
- D-08/D-09/D-10 satisfied; 3 Rule-1 deviations documented (rail regex, line-join spec, preview docroot)
2026-10-22 08:11:33 +08:00

16 KiB
Raw Blame History


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 <verification>: 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