832 lines
85 KiB
Markdown
832 lines
85 KiB
Markdown
# Lumen — Implementation Contract
|
||
|
||
- **Phase:** Implementation Preparation — handoff from Architecture Validation to coding agent
|
||
- **Date:** 2026-08-30
|
||
- **Validated architecture freeze:** `ARCHITECTURE-VALIDATION.md:1` (SPIKE-01…08 reconciled)
|
||
- **Source of truth chain:** `DISCOVERY.md` → `ARCHITECTURE-DESIGN.md` + `ARCHITECTURE-DECISIONS.md` + `ASSUMPTIONS-AND-OPEN-QUESTIONS.md` → `ARCHITECTURE-VALIDATION.md` → this contract
|
||
- **Scope guard:** No application code, no `package.json`, no `node_modules`, no PWA/DB/network implementation exists when this contract was written. A future coding agent must not reinterpret the architecture to simplify it.
|
||
|
||
---
|
||
|
||
# 0. How to read this contract
|
||
|
||
Every rule below traces to a validated ADR or spike. Where the contract says "normative" the rule was proven or hardened by a spike. Where it says "provisional" the architecture allows implementation to proceed but production still requires the physical-device or content checks listed in `ARCHITECTURE-VALIDATION.md:4` and `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:9`.
|
||
|
||
If an implementation conflict is found, stop and document it — do not silently change the architecture (`RULES FOR FUTURE AI CODING AGENTS`).
|
||
|
||
---
|
||
|
||
# 1. V1 scope
|
||
|
||
V1 ships an **offline-first, mobile-first Progressive Web App** for 300–500 attendees (`DISCOVERY.md:103`).
|
||
|
||
Included:
|
||
|
||
- Four destinations: **EMERGENCY · SCHEDULE · MAP · FESTIVAL** (`DISCOVERY.md:145` R-N1, `ARCHITECTURE-DESIGN.md:207`)
|
||
- Emergency 3-tier model (floor + dataset section + reserved T3) (`ARCHITECTURE-DECISIONS.md:272`, `SPIKE-06:12`)
|
||
- Schedule: browse, Now/Next, My Schedule (favorites), filters, search — all offline (`DISCOVERY.md:162` R-S1…S5)
|
||
- Map: raster WebP ≤2 levels + DOM POI overlay + Facilities list — all offline, no GPS in V1 (`ARCHITECTURE-DECISIONS.md:315`, `SPIKE-03:58`)
|
||
- Festival info: structured blocks — all offline (`DISCOVERY.md:182` R-F1)
|
||
- Pull-only sync on open / online hint / manual (`ARCHITECTURE-DECISIONS.md:406`)
|
||
- Honest readiness states: READY/PARTIAL/NOT_READY/RECOVERY/BASELINE_ONLY/FAILED (`SPIKE-05:60`)
|
||
- Bootstrap L0–L2 preparation (`SPIKE-07:31`)
|
||
- Time model: UTC + festival IANA zone + ClockService (`ARCHITECTURE-DECISIONS.md:362`)
|
||
|
||
Trace: ADR-001…ADR-010, `ARCHITECTURE-DESIGN.md:25` executive summary.
|
||
|
||
# 2. Explicitly excluded functionality (V1)
|
||
|
||
Do not build these in V1 unless a later ADR explicitly approves them:
|
||
|
||
- Mesh networking in any form (seam only, `DISCOVERY.md:270` NR-1, ADR-012) — see §27
|
||
- Native apps / App Store wrapping (NR-2, NR-3)
|
||
- User accounts / cross-device sync of favorites (NR-4, NR-5; `DISCOVERY.md:240` constraints 11–12)
|
||
- Real-time collaboration/social (NR-6), payments (NR-9), UGC (NR-10)
|
||
- GPS-required features / blue dot, online tiles as core map (NR-7, NR-8, ADR-008) — hook preserved only
|
||
- Push as critical channel (NR-13, `ARCHITECTURE-DECISIONS.md:406` pull-only)
|
||
- Background Sync / Periodic Sync (`DISCOVERY.md:433` PW-4, `ARCHITECTURE-DESIGN.md:78` C-19)
|
||
- Admin/CMS UI (NR-14; pipeline is file-based per ADR-014), private analytics (NR-15)
|
||
- Multi-language i18n (NR-11, DD-6): keep strings in data but English-only
|
||
|
||
Violations fail the Definition of Done `§40`.
|
||
|
||
# 3. Technology stack (normative)
|
||
|
||
| Concern | Selection | Trace |
|
||
|---|---|---|
|
||
| Language | TypeScript strict | ADR-003 (`ARCHITECTURE-DECISIONS.md:86`) |
|
||
| UI framework | None — vanilla TS + tiny store + history router + escaping renderers | ADR-003; fallback trigger → Preact only if store→render proves unworkable (`ARCHITECTURE-DECISIONS.md:86`) |
|
||
| Build | Single-bundle bundler that emits hashed immutable assets + precache manifest (esbuild/vite-class) — implementation pick | `ARCHITECTURE-DESIGN.md:126` |
|
||
| Service worker | Hand-written, tiny (~precache + navigation fallback only) — no Workbox | ADR-002 (`ARCHITECTURE-DESIGN.md:14`) |
|
||
| Structured storage | IndexedDB via thin internal promise wrapper; Cache Storage for shell only; localStorage guarded non-load-bearing | ADR-004 (`ARCHITECTURE-DECISIONS.md:129`, `SPIKE-01:140` P1–P8) |
|
||
| Crypto | WebCrypto SHA-256 + bundled audited pure-JS Ed25519 verifier (WebCrypto Ed25519 not baseline on iOS 16.4) | ADR-013 (`ARCHITECTURE-DESIGN.md:14`), TQ-9 — YELLOW pending audit `ARCHITECTURE-VALIDATION.md:522` |
|
||
| Map | WebP raster + DOM buttons + CSS-transform pan/zoom + Facilities list | ADR-008 + SPIKE-03 |
|
||
| Hosting | Static HTTPS CDN, single origin | ADR-014 (`ARCHITECTURE-DESIGN.md:104`), `DISCOVERY.md:261` C-20 |
|
||
| Push/analytics | None in V1 | ADR-010 / ADR-013 |
|
||
|
||
Forbidden without ADR amendment: React/Angular/Vue, Workbox, Dexie/idb, SQLite-WASM, any map SDK, any mesh lib, any auth system, any dynamic backend (`ARCHITECTURE-DESIGN.md:144`). Target: only the audited Ed25519 verifier beyond stdlib (`ARCHITECTURE-DESIGN.md:930` rule 3).
|
||
|
||
# 4. Project structure (recommended)
|
||
|
||
Structure follows the layering in `ARCHITECTURE-DESIGN.md:148` §6 and the boundaries B-1…B-7 `ARCHITECTURE-DESIGN.md:195`. A coding agent may rename leaves but must keep the boundaries and allowed/forbidden sets.
|
||
|
||
```
|
||
src/
|
||
platform/ # Cross-cutting low-level adapters
|
||
idb/ # Thin promise wrapper over raw IDB (ADR-004, SPIKE-01 P1–P8) — no business logic
|
||
cache/ # Cache Storage adapter for shell
|
||
sw/ # SW bridge (page side, message handling)
|
||
storage/ # Re-exports platform adapters with typed contracts — no UI, no fetch
|
||
data/ # DatasetStore + UserStore — read APIs + writes (typed, transactional)
|
||
sync/ # SyncService orchestration (check→stage→verify→activate) + staging state
|
||
transport/ # Transport interface + HttpTransport (V1 only)
|
||
verifier/ # SHA-256 + Ed25519, quarantine, budgets, schema checks
|
||
domain/ # Domain services, pure logic over data layer — no persistence, no network
|
||
emergency/ # Resolution: floor vs dataset section, provenance, hardening F-1
|
||
schedule/ # Queries: Now/Next, filters, search, conflict detection
|
||
map/ # POI interpretation, level math, Facilities list queries
|
||
festival/ # Info blocks
|
||
readiness/ # ReadinessService predicate C1–C8 + state taxonomy (SPIKE-05)
|
||
clock/ # ClockService (skew + monotonic + sanity F-3)
|
||
favorites/ # FavoritesService over UserStore (B-6)
|
||
ui/ # Views + presentation stores + router + escaping renderer
|
||
components/ # Shared primitives (safe DOM helpers, status chip, etc.)
|
||
views/ # emergency/ / schedule/ / map/ / festival/ / status/
|
||
router/ # history-API router (one index.html, offline-safe deep links)
|
||
render/ # Structured-node → safe DOM (C-22, no innerHTML of raw strings)
|
||
app/ # App bootstrap, boot sequence (light verify → readiness → render), composition root
|
||
emergency-baseline/ # Compiled-in floor JSON (generated, immutable per build)
|
||
assets/ # Hashed shell assets (generated)
|
||
public/
|
||
index.html # Cached shell entry (no-cache per ARCH §24)
|
||
manifest.webmanifest
|
||
fallback.html # Ring-0 static fallback (emergency text, no JS needed)
|
||
sw.js # Hand-written SW (generated precache list injected at build)
|
||
content/ # Not shipped — authoritative source for pipeline (ADR-014)
|
||
emergency/ schedule/ map/ info/ assets/
|
||
pipeline/ # Validate→build→hash→sign→upload→smoke (separate from app)
|
||
tests/
|
||
unit/ # Verifier, ClockService, readiness predicate, A/B transitions, escaping
|
||
integration/ # Full update lifecycle with mocked transport + fault injection (9 stages)
|
||
e2e/ # Headless/offline harness where useful
|
||
device-matrix/ # Scripted manual protocols per ARCHITECTURE-VALIDATION.md §4
|
||
```
|
||
|
||
### Directory responsibilities
|
||
|
||
| Directory | Owns | May import | Must never import |
|
||
|---|---|---|---|
|
||
| `platform/idb`, `platform/cache` | Transaction discipline, durability, quota | Browser APIs | Any domain/ui/sync logic |
|
||
| `data/` | Typed local truth (read/write contracts) | `platform/` | `sync/`, `ui/` |
|
||
| `sync/` | Orchestration, staging, version logic, activation txn | `data/`, `sync/verifier`, `sync/transport` | `domain/`, `ui/` internals |
|
||
| `sync/verifier` | Authenticity/integrity verdicts + budgets + schema | `platform/` (hash), bundled verifier | Rollback/policy (lives in `sync/`) |
|
||
| `sync/transport` | Bytes in/out, availability | `fetch` (or equivalent) | `data/`, `domain/`, `ui/` |
|
||
| `domain/*` | Feature logic over `data/` read APIs + `clock/` | `data/` read, `clock/` | `platform/`, `sync/`, `network` |
|
||
| `ui/` | Rendering, routing, user intent, readiness display | `domain/`, `readiness/` | `platform/`, `storage/`, `sync/transport`, raw `fetch`, `indexedDB` |
|
||
| `app/` | Boot sequence, system-meta read, compatibility check (§18.5), composition | Everything via its public APIs | — |
|
||
| `public/sw.js` | Shell-cache precache + navigation fallback only | `caches` | Datasets, sync, IDB |
|
||
|
||
This encodes `ARCHITECTURE-DESIGN.md:704` responsibilities and the mesh anti-leak rule (invariant 12).
|
||
|
||
# 5. Architectural layers (validity: `ARCHITECTURE-DESIGN.md:148`)
|
||
|
||
Layers are UI → Domain → Data → Sync → Transport → Platform + SW (separate context). The layering diagram is `ARCHITECTURE-DESIGN.md:148`. Implementation must not collapse layers even if it seems convenient.
|
||
|
||
# 6. Dependency rules (normative, per `ARCHITECTURE-DESIGN.md:195` B-1…B-7 + `ARCHITECTURE-DESIGN.md:930` hard rules)
|
||
|
||
| ID | Rule |
|
||
|---|---|
|
||
| B-1 | Views never touch `indexedDB`, `fetch`, `caches`, `localStorage`, or SW internals. |
|
||
| B-2 | Domain services read only through `data/` read APIs (`DatasetStore`/`UserStore`). |
|
||
| B-3 | Feature modules never know which transport delivered data; transports never know content meaning (invariant: mesh concerns never leak). |
|
||
| B-4 | Only `sync/verifier` decides authenticity/integrity; Sync orchestrates, never validates alone. |
|
||
| B-5 | SW never writes dataset data; staging lives in page context (C-21, P8, `ARCHITECTURE-DESIGN.md:83`). |
|
||
| B-6 | User state (`lumen-user`) is never touched by dataset updates/rollback/GC (`SPIKE-02:69` X3). |
|
||
| B-7 | No festival content rendered via `innerHTML` of raw strings (C-22, `ARCHITECTURE-DESIGN.md:195`). |
|
||
|
||
Hard rules carried into implementation `ARCHITECTURE-DESIGN.md:928`: no feature→persistence/transport imports; no raw-HTML ingestion; no runtime dep without ADR amendment; no background work (C-19); every readiness state reachable in tests; budgets CI-enforced; no analytics/third-party/permissions.
|
||
|
||
# 7. UI / application boundaries
|
||
|
||
- Single-page app, one cached `index.html`, history-API router (`ARCHITECTURE-DESIGN.md:207`).
|
||
- Persistent bottom nav EMERGENCY · SCHEDULE · MAP · FESTIVAL (Emergency first, visually dominant) — satisfies R-N3 one-tap emergency (`ARCHITECTURE-DESIGN.md:207`).
|
||
- Persistent status chip (READY/PARTIAL/NOT_READY/RECOVERY/BASELINE_ONLY/FAILED) in header → Status screen per `SPIKE-05:60`.
|
||
- Views → VM/presentational stores → `domain/*` → `data/` read APIs. Never direct persistence.
|
||
|
||
# 8. Local storage rules
|
||
|
||
- Storage is best-effort, silent eviction, per-origin all-or-nothing (`SPIKE-01:97`, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:43` B-03). Detection—not prevention—is the architecture: boot light verify (§20) + RECOVERY + baseline.
|
||
- Do not assume `navigator.storage.persist()` is granted (`ARCHITECTURE-DESIGN.md:285`); request it once after READY (P6) but never rely on grant.
|
||
- Do not handle storage pressure with retry loops; pre-check free space via `storage.estimate()` (P4).
|
||
- Do not keep long transactions; one short txn per file (P1) — no `await` of non-IDB work inside a txn.
|
||
|
||
# 9. IndexedDB schema requirements (normative, ADR-004 + SPIKE-01/02)
|
||
|
||
| Database | Stores | Keys / contents | Lifecycle |
|
||
|---|---|---|---|
|
||
| `lumen-system` | `meta` (single object store) | Active slot pointer (`A`/`B`), `activeEdition`, `activePackageVersion`, verification record (what/when/which, fingerprint, manifestSha256), `appVersionAtActivation`, `readbackPending`, clock skew cache pointer | Single source of truth for active/readback metadata; rewritten atomically on activation (P2); no per-file staging progress |
|
||
| `lumen-slot-a` | `files`, `assets`, `staging` | `files`: section docs keyed by section id (`emergency`/`schedule`/`map`/`info`/`assets.json`); `assets`: Blobs keyed by asset id (`map-base-*`, icons); `staging`: authoritative slot-local journal | Inactive slot is staging target; file/asset + journal commit in one transaction; active slot is truth; GC per §13 |
|
||
| `lumen-slot-b` | `files`, `assets`, `staging` | Same as above | Same |
|
||
| `lumen-user` | `favorites` (by stable event id), `prefs`, `diag` (scrubbed ring buffer) | Favorites `id → { addedAt }`; `prefs` (theme, clock, `displayMode` flags); `diag` (quarantined versions, recent errors) | Never GC'd by dataset logic (B-6); own idempotent migrations (package schema separate, `SPIKE-04:210`) |
|
||
| `lumen-shell-v<build>` (Cache) | Cache Storage | Precached shell (hashed JS/CSS/icons, `index.html`, `fallback.html`) | Versioned per build; old caches deleted on SW `activate` |
|
||
| (localStorage) | Guarded flags only | Coach dismissed, etc. — `try/catch` | Non-load-bearing |
|
||
|
||
Notes: assets stored as Blobs in the slot DB to keep rollback in one failure domain (`ARCHITECTURE-DESIGN.md:275`, `SPIKE-01:140`). Dataset JSON parsed once per section per session; no persistent derived indexes.
|
||
|
||
# 10. A/B dataset rules (normative, ADR-006 + SPIKE-02)
|
||
|
||
Invariant: at every observable moment `lumen-system.meta.activeSlot` points at either the old verified dataset or the new verified dataset — there is no third state (`ARCHITECTURE-DESIGN.md:659`).
|
||
|
||
- Two dataset slot databases; updates stage exclusively into the **inactive** slot (`ARCHITECTURE-DESIGN.md:659`).
|
||
- Per-file staging is one short txn: `bytes + progress record` together (P1, F-1 `SPIKE-02:89`); no txn spans non-IDB await.
|
||
- Staging progress is authoritative in the target slot's `staging` journal; resume is keyed on committed slot-local progress; staging older than 7 days is discarded (`ARCHITECTURE-DESIGN.md:669`).
|
||
- Beginning a new staging run wipes the inactive slot — rollback depth is exactly **1** (three-slot rejected for footprint, `SPIKE-02:100` F-4). Invariant holds (valid dataset always active); only undo depth is bounded.
|
||
- Downloads run in **page context**, never SW (C-21, P8).
|
||
|
||
Violations (e.g., staging into active slot or cross-DB txn) contradict `SPIKE-02:73` ordering proof and fail `§40` acceptance.
|
||
|
||
# 11. Dataset validation rules (normative, ADR-005/013 + SPIKE-02/04)
|
||
|
||
Cheap + authoritative checks first; untrusted data never forces work.
|
||
|
||
1. Fetch `latest.json` → monotonic version comparison: reject `≤ active` from any network source (downgrade protection, `ARCHITECTURE-DESIGN.md:349`). Purely informational `generatedAt` never gates.
|
||
2. Fetch candidate `manifest.json`.
|
||
3. **Signature** over exact manifest bytes (Ed25519, `signature.json` `manifestSha256` + `publicKeyFingerprint`) — verified against embedded key set (fingerprinted, rotation-capable, `ARCHITECTURE-DESIGN.md:358`) — **before parsing or downloading anything else** (`SPIKE-02:29` step 3). Fail → quarantine version, diag entry, keep active (S07 PASS).
|
||
4. Parse manifest → **compatibility** (`appCompatibility.minAppVersion/maxAppVersion` ∧ `schemaVersion ∈ shell.supportedRange` — `SPIKE-04:22`, `ARCHITECTURE-DESIGN.md:688`) and **budgets** (per-file ≤6 MB, totals, image caps) — before downloading files. Fail → user-visible "please update app" if app too old (`ARCHITECTURE-DESIGN.md:668`), else quarantine.
|
||
5. **Stage files** into inactive slot; per-file SHA-256 + size check on each (`SPIKE-02:29` step 5).
|
||
6. **Schema validation** of staged sections (strict on required, allow unknown forward-compat fields, `SPIKE-04:189` gate 1).
|
||
7. Full verification of all staged files → only then eligible to activate.
|
||
|
||
No step may be skipped, reordered, or weakened without ADR amendment. Every rejection is quarantined so a known-bad version is not re-fetched until `latest.json` advances (`ARCHITECTURE-DESIGN.md:675`).
|
||
|
||
# 12. Dataset activation rules (normative, ADR-006 + SPIKE-02 F-2/F-3)
|
||
|
||
- Activation is **exactly one transaction** on `lumen-system` (P2) that **atomically** flips `activeSlot` + `activePackageVersion` + **verification record** (F-2 `SPIKE-02:89`) and sets `readbackPending`. No cross-database transaction is attempted — single-DB atomicity suffices because inactive slot is already complete before flip (`SPIKE-02:73`).
|
||
- On txn failure: retry once → keep old → diag (`ARCHITECTURE-DESIGN.md:672`).
|
||
- After commit: **readback spot-check** synchronously if possible, otherwise left pending and checked at next boot (`SPIKE-02:97` F-3). Spot-check reads the slot the pointer now points at.
|
||
|
||
A partially staged, un-verified, quarantined, or incompatible dataset can never be activated (invariants §3 #4–#5).
|
||
|
||
# 13. Dataset rollback rules (normative, `ARCHITECTURE-DESIGN.md:677` §18.3)
|
||
|
||
- **Automatic:** post-activation readback fails, or next-boot light verify of active slot fails while the other slot is still complete → flip back to previous slot (`SPIKE-02:50` S14, X1). If both slots lack a complete dataset → RECOVERY + baseline (`SPIKE-02:69` X2) — favorites in `lumen-user` remain.
|
||
- **User-initiated:** Status screen "Restore previous version" while previous slot exists (until next staging or GC).
|
||
- **Organizer-initiated (canonical):** republish old content at a **new higher packageVersion** (runbook, `ARCHITECTURE-DESIGN.md:682`) — clients accept it as a normal monotonic update. Local rollback is depth-1 only (F-4); server-side re-publish is the general rollback.
|
||
|
||
GC: previous slot retained until next staging needs it or `N≥3` successful boots AND storage pressure (`ARCHITECTURE-DESIGN.md:685`). User data never GC'd.
|
||
|
||
# 14. Emergency baseline rules (normative, ADR-007 + SPIKE-06)
|
||
|
||
Three tiers — no tier may be omitted or merged silently:
|
||
|
||
| Tier | Content | Mutability | Delivery | Survives |
|
||
|---|---|---|---|---|
|
||
| T1 Floor | Emergency number + dial, address/coordinates, security contact, first-aid/AED summaries, muster/exits summaries, core procedures (`medical`/`weather`/`fire`/`lost`) — ≤16 KB, life-safety minimum only | Immutable per shell build, compiled from the **same emergency source sheet** that produces the dataset section (no drift, `ARCHITECTURE-DESIGN.md:345`) | Shell bytes | **Everything** — eviction, failed update, incompatible dataset, BASELINE_ONLY |
|
||
| T2 Dataset section | Full detail (all emergency POIs with map links, full procedure text, per-role contacts, `contentVersion`/`updatedAt`, `emergencySchemaVersion`) | Updateable via signed dataset publish | Package `emergency.json` | Last-good dataset |
|
||
| T3 Notices (reserved) | `{id,severity,title,body,publishedAtUtc,expiresAtUtc,signature}` — display-only, additive | Ephemeral, signed, expiring | Package or transport seam — **deferred in V1** | Only if delivered |
|
||
|
||
Resolution `SPIKE-06:60`: prefer highest verified compatible tier with defensive merge (floor-only fields rendered even when section omits them); provenance always labeled (`BASELINE v<floor>` vs `FESTIVAL DATA v<package> · <generatedAt>`). Floor renderer is **forward-tolerant** (ignores unknown fields, never throws) and reachable with **zero IDB access** (F-1 `SPIKE-06:75`) — this is what makes invariants §41 #1–#2 hold.
|
||
|
||
Hard limits: 16 KB cap CI-enforced; floor version stamp validated (`ARCHITECTURE-DESIGN.md:367`). No `localStorage` copy — dies with eviction (`SPIKE-06:102`).
|
||
|
||
# 15. Festival Data Package rules (normative, ADR-005 + SPIKE-04)
|
||
|
||
An immutable versioned directory on the CDN:
|
||
|
||
```
|
||
Package /editions/<edition>/packages/<packageVersion>/
|
||
manifest.json → inventory, hashes, sizes, compatibility, festival metadata
|
||
signature.json → Ed25519 over sha256(exact manifest bytes) + publicKeyFingerprint
|
||
emergency.json → emergency/1 (+ independent emergencySchemaVersion/contentVersion)
|
||
schedule.json → schedule/1 (stages, artists, events with stable id, dayKey)
|
||
map.json → map/1 (levels + POIs normalized x/y + categories)
|
||
info.json → info/1 (ordered blocks of structured nodes)
|
||
assets.json → inventory {id,file,sha256,bytes,kind,role}
|
||
assets/... → WebP/PNG binaries, each listed in assets.json
|
||
/editions/<edition>/latest.json → mutable pointer {edition,packageVersion,manifestUrl,generatedAt}
|
||
```
|
||
|
||
Normative schema fragments: `SPIKE-04:79` (manifest, signature, emergency/schedule/map/assets). Rules:
|
||
|
||
- Format `lumen.package/1` (`SPIKE-04:79`).
|
||
- `packageVersion` strictly monotonic per edition; never accept `≤ active` from network (`ARCHITECTURE-DESIGN.md:349`). Organizer rollback = new higher version.
|
||
- `schemaVersion` envelope with ordering rule `ARCHITECTURE-DESIGN.md:690` C-23: schemas ship in shell range `{1,2}` first, datasets follow, freeze 7 days before festival.
|
||
- `sections[*].required` bool (default true) gates readiness (`SPIKE-04:64`, `SPIKE-05:47` F-2).
|
||
- **No expiration** for datasets/sections (`SPIKE-04:50` F-2) — only future announcements/notices may carry `expiresAtUtc` and expiry hides the notice, never critical data (time-independence, `SPIKE-05:47`).
|
||
- No binary sections (F-5 `SPIKE-04:203`); per-file ≤6 MB; total ≤40 MB target / 50 MB ceiling `ARCHITECTURE-DESIGN.md:363` enforced by pipeline.
|
||
- Pipeline gates (publisher side, all blocking): 1) schema + unknown-fields forward-compat, 2) stable IDs (removals require `status: cancelled`), 3) time sanity (`dayKey` matches festival-zone date, no zero-length, within window ±1d), 4) budgets/dimensions, 5) hash→sign→upload→flip→smoke-fetch (`SPIKE-04:189`).
|
||
|
||
Identity vs location: `edition + packageVersion` and `sha256(manifest)` are identity; URL is location (`SPIKE-04:15`).
|
||
|
||
# 16. Service worker responsibilities (normative, `ARCHITECTURE-DESIGN.md:241`)
|
||
|
||
Scope deliberately tiny (`ARCHITECTURE-DESIGN.md:243`):
|
||
|
||
- `install`: precache versioned shell list (generated at build, hashed `index.html` + JS/CSS + icons).
|
||
- `activate`: delete previous shell caches.
|
||
- `fetch`: navigation → cache-first against shell cache, fallback to network to re-fill; if both fail, serve built-in static fallback page (emergency text, Ring-0). Hashed `/assets/<hash>.*` → cache-first. **Package endpoints (`/latest.json`, `/editions/**`) are NOT intercepted — pass through to network so app-layer sync controls staging (`ARCHITECTURE-DESIGN.md:243` C-21).**
|
||
- `message`: minimal `SKIP_WAITING` handling only.
|
||
|
||
Shell update policy `ARCHITECTURE-DESIGN.md:255`: new SW installs under new cache name; serves old session; activation on **next full app start** (no mid-session `clients.claim`), except explicit user "Restart to update" on Status screen. Then §18.5 compatibility check (`ARCHITECTURE-DESIGN.md:688`) runs before data screens.
|
||
|
||
Every SW operation is idempotent/re-runnable; page never depends on SW in-memory state (`ARCHITECTURE-DESIGN.md:261`).
|
||
|
||
# 17. Service worker non-responsibilities (normative)
|
||
|
||
The SW **must not** do any of: dataset download/staging, background sync, retry logic, announcements, anything stateful beyond caches (`ARCHITECTURE-DESIGN.md:253`), any IDB access, any verification. Dataset data must never live in SW state — P8 `SPIKE-01:140` + C-21 `ARCHITECTURE-DESIGN.md:83`. A coding agent that moves staging into the SW violates invariants §41 #3–#4.
|
||
|
||
# 18. Cache Storage responsibilities
|
||
|
||
- Owns **shell only**: `lumen-shell-v<build>` versioned per build, enumerated in readiness predicate C1 (`SPIKE-05:28`).
|
||
- Immutable hashed assets cached with long TTL; `index.html` with `no-cache` (revalidate) per deploy layout `ARCHITECTURE-DESIGN.md:807`.
|
||
- Old caches purged on SW activate — no manual GC.
|
||
|
||
# 19. IndexedDB responsibilities
|
||
|
||
- Owns **datasets + user state** as in §9; plus `lumen-system.meta` as the atomic activation/verification record.
|
||
- Enforces P1–P5: single-file atomic, single-txn flip + verification, 6 MB cap, pre-stage free-space check, wrapped Quota handling.
|
||
- Boot light verify C1…C5 is the detection mechanism for eviction/corruption; heavy hashing lives only in full verification (after staging, user-initiated "Check my data", after IDB error) per `SPIKE-05:82` cadence.
|
||
|
||
# 20. Offline Ready state machine (normative, SPIKE-05 + §12)
|
||
|
||
## 20.1 Predicate (time-independent — `SPIKE-05:47` F-1)
|
||
|
||
```
|
||
OFFLINE_READY ⇔
|
||
C1 shell_valid every precache entry present in current shell cache
|
||
∧ C2 dataset_present active slot exists for active edition
|
||
∧ C3 authenticity manifest signature verified against embedded key set; recorded verification matches active packageVersion
|
||
∧ C4 integrity manifest-listed files present, sizes match; hashes verified at staging (recorded), spot-check at boot
|
||
∧ C5 schema_compatible package schemaVersion ∈ shell.supportedRange ∧ appCompatibility satisfied
|
||
∧ C6 required_sections sections with required=true present+parseable {emergency,schedule,map,info,assets.json}
|
||
∧ C7 required_assets assets referenced by required sections present (size at boot, hash at staging)
|
||
∧ C8 emergency_floor embedded baseline present (asserted; defense against broken build dropping floor)
|
||
```
|
||
|
||
Rules: predicate never consults clocks (`SPIKE-08` parity); optional sections (`required=false`) never gate READY (`SPIKE-05:47` F-2); evidence-based via stored verification record (`SPIKE-02:89` F-2) — missing/mismatched record → no READY until full re-verify; all-or-nothing for READY.
|
||
|
||
## 20.2 States (six-state taxonomy, `SPIKE-05:60`)
|
||
|
||
| State | Definition | Chip | Action offered |
|
||
|---|---|---|---|
|
||
| READY | C1–C8 all true | Green "OFFLINE READY ✓" | None (status detail on tap) |
|
||
| PARTIAL(list) | Shell valid; required sections/assets missing (none corrupt) — e.g., prep interrupted | Amber | "Continue setup" (resumable staging) |
|
||
| NOT_READY | Shell valid; no staged/active dataset (fresh install) | Neutral "Get festival data" | Preparation flow |
|
||
| RECOVERY(reason=`missing`/`corrupt`/`incompatible`) | Previously verified now fails (eviction, corruption, app/dataset skew) | Red | "Restore festival data" (re-prep; needs online) — floor works meanwhile |
|
||
| BASELINE_ONLY | Storage entirely unavailable (private mode, blocked "website data", quota 0) | Distinct notice | Install/normal-mode/free-space guidance |
|
||
| FAILED(op) | Activation txn repeatedly failed or IDB error — distinct from RECOVERY (data gone vs error) | Error screen | Retry; re-prep; diagnostics share |
|
||
|
||
Edge cases adjudicated `SPIKE-05:90`: eviction→RECOVERY, file-gone→RECOVERY, record-missing→PARTIAL→full re-verify, incompatible→RECOVERY(incompatible), wrong clock→READY unaffected (warning separate), optional missing→READY, private→BASELINE_ONLY, activation fail twice→FAILED.
|
||
|
||
## 20.3 Cadence and budgets
|
||
|
||
| When | Check | Cost target |
|
||
|---|---|---|
|
||
| Every boot | Light check C1+C2+C4-light+C5 (no hashing) | ≤~150 ms `SPIKE-05:82` |
|
||
| After staging | Full C3–C7 (all hashes + schema) | Seconds; once per update |
|
||
| Activation | Recorded with flip; readback spot-check (pending flag) | ms |
|
||
| User "Check my data" | Full re-verification with progress | Seconds |
|
||
| After any IDB error | Full re-verify active slot; quarantine on failure | Seconds |
|
||
|
||
## 20.4 Preparation mapping (SPIKE-07)
|
||
|
||
L0 (shell+floor) ↔ NOT_READY/BASELINE_ONLY; L1 (L0 + emergency + schedule) ↔ PARTIAL ("core ready"); L2 (L1 + map+info+assets) ↔ READY (`SPIKE-07:31`, `SPIKE-05:108` cross-check). Download order `emergency→schedule→info→map-base→assets` maximises achievable level when interrupted (`SPIKE-07:39`).
|
||
|
||
## 20.5 UX contract
|
||
|
||
Status chip visible on every screen; Status screen shows per-section checklist, dataset version, `generatedAt`/`fetchedAt`, usage, plus "Check my data" / "Get festival data" / "Restore previous version" (`ARCHITECTURE-DESIGN.md:424`). App never shows READY unless predicate holds.
|
||
|
||
# 21. Schedule / time rules (normative, ADR-009 + SPIKE-08)
|
||
|
||
- **Storage:** `startUtc`/`endUtc` UTC epoch ms on every event (`DISCOVERY.md:297` A-10, ADR-009). Includes `dayKey` precomputed at publish as festival-zone calendar date (`SPIKE-08:43` T2).
|
||
- **Zone:** manifest carries `festival.timezone` (IANA) + `festival.startUtc/endUtc` (`SPIKE-04:79`).
|
||
- **Rendering:** `Intl.DateTimeFormat` with `timeZone = festival zone` by default; toggle to device zone. **Only** explicit-zone Intl is allowed (clarification F-4 `SPIKE-08:148`); never `Date.toLocaleString()` without options, manual offsets, or wall-clock strings. `dayKey` groups days; no client day math.
|
||
- **Queries (§14.2 `ARCHITECTURE-DESIGN.md:508`):**
|
||
- `Now: startUtc ≤ now < endUtc`; `Up Next: startUtc > now` (next-N sorted). Overlaps shown as multiple `now` (`SPIKE-08:43` T7).
|
||
- Favorites join via stable event `id` (contractual — §24); `status: scheduled|moved|cancelled` rendering.
|
||
- Filters/search: naive scan over ≤~1k events; no index lib unless proven slow (`DISCOVERY.md:391` TQ-10 decision documented, `ARCHITECTURE-DESIGN.md:508`).
|
||
- Change markers when `status=moved|cancelled` or manifest change notes.
|
||
- **ClockService (`ARCHITECTURE-DESIGN.md:523`, `SPIKE-08:26`):**
|
||
|
||
```
|
||
now() = deviceClock + skew if persisted offset exists else deviceClock
|
||
skew = serverNow − deviceNow captured from any sync HTTP Date header, persisted {skew,capturedAtDevice,capturedAtMono,source}
|
||
drift = |observedDevice − (base + monotonicElapsed)| > 2 min → warn + re-derive base
|
||
sanity = now within festival window ±45d → else warn, but ONLY if now ≥ festivalStart−45d or skew was captured (F-3 suppression SPIKE-08:97) → READY unaffected
|
||
```
|
||
|
||
No NTP endpoint, no timers, no background work (C-19); computed on demand (`SPIKE-08:43` T5/T6). HTTP `Date` is NTP-synced CDN source, INFERRED accurate (`SPIKE-08:84` B-10).
|
||
- **DST/unusual zones:** Rendering inherits IANA database; epoch arithmetic correct across spring-forward/fall-back (`SPIKE-08:43` T3/T4 validated 24/24 on V8/ICU; JSC parity UNVERIFIED queued for device T15 `ARCHITECTURE-VALIDATION.md:301`).
|
||
|
||
# 22. Map architecture (normative, ADR-008 + SPIKE-03 + §15)
|
||
|
||
Decision: **Raster WebP base (≤2 levels) + data-driven DOM POI overlay + CSS-transform pan/zoom + first-class Facilities list** (`ARCHITECTURE-DESIGN.md:543`). No tiles, no SDK, no GPS in V1.
|
||
|
||
- **Levels:** `overview` longest edge ≤1600 px (~0.3–0.7 MB encoded, ~7.3 MB decoded), optional `detail` ≤3072 px (~1.5–3.5 MB encoded, ~27 MB decoded), total decoded ≤~35 MB, **at most one detail resident** (overview swapped out/downscaled when detail shown) — F-1 `SPIKE-03:58` replaces prior 4096 cap (`ARCHITECTURE-DESIGN.md:571`). Per-file ≤6 MB cap applies.
|
||
- **Levels in `map.json`:** `SPIKE-04:157` — `levels: {overview assetId width height, detail assetId width height nightAssetId?}` with `nightAssetId` optional hook for F-4 dark mode. Alternatives SVG/Canvas/tile-pyramid rejected per matrix `SPIKE-03:11`.
|
||
- **Coordinate space:** POIs normalized `x:0..1, y:0..1` relative to base image (`ARCHITECTURE-DESIGN.md:561`, D-02). Authorship via tap-tool or measured coords; optional `lat/lng` hook preserved but unused in V1 (invariant 9).
|
||
- **Pan/zoom:** pointer-events + pinch → single container `transform: translate() scale()`; markers counter-scaled constant on-screen; bounds clamped (`ARCHITECTURE-DESIGN.md:561`).
|
||
- **Interactivity/accessibility:** POIs are real `button`s with labels and 48 px targets; Facilities list is first-class, not fallback, and is the screen-reader path (`SPIKE-03:79`). Search shares schedule component (highlight + list). Category chip filtering via class toggles, no re-layout of base.
|
||
- **Dark mode (F-4 `SPIKE-03:88`):** default `filter: brightness(.72) saturate(.85)` on base in dark, markers full-brightness (GPU-composited); optional `map-base/overview-night` asset — no white flash, no re-download.
|
||
- **Object URLs (F-3 `SPIKE-03:68`):** lazy-create per visible level; revoke on switch — leak freedom is part of acceptance.
|
||
- **Flip conditions:** vector art supplied → reconsider S1/S5; art >3072 px legible → 2×2 tile-split (DD-9, `SPIKE-03:98`); fps jank low-end → overview-only fallback.
|
||
- **Failure behavior:** map section missing → list view if POIs present, else "Map not downloaded" card (`ARCHITECTURE-DESIGN.md:568`).
|
||
|
||
# 23. Festival information architecture
|
||
|
||
- `info.json` ordered blocks `{id,title,kind,body: structured nodes}` where nodes ∈ {paragraphs,lists,links,emphasis,contact,address,hours} (`ARCHITECTURE-DESIGN.md:572`). **No raw HTML (C-22), no `innerHTML` of raw strings — renderer maps node types → safe DOM** (`ARCHITECTURE-DESIGN.md:573`, ADR-013 TH-4).
|
||
- Categories data-driven, reorderable: About, Rules, FAQ, What to bring/Not, Parking, Camping, Transport, Accessibility, Food/Drink, Merch, Activities, Contacts, Hours, Venue (`ARCHITECTURE-DESIGN.md:573`).
|
||
- Search indexes titles + text via shared search component.
|
||
- Signed as required section; readiness gates on it (`SPIKE-04:30`); BASELINE_ONLY excludes it — floor is what must survive, per `ARCHITECTURE-DESIGN.md:573` static-important note.
|
||
|
||
# 24. User preferences / favorites architecture (normative, `DISCOVERY.md:545` §15.8, `SPIKE-04:154`)
|
||
|
||
- Store: `lumen-user.favorites` keyed by **stable event id** (`SPIKE-04:154` — contractual across versions). **Never part of package**; never overwritten by updates; survives rollback and GC (B-6, `SPIKE-02:69` X3 PASS).
|
||
- Event model `SPIKE-04:140`: `{id stable, title, stageId, artistIds[], startUtc, endUtc, dayKey, tags[], status: scheduled|moved|cancelled, originalStartUtc?}` — publisher must not churn ids; pipeline mints via deterministic key if source lacks ids (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:74` D-01, AQ-20). Removals require `cancelled` rather than deletion (`SPIKE-04:189` gate 2).
|
||
- On dataset update: event moved → keep fav with new time; `cancelled` → mark; removed → orphan policy (show as "no longer scheduled" but keep fav entry, do not crash).
|
||
- Favorites: write-through persistence, no memory-only truth (`DISCOVERY.md:456` OF-11). Includes overlap conflict surfacing (`ARCHITECTURE-DESIGN.md:508`). Duplicated export later (DD-4).
|
||
- `prefs` (in `lumen-user`) includes theme/clock `displayMode` toggles; clock skew cache reference per §21. Small flags in `localStorage` only for non-load-bearing coach dismissed etc., guarded `try/catch` (ADR-004).
|
||
|
||
# 25. Networking abstraction (normative, ADR-011 + §17.2)
|
||
|
||
```
|
||
Application (features) ──→ reads DatasetStore; sees readiness — nothing else
|
||
Data/Domain ──→ DatasetStore, Verifier
|
||
Sync layer ──→ SyncService: orchestrate(check→stage→verify→activate)
|
||
Transport interface isAvailable() / fetchPointer(edition) / fetchBytes(path) byte-oriented, tiny
|
||
Transport impls ──→ HttpTransport (V1, the only one) / future transports implement same iface
|
||
Verifier ──→ hashes + Ed25519 (sole decider)
|
||
Service worker ──→ shell cache + navigation fallback — exclusive
|
||
```
|
||
|
||
Rules: features never import sync/transport (B-3); transports move bytes, Verifier decides truth identically for every transport (signed-payload rule, ADR-012); seam is one interface + one impl — no registry/plugin/negotiation (`ARCHITECTURE-DESIGN.md:628`). SyncService is transport-parameterized for fault-injection testing (`ARCHITECTURE-DESIGN.md:628`).
|
||
|
||
# 26. V1 networking limitations (normative, C-19 + ADR-010)
|
||
|
||
- Triggers: **on app open**, **on `online` event (opportunistic hint)**, and **manual "Update now"** (`ARCHITECTURE-DESIGN.md:584` §17.1). Nothing runs while app hidden; no timers, no periodic sync.
|
||
- Model: **pull-only**. Only operations: `fetch latest.json` → monotonic comparison → `fetch candidate manifest` → verify → stage. No uploads, no user-state sync, no telemetry, no push registration, no Background Sync registration (`ARCHITECTURE-DESIGN.md:637` §17.4).
|
||
- Announcements schema reserved but not implemented unless demanded during build (DD-1, `ARCHITECTURE-DESIGN.md:631` §17.3). Emergency tier-T3 notices are reserved with that shape.
|
||
- Rejected: periodic background sync (unsupported on iOS PW-4), push-based updates (iOS fragility), WebSocket/long-poll (no V1 need) (`ARCHITECTURE-DECISIONS.md:406`).
|
||
- Latency = time-until-next-open-online is the accepted trade-off, documented and communicated via `generatedAt`/`fetchedAt` and changed-event markers.
|
||
|
||
# 27. Future mesh boundary (normative, ADR-012 + DISCOVERY §17.1/§20)
|
||
|
||
- V1 does three things for future mesh and then stops: 1) Transport seam `§25`, 2) **signed-payload rule** — bytes from any transport are inert until Verifier passes them, so untrusted peers can never inject content (ME-4 `DISCOVERY.md:596`, `ARCHITECTURE-DESIGN.md:718` §20.3), 3) package/announcement formats immutable, content-addressed, version-monotonic (`ARCHITECTURE-DESIGN.md:717` §20.1–2). Cost in V1 is ~one small module (`ARCHITECTURE-DECISIONS.md:482`).
|
||
- Feasibility posture: meaningful mesh will require something outside the browser (native companion, hardware relay, organizer LAN) because mobile browsers expose no BLE peripheral, ad-hoc Wi-Fi, background sockets, and iOS lacks Web Bluetooth (`DISCOVERY.md:581`, `ARCHITECTURE-DESIGN.md:717`). A future transport attaches as e.g. `MeshTransport` bridging to a companion over a browser-accessible channel, or a LAN mirror acting as a package source. **If even that proves impossible, V1 loses nothing — HTTP is complete product functionality** (`ARCHITECTURE-VALIDATION.md:475` §6 scenario 20).
|
||
- **Deferred to future work (not V1):** peer discovery, routing, store-and-forward, dedupe/replay beyond monotonic, peer auth, message-size adaptation, emergency broadcast semantics, conflict handling beyond version monotonicity (`ARCHITECTURE-DESIGN.md:718` §20.4).
|
||
- Anti-leak: no feature/UI code may reference transport identity, connectivity modality, or peer concepts; Status UI shows "updated <when>", never "via what" (`ARCHITECTURE-DESIGN.md:718` §20.5).
|
||
|
||
# 28. Security requirements (normative, ADR-013 + TH-1…TH-11 `ARCHITECTURE-DESIGN.md:728`)
|
||
|
||
| Threat | Defense (must implement) |
|
||
|---|---|
|
||
| TH-1 tampered/malicious package (MITM, compromised hosting, `DISCOVERY.md:616` SE-1) | Ed25519-signed manifest (SHA-256 of exact manifest bytes) + per-file SHA-256+size (`ARCHITECTURE-DESIGN.md:358`), plus HTTPS+HSTS (`ARCHITECTURE-DESIGN.md:743`), single origin C-20; activation impossible without valid signature (ordering `§11`) |
|
||
| TH-2 publish path compromise | Signing key offline; singular publisher + deputy (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:60` O-01); repo audit trail; smoke re-verify; rollback via new version |
|
||
| TH-3 malicious/stale emergency | Emergency section signed; baseline immutable per build + same-source; provenance labels on every screen (`SPIKE-06:60`) |
|
||
| TH-4 XSS/content injection | C-22 structured-data rendering only; CSP `default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' blob: data:; connect-src 'self'` (`ARCHITECTURE-DESIGN.md:743`); no inline script, no `eval`, no `innerHTML` of raw |
|
||
| TH-5 replay/downgrade | Monotonic `packageVersion` acceptance; never accept ≤ active from network (quarantine) |
|
||
| TH-6 DoS oversized | Budgets in manifest validated before download + per-file 6 MB caps + quota pre-check (`ARCHITECTURE-DESIGN.md:358`, `SPIKE-01:140` P4) |
|
||
| TH-7/TH-8 privacy/storage | No accounts, no telemetry, no location capture; no secrets stored; dataset public; favorites innocuous (data minimization `ARCHITECTURE-DECISIONS.md:524`) |
|
||
| TH-9 SW/cache poisoning | Single origin, same-origin caches, no CORS relied upon |
|
||
| TH-10 mesh injection | Signed-payload rule §27 — mesh auth deferred but rule enforced from V1 |
|
||
| TH-11 supply chain | Minimal deps (target zero beyond audited verifier), lockfiles, CI, pin |
|
||
|
||
Platform hardening `ARCHITECTURE-DESIGN.md:743`: HTTPS+HSTS, no mixed content, no third-party scripts/fonts/analytics, `tel:` data-driven + explicit tap only (A-14), key set (fingerprinted) for rotation, lost-key runbook (ship new set via shell).
|
||
|
||
Device matrix must verify `tel:` in standalone (T19, `SPIKE-01:171`) and CSP allows blob object URLs for map assets (`ARCHITECTURE-DESIGN.md:743` `img-src blob:` — must include).
|
||
|
||
# 29. Privacy requirements (normative, ADR-013 + NR-15)
|
||
|
||
- No accounts, no auth, no session/token (NR-4).
|
||
- No telemetry/ analytics collection by default (NR-15, `ARCHITECTURE-DECISIONS.md:524` 4): diagnostics are on-device, scrubbed, and user-initiated — manual "share diagnostics" only, no automatic upload.
|
||
- No location capture in V1 (`ARCHITECTURE-DESIGN.md:222` zero-permission).
|
||
- No PII in content or favorites (favorites = `public event id` list, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:87` S-03).
|
||
- Data minimization as a rule (`ARCHITECTURE-DECISIONS.md:524`).
|
||
|
||
# 30. Error handling requirements
|
||
|
||
- Every failure maps to a defined renderable state — **no-blank-screen rule** `ARCHITECTURE-DESIGN.md:775` §22. Contract in §20.2 + §31.
|
||
- IDB errors (`QuotaExceededError`, txn abort) are caught, wrapped, and keep active dataset — never crash (`SPIKE-01:140` P3, `ARCHITECTURE-DESIGN.md:672` retry once).
|
||
- Network errors (fetch pointer/manifest timeout) are silent no-ops; retry next trigger (`ARCHITECTURE-DESIGN.md:664`).
|
||
- Hash/signature/schema mismatches → quarantine + diag, never activate (`ARCHITECTURE-DESIGN.md:667`).
|
||
- `localStorage` guarded `try/catch` (non-load-bearing) per ADR-004.
|
||
- Renderer forward-tolerant for floor path; dataset content unknowns tolerated (`SPIKE-06:75`).
|
||
|
||
# 31. Recovery requirements (normative, `ARCHITECTURE-DESIGN.md:755` §22 + SPIKE-05/02)
|
||
|
||
Behavior matrix is the contract (`ARCHITECTURE-DESIGN.md:755` FA-1…FA-16). Coding agent must implement verbatim:
|
||
|
||
FA-1 offline startup / FA-2 browser restart / FA-3 phone restart → boot from `caches`+IDB, light verify, budgets `§33`. FA-4 SW killed → page unaffected (C-21). FA-5 evicted → RECOVERY + floor + one-tap re-prep (needs online to re-prep). FA-6 corrupt → quarantine/rollback `§13` + baseline. FA-7 unavailable → BASELINE_ONLY. FA-8 interrupted → resume `§10`. FA-9 invalid package published → reject+quarantine, client-side no online needed to reject. FA-10 wrong clock → corrected or warned per `§21` (F-3). FA-11/12 GPS/low battery → no degradation. FA-13 schedule changed → update on next online, last-good meanwhile. FA-14 emergency unreachable → T1/T2 remain + physical channels (A-16). FA-15 browser update → baseline targets conservative. FA-16 shell mid-festival → next-start activation + §18.5 compatibility check.
|
||
|
||
Every row ends renderable; Ring-0 static `fallback.html` covers shell-cache-missing case (`ARCHITECTURE-DESIGN.md:775`).
|
||
|
||
# 32. Accessibility requirements (normative, WCAG 2.1 AA per `DISCOVERY.md:334` AMB-10, `ARCHITECTURE-DESIGN.md:783`)
|
||
|
||
- Landmarks/heading order per view; visible focus.
|
||
- POIs are real `button`s/links (not canvas hit-testing); Facilities list is the primary screen-reader path (`SPIKE-03:79`).
|
||
- Emergency: giant targets, max contrast both themes, zero clutter, one-handed reachable (`ARCHITECTURE-DESIGN.md:783` + `DISCOVERY.md:470` EM-5); `tel:` with selectable-text copy fallback.
|
||
- Touch targets ≥48 CSS px (`ARCHITECTURE-DESIGN.md:783`).
|
||
- `prefers-reduced-motion` honoured (no vestigial autoplay).
|
||
- High-contrast light + true dark themes; map handles F-4 dark filter without breaking markers (`SPIKE-03:88`).
|
||
- In-app back affordances (standalone safe), history-API back (`ARCHITECTURE-DESIGN.md:783`).
|
||
- Screen-reader pass required on device T13 `ARCHITECTURE-VALIDATION.md:283` using VoiceOver/TalkBack.
|
||
|
||
Do not weaken this for convenience — `§40` fails if a11y is degraded to save code.
|
||
|
||
# 33. Performance budgets (hard ceilings, CI-enforced — `ARCHITECTURE-DESIGN.md:848` §27 + `ARCHITECTURE-DESIGN.md:363` §10.6)
|
||
|
||
| Part | Target / Ceiling | Enforcement |
|
||
|---|---|---|
|
||
| Shell JS (gz) | ≤150 KB | Bundler gate (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:32` AQ-10) |
|
||
| Shell total (JS+CSS+icons, gz) | ≤1 MB | Same |
|
||
| Emergency floor | ≤16 KB | Build asserted (`ARCHITECTURE-DESIGN.md:367`) |
|
||
| Sections JSON total (emergency+schedule+map.json+info+assets.json) | ≤3 MB | Pipeline gate `SPIKE-04:189` + client budget check |
|
||
| Map base images (all levels) | ≤28 MB (overview ≤1600 px / detail ≤3072 px, total decoded ≤~35 MB) | Pipeline + client `§22` |
|
||
| Other assets (icons, etc.) | ≤6 MB | Same |
|
||
| Total dataset | ≤40 MB target / **50 MB hard ceiling** (`DISCOVERY.md:297` A-02) | Pipeline reject above 40 MB; client refuses above 50 MB |
|
||
| Single file | ≤6 MB | Hash/digest memory bounded `SPIKE-01:140` P5 |
|
||
| Cold start → Emergency usable (D3 cached, mid-range 2021 Android) | ≤2 s | Device matrix T06 |
|
||
| Cold start → interactive | ≤3 s | T06 |
|
||
| Schedule list (1k events) | ≤100 ms interaction; window rendering if needed | Unit/perf |
|
||
| Map pan/zoom | ≥30 fps | T13 |
|
||
| Boot light verify | ≤150 ms | T03 |
|
||
|
||
Tactics: vanilla JS parse trivial, WebP, single JSON parse per section per session, no idle timers/observers, zero background work (`ARCHITECTURE-DESIGN.md:858`).
|
||
|
||
Silently increasing any budget violates `§40`.
|
||
|
||
# 34. Browser support (normative, `ARCHITECTURE-DESIGN.md:819` §25)
|
||
|
||
| Dimension | Baseline (must support) | Best-effort | Explicitly unsupported |
|
||
|---|---|---|---|
|
||
| iOS Safari | 16.4+ installed PWA | 16.0–16.3 tab (eviction-prone, coach pushes install) | <16 |
|
||
| Android Chrome | ~110+ (including 2021 mid-range) | Older with SW+IDB | Without SW |
|
||
| Other (Samsung Internet, Firefox Android) | Where SW+IDB exist | Same code paths | — |
|
||
| Desktop | Works via same code | Not a target | IE, legacy |
|
||
| JS disabled / storage blocked | Static `fallback.html` with emergency text (Ring-0) | — | Full app |
|
||
|
||
Capability detection (not UA sniffing): `'serviceWorker' in navigator`, `indexedDB`, `caches`, `Intl.DateTimeFormat`, `navigator.storage.estimate`. Missing → degrade per `§20`/`§31` (never crash) `ARCHITECTURE-DESIGN.md:829`.
|
||
|
||
# 35. Testing requirements (normative, `ARCHITECTURE-DESIGN.md:832` §26)
|
||
|
||
| Layer | What | How |
|
||
|---|---|---|
|
||
| Pipeline / schema / contract | Package schema, manifest, budgets, stable IDs, dayKey/time sanity (≤40 MB, ≤6 MB/file, dimension caps, no-zero-length, `dayKey` matches zone), forward-compat unknown fields | Pipeline validation + golden-package fixture tests |
|
||
| Unit | ClockService (skew/drift/sanity + F-3 suppression), Verifier (bad sig, bad hash, replay/lower version, quarantine), readiness predicate C1–C8 (`SPIKE-05:28`), A/B state transitions (14+3 scenarios), escaping renderer, forward-tolerant floor | Standard unit suite |
|
||
| Integration (sync) | Full update lifecycle including all 9 failure stages `ARCHITECTURE-DESIGN.md:664` §18.2 — mocked transport + fault injection (kill mid-stage, corrupt bytes, quota errors); readbackPending + rollback; favorites survive (X3) | Scripted harness modeled on `exp2-ab-update-sim.mjs:1` |
|
||
| PWA audits | Manifest/SW/offline load | Lighthouse PWA + scripted offline reload |
|
||
| Content | Emergency provenance stamps, dataset age/staleness, wrong-clock warnings (absolute times always shown) | Scenario tests |
|
||
| Accessibility | Emergency/Schedule/ Facilities list screen-reader flows + contrast both themes + 48 px targets | Automated + manual (VoiceOver/TalkBack) — must run on devices T13 |
|
||
|
||
Rule: any bug found on a real device that harness missed becomes a harness case (`ARCHITECTURE-DESIGN.md:843`). Every readiness state `§20.2` must be reachable in tests (`ARCHITECTURE-DESIGN.md:930` rule 5).
|
||
|
||
# 36. Physical-device validation requirements (blocking for production)
|
||
|
||
Required to declare storage/map/time/SW production-ready. Defined in `ARCHITECTURE-VALIDATION.md:301` §4 (19 groups × 4 devices, 76 device-tests) and queued throughout spikes as UNVERIFIED.
|
||
|
||
Device families: D1 iPhone 16.4 low-bound (tab + installed), D2 iPhone latest installed, D3 low-end Android 2021 mid-range Chrome ~110+ (primary perf), D4 modern Android Chrome latest (`ARCHITECTURE-VALIDATION.md:158`).
|
||
|
||
Minimum required before a real festival: T01 first visit → T02 install → T03 offline launch → T04 airplane simulation → T05 browser restart → T06 phone restart → T07 `persist()`/exemption → T08 storage pressure → T09 happy update → T10 interrupted updates (kill at S01–S14 points `SPIKE-02:50`) → T11 recovery (eviction/corrupt/private/incompatible) → T12 map rendering (1600/3072/35 MB) → T13 map interaction (≥30 fps, leaks, a11y) → T14 GPS absence → T15 incorrect clock (5 steps per `SPIKE-08:152`, including F-3 near-festival gating) → T16 SW lifecycle (C-21) → T17 shell update (next-start + §18.5) → T18 dataset compatibility → T19 emergency floor in every failure state.
|
||
|
||
Severity per `ARCHITECTURE-VALIDATION.md:171`: T01,T03–T06,T08–T13,T16–T19 BLOCKING; T02 BLOCKING on iOS; T07/T14/T15 graded HIGH/MEDIUM as marked. Device matrix is **BLOCKING for production** but does not block starting implementation on fixtures (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:138`, `ARCHITECTURE-VALIDATION.md:493`).
|
||
|
||
# 37. Deployment assumptions (normative, ADR-014 + DISCOVERY AMB-4)
|
||
|
||
- Static HTTPS CDN, **single origin** (`DISCOVERY.md:261` C-20, `ARCHITECTURE-DESIGN.md:88`) with layout `/` (`no-cache`) + `/assets/<hash>.*` (immutable 1y) + `/latest.json` (`max-age 60`) + `/editions/<ed>/packages/<v>/**` (immutable 1y) `ARCHITECTURE-DESIGN.md:807`.
|
||
- Two environments: `staging` origin + `production` origin, same code, different origins (storage isolation automatic) — `ARCHITECTURE-DESIGN.md:817`.
|
||
- No dynamic backend, DB, or auth endpoint in V1; publisher auth at pipeline/CDN credential layer (`ARCHITECTURE-DESIGN.md:815`).
|
||
- Origin is hard to reverse post-users (origin-keyed storage strands data) — choose production origin deliberately before any public staging link (AQ-18 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:116`); provisional staging origin is fine for development.
|
||
- Provider choice (AQ-14 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:112`) and budget (OQ-8) are **provisional** — pick before public staging share, keep stable.
|
||
- Publisher runbook: publish → smoke re-verify → monitor `latest.json`; rollback runbook: republish old content at new higher `packageVersion`; emergency runbook gated by sign-off (`ARCHITECTURE-DESIGN.md:697`).
|
||
|
||
# 38. Content / data assumptions (normative, ADR-005/014 + ASSUMPTIONS §5)
|
||
|
||
- Single annual edition (A-01 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:15`), keyed `edition` for cheap multi-event later; budgets `§33`.
|
||
- Organizers can supply schedule/map art/POIs/emergency/info digitally (`DISCOVERY.md:297` A-04, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:73` A-04) via intake templates (CSV/JSON + raster art) or existing CMS adapter must stay import-friendly (AMB-2 `DISCOVERY.md:324`).
|
||
- Event/POI IDs stable (`SPIKE-04:189` gate 2, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:74` D-01) — pipeline mints deterministically if source lacks ids and persists mapping (AQ-20); favorites depend on it.
|
||
- Map art: raster with known dimensions, POIs locatable normalized `x/y` (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:75` D-02); if only vector/GIS arrives, representation flip `SPIKE-03:98` DD-9.
|
||
- Emergency contacts/coordinates valid through event, provenance `contentVersion`/`updatedAt` + `emergencySchemaVersion` (`SPIKE-04:70`, `SPIKE-06:110`); sign-off gate O-02 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:62`.
|
||
- Single festivals IANA zone (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:78` D-05); multi-zone would amend ADR-009.
|
||
- Volumes ≤1k events / ≤200 POIs / ≤30 info blocks within budgets (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:77` D-04).
|
||
- Offline LAN/mesh update delivery not in V1 (A-09).
|
||
- **Provisional until real content arrives:** map art quality, exact schedule sample; pipeline gates prove but cannot pass without samples (`ARCHITECTURE-VALIDATION.md:501`).
|
||
|
||
# 39. Feature acceptance criteria (normative per DISCOVERY requirements)
|
||
|
||
| Area | Requirement | Must hold |
|
||
|---|---|---|
|
||
| Platform | R-P1…P4 `DISCOVERY.md:132` | Mobile-first PWA, iOS Safari + Android Chrome; HTTPS; no native; desktop only incidental. |
|
||
| Navigation | R-N1…N4 `DISCOVERY.md:143` | 4 destinations; nav obvious; Emergency one tap from anywhere (persistent); one-handed 48 px, high-contrast day/night. |
|
||
| Emergency | R-E1…E5 `DISCOVERY.md:152` | All tiers; `tel:` from standalone (`SPIKE-01:171`); dial requires explicit tap; address/coordinates/GPS multiple formats; procedures/muster/exits/AEDs; provenance stamps. |
|
||
| Schedule | R-S1…S5 `DISCOVERY.md:162` | Full schedule offline ≤~1k; Now/Next via ClockService; favorites local stable ids; filters/search offline; R-S5 time model `§21` + device-clock handling + dayKey groups. |
|
||
| Map | R-M1…M5 `DISCOVERY.md:172` | Offline raster+POI+list `§22`; organizer raster+POIs supplied; geolocation never required (M4 narrowed per `ARCHITECTURE-DESIGN.md:18`); representation validated 1600/3072. |
|
||
| Festival | R-F1…F2 `DISCOVERY.md:182` | Info blocks offline, data-driven, not hardcoded. |
|
||
| Offline | R-O1…O9 `DISCOVERY.md:189` | Shell without network; SW caches shell; local data truth; startup never needs network; atomic updates; OFFLINE READY evidenced `§20`; survive restarts/pressure/eviction; storage budgets `§33`; versioning/migrations. |
|
||
| Package | R-D1…D3 `DISCOVERY.md:203` | Versioned signed package `§15`; manifest inventory; source/pipeline/delivery/version/tracking/atomic/rollback defined `§§10–13`. |
|
||
| Extensions | R-X1…X3 `DISCOVERY.md:211` | Online enhancements never gate V1; transport seam (§25) behind SyncService; no mesh in V1. |
|
||
| Security | R-G1…G3 `DISCOVERY.md:219` | TH-1…TH-11 `§28` defended; package-signed; no secrets client-side (C-24). |
|
||
|
||
Failure matrix `ARCHITECTURE-DESIGN.md:755` FA-1…FA-16 must be satisfied for each feature; §31 is the contract.
|
||
|
||
# 40. Definition of done (checklist before merging or declaring READY)
|
||
|
||
- [ ] No rule in `§6` B-1…B-7 violated (verified by module-import lint + review).
|
||
- [ ] No direct `innerHTML` of festival content; escaping renderer + CSP `self`-only; no inline/eval (`ARCHITECTURE-DESIGN.md:743`).
|
||
- [ ] Emergency floor compiled from same sheet as dataset section; ≤16 KB; forward-tolerant; zero-IDB path renders in every failure state; provenance visible (`SPIKE-06:60`, `ARCHITECTURE-DESIGN.md:367`).
|
||
- [ ] IDB protocol P1–P8 `SPIKE-01:140` implemented exactly; no txn spans non-IDB await; `QuotaExceededError` path keeps active; pre-check 2×.
|
||
- [ ] A/B ordering `§§10–13` holds; per-file atomic; verification record + `readbackPending` + rollback depth 1; page-context downloads only (C-21).
|
||
- [ ] Validation ordering `§11`: signature before hash before schema before activate; budgets checked before download; quarantine + monotonic anti-replay.
|
||
- [ ] Shell/dataset compatibility dual-checked at boot (§18.5 `ARCHITECTURE-DESIGN.md:688`) and respects C-23 ordering `ARCHITECTURE-DESIGN.md:690` (freeze 7d before festival).
|
||
- [ ] Readiness predicate C1–C8 `§20.1` computed locally; every state `§20.2` reachable in tests; READY is time-independent (`SPIKE-05:47`); chip always honest.
|
||
- [ ] ClockService: UTC+I`ANA + `dayKey`, skew capture, monotonic guard, F-3 suppression `SPIKE-08:97`, only explicit-zone Intl (F-4).
|
||
- [ ] Map: 1600/3072/35 MB budgets, lazy object URLs (F-3), dark filter (F-4), DOM buttons 48 px + Facilities list, a11y `§32`.
|
||
- [ ] Schedule: Now/Next, favorites with stable ids, filters/search, `§21` queries, status markers.
|
||
- [ ] Info: structured nodes → safe DOM `§23`.
|
||
- [ ] Sync pull-only at open/`online`/manual `§26`; HTTP transport only; transport seam empty for future mesh `§27`.
|
||
- [ ] Security `§28`: signed manifest + per-file SHA-256, CSP, HSTS, no secrets, key set rotation-capable; verifier library audit recorded (AQ-06) even if not blocking staging.
|
||
- [ ] Privacy `§29`: no accounts, no telemetry, no location capture, manual diagnostics only.
|
||
- [ ] Recovery `§31`: every FA row renderable; Ring-0 fallback, no blank screen.
|
||
- [ ] Performance `§33` budgets CI-gated (shell ≤150 KB gz / 1 MB, dataset ≤40 MB, single file ≤6 MB, decode ≤35 MB, cold 2 s/3 s, boot ≤150 ms, map ≥30 fps).
|
||
- [ ] Unit + contract + integration (fault-injected) + content + a11y tests `§35`; any device bug that harness missed becomes a harness case `ARCHITECTURE-DESIGN.md:843`.
|
||
- [ ] Device-matrix hand-off documented per `§36` — remaining UNVERIFIED items tracked as YELLOW in `ARCHITECTURE-VALIDATION.md:476` §7 (IDB jetsam, map/GPU, JSC parity, `tel:`/SW).
|
||
|
||
Implementation may not be declared done for a production festival until the BLOCKING items in `§36` and ops gates `§37` (origin, key custody runbook) have been exercised.
|
||
|
||
---
|
||
|
||
# ARCHITECTURAL INVARIANTS (implementation-oriented, non-negotiable)
|
||
|
||
These copy the validated invariants into rules a coding agent cannot break. Each cites the validated freeze (`ARCHITECTURE-VALIDATION.md:301` §3, DISCOVERY constraints `DISCOVERY.md:230` §5, and specific hard rules).
|
||
|
||
| # | Invariant (implement as stated) | Trace |
|
||
|---|---|---|
|
||
| I-1 | **Emergency baseline is Ring-0 shell bytes and never uses IndexedDB.** Tier-1 floor is compiled into the shell at build time from the same source as the dataset emergency section; its renderer parses a frozen schema, is forward-tolerant, and must succeed with zero IDB access in every state (NOT_READY, PARTIAL, READY, RECOVERY, BASELINE_ONLY, FAILED, eviction, corrupt, incompatible, offline). | ADR-007 `ARCHITECTURE-DESIGN.md:465`, `SPIKE-06:12`, `SPIKE-06:75` F-1, C8 `SPIKE-05:43` |
|
||
| I-2 | **No emergency path may fetch the network.** Rendering preferences (floor vs dataset section), provenance stamps, and `tel:` number display must be satisfiable from shell + verified active slot only. Online is an enhancement for receiving updates, never a prerequisite for displaying emergency info. | DISCOVERY C17, ADR-001 `ARCHITECTURE-DECISIONS.md:14`, `SPIKE-06:81`, `ARCHITECTURE-DESIGN.md:492` |
|
||
| I-3 | **Schedule works fully offline.** Full browse, day groups by `dayKey`, Now/Next via ClockService, My Schedule (favorites), stage/type filters, and search over ≤~1k events all over the active slot — zero network calls, zero hidden APIs. | DISCOVERY R-S1…S4, R-O1…R-O9, ADR-009, `ARCHITECTURE-DESIGN.md:508`, `SPIKE-08:43` |
|
||
| I-4 | **Map works fully offline.** Overview (+ optional detail) WebP bases + POI normalized coords + Facilities list + search/filter all from active slot Blobs and JSON — no online tiles, no map SDK, no network. Factors: capped decode `§22`, no GPS. | DISCOVERY R-M1…M5, ADR-008 `SPIKE-03:58`, `ARCHITECTURE-DESIGN.md:543` |
|
||
| I-5 | **Festival information works fully offline.** All info blocks rendered as structured safe DOM from the active slot — no remote fetches while festival is running. | DISCOVERY R-F1, `ARCHITECTURE-DESIGN.md:572` |
|
||
| I-6 | **Favorites / My Schedule works fully offline and survives every data transition.** `lumen-user` is separate from `lumen-slot-*` and `lumen-system`; updates/rollback/GC never touch it; writes are write-through. | DISCOVERY `DISCOVERY.md:545` §15.8, `SPIKE-02:69` X3, `ARCHITECTURE-DESIGN.md:275` B-6 |
|
||
| I-7 | **GPS is optional and V1 has no blue dot.** Never request location permission in V1; find-nearest-facility is solved by the Facilities list and categorised POIs, not GPS. `lat/lng` on POIs is an optional hook only. | DISCOVERY `DISCOVERY.md:172` R-M4 narrowed `ARCHITECTURE-DESIGN.md:18`, invariant 9, `SPIKE-03:112` |
|
||
| I-8 | **Network loss cannot break the core app.** After first successful shell+dataset bootstrap, every critical path (boot, nav, emergency, schedule, map, festival info, readiness display, favorites) renders from Cache Storage + IDB with no blocking fetch — online is only `SyncService` enhancement (`ARCHITECTURE-DESIGN.md:383`). | DISCOVERY C19/C20 + ADR-001, C-19 `ARCHITECTURE-DESIGN.md:78`, FA-1 `ARCHITECTURE-DESIGN.md:755` |
|
||
| I-9 | **No partial dataset can ever become the active dataset.** Staging exclusively into the inactive slot; per-file hash verified; full verification before pointer move; active is observable only as "old verified" or "new verified" (`SPIKE-02:50` 14 crash points, ordering proof `SPIKE-02:73`). | DISCOVERY C8, ADR-006, `ARCHITECTURE-DESIGN.md:659` |
|
||
| I-10 | **No invalid dataset (bad sig, bad hash, bad schema, incompatible, over-budget) can ever become active.** Cheap+authoritative validation order `§11`; Verifier is sole decider (B-4); quarantine prevents refetch-loop; incompatible bubbles "please update app" (`ARCHITECTURE-DESIGN.md:668`). | DISCOVERY C22, R-G2, ADR-013 TH-1/TH-3/TH-5, `SPIKE-02:60` S06–S09 |
|
||
| I-11 | **Existing valid data must survive every failed or interrupted update.** Kill at any of the 9 failure stages → old active untouched or restored via rollback/readbackPending (`ARCHITECTURE-DESIGN.md:664` §18.2, `SPIKE-02:50` S01–S14, `SPIKE-02:97` F-3, `ARCHITECTURE-DESIGN.md:672` retry once). | DISCOVERY C9, ADR-006 F-4, `ARCHITECTURE-DESIGN.md:659`, W-4 `ARCHITECTURE-DESIGN.md:898` |
|
||
| I-12 | **Dataset authenticity/integrity must be proved before activation.** Ed25519 over `sha256(exact manifest bytes)` against embedded key set + per-file SHA-256+size + schema; recorded in `lumen-system.meta` verification record (F-2) and spot-checked at boot. | ADR-005/013, `ARCHITECTURE-DESIGN.md:358`, `SPIKE-02:29`, `SPIKE-04:203` F-5, `SPIKE-01:140` P2 |
|
||
| I-13 | **Shell and dataset compatibility must be checked at two points.** Before staging (manifest `appCompatibility` + `schemaVersion`) and at boot (`schemaVersion ∈ shell.supportedRange` + `appCompatibility`) per `ARCHITECTURE-DESIGN.md:688` §18.5; ordering rule C-23 `ARCHITECTURE-DESIGN.md:690` — shell range ships first, datasets follow, freeze 7d before festival. |
|
||
| I-14 | **V1 has no mesh networking — exactly one transport exists.** No WebRTC/BLE/ad-hoc/HP code, no discovery/routing/dedupe in V1. The only transport is `HttpTransport` implementing the byte interface `§25`. | DISCOVERY `DISCOVERY.md:268` NR-1, NR-12, ADR-011/012, `ARCHITECTURE-DESIGN.md:704` |
|
||
| I-15 | **V1 has no user accounts.** No login, JWT, session, or server-side identity. All state stays device-local. | DISCOVERY `DISCOVERY.md:270` NR-4, ADR-010 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:60` A-07, `ARCHITECTURE-DESIGN.md:930` rule 7 |
|
||
| I-16 | **UI code must never directly manipulate persistence or transport.** Views/components/router may import only `domain/*` read APIs and `readiness/` — never `platform/`, `storage/`, `sync/`, `fetch`, `indexedDB`, `caches`. Enforce via import lint and review (B-1…B-3 `ARCHITECTURE-DESIGN.md:195`, hard rules `ARCHITECTURE-DESIGN.md:928`). |
|
||
| I-17 | **Future transport/mesh concerns remain behind the transport boundary.** No feature domain service or UI may import `sync/transport` nor branch on transport identity; Verifier decides truth for every transport equally (signed-payload rule `SPIKE-06:53` ME-4, `ARCHITECTURE-DESIGN.md:718`). Status shows "updated <when>", never "via what". | ADR-011/012, `DISCOVERY.md:596` ME-4, `ARCHITECTURE-DESIGN.md:704`/`ARCHITECTURE-DESIGN.md:718` §20.5 |
|
||
|
||
Violations of any I-1…I-17 fail `§40` Definition of Done.
|
||
|
||
---
|
||
|
||
# IMPLEMENTATION SEQUENCE (do not attempt to build everything simultaneously)
|
||
|
||
Prerequisites use `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:138` and `ARCHITECTURE-VALIDATION.md:493` — every stage is buildable against a provisional staging origin and fixtures. Do not block on production origin (AQ-18), real map art (OQ-6/AQ-19), or emergency sign-off (OQ-2) — model them with placeholders that respect budgets/schemas.
|
||
|
||
## Stage 1 — Project foundation
|
||
|
||
- **Prerequisites:** Contract read; architecture docs understood; decision to use provisional staging origin.
|
||
- **Work:** Dedicated git repo (VCS-1 `DISCOVERY.md:34`), TypeScript strict base, lint (import boundaries for B-1…B-7 + §6), formatter, commit hooks, CI skeleton.
|
||
- **Tests:** CI runs lint + typecheck on empty src.
|
||
- **Acceptance:** Import-lint for forbidden edges (`ui`→`platform`, etc.) passes; no app code yet.
|
||
- **Must NOT yet:** PWA, DB, any feature code; no runtime deps except possibly the audited verifier placeholder.
|
||
|
||
## Stage 2 — Application shell
|
||
|
||
- **Prerequisites:** Stage 1.
|
||
- **Work:** `index.html` (single entry), manifest (`display: standalone`, icons), build emits hashed `/assets/<hash>.*` + precache manifest deterministically (AQ-12 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:36` Validated), production `no-cache` vs immutable headers `ARCHITECTURE-DESIGN.md:807`. Install coach visuals scaffolded (iOS manual steps + Android `beforeinstallprompt` detection, `ARCHITECTURE-DESIGN.md:217`).
|
||
- **Tests:** Lighthouse PWA (shell installable), scripted offline reload (`ARCHITECTURE-DESIGN.md:838`).
|
||
- **Acceptance:** `tests` PWA audits pass; budgets `§33` bundle gate visible.
|
||
- **Must NOT yet:** IDB, dataset, sync.
|
||
|
||
## Stage 3 — PWA / service worker
|
||
|
||
- **Prerequisites:** Stage 2 shell hashes stable.
|
||
- **Work:** Hand-written `public/sw.js` `§16` (install/activate/fetch/message). Cache name `lumen-shell-v<build>`; navigation cache-first with `fallback.html`; hashed assets cache-first; package endpoints passthrough (C-21). `SKIP_WAITING` + next-start activation + in-app "Restart to update" affordance `ARCHITECTURE-DESIGN.md:255`.
|
||
- **Tests:** SW lifecycle harness: install → activate → old SW serves current session → next-start flip; idempotent ops; package passthrough not cached. Offline launch without IDB still renders shell+floor.
|
||
- **Acceptance:** T01/T03 blue/green path passes on Chromium headless; T16 readiness for device kill test deferred to `§36`.
|
||
- **Must NOT yet:** Any IDB write from SW — keep P8 `SPIKE-01:140`.
|
||
|
||
## Stage 4 — Data model
|
||
|
||
- **Prerequisites:** §8 budgets known.
|
||
- **Work:** Festival Data Package types (`SPIKE-04:79` manifests + section schemas), emergency floor schema (`SPIKE-06:12`), map/POI categories, info structured nodes. Define `schemaVersion` envelope and `sections[*].required` `SPIKE-04:64`, no-expiration rule `SPIKE-04:50`, stable-id contract `SPIKE-04:189`.
|
||
- **Tests:** Contract fixtures — golden packages pass gates `SPIKE-04:189`; stable-id invariant tests; dayKey contract `SPIKE-08:43`.
|
||
- **Acceptance:** Pipeline can validate/reject a fixture package deterministically; no UI yet.
|
||
|
||
## Stage 5 — Local persistence
|
||
|
||
- **Prerequisites:** §4 types.
|
||
- **Work:** `platform/idb` thin wrapper (`SPIKE-01:140` P1–P8), `data/` stores, `lumen-system/meta` single-txn discipline (P2), `lumen-user` migrations, no txn spans await, `QuotaExceededError` keeps active (P3), 6 MB single-record cap (P5), `storage.estimate()` free-space (P4), persist request (P6). Assets as Blobs in slot DB `ARCHITECTURE-DESIGN.md:275`.
|
||
- **Tests:** Wrapper atomicity tests `SPIKE-01:60`; QuotaError injection keeps active; boot light verify detects missing/corrupt; heavy BLOB read-back timing instrumentation.
|
||
- **Acceptance:** Unit tests mirror `exp2-ab-update-sim.mjs:1` infrastructure at harness level (without claiming device proof).
|
||
- **Must NOT yet:** Verifier/crypto — reads only.
|
||
|
||
## Stage 6 — Festival Data Package (content pipeline stub)
|
||
|
||
- **Prerequisites:** §4 types + §5 stores exist (even without real art).
|
||
- **Work:** `content/` templates + pipeline script `validate→build→hash→sign→upload→smoke` (`ARCHITECTURE-DESIGN.md:697` runbook). Share same emergency source sheet for floor + dataset section (`ARCHITECTURE-DESIGN.md:345`) using test emergency data. Mutable `latest.json`.
|
||
- **Tests:** Pipeline gates `SPIKE-04:189` on synthetic data; signed fixture → smoke fetch re-verifies.
|
||
- **Acceptance:** A staging `lumen-2026` edition with Budget ≤40 MB produces a smoke-verified signed package reachable via immutable URLs; test key only.
|
||
- **Must NOT yet:** Real organizer content — provisional placeholder.
|
||
|
||
## Stage 7 — Dataset validation
|
||
|
||
- **Prerequisites:** Stages 5–6.
|
||
- **Work:** `sync/verifier` (SHA-256 via WebCrypto + bundled pure-JS Ed25519 verifier placeholder). Implements ordering `§11` before any download, quarantine store, budgets checked before download (`ARCHITECTURE-DESIGN.md:358`, `SPIKE-02:29`).
|
||
- **Tests:** Bad signature → keep old, no file fetched (S07), bad hash (S06), bad schema (S08), incompatible (S09), replay lower version, bundled verifier smoke against `signature.json` `SPIKE-04:79`. Enforces quarantine no-loop.
|
||
- **Acceptance:** Every rejection keeps active; Verifier is sole decider (B-4).
|
||
|
||
## Stage 8 — A/B activation
|
||
|
||
- **Prerequisites:** Stages 5–7.
|
||
- **Work:** `sync/` staging into inactive slot (per-file atomic, resumable 7-day discard), verification record + `readbackPending` (F-2/F-3 `SPIKE-02:89`), single-txn flip `§12`, retry-once, automatic rollback on spot-check/next-boot, user-initiated restore (`ARCHITECTURE-DESIGN.md:677`).
|
||
- **Tests:** Fault-injection integration exactly covering `SPIKE-02:50` S01–S14 + X1–X3 (kill between files, before/during/after flip, corrupt at boot, both-gone RECOVERY, favorites survive). Mirrors `exp2-ab-update-sim.mjs:1`.
|
||
- **Acceptance:** 16/16 analogous PASS at state-machine level; coverage does not claim iOS jetsam proof — that is device T10 `§36`.
|
||
|
||
## Stage 9 — Offline Ready
|
||
|
||
- **Prerequisites:** Stages 3 (C1), 5/8 (C2–C8).
|
||
- **Work:** `domain/readiness` predicate C1–C8 `§20.1`, six-state taxonomy `§20.2`, cadence `§20.3` (light at boot ≤150 ms, full after staging / user check / error), persistence of `readbackPending`, chip mapping, Status screen checklist/version/usage.
|
||
- **Tests:** Predicate truth table (`SPIKE-05:90` edge cases: eviction→RECOVERY, incompatible→RECOVERY, private→BASELINE_ONLY, wrong clock→READY+warning, optional missing→READY, activation fail→FAILED). Every state reachable `ARCHITECTURE-DESIGN.md:930` rule 5.
|
||
- **Acceptance:** Time-independent READY (`SPIKE-05:47` F-1) — wrong clock produces warning `§21`, never demotion.
|
||
|
||
## Stage 10 — Emergency
|
||
|
||
- **Prerequisites:** Ready §9 + baseline generation from content pipeline §6.
|
||
- **Work:** Compiled `emergency-baseline/` ≤16 KB (`ARCHITECTURE-DESIGN.md:367`), three-tier resolution `SPIKE-06:60`, forward-tolerant zero-IDB renderer `SPIKE-06:75` F-1, provenance stamps, `tel:` (primary) + copyable text, explicit tap to dial (`DISCOVERY.md:470` EM-5/EM-10), persistent nav one-tap reach (`ARCHITECTURE-DESIGN.md:207`).
|
||
- **Tests:** Every `§20.2` state → Emergency renders floor or highest compatible tier; floor with zero-IDB (BASELINE_ONLY); incompatible demotes to floor with guidance; signature/compatibility failure never removes floor (`SPIKE-06:81` 7 cases).
|
||
- **Acceptance:** Emergency coverage is Ring-0 — invariants I-1/I-2 pass in all failure states `§31`.
|
||
|
||
## Stage 11 — Schedule
|
||
|
||
- **Prerequisites:** §9 readiness, `§21` ClockService.
|
||
- **Work:** `domain/schedule` + `views/schedule` + `domain/favorites`. Stages/artists/events with stable ids + `dayKey`; Now/Next (§21), per-stage/global up-next, conflict detection, stage/tag filters, substring search (naive, ≤1k) (`ARCHITECTURE-DESIGN.md:508`). `status: scheduled|moved|cancelled` rendering, changed-event notes. ClockService integration: device+skew → monotonic guard → F-3 suppressed sanity → absolute times always visible `SPIKE-08:97`.
|
||
- **Tests:** Intl zone rendering T1a/b, dayKey T2a–c, DST T3a–e, unusual zones T4a–d (`SPIKE-08:43`); skew arithmetic T5a–c; sanity T6a–c with F-3; overlap T7a–d — unit suite mirroring `exp1-time-model.mjs:1`. Display stays inside festival zone vs device toggle.
|
||
- **Acceptance:** `§21` queries work offline; wrong-never-online clock is accepted residual with warnings, not READY demotion.
|
||
|
||
## Stage 12 — Map
|
||
|
||
- **Prerequisites:** §9 + `§22` types; provisional art meeting caps suffices for build even without real art (placeholder art does not block dev `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:105` OQ-6).
|
||
- **Work:** `domain/map` + `views/map` — two-level WebP decoding (≤1600/≤3072/≤35 MB, one resident), DOM POI buttons, CSS transform pan/zoom with counter-scaled markers, Facilities list first-class (`SPIKE-03:79`), search highlight, category filter, object-URL lifecycle F-3, dark filter F-4 (`SPIKE-03:88`).
|
||
- **Tests:** Headless mechanics OK (EXP-3 caveat `experiments/README.md:36`); unit decode-budget guards; filter/search; navigation without GPS.
|
||
- **Acceptance:** Correct at mechanism level on headless; production proof deferred to device T12/T13 `§36` (`SPIKE-03:116` 6-item protocol: ≥30 fps, no leaks, a11y VoiceOver/TalkBack, texture-cap fallback).
|
||
|
||
## Stage 13 — Festival information
|
||
|
||
- **Prerequisites:** Structured blocks types `§23`.
|
||
- **Work:** `domain/festival` + `views/festival` — renderer for structured nodes (no raw HTML, C-22). Category-driven nav, shared search index, readability (day/night contrast).
|
||
- **Tests:** Escaping/structured-node mapping + search index; no `innerHTML` of content lint passes (B-7).
|
||
- **Acceptance:** Signed required section; degraded BASELINE_ONLY correctly excludes it while emergency stays.
|
||
|
||
## Stage 14 — User state
|
||
|
||
- **Prerequisites:** Earlier favorite model must align with stable ids (§24).
|
||
- **Work:** `domain/favorites` over `lumen-user.favorites` (write-through), `prefs` (theme/clock), scrubbed `diag` ring buffer, flags for coach. Idempotent `lumen-user` migrations separate from package schema (`SPIKE-04:210`).
|
||
- **Tests:** Favorite survives A/B updates/rollback (`SPIKE-02:69` X3); moved/cancelled/orphan handling; no cross-slot GC into `lumen-user`.
|
||
- **Acceptance:** X3 analog passes at integration level.
|
||
|
||
## Stage 15 — Synchronization
|
||
|
||
- **Prerequisites:** Ready `§9` + verifier `§7` + activation `§8` + `§25` seam.
|
||
- **Work:** `SyncService` pull-only on open/`online`/manual `§26` — `latest.json` monotonic check → manifest verify → budget/compat → stage → full verify → flip; quarantine; budget + compatibility + hash rejection paths. `Transport` seam with `HttpTransport` only (`DISCOVERY.md:596` ME-4).
|
||
- **Tests:** §18.2 nine-stage fault tests (`ARCHITECTURE-DESIGN.md:664`): pointer error, manifest error, bad sig (quarantine), incompatible, interrupted download, quota, full-verify mismatch, activation txn fail, post-activation readback rollback.
|
||
- **Acceptance:** Every row in `§31` FA-8/FA-9/FA-13 satisfied; no background work (C-19, `ARCHITECTURE-DESIGN.md:78`).
|
||
|
||
## Stage 16 — Accessibility
|
||
|
||
- **Prerequisites:** Views exist (10–14).
|
||
- **Work:** Landmarks, heading order, focus management per `§32`; map Facilities list; emergency targets/contrast; 48 px targets; reduced-motion; standalone safe-area handling (`ARCHITECTURE-DESIGN.md:217`).
|
||
- **Tests:** Automated a11y (headings, landmarks, contrast) + manual screen-reader passes on Emergency/Schedule/Facilities per `§32` / `§36` T13.
|
||
- **Acceptance:** No feature merges without a11y checklist in `§40`.
|
||
|
||
## Stage 17 — Performance
|
||
|
||
- **Prerequisites:** Meaningful budgets from cached staging.
|
||
- **Work:** CI gates enforcing `§33` (bundle JS 150 KB gz / shell 1 MB, dataset 40 MB / file 6 MB, decode 35 MB, cold 2 s/3 s, boot 150 ms, map 30 fps). Optimisations: windowed schedule list if needed, single JSON parse per section, no idle timers `ARCHITECTURE-DESIGN.md:858`.
|
||
- **Tests:** Light perf harness for boot verify, schedule 1k, WebP decode memory meter.
|
||
- **Acceptance:** Every gate green; any exceedance requires ADR amendment — do not silently raise budgets (`RULES`).
|
||
|
||
## Stage 18 — Device testing
|
||
|
||
- **Prerequisites:** All above buildable with fixtures; at least one staging edition smoke-verified (stage 6).
|
||
- **Work:** Execute the full physical-device matrix `§36` (19 groups × D1–D4, 76 tests). Devices: D1 iOS 16.4, D2 iOS latest, D3 low-end Android 2021 ~Chrome 110+, D4 modern Android `ARCHITECTURE-VALIDATION.md:158`. Scripts cover T01–T19 as worded there.
|
||
- **Tests:** Device tests themselves — every BLOCKING must pass before production festival; bugs that harness missed feed back into harness (`ARCHITECTURE-DESIGN.md:843`).
|
||
- **Acceptance:** UNVERIFIED items from spikes (IDB jetsam, quota/Blob, map/GPU, JSC Intl, `tel:`, SW re-registration, cold-start) become CONFIRMED by observation or fall back to documented degraded paths (overview-only, tile-split, re-prep).
|
||
|
||
## Stage 19 — Deployment
|
||
|
||
- **Prerequisites:** Pipeline producing signed packages; verifier audit (AQ-06) and key custody drill (AQ-21) in progress.
|
||
- **Work:** Chosen provider (AQ-14) and production origin (AQ-18, hard to reverse) + HSTS/CSP/headers `ARCHITECTURE-DESIGN.md:807`, two envs `staging`/`production` `ARCHITECTURE-DESIGN.md:817`, runbooks `ARCHITECTURE-DESIGN.md:697` (publish smoke re-verify, rollback via new version, emergency gate). Provisional staging remains usable until public link (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:116`).
|
||
- **Tests:** Staging → production dry-run festival (AQ-23 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:121`) with test edition + field devices.
|
||
- **Acceptance:** Staging smoke-verified package activates on real devices; rollback via `latest.json` advance is exercised; emergency floor provenance correct.
|
||
|
||
---
|
||
|
||
# RULES FOR FUTURE AI CODING AGENTS
|
||
|
||
1. **Read before you code.** Read — in full — `ARCHITECTURE-VALIDATION.md`, `ARCHITECTURE-DECISIONS.md`, `ARCHITECTURE-DESIGN.md`, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md`, all eight SPIKE docs, and `DISCOVERY.md` before touching code.
|
||
2. **Do not invent infrastructure.** Static CDN + import maps only; no backend/DB/auth/mesh service because none exists in the architecture (`ARCHITECTURE-DESIGN.md:815`). If you need one, file an ADR and wait for approval.
|
||
3. **Do not add dependencies without justification.** Target only the audited Ed25519 verifier beyond stdlib (`ARCHITECTURE-DESIGN.md:930` rule 3). Any new runtime dep requires an ADR addendum with size impact (JSP 150 KB gz, `§33`).
|
||
4. **Do not silently replace decisions.** If vanilla TS + tiny store feels inconvenient, use the Preact reconsider trigger in ADR-003 (`ARCHITECTURE-DECISIONS.md:86`) via a documented proposal — do not just switch.
|
||
5. **Do not introduce accounts/authentication.** V1 has no login (`§2`, `INVARIANT I-15`); favorites are device-local. Any future account needs explicit approval and must not touch emergency/schedule paths.
|
||
6. **Do not introduce mesh in V1.** Seam costs one interface `§25`; no WebRTC/BLE/ad-hoc code, no discovery/routing (`DISCOVERY.md:270` NR-1, `INVARIANT I-14`). A future transport is the only extension.
|
||
7. **Do not create a hidden network dependency.** No critical view, domain service, readiness check, emergency render, or boot path may `fetch` or await a server. Sync runs only as `SyncService` enhancement with `online` hint (`INVARIANT I-8`, C-19 `ARCHITECTURE-DESIGN.md:78`).
|
||
8. **Do not put festival content directly into UI components.** Content lives in the signed Festival Data Package `§15` and is read via `data/` APIs; no hard-coded schedules, POIs, or emergency strings in components. `§6` B-2, `DISCOVERY.md:230` C13, failures `FA-6`/`FA-9`.
|
||
9. **Do not duplicate emergency data by hand.** Floor and dataset section share the same source sheet (`ARCHITECTURE-DESIGN.md:345`, `SPIKE-06:12`). Manual copy-paste creates drift — generate, do not duplicate `§14` (and enforce the 16 KB cap).
|
||
10. **Do not bypass validation or A/B activation.** Check signature before hash before schema before activate, quarantine, and flip in a single `lumen-system` txn `§§11–12` — every rejection path in `§31`. B-4 `ARCHITECTURE-DESIGN.md:195`. Shortcuts fail `INVARIANTS I-9…I-12` and `§40`.
|
||
11. **Do not drop the defensive IDB protocol.** P1–P8 `SPIKE-01:140`: short txns, `QuotaExceededError` keeps active, free-space 2× pre-check, 6 MB cap, no dataset in SW, boot light detection.
|
||
12. **Do not weaken accessibility for convenience.** A11y is BLOCKING in `§36` T13 and `§40` checklist; POIs remain real `button`s with 48 px and Facilities list is first-class, not fallback `SPIKE-03:79`.
|
||
13. **Do not silently raise budgets.** Map 1600/3072/35 MB (`SPIKE-03:58`), dataset 40 MB, file 6 MB, bundle 150 KB gz / 1 MB, boot 150 ms, cold 2 s/3 s, map 30 fps — all in `§33`. Negotiate an ADR if genuinely needed; do not inflate in code.
|
||
14. **Do not treat UNVERIFIED as confirmed.** IDB jetsam atomicity (`SPIKE-01:75`), JSC Intl parity (`SPIKE-08:152`), SW lifecycle (`SPIKE-01:82`), storage pressure/Blob (`SPIKE-01:159`), map decode (`SPIKE-03:116`), `tel:` standalone (`SPIKE-01:171`) are queued for `§36` device matrix. A desktop pass does not clear them.
|
||
15. **When a conflict is discovered, stop and document.** File a gap note citing the conflicting lines (e.g., `ARCHITECTURE-DECISIONS.md:129` vs `SPIKE-01:140`) and propose a patch — do not silently pick a side. The valid resolver is the validation report `ARCHITECTURE-VALIDATION.md:1` which already reconciles all eight spikes.
|
||
16. **Respect the ordering and layering contracts.** Per-file atomic + quarantine (F-1, `§10`), activation single txn (P2/F-2 `§12`), time-sane rendering via explicit-zone `Intl` only (F-4 `§21`), content-as-data via structured nodes (C-22 `§23`), transport byte-seam (`§25`), and `INVARIANTS I-16/I-17` behind the transport boundary.
|
||
|
||
---
|
||
|
||
# CROSS-TRACE: every major rule → validated decision
|
||
|
||
| Rule in contract | Source decision(s) | Spike reconciliation |
|
||
|---|---|---|
|
||
| V1 scope / NL-1…NR-15 §1/§2 | ADR-001…ADR-014, `ARCHITECTURE-DESIGN.md:25` | `ARCHITECTURE-VALIDATION.md:40` §2 (no spike contradicts scope) |
|
||
| Stack vanilla TS / no framework §3 | ADR-003 `ARCHITECTURE-DECISIONS.md:86` | SPIKE-03 EXP-3 `SPIKE-03:28` + `ARCHITECTURE-VALIDATION.md:56` (no dedicated spike, YELLOW device perf) |
|
||
| Thin IDB wrapper + Cache for shell §3/§8/§9 | ADR-004 `ARCHITECTURE-DECISIONS.md:129` | SPIKE-01 P1–P8 `SPIKE-01:140`, ACCEPT WITH CHANGES |
|
||
| Package JSON+assets+signed manifest §15 | ADR-005 `ARCHITECTURE-DECISIONS.md:177` | SPIKE-04 F-1…F-5 `SPIKE-04:203`, ACCEPT WITH CHANGES |
|
||
| A/B inactive staging + single-txn activation §10/§12 | ADR-006 `ARCHITECTURE-DECISIONS.md:225` | SPIKE-02 F-1…F-4 `SPIKE-02:89` 16/16 PASS, proof `SPIKE-02:73` |
|
||
| Emergency 3 tiers (floor compiled) §14 | ADR-007 `ARCHITECTURE-DECISIONS.md:272` | SPIKE-06 `SPIKE-06:12` + F-1 `SPIKE-06:75` |
|
||
| Map raster+DOM+list §22 | ADR-008 `ARCHITECTURE-DECISIONS.md:315` | SPIKE-03 F-1 `SPIKE-03:58`, F-3 `SPIKE-03:68`, F-4 `SPIKE-03:88` |
|
||
| Time UTC+IANA+ClockService §21 | ADR-009 `ARCHITECTURE-DECISIONS.md:362` | SPIKE-08 24/24 `SPIKE-08:43`, F-3 `SPIKE-08:97`, F-4 `SPIKE-08:148` |
|
||
| Sync pull-only §26 | ADR-010 `ARCHITECTURE-DECISIONS.md:406` | C-19/B-06/PW-4 `DISCOVERY.md:433` + cheapest-first ordering `SPIKE-02:29` |
|
||
| Transport byte seam §25 | ADR-011 `ARCHITECTURE-DECISIONS.md:441` | `DISCOVERY.md:596` ME-4 + signed-payload rule |
|
||
| Mesh future-only §27 | ADR-012 `ARCHITECTURE-DECISIONS.md:482` | `ARCHITECTURE-VALIDATION.md:475` scenario 20 + feasibility `DISCOVERY.md:581` |
|
||
| Security: sig+hash+key set+CSP §28 | ADR-013 `ARCHITECTURE-DECISIONS.md:524` | TQ-9/SQ-1 pure-JS verifier correct per `ARCHITECTURE-DESIGN.md:14`, SPIKE-02 rejection paths `SPIKE-02:60` |
|
||
| Static CDN single origin §37 | ADR-014 `ARCHITECTURE-DECISIONS.md:575` | SPIKE-04 gates `SPIKE-04:189` + origin-keyed strand risk `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:116` |
|
||
| Offline Ready C1–C8 + six states §20 | `ARCHITECTURE-DESIGN.md:388` §12 + ADR-006 | SPIKE-05 `SPIKE-05:28`/`SPIKE-05:60` + time independence `SPIKE-05:47` |
|
||
| Invariants I-1…I-17 | `ARCHITECTURE-VALIDATION.md:301` §3 + `DISCOVERY.md:230` §5 | Every freeze-test scenario `ARCHITECTURE-VALIDATION.md:524` §6 proven after spikes |
|
||
| Device matrix §36 | `ARCHITECTURE-DESIGN.md:832` §26 → `ARCHITECTURE-VALIDATION.md:156` §4 | All §5 UNVERIFIED queues in spikes |
|
||
| Hard rules B-1…B-7 `§6` | `ARCHITECTURE-DESIGN.md:195` + `ARCHITECTURE-DESIGN.md:930` | Reinforced by `SPIKE-01:140` P8 + `SPIKE-06:75` |
|
||
|
||
---
|
||
|
||
# INTENTIONALLY PROVISIONAL (do not pretend otherwise)
|
||
|
||
Per `ARCHITECTURE-VALIDATION.md:476` §7 YELLOW tables and `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:9` validation plan:
|
||
|
||
- **Physical-device YELLOW (not blocking start, blocking production):** IDB atomicity near-quota/jetsam (AQ-04), map decode/fps/leaks/texture-cap (AQ-11, `SPIKE-03:116`), JSC/DayKey/ClockService on iOS (AQ-08, `SPIKE-08:152`), SW re-registration, `tel:` in standalone, `persist()` heuristics. These are `§36` T07/T08/T10/T12/T13/T15/T16, not architecture gaps.
|
||
- **Content YELLOW (not blocking start):** A-02 budgets `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:16` with pipeline gates `SPIKE-04:189` + real schedule/map samples (D-01/D-02/AQ-11) `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:73`, OQ-6 illustrated art.
|
||
- **Ops YELLOW (not blocking start on provisional origin):** production origin (AQ-18 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:116`), provider (AQ-14 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:112`), emergency sign-off gate O-02/OQ-2 `DISCOVERY.md:345`, legal review, key custody drill AQ-21/S-01 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:85` (verifier audit AQ-06 `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:29`).
|
||
|
||
The only class that could ever turn YELLOW to RED is a device-matrix failure for which fallbacks are already defined (overview-only mode, 2×2 tile-split DD-9 `ARCHITECTURE-DESIGN.md:918`, Preact trigger ADR-003, re-prep path).
|
||
|
||
---
|
||
|
||
# FIRST IMPLEMENTATION PHASE (upon approval)
|
||
|
||
Stage 1 — Project foundation (`IMPLEMENTATION SEQUENCE Stage 1`): dedicated repo at `/home/avi/Projects/Lumen` (per VCS-1 `DISCOVERY.md:34`), TypeScript strict base, lint rule enforcing B-1…B-7 `§6`, commit hooks, CI skeleton — no deps yet. This is gated only by the decision to proceed; no provisional item blocks it.
|
||
|
||
---
|
||
|
||
*End of Implementation Contract. No application source was created to write it. Cross-check instruction: verify every row above appears in `ARCHITECTURE-VALIDATION.md` and reconcile any drift by preferring the validation report.*
|