forked from c-fu/arah2
docs: generate + verify project documentation — canonical docs, per-package READMEs, verified fix pass
- New: docs/ARCHITECTURE.md, docs/GETTING-STARTED.md, docs/DEVELOPMENT.md, docs/TESTING.md, docs/CONFIGURATION.md (colored mermaid diagrams, quoted labels) - Regenerated README.md (arah² / arah2bot naming, canonical clone URL) - New per-package READMEs: theme/, bot/, web/ - Verified 13 docs against the live codebase (2 fix iterations + 2 residual corrections); corrected 56 stale claims across AGENTS/DESIGN/IDEAS/GSD_FIX
This commit is contained in:
+34
-8
@@ -23,7 +23,7 @@ Decimal phases appear between their surrounding integers in numeric order.
|
||||
|
||||
- [ ] **Phase 1: Design Tokens + Rams Foundation** - Distill the Telemetry Rams system into a single token package and Rams-styled map basemap, with a mockup-vs-implementation approval artifact — before any screen is built
|
||||
- [ ] **Phase 2: Bot Skeleton + initData Security** - grammY bot on one Cloudflare Worker: /start, command menu, /privacy, and HMAC-validated identity with TTL-only KV storage
|
||||
- [ ] **Phase 3: Mini App Shell + Geolocation** - Telegram WebApp shell that boots, authenticates, and reads a reliable live location across the Android/iOS/Desktop client matrix
|
||||
- [x] **Phase 3: Mini App Shell + Geolocation** - Telegram WebApp shell that boots, authenticates, and reads a reliable live location across the Android/iOS/Desktop client matrix (completed 2026-10-23)
|
||||
- [ ] **Phase 4: Destination Search + KL Gazetteer** - Free-text KL search that resolves landmarks despite Malay abbreviations and typos, venue cards, and Mini App launch with destination preloaded
|
||||
- [ ] **Phase 5: Routing + Route Display** - Valhalla-backed route calculation shown as a Rams route line with quiet alternative cards; the full search→route→render loop lands
|
||||
- [ ] **Phase 6: Turn-by-Turn Guidance (Live Driving)** - Client guidance state machine: maneuver banner, missed-turn auto-recalculation, honest ETA, haptics, seamless resume, and backgrounded chat turn-cards
|
||||
@@ -43,10 +43,10 @@ Decimal phases appear between their surrounding integers in numeric order.
|
||||
**Plans**: 4 plans
|
||||
|
||||
Plans:
|
||||
- [ ] 01-01-PLAN.md — Repo hygiene + Tailwind v4 token package (`theme/`) + self-hosted fonts + DESIGN.md supersession (D-01/02/04/05/06/07/16)
|
||||
- [ ] 01-02-PLAN.md — Component sheet: `theme/src/components.css` + `sheet.html` consuming tokens only (D-03/D-11)
|
||||
- [ ] 01-03-PLAN.md — Rams basemap: token-derived OpenFreeMap liberty fork + KL preview (D-08/09/10)
|
||||
- [ ] 01-04-PLAN.md — Off-token audit (D-13) + Playwright captures (D-14) + DSGN-02 approval gallery (D-12), blocking user checkpoint
|
||||
- [x] 01-01-PLAN.md — Repo hygiene + Tailwind v4 token package (`theme/`) + self-hosted fonts + DESIGN.md supersession (D-01/02/04/05/06/07/16)
|
||||
- [x] 01-02-PLAN.md — Component sheet: `theme/src/components.css` + `sheet.html` consuming tokens only (D-03/D-11)
|
||||
- [x] 01-03-PLAN.md — Rams basemap: token-derived OpenFreeMap liberty fork + KL preview (D-08/09/10)
|
||||
- [x] 01-04-PLAN.md — Off-token audit (D-13) + Playwright captures (D-14) + DSGN-02 approval gallery (D-12), blocking user checkpoint
|
||||
|
||||
Notes:
|
||||
- Repo hygiene lands here first: scrub `*:Zone.Identifier` artifacts, `.gitignore` for OS junk/`node_modules`/secrets (pre-dates any credential handling).
|
||||
@@ -63,7 +63,12 @@ Notes:
|
||||
2. /privacy gives an honest, visible statement of what araharah stores and does not store (PRIV-01) — the in-app "trip ended, data deleted" messaging half completes with guidance in Phase 6, riding this requirement's message design
|
||||
3. Tampered, stale, and replayed initData payloads are rejected with CI test vectors and known-good HMAC cases passing; only validated identities receive a one-time session token (security gate — the phase's exit criterion)
|
||||
4. A storage spot-check shows KV with TTL-only namespaces and zero persistent coordinates or user profiles — privacy by construction holds from the first line of backend code (PRIV-02; re-audited in Phase 7)
|
||||
**Plans**: TBD
|
||||
**Plans**: 3 plans
|
||||
|
||||
Plans:
|
||||
- [x] 02-01-PLAN.md — bot workspace scaffold + Wave-0 test harness (RED initdata suite)
|
||||
- [x] 02-02-PLAN.md — initData HMAC validator (rejection classes) + TTL-only KV sessions (GREEN)
|
||||
- [x] 02-03-PLAN.md — command surface (en/ms, aliases) + Worker wiring (/telegram/webhook, /api/session) + deploy checkpoint
|
||||
|
||||
Notes:
|
||||
- One Cloudflare Worker deployable (static assets + webhook + `/api/*`); KV `chat_sessions` (minutes TTL) is the first namespace; the `guidance_sessions` (~2h TTL) envelope is only designed with its consumer in Phase 5.
|
||||
@@ -80,11 +85,32 @@ Notes:
|
||||
3. Location denial shows a deliberate denied state (never a blank map), and the app renders correctly under Telegram light and dark themes while the map keeps Rams cartography constant (PX-02, platform themes)
|
||||
4. No coordinate written anywhere persists: storage remains TTL-ephemeral only, spot-checkable at phase exit (GEO-03)
|
||||
5. Device-matrix exit pass: Android + iOS + Desktop + plain browser each demonstrate boot → session → position, exercising the full fallback chain (pitfall research device-matrix criterion)
|
||||
**Plans**: TBD
|
||||
**Plans**: 4 plans
|
||||
|
||||
Plans:
|
||||
|
||||
**Wave 1** (baseline):
|
||||
- [x] 03-01-PLAN.md — `web/` workspace + Wave-0 test runner; TTL-only location-pin KV, GEO-02 chat keyboard handler, and the `GET /api/location` relay route
|
||||
|
||||
**Wave 2** *(blocked on Wave 1 completion)*:
|
||||
- [x] 03-02-PLAN.md — Location chain: three source adapters normalizing to one `Fix`, the D-04 state machine with freshness as a separate axis, memory-only session store, Telegram platform wrappers, Mock-Telegram harness, and the GEO-01/02/03 + PX-02 gates
|
||||
|
||||
**Wave 3** *(blocked on Wave 2 completion)*:
|
||||
- [x] 03-03-PLAN.md — Rams shell: UI-SPEC copy module, token-only stylesheet, MapLibre v6 bootstrap with the WebGL2 gate, marker/follow/recenter, status strip and state cards, and the map-first boot choreography
|
||||
|
||||
**Wave 4** *(blocked on Wave 3 completion)*:
|
||||
- [x] 03-04-PLAN.md — Worker `[assets]` binding + one-command deploy + D-14 build-artifact gate; Playwright capture harness with the GEO-03 runtime probe; blocking device-matrix exit gate
|
||||
|
||||
Cross-cutting constraints (truths appearing in 2+ plans, binding at every wave):
|
||||
- denied/blank is forbidden — every dependency-failure state renders a deliberate Rams surface (03-02, 03-03, 03-04)
|
||||
- zero coordinate persistence — TTL-ephemeral KV only; grep gate + runtime probe (03-01, 03-02, 03-03, 03-04)
|
||||
- Rams token-only styling — Telemetry Rams tokens binding over skill defaults (03-02, 03-03, 03-04)
|
||||
|
||||
Notes:
|
||||
- Highest platform-integration uncertainty in the project — research recommends `/gsd-research-phase` (Android LocationManager stale-fix workaround, `getLiveData()` wrapper, WebView permission bridges, browser testing harness).
|
||||
- BackButton wiring and safe-area groundwork are built here as shell infrastructure; the observable PX-01/PX-03 behaviors are claimed by Phase 6 where the full state graph exists.
|
||||
- Research findings that shaped the plan: `getLiveData()` does not exist (D-05's gated streaming is our own `Fix`-shaped abstraction with a pluggable cadence — Phase 6 picks poll vs `watchPosition`); the Android stale-fix inverts the naive D-04 chain reading, so freshness is a separate axis from availability; Desktop Telegram has no geolocation at all (issue #77), making the chat-keyboard path the only Desktop origin; `GET /api/location` is required scope; MapLibre v6 is ESM-only and WebGL2-only (the map-less shell state is a deliberate `--color-surface-dim` field, not a crash); Bot API 10.2's origin restriction is enforced as of 2026-07-20.
|
||||
- 03-04 task 3 is a **blocking human checkpoint**: real Android, iOS, and Telegram Desktop are listed in RESEARCH as required with no fallback. If no device is available, Success Criterion 5 is recorded UNVERIFIED and the phase is held open rather than silently closed.
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 4: Destination Search + KL Gazetteer
|
||||
@@ -178,7 +204,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. Design Tokens + Rams Foundation | 0/TBD | Not started | - |
|
||||
| 2. Bot Skeleton + initData Security | 0/TBD | Not started | - |
|
||||
| 3. Mini App Shell + Geolocation | 0/TBD | Not started | - |
|
||||
| 3. Mini App Shell + Geolocation | 0/4 | Ready to execute | - |
|
||||
| 4. Destination Search + KL Gazetteer | 0/TBD | Not started | - |
|
||||
| 5. Routing + Route Display | 0/TBD | Not started | - |
|
||||
| 6. Turn-by-Turn Guidance (Live Driving) | 0/TBD | Not started | - |
|
||||
|
||||
@@ -7,12 +7,14 @@ A privacy-respecting GPS navigation app delivered through Telegram: a waze-like
|
||||
route navigator, but Telegram-first instead of an app store install, built on
|
||||
OpenStreetMap data, and designed to Dieter Rams' principle of "less, but better".
|
||||
Users search a destination (e.g. "KLCC") with the bot, tap into a Telegram Mini
|
||||
App (webview hosted on Cloudflare Pages), and receive live route guidance while
|
||||
App (webview served as Cloudflare Workers static assets (Workers superseded Pages — see bot/wrangler.jsonc)), and receive live route guidance while
|
||||
driving. First users are in the Kuala Lumpur metro area.
|
||||
|
||||
Reference design docs: `DESIGN.md` (design philosophy), `IDEAS.md` (vision +
|
||||
stack), `stitch_minimalist_route_navigator/` (4 UI mockup screens), and
|
||||
`.planning/codebase/` (codebase map; current state is design-only, no app code).
|
||||
`.planning/codebase/` (codebase map; the repo has substantial app code: `bot/`
|
||||
(grammY TypeScript worker), `web/` (Vite MapLibre Mini App), `theme/` (Tailwind v4
|
||||
token package)).
|
||||
|
||||
**Core Value:** A user finds a local destination via Telegram and gets there with reliable
|
||||
turn-by-turn guidance — tracked by nothing beyond what they explicitly opted in
|
||||
@@ -40,20 +42,20 @@ to.
|
||||
## Project Maturity
|
||||
- Concept and design-system documents: `DESIGN.md`, `IDEAS.md`
|
||||
- A design-system extension: `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md` (frontmatter-structured design tokens for colors, typography, spacing)
|
||||
- Static HTML UI mockup screens (one `code.html` + `screen.png` per screen) — these are the **only executable code** in the repo
|
||||
- Planning outputs: `.planning/` (not product code)
|
||||
- Static HTML UI mockup screens (one `code.html` + `screen.png` per screen) — legacy design reference; the executable code is the `bot/`, `web/`, and `theme/` workspaces
|
||||
- Planning outputs: `.planning/` — not product code; it holds phase plans for the code that now exists
|
||||
## Languages
|
||||
- HTML 5 — the four mockup screens: `stitch_minimalist_route_navigator/active_route_guidance/code.html`, `stitch_minimalist_route_navigator/navigate_map_overview/code.html`, `stitch_minimalist_route_navigator/community_incident_reporter/code.html`, `stitch_minimalist_route_navigator/telegram_live_traffic_feed/code.html` (each self-contained, inline `<script>` Tailwind config, minimal inline JS for markup behavior)
|
||||
- HTML 5 — the mockup screens under `stitch_minimalist_route_navigator/*/code.html` are legacy reference; the active UI entry points are `web/` and `theme/gallery.html`
|
||||
- TypeScript — the bot Worker (`bot/src/`) and the Vite Mini App (`web/src/`)
|
||||
- Markdown — design docs (`DESIGN.md`, `IDEAS.md`, `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md`)
|
||||
- A backend language/runtime for the Telegram bot (not yet chosen in any doc; nothing in the repo constrains it)
|
||||
## Runtime
|
||||
- None installed. No `package.json`, no `requirements.txt`, no lockfile, no `.nvmrc`, no CI config, no test config anywhere in the repo.
|
||||
- Installed: root `package.json` with npm workspaces `theme`, `bot`, `web`, plus `package-lock.json`. Test config lives in custom harnesses (`bot/test/runner.mjs`, `web/test/runner.mjs`, `theme/scripts/audit.test.mjs`). No `.nvmrc` and no CI config yet.
|
||||
- Mockups run by opening `code.html` directly in a browser (CDN-based dependencies, no local dev server required).
|
||||
## Frameworks
|
||||
- Tailwind CSS via Play CDN (`https://cdn.tailwindcss.com`) — loaded as a `<script>` tag with an inline `tailwind.config` block defining the full "Telemetry Rams" theme (colors like `signal-orange`, `matte-black`, `soft-beige`; fonts; spacing/border-radius tokens). Present in all four mockup screens.
|
||||
- Google Fonts CDN — `Geist` (body) and `Space Grotesk` (headings/telemetry), plus Material Symbols Outlined for icons (`https://fonts.googleapis.com/css2?...`). Present in all four mockup screens.
|
||||
- Not detected. No test framework, no test files.
|
||||
- Not detected. No bundler, no formatter, no linter config. Mockups are standalone static files.
|
||||
- Not detected. No third-party test framework; tests use custom `node:test`-style harnesses in `bot/test/` and `web/test/` (`runner.mjs` + `*.test.mjs` suites) plus `theme/scripts/audit.test.mjs`.
|
||||
- Bundler: Vite (`web/vite.config.ts`). Theme has a token audit tool (`npm run audit -w theme`). No formatter or linter config.
|
||||
- The Play CDN is explicitly a prototyping tool — a first implementation phase should migrate mockup designs to a committed Tailwind setup (build pipeline or v4 binary) before production use.
|
||||
## Key Dependencies
|
||||
- `cdn.tailwindcss.com` — theming engine for all screens
|
||||
@@ -61,18 +63,18 @@ to.
|
||||
- Mockup images are hosted/lifted from Google-hosted sample assets (`lh3.googleusercontent.com/aida/...` URLs appear in some screens' `<img>`/`src` attributes) — placeholders from the Stitch tooling, not project data
|
||||
- Telegram Bot API — frontend transport (`IDEAS.md`: "Telegram-first")
|
||||
- Neon Postgres + PostGIS — event/user storage (`IDEAS.md`)
|
||||
- OSRM — routing engine with dynamic traffic weighting (`IDEAS.md`, restated in `DESIGN.md`: `/navigate origin → destination → OSRM route`)
|
||||
- Routing engine: Valhalla + Photon are the validated stack per the project constraints; the OSRM references in older docs (`IDEAS.md`, `DESIGN.md`) are superseded
|
||||
- Maptiler — map tile provider for grayscale snapshots (`IDEAS.md`, `DESIGN.md`)
|
||||
- Cloudflare Pages — optional PWA hosting (`IDEAS.md`); Cloudflare R2 — image/snapshot storage (`IDEAS.md`)
|
||||
- Cloudflare Workers with static assets — hosts the Mini App (`bot/wrangler.jsonc` binds `../web/dist`); Cloudflare Pages is superseded. Cloudflare R2 — image/snapshot storage (`IDEAS.md`)
|
||||
- RabbitMQ — event queue for broadcast-nearby flows (`IDEAS.md`)
|
||||
- Prometheus + Grafana — monitoring (`IDEAS.md`)
|
||||
- Resend — email (`IDEAS.md`)
|
||||
## Configuration
|
||||
- No `.env*` files, no config files exist. Nothing to document; note that a first backend phase will need to introduce one (e.g., Telegram bot token, Neon connection string, Maptiler key) — treat secret handling from day one since none exists yet.
|
||||
- `bot/.dev.vars.example` is the template (the real `bot/.dev.vars` is gitignored local secrets), plus `bot/wrangler.jsonc` and `bot/src/config.ts` (the typed `Env` contract). See `docs/CONFIGURATION.md` for the full variable list.
|
||||
- No build config files. Tailwind config lives inline per-mockup in `<script id="tailwind-config">`; the canonical design-token source of truth is `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md` (YAML frontmatter with `colors`, `typography`, `spacing` blocks).
|
||||
## Platform Requirements
|
||||
- Any modern browser to view the mockups; any text editor. No Node/Python runtime is required by current code.
|
||||
- Telegram bot process (runtime/tooling TBC), Neon Postgres (with PostGIS extension enabled), OSRM instance or hosted API, Maptiler API key, Cloudflare Pages + R2, RabbitMQ broker, Prometheus/Grafana stack, Resend account.
|
||||
- Any modern browser to view the UI; any text editor. Node/npm are required (workspaces, Vite, wrangler); `python3` only for the theme gallery preview server (`npm run gallery` → `python3 -m http.server 8123`).
|
||||
- Settled runtime: TypeScript + grammY on Cloudflare Workers with KV (`chat_sessions`). Neon Postgres, OSRM, RabbitMQ, and Resend are future/aspirational and are not deployed.
|
||||
## Design-System Constraints (binding for future code)
|
||||
- Palette: Signal Orange `#EB8008`, Functional Red `#ED3F1C`, Olive Green `#736B1E`, Soft Beige `#D9D2C6`, Matte Black `#1A1A1A` (as defined in `DESIGN.md` and in the inline Tailwind config of every mockup)
|
||||
- Typography: Space Grotesk for headings/telemetry numbers, Geist for body/labels; uppercase + expanded letter-spacing for labels
|
||||
@@ -111,9 +113,9 @@ to.
|
||||
## Where Conventions Are NOT Yet Established
|
||||
- **Languages/runtime:** no manifest declares TypeScript/JavaScript versions, Node, or
|
||||
- **Formatting & linting:** no Prettier/ESLint/Biome/editorconfig. HTML mockups are
|
||||
- **Module layout / imports:** no application code, no `src/`, no import graph.
|
||||
- **Error handling, logging, validation:** no code exists to define patterns for these.
|
||||
- **Git hygiene:** the workspace directory is not yet a git repository; when initialized,
|
||||
- **Module layout / imports:** three `src/` trees with a full import graph: `theme/src/`, `web/src/`, `bot/src/`.
|
||||
- **Error handling, logging, validation:** patterns exist — `bot/src/webhook.ts` secret validation + error handling, `bot/src/security/initdata.ts` HMAC validation, `web/test/no-storage.test.mjs` storage guard.
|
||||
- **Git hygiene:** the workspace is a git repository (branch `main`); see the `.gitignore` rules for generated output and local secrets.
|
||||
- **Path casing:** any backend service (Telegram bot, OSRM, Neon/Postgres — the stack
|
||||
## Prescriptive Guidance for New Code
|
||||
<!-- GSD:conventions-end -->
|
||||
@@ -129,7 +131,7 @@ to.
|
||||
|-----------|----------------|------|
|
||||
| `active_route_guidance/` | Turn-by-turn navigation HUD mockup: maneuver strip, incident telemetry band, map corridor viewport | `stitch_minimalist_route_navigator/active_route_guidance/code.html` |
|
||||
| `navigate_map_overview/` | Full-map navigation screen mockup: route rendering, layer toggles, recenter control | `stitch_minimalist_route_navigator/navigate_map_overview/code.html` |
|
||||
| `telegram_live_traffic_feed/` | Telegram-bot-styled traffic feed mockup: inline commands (`/traffic`, `/hazard`, `/status`, `/clear`), dispatch confirm | `stitch_minimalist_route_navigator/telegram_live_traffic_feed/code.html` |
|
||||
| `telegram_live_traffic_feed/` | Telegram-bot-styled traffic feed mockup: inline commands (`/traffic`, `/hazard`, `/status`, `/clear`) via `insertCommand` handlers only — the dispatch-confirm interaction lives in `navigate_map_overview/code.html` (`confirmDispatch`) | `stitch_minimalist_route_navigator/telegram_live_traffic_feed/code.html` |
|
||||
| `community_incident_reporter/` | Incident-report flow mockup: hazard drawer, modal report, incident type selection | `stitch_minimalist_route_navigator/community_incident_reporter/code.html` |
|
||||
| `telemetry_rams/DESIGN.md` | Formal design system spec (design tokens: colors, typography, spacing, breakpoints, component rules) | `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md` |
|
||||
| `DESIGN.md` | High-level Rams design philosophy: palette, typography, iconography, interaction flows | `DESIGN.md` |
|
||||
@@ -139,15 +141,16 @@ to.
|
||||
- Styling is Tailwind via **CDN script** (`https://cdn.tailwindcss.com`) with a per-file inline `tailwind.config` object encoding the design tokens (colors, borderRadius, spacing). No Tailwind build step.
|
||||
- Fonts and icons are loaded from CDNs: **Google Fonts** (Space Grotesk for headlines/telemetry, Geist for body) and **Material Symbols Outlined** for iconography.
|
||||
- Imagery is either **inline SVG** (route vectors, directional glyphs drawn with `stroke-linecap="square"`) or **remote hosted URLs** (`lh3.googleusercontent.com/aida/...` logo images) — no local image assets besides `screen.png` captures.
|
||||
- Interactivity is **vanilla JS via inline `onclick` handlers** only (demo stubs — e.g., `alert('Reroute broadcast loaded.')`). No state management, no routing, no data layer.
|
||||
- The mockups remain inline-`onclick` demo stubs with no state management, but the real app interactivity lives in `web/` (Vite/TypeScript Mini App) and the `bot/` command handlers.
|
||||
## Layers
|
||||
- Presentation: `stitch_minimalist_route_navigator/*/code.html` — Tailwind markup + inline SVG + demo JS
|
||||
- Design tokens: `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md` (YAML front matter: `colors`, `typography`, `spacing`) duplicated as inline `tailwind.config` in each mockup's `<head>`
|
||||
- Specs/docs: `DESIGN.md`, `IDEAS.md` (root)
|
||||
- Frontend: Telegram Bot interface + optional PWA hosted on Cloudflare Pages
|
||||
- Frontend: Telegram Bot interface + Mini App served by the Cloudflare Worker's static assets (Pages superseded); optional PWA remains future
|
||||
- Transport: Telegram API (bot commands, inline queries, location sharing)
|
||||
- Services: OSRM routing with dynamic traffic weighting; RabbitMQ for event queueing; Resend for email
|
||||
- Data: Neon (Postgres + PostGIS) for event/route storage; Cloudflare R2 for map snapshot storage; Maptiler for basemap tiles
|
||||
- Routing: not wired yet — Valhalla + Photon is the validated target stack (OSRM references superseded)
|
||||
- Services: RabbitMQ for event queueing; Resend for email
|
||||
- Data: Neon (Postgres + PostGIS) for event/route storage; Cloudflare R2 for map snapshot storage; Tiles: OpenFreeMap today (`theme/basemap/liberty-rams.json`); Maptiler was the original idea
|
||||
- Observability: Prometheus + Grafana
|
||||
## Data Flow
|
||||
### Mockup Screen Flow (current)
|
||||
@@ -158,10 +161,10 @@ to.
|
||||
- Purpose: Single source of truth for colors, typography, spacing, component rules
|
||||
- Examples: `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md`, duplicated inline in each `code.html` `<head>` as `tailwind.config`
|
||||
- Pattern: YAML-front-matter spec → inline Tailwind config (manual duplication — a known inconsistency risk)
|
||||
- Each screen is one directory holding exactly `code.html` (the mockup) and `screen.png` (a visual capture)
|
||||
- Each of the four mockup screen directories holds exactly `code.html` (the mockup) and `screen.png` (a visual capture); `telemetry_rams/` holds only its `DESIGN.md`
|
||||
- Examples: all four screen directories under `stitch_minimalist_route_navigator/`
|
||||
- Event types appear consistently across docs and mockups: CONGESTION (traffic/orange), ACCIDENT/CRASH-BLOCK (red), POLICE RADAR / SPEED_TRAP (green)
|
||||
- Examples: `stitch_minimalist_route_navigator/telegram_live_traffic_feed/code.html` (`confirmDispatch('CONGESTION')`, `confirmDispatch('CRASH / BLOCK')`, `confirmDispatch('POLICE RADAR')`)
|
||||
- Examples: `stitch_minimalist_route_navigator/telegram_live_traffic_feed/code.html` (its actual handlers are `insertCommand('/traffic ')` / `insertCommand('/hazard ')` / `insertCommand('/status ')` / `insertCommand('/clear ')` plus a single `alert('Reroute broadcast loaded.')`; no `confirmDispatch` exists)
|
||||
## Entry Points
|
||||
- Location: `stitch_minimalist_route_navigator/<screen>/code.html` for each of the four screens
|
||||
- Triggers: opening the file directly in a browser (`file://` works — all dependencies are CDN-hosted)
|
||||
@@ -171,7 +174,7 @@ to.
|
||||
- **Global state:** None. Each mockup is fully isolated; no shared modules exist.
|
||||
- **Circular imports:** Not applicable (no modules).
|
||||
- **Token duplication:** The palette/typography/spacing defined in `telemetry_rams/DESIGN.md` is hand-copied into every mockup's inline `tailwind.config`. Any token change must be replicated N times until a real build pipeline exists.
|
||||
- **Windows Zone.Identifier files:** `*:Zone.Identifier` metadata files are scattered throughout and should be excluded from any future build/lint tooling.
|
||||
- **Windows Zone.Identifier files:** no `*:Zone.Identifier` files exist anymore; the `.gitignore` rule for them remains as hygiene.
|
||||
## Anti-Patterns (recommendations for the target codebase)
|
||||
### Per-file Tailwind config duplication
|
||||
### Inline onclick demo handlers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
> ⚠ SUPERSEDED FOR SPECIFICS — the centralized token set is the single source of truth for all numeric values (colors, sizes, spacing).
|
||||
> This document is kept for Rams design philosophy only. Consult the token package (see README.md) for specific value definitions.
|
||||
> Conflicting typography claims in this document (Helvetica/Inter, 5-color palette) are superseded by the token package.
|
||||
> The token package defines 55 color tokens (8 Rams brand colors + Material-style neutrals); palette and typography specifics in this document are superseded.
|
||||
|
||||
DESIGN.md
|
||||
|
||||
@@ -18,13 +18,13 @@ Generous whitespace
|
||||
|
||||
Typography
|
||||
|
||||
Helvetica Neue / Inter
|
||||
Geist (body) / Space Grotesk (headings and telemetry numbers) — per the token package; no Helvetica/Inter anywhere in the design system
|
||||
|
||||
Regular for body, Bold for emphasis
|
||||
|
||||
Sizes: 14px body, 18px headings, 24px titles
|
||||
Sizes come from the --type-* tokens: 14px body (body-md), 20px/32px headings (headline-md/lg), display-lg 48px — exact scale lives in theme/src/tokens.css
|
||||
|
||||
Line height: 1.4× font size
|
||||
Line heights are per-token absolute leading (ratios ~1.08–1.43), defined in theme/src/tokens.css
|
||||
|
||||
Color Palette
|
||||
|
||||
@@ -40,7 +40,7 @@ Matte Black (#1A1A1A)
|
||||
|
||||
Usage:
|
||||
|
||||
Background: Soft Beige
|
||||
Background: --color-background #FFF8F0 (warm white); Soft Beige #D9D2C6 remains a surface/accent token, not the background
|
||||
|
||||
Text: Matte Black
|
||||
|
||||
@@ -70,9 +70,9 @@ Map Snapshots: Grayscale tiles with accent overlays
|
||||
|
||||
Interaction Flows
|
||||
|
||||
Report Event: /report accident → bot confirms → stored in DB → broadcast nearby
|
||||
Report Event (target flow, not built): /report accident → bot confirms → stored in DB → broadcast nearby — no /report command exists yet (implemented commands: /start + /mula, /help, /privacy + /privasi)
|
||||
|
||||
Navigation: /navigate origin → destination → OSRM route → text directions + map snapshot
|
||||
Navigation (target flow, not built): /navigate origin → destination → OSRM route → text directions + map snapshot — no /navigate command and no routing engine wired in yet (routing-adjacent stack today: MapLibre GL + OpenFreeMap tiles); validated future stack is Valhalla + Photon
|
||||
|
||||
Feedback: Minimal confirmation messages
|
||||
|
||||
|
||||
+7
-6
@@ -27,8 +27,8 @@ This layout predates OpenCode V2's command discovery rules. In V2, nested paths
|
||||
|
||||
Verified against the running service via `opencode api get /api/command`: every command was
|
||||
registered as `gsd/gsd-*`, so typing `/gsd-*` found nothing — they "seemed" missing even though
|
||||
the files were all present (the installer's `gsd-opencode check` reported 755/755 files intact,
|
||||
which is exactly why the health check did not catch this).
|
||||
the files were all present (the installer's `gsd-opencode check` reported 755/843 files verified
|
||||
— 755 passing out of the 843-file manifest — which is exactly why the health check did not catch this).
|
||||
|
||||
**Fix:** flattened the command files:
|
||||
|
||||
@@ -63,15 +63,16 @@ Verified with `gsd-sdk query state.load` (succeeds) and `gsd-sdk --version`.
|
||||
|
||||
## Side notes
|
||||
|
||||
- `which gsd-opencode` also resolved to a **Windows npm shim** (`/mnt/c/Users/.../npm/gsd-opencode`)
|
||||
on the WSL PATH from a previous install on the Windows side. Harmless but confusing; the Linux
|
||||
binaries under `~/.nvm/.../bin` now take precedence for `gsd-sdk`.
|
||||
- At the time of this repair, `which gsd-opencode` also resolved to a **Windows npm shim**
|
||||
(under `/mnt/c/Users/.../npm/`) on the WSL PATH, from a previous install on the Windows side.
|
||||
Harmless but confusing; the Linux binaries under `~/.nvm/.../bin` now take precedence for
|
||||
`gsd-sdk`. That shim has since been removed and is no longer on the current PATH.
|
||||
- The installer's own health checker (`gsd-opencode check`) still expects the old nested
|
||||
`commands/gsd/` layout and will report the 88 flattened files as "missing" — this is a false
|
||||
positive caused by the checker not knowing about OpenCode V2's namespacing rules. The files
|
||||
are fine where OpenCode can find them.
|
||||
- After changing GSD model profiles, note the package requires a full OpenCode restart
|
||||
(agent→model mappings come from `opencode.json` and are read at startup).
|
||||
(agent→model mappings come from `opencode.jsonc` and are read at startup).
|
||||
|
||||
## Current state
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Map snapshots: Delivered as clean images with overlays for traffic events.
|
||||
|
||||
Navigation output: Text-based directions with optional minimalist map tiles.
|
||||
|
||||
Reports: Simple commands (/accident, /traffic, /hazard) with location sharing.
|
||||
Reports: Simple commands with location sharing — planned, not built. Implemented commands today are /start (+ /mula), /help, /privacy (+ /privasi); /accident, /traffic, /hazard are future report commands.
|
||||
|
||||
Feedback loop: Concise updates about nearby events.
|
||||
|
||||
@@ -42,7 +42,7 @@ Matte Black (#1A1A1A)
|
||||
|
||||
Typography:
|
||||
|
||||
Helvetica Neue / Inter
|
||||
Geist (body) / Space Grotesk (headings) — as implemented in the token package; Helvetica Neue / Inter was the original idea
|
||||
|
||||
Regular for body, Bold for emphasis
|
||||
|
||||
@@ -54,23 +54,23 @@ Accident = ⚠️, Traffic = 🚗, Hazard = ⛔
|
||||
|
||||
Architecture Overview
|
||||
|
||||
Frontend: Telegram Bot + optional PWA (Cloudflare Pages)
|
||||
Frontend: Telegram Bot + Mini App on Cloudflare Workers with static assets (Workers superseded Pages; PWA still optional/future)
|
||||
|
||||
Backend Transport: Telegram API
|
||||
Backend: grammY on Cloudflare Workers receiving updates at the POST /telegram/webhook endpoint
|
||||
|
||||
Database: Neon (Postgres + PostGIS)
|
||||
Database today: Cloudflare Workers KV (TTL-only chat sessions); Neon (Postgres + PostGIS) remains the target for event storage
|
||||
|
||||
Routing: OSRM with dynamic traffic weighting
|
||||
Routing: not wired yet — validated target stack is Valhalla + Photon (OSRM references in older docs are superseded)
|
||||
|
||||
Tiles: Maptiler
|
||||
Tiles today: OpenFreeMap (theme/basemap/liberty-rams.json); Maptiler was the original idea
|
||||
|
||||
Queue: RabbitMQ
|
||||
Queue: none yet — RabbitMQ is future/aspirational, deferred out of v1
|
||||
|
||||
Storage: Cloudflare R2
|
||||
Storage: none yet (no R2 binding in bot/wrangler.jsonc); R2 remains a future option for map snapshots
|
||||
|
||||
Monitoring: Prometheus + Grafana
|
||||
Monitoring: none yet — Prometheus + Grafana are future/aspirational
|
||||
|
||||
Email: Resend
|
||||
Email: none yet — Resend is future/aspirational
|
||||
|
||||
Gamification
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ curl -H "Authorization: Bearer <opaque>" http://localhost:8787/api/location
|
||||
|
||||
### Consuming the design tokens
|
||||
|
||||
`theme/src/tokens.css` is the single source of truth — 108 custom properties in the `@theme static` block, declared `static` so Tailwind v4's tree-shaking cannot drop tokens the MapLibre basemap derivation and plain-CSS consumers need.
|
||||
`theme/src/tokens.css` is the single source of truth — 108 custom properties in all — 82 in the `@theme static` block, declared `static` so Tailwind v4's tree-shaking cannot drop tokens the MapLibre basemap derivation and plain-CSS consumers need, plus the 26 `--type-*` typography tokens in a separate `:root { }` block.
|
||||
|
||||
```css
|
||||
/* raw token access — any stylesheet, no Tailwind required */
|
||||
|
||||
+2
-2
@@ -162,7 +162,7 @@ Run from the repo root (`npm run bot:<script>`) or inside `bot/`.
|
||||
| `npm run typecheck` | `tsc --noEmit` against `src/` with Workers types. |
|
||||
| `npm run dev` | `wrangler dev --port 8787 --local`. |
|
||||
| `npm run deploy` | `wrangler deploy` followed by `scripts/set-webhook.mjs`. |
|
||||
| `npm run commands` | `scripts/register-commands.mjs` — one-time `setMyCommands` in the default and `ms` scopes. |
|
||||
| `npm run commands` | `scripts/register-commands.mjs` — one-time `setMyCommands` in the default scope, plus a `language_code: "ms"` variant of that same default scope (both calls send `bot_command_scope: { type: "default" }`). |
|
||||
|
||||
`scripts/set-menu-button.mjs` is run directly (`node scripts/set-menu-button.mjs`) rather than through a package script; it points the bot's menu button at a `web_app` URL derived from `WEBHOOK_URL`'s origin.
|
||||
|
||||
@@ -229,7 +229,7 @@ Session TTL is deliberately tied to the 900 s initData freshness window: a sessi
|
||||
|
||||
### initData validation
|
||||
|
||||
`validateInitData(raw, botToken, opts?)` parses the Telegram-style querystring manually — values are decoded once and `+` stays literal, so `URLSearchParams` is deliberately not used. The data-check string excludes **only** `hash` (the `signature` field participates; excluding it broke real webviews), sorts by field name, and joins with newlines. The key is derived with `HMAC-SHA256(key="WebAppData", message=botToken)` and compared in constant time. `auth_date` older than `maxAgeSeconds` (default 900) is `stale`; `nowSeconds` is injectable so freshness boundaries are testable at 899/900/901 s.
|
||||
`validateInitData(raw, botToken, opts?)` parses the Telegram-style querystring manually — values are decoded once and `+` stays literal, so `URLSearchParams` is deliberately not used. The data-check string excludes **only** `hash` (the `signature` field participates; excluding it broke real webviews), sorts by field name, and joins with newlines. The key is derived with `HMAC-SHA256(key="WebAppData", message=botToken)` and compared in constant time. `auth_date` older than `maxAgeSeconds` (default 900) is `stale`; `nowSeconds` is injectable, and the suite covers the boundary at 899 s (ok) and 901 s (stale) — the 900 s boundary itself is not asserted by any case.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ style, and carries neither `installMockWebApp` nor `__ARAHARAH_MOCK__`.
|
||||
| `createBot(env, opts)` | `bot/src/bot/bot.ts` | grammY factory; wires `/start` + `/mula`, `/help`, `/privacy` + `/privasi`, and the location-fallback message handler. |
|
||||
| `requestLocationFallback(bot, chatId, locale)` | `bot/src/bot/handlers/location-fallback.ts` | Sends the `request_location` reply keyboard. Single caller: `handleLocationFallback`. |
|
||||
| `strings` / `pickLocale` | `bot/src/i18n/strings.ts` | The bot's entire copy surface (`en`, `ms`). `pickLocale` maps an `ms`/`ms-*` language code to Malay, everything else to English. |
|
||||
| `KvLike` | `bot/src/kv/*.ts` | Structural KV interface — lets tests pass a fake without depending on Workers types. |
|
||||
| `KvLike` | `bot/src/kv/*.ts` | Structural KV interface (module-private, not exported — the derived `MintSessionKv`/`LocationPinKv` are the public surface) — lets tests pass a fake without depending on Workers types. |
|
||||
|
||||
### `@araharah/web`
|
||||
|
||||
@@ -375,7 +375,7 @@ style, and carries neither `installMockWebApp` nor `__ARAHARAH_MOCK__`.
|
||||
| Abstraction | File | Description |
|
||||
| --- | --- | --- |
|
||||
| `@theme static` block | `theme/src/tokens.css` | 55 `--color-*` tokens plus spacing and typography. `static` forces every token into the compiled CSS so the package is consumable as plain custom properties. |
|
||||
| `theme/src/theme.css` | `theme/src/theme.css` | Entry stylesheet — `@import`s tailwindcss, tokens, components; declares eight self-hosted `@font-face` rules; defines the `--shell-bg` / `--shell-on-surface` theme slots. |
|
||||
| `theme/src/theme.css` | `theme/src/theme.css` | Entry stylesheet — `@import`s tailwindcss, tokens, components; declares seven self-hosted `@font-face` rules; defines the `--shell-bg` / `--shell-on-surface` theme slots. |
|
||||
| Component classes | `theme/src/components.css` | `.card`, `.btn-primary`, `.btn-secondary`, `.chip-*`, `.maneuver-*`, `.text-*`, `.toggle*`, `.input-field`, `.checkbox`. |
|
||||
| `build.mjs` | `theme/basemap/build.mjs` | Forks OpenFreeMap `liberty`, deriving every cartographic color from `tokens.css` via `desaturate(tokenHex, satMultiplier, lightnessPerRole)` with saturation clamped to `[0.06, 0.12]`. |
|
||||
| `liberty-rams.json` | `theme/basemap/liberty-rams.json` | The generated style. Accent guard fails the build if `#EB8008` / `#ED3F1C` / `#736B1E` leak into any layer. `building-3d` is removed (faux 3D). |
|
||||
@@ -430,8 +430,10 @@ Rationale for the boundaries:
|
||||
`kv/*.ts` take a structural `KvLike` so the suites pass fakes.
|
||||
- **`bot/src/i18n/strings.ts` is the single copy source.** The commands register
|
||||
script derives its menu text from the same `helpMenu` lines, and `bot/test/commands.test.mjs`
|
||||
gates that the `/privacy` copy still matches the `SessionClaim` fields in
|
||||
`sessions.ts`.
|
||||
gates that the `/privacy` copy still names the four never-stored facts (location, routes,
|
||||
trip history, profile) and keeps the ~15-minute TTL wording, in both locales. That gate
|
||||
does not assert against the `SessionClaim` fields in `sessions.ts` — keeping the copy in
|
||||
step with those field names is a convention, not a test.
|
||||
- **`web/src/location/` separates sources from the chain.** Each source is a small module
|
||||
that normalizes to `Fix` and answers `null` for denial; `chain.ts` owns ordering and
|
||||
knows nothing about any single source's API.
|
||||
|
||||
@@ -161,8 +161,8 @@ Key notes:
|
||||
|
||||
## Build and run scripts
|
||||
|
||||
Root `package.json` (workspaces: `theme`, `bot`, `web`; all packages are `"private": true`, ESM via
|
||||
`"type": "module"`):
|
||||
Root `package.json` (workspaces: `theme`, `bot`, `web`; the root, `bot`, and `web` packages set
|
||||
`"private": true` while `@araharah/theme` omits the field, ESM via `"type": "module"`):
|
||||
|
||||
| Command | Description |
|
||||
| --- | --- |
|
||||
|
||||
@@ -244,7 +244,7 @@ There is no Worker behind it, so `web/src/main.ts` falls back to the mock Telegr
|
||||
## Verify the setup
|
||||
|
||||
Run both suites and both typechecks — the runners are hand-rolled and zero-dependency
|
||||
(`node test/runner.mjs`), so no test framework needs installing:
|
||||
(`node bot/test/runner.mjs` and `node web/test/runner.mjs`), so no test framework needs installing:
|
||||
|
||||
```bash
|
||||
npm run bot:test # 6 suites in bot/test/*.test.mjs
|
||||
|
||||
+2
-2
@@ -283,7 +283,7 @@ Structural claims — "no menu surface exists", "copy lives only in `strings.ts`
|
||||
- `web/test/no-storage.test.mjs` GEO3 — no banned storage token appears in `web/src` code.
|
||||
- `web/test/session.test.mjs` SESS4, `web/test/theme.test.mjs` PX02 case 3 — same shape.
|
||||
|
||||
**Every one of these gates strips comments first.** The convention is two replaces, repeated verbatim across files:
|
||||
**The `web/` gates strip comments first; the three `bot/` gates do not.** `bot/test/commands.test.mjs` CS4 and `bot/test/privacy.test.mjs` PG1/PG2 read raw `readFileSync(file, "utf8")` and match lines directly, with no `replace(` anywhere in `bot/test`. In `web/`, the convention is two replaces, repeated verbatim across files:
|
||||
|
||||
```js
|
||||
const code = src
|
||||
@@ -291,7 +291,7 @@ const code = src
|
||||
.replace(/(^|[^:\\])\/\/[^\n]*/g, "$1"); // line comments (skips URLs)
|
||||
```
|
||||
|
||||
The reason is documented in `no-storage.test.mjs`: `web/src/mock/telegram-mock.ts` documents the storage ban *in prose*, and prose is not a call site.
|
||||
The reason the `web/` gates strip is documented in `no-storage.test.mjs`: `web/src/mock/telegram-mock.ts` documents the storage ban *in prose*, and prose is not a call site.
|
||||
|
||||
### 4. Build-artifact gates (assert on the shipped bundle)
|
||||
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@ npm install
|
||||
`@araharah/theme` is a workspace dependency (`"@araharah/theme": "*"`), so the theme package must be built before this one:
|
||||
|
||||
```bash
|
||||
npm run build -w theme # emits theme/dist/rams.css + the basemap style
|
||||
npm run build -w theme # emits theme/dist/rams.css (only)
|
||||
npm run basemap -w theme # separately emits theme/basemap/liberty-rams.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user