Stage 1: project foundation (strict TS, lint boundaries B-1..B-7, directory structure, CI, boundary tests)
- dedicated git repo at /home/avi/Projects/Lumen (main)
- TypeScript strict (target ES2022, bundler, exactOptionalPropertyTypes, noUncheckedIndexedAccess)
- ESLint 9 + typescript-eslint strictTypeChecked + eslint-plugin-boundaries for B-1..B-7, no-restricted-globals/syntax for B-1/B-7
- Prettier 3.5
- Structure per IMPLEMENTATION-CONTRACT.md §4 (src/platform/idb|cache|sw, storage, data, sync/{transport,verifier}, domain/{emergency,schedule,map,festival,readiness,clock,favorites}, ui/{components,views,router,render}, app, emergency-baseline, assets, public, content, pipeline, tests, scripts)
- CI: .github/workflows/ci.yml (typecheck + lint + format + test)
- Boundary tests: tests/unit/boundaries.test.ts (4 tests) + scripts/check-boundaries.ts
- No feature code, no PWA/IDB/sync/mesh/accounts per contract Stage 1
This commit is contained in:
commit
c0bfd413ff
100 changed files with 9863 additions and 0 deletions
573
ARCHITECTURE-VALIDATION.md
Normal file
573
ARCHITECTURE-VALIDATION.md
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
# Lumen — Architecture Validation Report
|
||||
|
||||
- **Phase:** Architecture Validation — consolidation of SPIKE-01 … SPIKE-08
|
||||
- **Date:** 2026-08-30
|
||||
- **Source documents:** `DISCOVERY.md`, `ARCHITECTURE-DESIGN.md`, `ARCHITECTURE-DECISIONS.md`, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md`, `SPIKE-01`…`SPIKE-08`, `experiments/README.md` + `exp1`/`exp2`/`exp3`
|
||||
- **Scope guard:** No application code, no `package.json`, no dependencies, no PWA/DB/network implementation was created in this phase. All claims below reconcile spike evidence against design decisions without introducing implementation.
|
||||
- **Previous state:** 7 spikes existed (`SPIKE-01`…`SPIKE-07`) when this report was requested; `SPIKE-08` was created immediately before this report (exp1 already existed per `experiments/README.md:11`). This report is the first document that reconciles all eight together and freezes the architecture.
|
||||
|
||||
## Relationship to prior phase
|
||||
|
||||
`DISCOVERY.md:9` and `ARCHITECTURE-DESIGN.md:9` noted narrowings/resolutions. This report does not re-litigate discovery; it checks whether the design decisions survive adversarial validation and records the exact changes the spikes require before implementation may start.
|
||||
|
||||
---
|
||||
|
||||
# 1. Validation summary
|
||||
|
||||
## 1.1 Spike inventory
|
||||
|
||||
| Spike | Decision(s) under test | Method | Evidence class | Result |
|
||||
|---|---|---|---|---|
|
||||
| SPIKE-01 IndexedDB + iOS (`SPIKE-01-INDEXEDDB-IOS.md:1`) | ADR-004, foundations of ADR-006 | Spec + WebKit/Chromium policy analysis; workload ≤40 MB/90 MB worst case (`SPIKE-01:22`) | CONFIRMED (spec), INFERRED (quota/BLOB), UNVERIFIED (device) | **ACCEPT WITH CHANGES** P1–P8 |
|
||||
| SPIKE-02 Atomic A/B (`SPIKE-02-ATOMIC-DATASET-UPDATES.md:1`) | ADR-006 | State-machine simulation `exp2-ab-update-sim.mjs:1` with crash/fault injection at 9 stages | CONFIRMED (simulation) 16/16 PASS | **ACCEPT WITH CHANGES** F-1…F-4 |
|
||||
| SPIKE-03 Map (`SPIKE-03-MAP-REPRESENTATION.md:1`) | ADR-008 | Candidate matrix + decode-memory analysis + `exp3-map-bench.html` (headless) | CONFIRMED (JS ~0ms), INFERRED (GPU/memory), UNVERIFIED (device fps) | **ACCEPT WITH CHANGES** F-1,F-3,F-4 |
|
||||
| SPIKE-04 Package (`SPIKE-04-FESTIVAL-DATA-PACKAGE.md:1`) | ADR-005, parts of ADR-013/ADR-007 | Schema design + pipeline gate analysis | CONFIRMED (design) | **ACCEPT WITH CHANGES** F-1…F-4 |
|
||||
| SPIKE-05 Offline Ready (`SPIKE-05-OFFLINE-READY.md:1`) | ARCH §12, invariants 8/10, ADR-006/ADR-005 | Predicate formalization | CONFIRMED (logic) | **ACCEPT (formalized)** C1–C8, time independence, FAILED state |
|
||||
| SPIKE-06 Emergency baseline (`SPIKE-06-EMERGENCY-BASELINE.md:1`) | ADR-007 | Tier analysis + failure cases | CONFIRMED (design) | **ACCEPT** + F-1 hardening |
|
||||
| SPIKE-07 Bootstrap (`SPIKE-07-BOOTSTRAP.md:1`) | ADR-002, ARCH §11/§12 | Journey + failure-point analysis | CONFIRMED (design) | **ACCEPT WITH CHANGES** L0–L2 |
|
||||
| SPIKE-08 Time model (`SPIKE-08-TIME-MODEL.md:1`) | ADR-009, SPIKE-05 time independence | `exp1-time-model.mjs:1` 24/24 PASS (V8/ICU ECMA-402) | CONFIRMED (V8/ICU), INFERRED (JSC), UNVERIFIED (device) | **ACCEPT WITH CHANGE** F-3 |
|
||||
|
||||
Experiments re-executed for this report on this host: `exp1-time-model.mjs:129` 24/24 PASS; `exp2-ab-update-sim.mjs:241` 16/16 PASS. `exp3` desktop caveat stands (`experiments/README.md:36`).
|
||||
|
||||
## 1.2 What was validated vs what was not
|
||||
|
||||
- **Validated at design-logic level on this host:** UTC/IANA rendering, dayKey, DST, Now/Next, A/B invariant under 14 crash/fault points, map-candidate JS cost, readiness predicate, emergency tier resolution, bootstrap levels, package schema/no-expiration/emergency sub-versioning.
|
||||
- **Inferred from vendor policy/spec but not observed here:** iOS storage quotas/eviction/persist heuristics, BLOB/IDB near-quota behaviour, JSC parity for Intl, CDN Date accuracy, SW lifecycle on iOS.
|
||||
- **Unverified and queued for physical devices:** all of the above as experienced by Lumen on actual iPhones/Androids plus performance (fps, decode, cold start). This is expected and documented in every spike's §5.
|
||||
|
||||
---
|
||||
|
||||
# 2. Decision reconciliation
|
||||
|
||||
For each ADR: original decision → spike finding → final validated decision → changes incorporated → unresolved.
|
||||
|
||||
## ADR-001 Offline-first
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:14`):** Accepted. Offline-first; local source of truth; no blocking network on critical paths.
|
||||
- **Spikes touching it:** All. SPIKE-05 makes the readiness predicate time-independent; SPIKE-07 defines L0–L2 so the invariant holds even partially provisioned; SPIKE-02 proves updates preserve it under crashes.
|
||||
- **Final:** **Unchanged — Accepted.** No spike contradicts it. Reinforced by SPIKE-05 F-1 (clock does not demote READY) and SPIKE-07 minimum-safe guarantees. No unresolved issues.
|
||||
|
||||
## ADR-002 PWA / installation
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:48`):** Installed-first PWA, single origin, manual iOS coach, functional in tab with degraded persistence warning.
|
||||
- **Spike findings:** SPIKE-01 §3.12/§5 confirms 7-day ITP tab eviction vs installed exemption is INFERRED/UNVERIFIED and load-bearing; SPIKE-07 introduces **L0–L2 preparation levels** (`SPIKE-07-BOOTSTRAP.md:31`), fixed download order `emergency→schedule→info→map→assets`, minimum-safe L1 (emergency+schedule), and explicit rule that **prep before install is allowed** (`SPIKE-07:58`). SPIKE-05 maps L0/L1/L2 to NOT_READY/PARTIAL/READY.
|
||||
- **Final:** **Accepted with addendum.** Install-first stands; the only change is the formal L0–L2 model, ordered download, and tab-before-install allowance. No scope creep. **Unresolved:** `persist()` grant rates and tab-eviction timing remain UNVERIFIED (device matrix).
|
||||
|
||||
## ADR-003 Frontend technology
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:82`):** Vanilla TypeScript, no framework; Preact as explicit reconsider trigger.
|
||||
- **Spike findings:** No dedicated spike (TQ-1 not run as a benchmark beyond EXP-3 JS cost). SPIKE-03 EXP-3 confirms no candidate has disqualifying JS cost (`SPIKE-03-MAP-REPRESENTATION.md:28`), which is consistent with but not proof of vanilla perf. DISCOVERY §11 TQ-1/TQ-10 remain device-dependent.
|
||||
- **Final:** **Accepted — unchanged.** The reconsider trigger is retained deliberately. No spike requires a framework. **Unresolved:** low-end Android render/JS parse cost remains UNVERIFIED until device matrix and real schedule-list measurement (≤100 ms target, `ARCHITECTURE-DESIGN.md:853`). Risk RK-7 unchanged; mitigation is the layering in `ARCHITECTURE-DESIGN.md:195` B-1…B-7 which keeps domain/data untouched by a future view-layer swap.
|
||||
|
||||
## ADR-004 Local storage
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:126`):** Proposed. IndexedDB (thin wrapper) + Cache Storage for shell + localStorage flags; assets as Blobs in slot DB; OPFS/SQLite-WASM rejected.
|
||||
- **Spike findings:** SPIKE-01 ACCEPT WITH CHANGES. Validates IDB is correct (spec CONFIRMED for transactions/atomicity/multiple DBs) but mandates defensive protocol **P1–P8** (`SPIKE-01-INDEXEDDB-IOS.md:140`): one short txn per file (bytes+progress together), activation single txn, wrapped IDB + QuotaExceededError handling, free-space pre-check 2× package, 6 MB single-record cap, persist requested but not relied upon, boot light verification as detection, no dataset state in SW. SPIKE-02 confirms this protocol plus F-1 (bytes+progress atomic) is what makes the A/B invariant hold.
|
||||
- **Final:** **Accepted with conditions — status moves from Proposed to Accepted (YELLOW, device-dependent).** The layout is validated at design-logic level; production readiness is conditional on the device tests in `SPIKE-01:159` (fresh install + reboot with ~40 MB, quota-near-full writes, kill-mid-download resume, Blob read-back). No alternative store is better for this workload. Changes P1–P8 are normative and now reflected in `ARCHITECTURE-DESIGN.md:9`.
|
||||
|
||||
## ADR-005 Festival Data Package
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:174`):** Accepted. JSON sections + assets + manifest + Ed25519 signature; content repo → pipeline → static CDN; immutable versioned URLs + mutable latest.json.
|
||||
- **Spike findings:** SPIKE-04 ACCEPT WITH CHANGES + SPIKE-05/06/08 cross-checks. Adds: **F-1** `sections[*].required` flag gating readiness (`SPIKE-04:64`), **F-2** no-expiration rule for datasets (`SPIKE-04:50`), **F-3** emergency independent `emergencySchemaVersion`/`contentVersion` (`SPIKE-04:70`, `SPIKE-06:110`), **F-4** user-data schema separate from package schema (`SPIKE-04:210`). SPIKE-08 confirms UTC+IANA+dayKey works offline; SPIKE-05 confirms time-independent readiness needs F-2.
|
||||
- **Final:** **Accepted with addendum.** Schema is now normative as in `SPIKE-04:79` fragments; no binary format needed at this scale (F-5). **Unresolved:** pipeline gates need real content to prove budgets; stable IDs (AQ-20) remain Proposed until source samples are inspected.
|
||||
|
||||
## ADR-006 Atomic updates and rollback
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:220`):** Accepted. A/B dual-slot, staged hash-verified, single-txn activation, rollback, page-context downloads (C-21), monotonic versions.
|
||||
- **Spike findings:** SPIKE-02 ACCEPT WITH CHANGES, 16/16 PASS (`SPIKE-02:50`). Mandates: **F-1** bytes+progress in same txn, **F-2** verification record in activation txn, **F-3** `readbackPending` flag carried across boots, **F-4** rollback depth = 1 (new staging wipes inactive slot — accepted trade-off). SPIKE-01 P1–P8 are preconditions. Ordering proof in `SPIKE-02:73` — single-DB atomicity is sufficient because staging completes and validates before the pointer moves.
|
||||
- **Final:** **Accepted with addendum (F-1…F-4).** Invariants 5–7 are proven at state-machine level given IDB transaction atomicity (which itself is INFERRED/UNVERIFIED on iOS per `SPIKE-01:75`). The only remaining blocker is physical proof of IDB atomicity under jetsam kill on iOS.
|
||||
|
||||
## ADR-007 Emergency baseline
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:266`):** Accepted. Three tiers: embedded floor (≤16 KB, same-source-generated), dataset section, reserved T3 notices; provenance labels.
|
||||
- **Spike findings:** SPIKE-06 ACCEPT. Fixes tier-1 contents (`SPIKE-06:12` exact JSON shape, life-safety minimum only), resolution rules (`SPIKE-06:60` prefers highest compatible, defensive merge), forward-tolerant floor renderer and **zero-IDB requirement** for floor path (`SPIKE-06:75` F-1), eviction/compatibility/corruption cases. SPIKE-05 C8 asserts floor presence in readiness.
|
||||
- **Final:** **Accepted — addendum records fixed tier contents, merge rules, and F-1.** No direction change. **Unresolved:** organizer-provided emergency content correctness still requires the sign-off gate (`DISCOVERY.md:346` OQ-2, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:62` O-02) — a product/ops gate, not an architecture defect.
|
||||
|
||||
## ADR-008 Map
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:308`):** Proposed. Raster WebP ≤2 levels + DOM POI overlay + CSS transform + Facilities list; no GPS; no tiles.
|
||||
- **Spike findings:** SPIKE-03 ACCEPT WITH CHANGES. **F-1** tightens caps: overview ≤1600 px longest edge, detail ≤3072 px, total decoded ≤~35 MB, at most one detail resident (`SPIKE-03:58`); replaces prior 4096 cap. **F-3** lazy object-URL lifecycle (`SPIKE-03:68`), **F-4** dark-mode treatment (`brightness(.72) saturate(.85)`, optional night asset hook `SPIKE-03:88`). EXP-3 desktop result is non-discriminating — decision rests on memory/a11y/workflow reasoning. Flip conditions documented (`SPIKE-03:98`).
|
||||
- **Final:** **Accepted with addendum — YELLOW (device-dependent).** Representation stands; production declaration is conditional on device protocol in `SPIKE-03:116` (fps ≥30, decode latency, no URL leaks, screen-reader pass, 2048 texture-cap fallback). Budgets updated (`ARCHITECTURE-DESIGN.md:855`).
|
||||
|
||||
## ADR-009 Time model
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:354`):** Accepted. UTC epoch + IANA zone + precomputed dayKey + ClockService (server offset + monotonic anchor + ±45d sanity window).
|
||||
- **Spike findings:** SPIKE-08 ACCEPT WITH CHANGE, 24/24 PASS (`SPIKE-08:43`). Confirms T1 (zone rendering), T2 (dayKey), T3 (DST), T4 (unusual zones), T5 (skew arithmetic), T7 (Now/Next). **F-3 change** is required: sanity warning suppressed until `now ≥ festivalStart − WINDOW` or a skew has been captured (`SPIKE-08-TIME-MODEL.md:97`), otherwise early preparation (July for September festival) would spuriously warn every user. F-4 clarification: only `Intl.DateTimeFormat` with explicit `timeZone` is allowed.
|
||||
- **Final:** **Accepted with change F-3 + clarification F-4.** Time remains the only user-visible heuristic; readiness is explicitly time-independent per `SPIKE-05:47`. **Unresolved:** JSC parity on iOS and real CDN `Date` header observation remain UNVERIFIED (device matrix).
|
||||
|
||||
## ADR-010 Synchronization
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:396`):** Accepted. Pull-only on open / online hint / manual; monotonic versions; no background sync, no push, no uploads.
|
||||
- **Spike findings:** No dedicated spike; covered indirectly by SPIKE-02 verification ordering (`SPIKE-02:29` cheapest/authoritative first) and SPIKE-05 time independence. Discovery C-19/B-06 (no background work) and PW-4 (no iOS Background Sync) confirm the pull model is the only viable one.
|
||||
- **Final:** **Accepted — unchanged.** Latency = time-until-next-open-online remains the documented trade-off. No spike contradicts it. **Unresolved:** `online` as a hint is best-effort; update urgency still depends on organizers opening the app online.
|
||||
|
||||
## ADR-011 Networking abstraction
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:431`):** Accepted. Minimal byte-oriented Transport interface `isAvailable()/fetchPointer()/fetchBytes()` under SyncService; V1 has only HttpTransport; Verifier decides truth.
|
||||
- **Spike findings:** Consistent with SPIKE-02 F-5/F-6 and SPIKE-06/ADR-012's signed-payload rule. No spike required a richer interface; mesh semantics are correctly pushed into future transport impls per `DISCOVERY.md:596`.
|
||||
- **Final:** **Accepted — unchanged.** Cost stays one interface + one impl. No evidence that a message/CRDT abstraction is needed in V1.
|
||||
|
||||
## ADR-012 Future mesh strategy
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:472`):** Accepted (strategy only). Three obligations: Transport seam, signed-payload rule, package/announcement monotonicity; all mesh concerns deferred; out-of-browser companion assumed likely per `DISCOVERY.md:581`.
|
||||
- **Spike findings:** SPIKE-04 F-2/F-3, SPIKE-02 ordering, SPIKE-06 T3 reserved shape are all compatible with this seam. No spike built mesh groundwork and none was needed. `ARCHITECTURE-DESIGN.md:895` risk 16 documents the impossibility-in-browser posture correctly.
|
||||
- **Final:** **Accepted — unchanged.** V1 loses nothing if future mesh proves impossible via browser; HTTP transport is complete product functionality.
|
||||
|
||||
## ADR-013 Security model
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:514`):** Accepted on model, Proposed on crypto library choice pending audit. Ed25519 over manifest SHA-256 + per-file SHA-256, WebCrypto SHA-256 + bundled pure-JS Ed25519 verifier (WebCrypto Ed25519 not on iOS 16.4), key set in shell, CSP `default-src 'self'` etc., data minimization.
|
||||
- **Spike findings:** SPIKE-04 §2 fixes signature/manifest/hash/compatibility ordering (`SPIKE-02:34`), SPIKE-02 proves rejection path (bad sig → keep old, quarantine). Discovery TQ-9/SQ-1 predicted the WebCrypto gap; the pure-JS verifier is the correct resolution per `ARCHITECTURE-DESIGN.md:14`. No spike claims to have audited the verifier library — AQ-06 remains Proposed. Strict CSP impacts hashed-asset build output — design-consistent.
|
||||
- **Final:** **Accepted as a model; crypto library choice remains Proposed (YELLOW) pending audit.** The scheme defeats TH-1…TH-6 with minimal machinery; key custody/rotation runbook remains an ops requirement (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:85` S-01, AQ-21).
|
||||
|
||||
## ADR-014 Deployment
|
||||
|
||||
- **Original (`ARCHITECTURE-DECISIONS.md:564`):** Accepted on topology (static CDN, single origin, immutable versioned URLs, two environments), Proposed on provider choice (AQ-14).
|
||||
- **Spike findings:** SPIKE-04 pipeline gates (`SPIKE-04:189` 5 gates) and SPIKE-01 storage origin-keying make provider choice the only unresolved piece. No spike proposes a dynamic backend; A-17 small-team ops budget still favours static.
|
||||
- **Final:** **Accepted on topology; provider remains Proposed (YELLOW).** Origin choice is hard to reverse (storage is origin-keyed → strands installed data on change), so provisional staging origin is fine for development but production origin must be chosen before any public staging link (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:116` AQ-18, explicitly not blocking implementation).
|
||||
|
||||
---
|
||||
|
||||
# 3. Critical invariants
|
||||
|
||||
Each invariant is checked against the reconciled architecture (after incorporating spike changes). All pass at design-logic level; device-dependent invariants are marked YELLOW pending physical proof.
|
||||
|
||||
| # | Invariant | Verdict | Evidence |
|
||||
|---|---|---|---|
|
||||
| 1 | Emergency baseline always available from app shell (`DISCOVERY.md:230` C2) | **PASS (GREEN)** | Tier-1 floor compiled into shell bytes; renderer has zero-IDB path and is forward-tolerant (`SPIKE-06:75` F-1); survives eviction, failed update, incompatible dataset, BASELINE_ONLY (`SPIKE-06:81`); C8 asserts floor in readiness (`SPIKE-05:43`). |
|
||||
| 2 | Emergency never requires internet | **PASS (GREEN)** | Floor path and T2 rendering have no network calls (`SPIKE-06:91`); sync is enhancement-only (`ADR-001`). |
|
||||
| 3 | Failed update cannot destroy active valid dataset (`DISCOVERY.md:230` C9) | **PASS (YELLOW)** | A/B inactive-slot staging + single-txn activation + automatic rollback on readback fail (`SPIKE-02:50` 14 scenarios + X1–X3 PASS). YELLOW only because IDB atomicity under iOS jetsam is INFERRED/UNVERIFIED (`SPIKE-01:75`). Logic is proven. |
|
||||
| 4 | Partially downloaded dataset can never become active (`DISCOVERY.md:230` C8) | **PASS (GREEN at state-machine level, YELLOW end-to-end)** | Per-file atomic staging + full verification before pointer flip + quarantine (`SPIKE-02:29` ordering). Same device caveat as #3. |
|
||||
| 5 | Integrity & authenticity verified before activation | **PASS (GREEN)** | Ordering: signature → compatibility/budgets → per-file SHA-256+size → schema → activate (`SPIKE-02:29`); verifier is sole decider (B-4). `SPIKE-02:52` bad-sig/bad-hash cases PASS (keep old). |
|
||||
| 6 | Shell/dataset compatibility explicitly checked | **PASS (GREEN)** | Dual check: `schemaVersion ∈ shell.supportedRange` ∧ `appCompatibility` (`SPIKE-04:22`); boot check prefers compatible slot else limited mode (`ARCHITECTURE-DESIGN.md:688`); ordering rule C-23 prevents most skew (`ARCHITECTURE-DESIGN.md:690`). |
|
||||
| 7 | OFFLINE READY = actual local availability, not connectivity (`DISCOVERY.md:230` C10) | **PASS (GREEN)** | Predicate C1–C8 is evidence-based and time-independent (`SPIKE-05:28` + `SPIKE-05:47`); states READY/PARTIAL/NOT_READY/RECOVERY/BASELINE_ONLY/FAILED are proven local evidence (`SPIKE-05:60`). |
|
||||
| 8 | GPS optional (invariant 9) | **PASS (GREEN)** | V1 has no GPS dependency at all; POI `lat/lng` hook preserved only (`SPIKE-03:112`, `ARCHITECTURE-DESIGN.md:540`/`565`). |
|
||||
| 9 | Schedule works offline (`DISCOVERY.md:132` R-S1…S4, R-O1…R-O5) | **PASS (GREEN)** | Schedule is signed section in active slot; all queries (Now/Next, My Schedule, filters/search) are local (`ARCHITECTURE-DESIGN.md:508`); time model works offline (`SPIKE-08:43`). |
|
||||
| 10 | Map works offline (`DISCOVERY.md:172` R-M1, R-O1) | **PASS (YELLOW)** | Raster WebP base + POI overlay all local; Facilities list is first-class GPS-free access (`SPIKE-03:79`). YELLOW pending device performance/decoding (`SPIKE-03:116`). |
|
||||
| 11 | Festival info works offline (`DISCOVERY.md:182` R-F1) | **PASS (GREEN)** | Info is signed required section (`SPIKE-04:30`); renderer maps structured nodes → safe DOM (C-22). |
|
||||
| 12 | Favorites/My Schedule work offline | **PASS (GREEN)** | `lumen-user` store separate; never touched by updates/rollback/GC (B-6, `SPIKE-02:70` X3 favourites survive) |
|
||||
| 13 | No V1 feature depends on mesh (`DISCOVERY.md:268` NR-1, invariant 11) | **PASS (GREEN)** | V1 builds no mesh; seam costs one interface (`ADR-012`). |
|
||||
| 14 | Future mesh has defined extension boundary | **PASS (GREEN)** | Transport seam (ADR-011) + signed-payload rule + immutable versioned payloads (`ADR-012`); location per `ARCHITECTURE-DESIGN.md:704`. |
|
||||
| 15 | No user accounts in V1 (`DISCOVERY.md:270` NR-4) | **PASS (GREEN)** | No auth, no server state; favourites device-local (`ADR-010`). |
|
||||
|
||||
No invariant requires new architecture. Two are YELLOW solely due to device-dependent platform behaviour already isolated and queued for physical testing.
|
||||
|
||||
---
|
||||
|
||||
# 4. Physical-device validation matrix
|
||||
|
||||
## 4.1 Device definitions
|
||||
|
||||
| ID | Device | OS/browser | Why it matters |
|
||||
|---|---|---|---|
|
||||
| D1 | iPhone low-bound | iOS 16.4 Safari (tab + installed PWA) | Baseline policy A-06; worst WebKit for storage/Persist/Intl |
|
||||
| D2 | iPhone latest | iOS latest Safari installed PWA | Current WebKit reality; EU variability sanity check |
|
||||
| D3 | Low-end Android | 2021 mid-range, Chrome ~110+ (and latest if different) | Performance budget target (`ARCHITECTURE-DESIGN.md:851`); 2048 texture-cap cohort |
|
||||
| D4 | Modern Android | Current flagship Android, Chrome latest, installed PWA | High-end correctness + Chrome persistence auto-grant |
|
||||
|
||||
All tests are manual scripted scenarios per `ARCHITECTURE-DESIGN.md:838`. Expired context after 24h; bugs found on device that harness missed become harness cases (`ARCHITECTURE-DESIGN.md:843`).
|
||||
|
||||
## 4.2 Legend
|
||||
|
||||
Severity: **BLOCKING** = cannot ship/recurring data loss or safety impact; **HIGH** = major degradation; **MEDIUM** = degraded UX/workaround exists; **LOW** = polish. BLOCKING tests must pass before public festival use.
|
||||
|
||||
## 4.3 Matrix (19 groups — each has 4 device columns; shared steps where identical)
|
||||
|
||||
### T01 — First visit
|
||||
|
||||
- **Objective:** Shell loads over HTTPS, SW registers, status is NOT_READY, emergency floor renders.
|
||||
- **Preconditions:** Site data cleared for origin; normal (non-private) browsing; HTTPS.
|
||||
- **Steps:** 1) Open `https://<origin>/` 2) Wait for load 3) Check status chip 4) Open Emergency.
|
||||
- **Expected:** Shell ≤1 MB loads quickly; SW installed; chip NOT_READY / "Get festival data"; emergency shows BASELINE with version and address/coordinates/procedures.
|
||||
- **Pass:** All visible within target (<2 s emergency usable on D3 per `ARCHITECTURE-DESIGN.md:851`) with no console errors.
|
||||
- **Severity:** BLOCKING. **Applies:** D1–D4.
|
||||
|
||||
### T02 — Installation
|
||||
|
||||
- **Objective:** Installed-first flow produces persistence-exempt, standalone launch.
|
||||
- **Preconditions:** First visit completed.
|
||||
- **Steps (D1/D2 iOS):** Share → Add to Home Screen → launch from home screen → check `display-mode: standalone` / `navigator.standalone`. (D3/D4 Android): trigger `beforeinstallprompt` where available → install → launch standalone. Attempt "install first (recommended)" and "get data now" paths per `SPIKE-07:58`.
|
||||
- **Expected:** Standalone launch with no browser chrome; `viewport-fit=cover` safe-area respected; install-state detectable; coach dismissible but re-surfaced until installed or READY.
|
||||
- **Pass:** Standalone detection true when launched from home screen; tab still functional when dismissed.
|
||||
- **Severity:** BLOCKING (iOS). HIGH (Android — eviction risk lower). **Precondition note:** failure to install must not block use (`SPIKE-07:48` FP-1) — verify degraded-persistence warning appears in tab mode.
|
||||
|
||||
### T03 — Offline launch
|
||||
|
||||
- **Objective:** Cold start with no network serves shell from Cache Storage + data from IDB; no error walls.
|
||||
- **Preconditions:** READY state achieved (see T09). Then enable airplane mode.
|
||||
- **Steps:** Kill app/browser → airplane ON → launch PWA from home screen → navigate all 4 destinations + Status.
|
||||
- **Expected:** Every screen renders from local stores; status READY (or PARTIAL if L1 only); no spinner that never resolves; light boot check ≤~150 ms (`SPIKE-05:83`).
|
||||
- **Pass:** Emergency, Schedule, Map, Festival all usable offline; no network calls in devtools.
|
||||
- **Severity:** BLOCKING. D1–D4 airplane mode.
|
||||
|
||||
### T04 — Airplane mode (festival simulation)
|
||||
|
||||
- **Objective:** Full festival day in airplane mode — schedule Now/Next, filters/search, favorites, facilities list all local.
|
||||
- **Preconditions:** READY. Airplane ON for entire test.
|
||||
- **Steps:** Browse schedule by day (dayKey groups), test Now/Next at several simulated times, toggle stage/type filters, use shared search, add favourites, open map + facilities list, follow emergency dial/text paths (do not actually dial).
|
||||
- **Expected:** All of the above work with zero connectivity. Favorites persist after kill/restart (write-through).
|
||||
- **Pass:** No feature silently disabled without indication (FA-1 requirement, `DISCOVERY.md:653`).
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T05 — Browser restart
|
||||
|
||||
- **Objective:** Browser/process kill does not lose committed data.
|
||||
- **Preconditions:** READY.
|
||||
- **Steps:** Force-quit browser (swipe away) or `chrome://restart` equivalent → relaunch PWA.
|
||||
- **Expected:** State intact; data verified via light check; favourites preserved.
|
||||
- **Pass:** READY without re-prep.
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T06 — Phone restart
|
||||
|
||||
- **Objective:** Device reboot preserves committed dataset and shell; SW re-registers; cold-start budget met.
|
||||
- **Preconditions:** READY.
|
||||
- **Steps:** Reboot device → launch PWA → measure cold start.
|
||||
- **Expected:** Data intact; SW rehydrates; emergency usable ≤2 s and interactive ≤3 s on D3 (`ARCHITECTURE-DESIGN.md:851`); boot verification is the detection, not prevention (`SPIKE-01:154` P7).
|
||||
- **Pass:** Meets budgets; no re-prep needed. UNVERIFIED on iOS until measured (`SPIKE-01:94`).
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T07 — Storage persistence (`persist()` / exempt)
|
||||
|
||||
- **Objective:** Installed PWA gets favourable persistence signalling; 7-day tab vs installed behaviour is honest.
|
||||
- **Preconditions:** Fresh profile.
|
||||
- **Steps:** Fresh install → prep READY → query `navigator.storage.persist()` (grant) and `persisted()`. Separately: prepare same origin in Safari tab (D1) and leave unused 7+ days vs installed PWA — observe eviction honesty.
|
||||
- **Expected:** D4 auto-granted; D1/D2 heuristic grant when installed (best-effort, never relied upon per `ARCHITECTURE-DESIGN.md:285`); tab data evictable per PW-1, installed exempt per `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:44` B-01 — but must be confirmed on our test devices (`SPIKE-01:162`).
|
||||
- **Pass:** Grant outcome recorded; missing grant does not block use — design never relies on it (P6). Failure of the exemption claim would be BLOCKING on iOS.
|
||||
- **Severity:** HIGH (grant itself) / BLOCKING if exemption fails. D1–D2 primary.
|
||||
|
||||
### T08 — Storage pressure
|
||||
|
||||
- **Objective:** QuotaExhaustedError path keeps active dataset; guidance shown; baseline still works.
|
||||
- **Preconditions:** READY. Device storage deliberately near-full (or devtools fill + `storage.estimate()` low free space).
|
||||
- **Steps:** Attempt new staging (publish new packageVersion) with free < 2× package; then attempt with free just barely enough; observe behaviour when writing blobs near quota.
|
||||
- **Expected:** Pre-check refuses staging if free < 2× size (P4) with plain guidance; mid-write QuotaExceeded aborts staging, active untouched (P3) — `SPIKE-02:60` hash/schema/validation failures map to keep-old.
|
||||
- **Pass:** No crash, no partial activation; Status shows guidance; emergency floor intact. UNVERIFIED on both platforms (`SPIKE-01:56`).
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T09 — Dataset update (happy path)
|
||||
|
||||
- **Objective:** Modern monotonic update stages, verifies, atomically activates, shows READY with new version.
|
||||
- **Preconditions:** READY vN. Staging origin has vN+1 published and signed; online.
|
||||
- **Steps:** Open app → online hint fires → fetch `latest.json` → fetch manifest → signature/compatibility/budget checks → stage sections in priority order `emergency→schedule→info→map` (`SPIKE-07:39`) → per-file hash → full verification → atomic flip → READY confirmation.
|
||||
- **Expected:** New version active after single txn; old slot retained for rollback; status shows version/generatedAt/fetchedAt; quarantine for rejected versions.
|
||||
- **Pass:** EXP-2 S10 PASS analog on device; favourites survive (X3); bloated version rejected by budget gate.
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T10 — Interrupted update
|
||||
|
||||
- **Objective:** Every interruption point preserves the old valid dataset; resume works.
|
||||
- **Preconditions:** READY vN, staging a new vN+1.
|
||||
- **Steps (repeat, kill at different points):** Kill app between files (staged 0/3/5), kill mid-file, kill before activation, kill during activation txn, kill immediately after flip but before readback — as modelled in `SPIKE-02:52` S01–S14. Also: connectivity lost mid-prep, phone reboot before activation.
|
||||
- **Expected:** Before flip → old pointer stands; during flip → txn uncommitted, old stands; after flip → new stands and next-boot light verify confirms or rolls back (`SPIKE-02:64`); progress persisted per file for resume (`SPIKE-01:145` P1); staging older than 7 days discarded (`ARCHITECTURE-DESIGN.md:669`).
|
||||
- **Pass:** No scenario exposes a partial dataset; boot always yields READY (old or new) or RECOVERY+baseline if both slots lost (X2). UNVERIFIED on iOS jetSAM kill (`SPIKE-01:75`).
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T11 — Recovery (eviction / corrupt / missing)
|
||||
|
||||
- **Objective:** Every "data is gone/wrong" state enters RECOVERY (or BASELINE_ONLY) with honest messaging and a one-tap fix.
|
||||
- **Preconditions:** READY.
|
||||
- **Steps:** 1) Simulate eviction: Settings → clear site data (or devtools → delete databases/caches) → relaunch. 2) Simulate corrupt: manually corrupt active slot (test harness analog `SPIKE-02:225` X1/X2) → relaunch. 3) Private-mode launch or storage blocked. 4) Incompatible shell/dataset: launch old data with new-shell supportedRange (`ARCHITECTURE-DESIGN.md:688`).
|
||||
- **Expected:** Missing/corrupt → RECOVERY with reason `missing`/`corrupt`/`incompatible`, emergency floor works; other slot's fallback served when intact (X1); both gone → RECOVERY+baseline+favourites logic preserved (`SPIKE-02:69`). BASELINE_ONLY when storage is entirely unavailable (`SPIKE-05:68`). FAILED(activation) distinct from RECOVERY for repeated txn failures (`SPIKE-05:62`).
|
||||
- **Pass:** No blank screen; correct chip colour; "Restore festival data" or "Check my data" affordances present (`SPIKE-05:62`).
|
||||
- **Severity:** BLOCKING (eviction/corrupt); HIGH (private-mode guidance). D1–D4.
|
||||
|
||||
### T12 — Map rendering
|
||||
|
||||
- **Objective:** Map base decodes within memory/performance budgets and renders legibly.
|
||||
- **Preconditions:** READY with real map art.
|
||||
- **Steps:** Launch Map with overview already staged; measure initial load; verify budgets: overview ≤1600 px / ~7.3 MB decoded, detail ≤3072 px / ~27 MB, total ≤~35 MB, at most one detail resident (`SPIKE-03:58` F-1, `ARCHITECTURE-DESIGN.md:855`).
|
||||
- **Expected:** Overview renders immediately (~0.5 MB WebP); detail lazy-decodes on first zoom-in; no 48–64 MB single bitmap; object URLs revoked on level switch (F-3).
|
||||
- **Pass:** Decode time acceptable; no OOM/jetsam; dimension caps enforced by pipeline.
|
||||
- **Severity:** BLOCKING (OOM/fails to render) / HIGH (quality). D3 primary, D1–D2 sanity.
|
||||
|
||||
### T13 — Map interaction
|
||||
|
||||
- **Objective:** Pan/zoom via pointer-events + pinch meets fps and hit-target and a11y contract.
|
||||
- **Preconditions:** READY; 200 POIs visible.
|
||||
- **Steps:** 1) Pan/zoom sustained 10 s, observe frame drops 2) Overview↔detail↔overview cycling, watch `performance.memory` (Chrome) / Instruments (iOS) for leaks 3) Dark-mode filter `brightness(.72) saturate(.85)` (`SPIKE-03:88`) visual check 4) Older 2048-texture-cap path on D3 low-end vs 3072 image 5) Tap POI → detail sheet, test category chip filtering, search highlight.
|
||||
- **Expected:** ≥30 fps sustained (`ARCHITECTURE-DESIGN.md:854`); no leaked object URLs; screen readers (TalkBack/VoiceOver) traverse POI buttons and Facilities list (`SPIKE-03:120` protocol); fallback overview-only mode engages if detail path janks.
|
||||
- **Pass:** All six items in `SPIKE-03:116` protocol PASS.
|
||||
- **Severity:** BLOCKING (fps/leak), HIGH (a11y), MEDIUM (filter). D3/B1 primary.
|
||||
|
||||
### T14 — GPS (V1 absence)
|
||||
|
||||
- **Objective:** App requests no location permission and shows no blue dot; map/facilities work GPS-free.
|
||||
- **Preconditions:** Fresh profile; READY.
|
||||
- **Steps:** Open app → verify no permission prompt → map → facilities list; optionally deny location if prompted by OS.
|
||||
- **Expected:** Zero permission requests (`ARCHITECTURE-DESIGN.md:222`); last-known behaviour from earlier discovery R-M4 narrowed to no-GPS in V1 (`ARCHITECTURE-DESIGN.md:18`); optional `lat/lng` hook does not affect UX.
|
||||
- **Pass:** No prompt; finding facilities never requires location; available without consent.
|
||||
- **Severity:** MEDIUM (UX trust). D1–D4.
|
||||
|
||||
### T15 — Incorrect device clock
|
||||
|
||||
- **Objective:** ClockService corrects when online, warns honestly when not, never bricks READY.
|
||||
- **Preconditions:** Two runs — (a) never-online device, (b) device later brought online so skew captures.
|
||||
- **Steps (per SPIKE-08 §5):** 1) Render July event → expect 01:00 PM CDT on `America/Chicago` (T1a) 2) Set device clock +30 min → open online → capture skew from `Date` header → go airplane → check Now/Next uses corrected time 3) Mid-session bump clock +10 min → expect drift warning 4) Set clock to month before festival → no warning; then +60 days past end → warning shown (F-3 suppressed-until-near) 5) Airplane + clock wrong by 2h at festival-time → warning, schedule still readable.
|
||||
- **Expected:** Corrected Now when skew exists; warning only near festival or when evidence of skew exists (`SPIKE-08:104` F-3); READY unaffected by clock (`SPIKE-05:47` time independence); never-online wrong clock is accepted residual (`SPIKE-08:124`).
|
||||
- **Pass:** Behaviours match lines above; JSC parity with V8/ICU at DST edges (T3a–T3d).
|
||||
- **Severity:** HIGH (festival-time correctness), MEDIUM (early-prep noise), LOW (never-online residual accepted).
|
||||
|
||||
### T16 — Service worker lifecycle
|
||||
|
||||
- **Objective:** Tiny SW (precache + navigation fallback only) survives iOS killing; page-context downloads are unaffected.
|
||||
- **Preconditions:** READY; staging not required for basic check.
|
||||
- **Steps:** Simulate SW idle kill (devtools terminate SW, or background app for 30 s on iOS) → stage a package in document context → kill SW again → resume staging → verify page progress persists.
|
||||
- **Expected:** C-21 respected: dataset staging runs in page context, not SW (`ARCHITECTURE-DESIGN.md:83`); SW handles only install/activate/fetch (`ARCHITECTURE-DESIGN.md:243`); operations are idempotent/re-runnable; no dataset state in SW (`SPIKE-01:157` P8); activation is next-start, not `clients.claim` mid-session (`ARCHITECTURE-DESIGN.md:256`).
|
||||
- **Pass:** Stage completes regardless of SW death; no corrupted slot.
|
||||
- **Severity:** BLOCKING (if SW death could corrupt — must not). D1/D2 iOS primary.
|
||||
|
||||
### T17 — Application shell update
|
||||
|
||||
- **Objective:** New shell installs under new cache name; activates on next full start; compatibility with both slots checked; mid-session not disrupted.
|
||||
- **Preconditions:** READY vX. New shell vY available (different `lumen-shell-v<build>`).
|
||||
- **Steps:** Fetch new shell → install new SW → verify old SW still serves current session → full restart → observe `SKIP_WAITING` / next-start activation → boot compatibility check (`ARCHITECTURE-DESIGN.md:688` §18.5) → prefer compatible slot, else limited mode with update guidance.
|
||||
- **Expected:** Old session uninterrupted; compatibility envelope publishing rule C-23 honoured (schemas `{1,2}` range, datasets only after shell window, freeze 7 days before festival — `ARCHITECTURE-DESIGN.md:690` §18.6).
|
||||
- **Pass:** No blank screen; no incompatible dataset exposed.
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T18 — Dataset compatibility
|
||||
|
||||
- **Objective:** Incompatible packageVersion or schemaVersion never activates.
|
||||
- **Preconditions:** READY. Staging server publishes v incompatible with this shell.
|
||||
- **Steps:** Attempt sync → observe manifest `appCompatibility`/`schemaVersion` check before download (`SPIKE-02:29` step 4) → attempt older monotonic version.
|
||||
- **Expected:** Incompatible → reject + quarantine + diag; "please update app" when `minAppVersion > current` (`ARCHITECTURE-DESIGN.md:668`); lower version from network never downgrades (`ARCHITECTURE-DESIGN.md:349` monotonic rule, SPIKE-02 F-5).
|
||||
- **Pass:** Active untouched; READY stays; no refetch loop.
|
||||
- **Severity:** BLOCKING. D1–D4.
|
||||
|
||||
### T19 — Emergency baseline availability
|
||||
|
||||
- **Objective:** Every failure path still renders emergency floor with provenance and dial/text paths.
|
||||
- **Preconditions:** Enumerate: never-prepared (NOT_READY), evicted (RECOVERY), corrupt (FAILED), incompatible, storage-blocked (BASELINE_ONLY), offline, update mid-flight.
|
||||
- **Steps:** In each state, open Emergency → verify contents (emergency number + security/first-aid summaries + muster/exits + procedures), labels (`BASELINE v<...> ` vs `FESTIVAL DATA v<package>` per `SPIKE-06:62`), forward-tolerant rendering (unknown fields ignored), `tel:` primary + copyable text, explicit tap to dial, zero IDB access required for floor path.
|
||||
- **Expected:** Floor always renders; highest compatible tier shown; 16 KB cap enforced by pipeline (`ARCHITECTURE-DESIGN.md:367`); single-source generation prevents drift (`ARCHITECTURE-DESIGN.md:345`).
|
||||
- **Pass:** No state shows blank emergency; `tel:` works on standalone (D1/D2) where possible, else number copyable (`SPIKE-06:81`); one-tap away from every screen via persistent nav.
|
||||
- **Severity:** BLOCKING (life-safety). D1–D4, with `tel:` verified separately per `SPIKE-01:171`.
|
||||
|
||||
**Total tests:** 19 groups × 4 device families = 76 device-tests before considering iOS tab vs installed splits for T07/T02 (handled as sub-cases). Every BLOCKING test must pass before a production festival deployment.
|
||||
|
||||
---
|
||||
|
||||
# 5. Known weaknesses
|
||||
|
||||
Weaknesses are not hidden; each has owner/category and whether it blocks implementation. Per `ARCHITECTURE-DESIGN.md:897` declared weaknesses W-1…W-6 and `ARCHITECTURE-DESIGN.md:863` RK-1…RK-10.
|
||||
|
||||
## W-1 — Installation / bootstrap dependency
|
||||
|
||||
- **Risk:** Offline-first requires one successful online bootstrap (shell + dataset) before arrival (`DISCOVERY.md:252` C-17). Attendees who never complete prep arrive with L0 floor only (`SPIKE-07:66` minimum safe).
|
||||
- **Impact:** HIGH — without prep the core promise (schedule/map/info) is not met, even though emergency still works.
|
||||
- **Likelihood:** Medium (depends on distribution effectiveness — `DISCOVERY.md:346` OQ-3, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:60` A-05).
|
||||
- **Mitigation:** L0–L2 with ordered/prioritised resumable downloads, honest PARTIAL messaging, tab-before-install allowed, QR/short-URL campaign, install coach with visuals (`ARCHITECTURE-DESIGN.md:428`), one-tap re-prep, organizer physical fallback (PA/signage/handout — `DISCOVERY.md:324` AMB-1, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:64` O-05).
|
||||
- **Residual:** Medium — a minority of unprepared attendees remains inevitable; architecture softens but cannot eliminate it.
|
||||
- **Owner/category:** Product/Ops (distribution) + Architecture (prep UX).
|
||||
- **Blocks implementation?** No — invalidates product promise only for the unprepared cohort, not the build. Flagged as RK-2.
|
||||
|
||||
## W-2 — Browser storage is best-effort; eviction can never be prevented
|
||||
|
||||
- **Risk:** Storage may be wiped under pressure or by ITP/policy (`DISCOVERY.md:429` PW-1, PW-3, PW-7). Eviction is silent and all-or-nothing (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:45` B-03).
|
||||
- **Impact:** HIGH — festival data lost.
|
||||
- **Likelihood:** Low for installed PWAs with persist granted and regular use (`SPIKE-01:105`), Medium for tab usage or near-full devices (`SPIKE-01:56`).
|
||||
- **Mitigation:** Install-first exempt, `persist()` request (P6, heuristic only), lean footprint (≤40 MB, 2× ≤80 MB bounded), free-space pre-check 2× (P4), boot light verification as detection (`SPIKE-01:154` P7), RECOVERY state with one-tap re-prep, embedded emergency floor survives everything (`SPIKE-06:81`).
|
||||
- **Residual:** Low-medium — loss remains possible, recovery is fast and honest.
|
||||
- **Owner/category:** Platform (browser) + Architecture (detection/recovery).
|
||||
- **Blocks implementation?** No — detection/recovery is the architecture. Device tests prove the path.
|
||||
|
||||
## W-3 — Never-online incorrect device clock
|
||||
|
||||
- **Risk:** Wrong clock makes Now/Next wrong without detection when device has never been online to capture skew (`SPIKE-08:124`, `DISCOVERY.md:452` OF-7).
|
||||
- **Impact:** Medium (misleading "now" during festival).
|
||||
- **Likelihood:** Medium (some devices drift; airplane mode prevents correction).
|
||||
- **Mitigation:** Persisted server offset when any sync occurs (`SPIKE-08:34`), monotonic anchor for mid-session jumps (`SPIKE-08:35`), sanity window warning near festival (F-3 suppressed-until-near, `SPIKE-08:104`), absolute times always shown alongside Now markers (`ARCHITECTURE-DESIGN.md:537`).
|
||||
- **Residual:** Medium for the never-online subset — accepted per `DISCOVERY.md:525` OF-7 and `ARCHITECTURE-DESIGN.md:898` W-2. No network fix exists offline.
|
||||
- **Owner/category:** Architecture (ClockService) + UX (warning/absolute times).
|
||||
- **Blocks implementation?** No.
|
||||
|
||||
## W-4 — Physical-device verification requirements
|
||||
|
||||
- **Risk:** This Linux-host validation is spec/policy-level; iOS JSC parity, IDB atomicity under jetsam, SW re-registration, quota-near-full writes, BLOB read-back, fps/decode, `tel:` in standalone are all UNVERIFIED (`SPIKE-01:59`, `SPIKE-03:116`, `SPIKE-08:152`, `experiments/README.md:28`).
|
||||
- **Impact:** HIGH if any assumption fails on target hardware.
|
||||
- **Likelihood:** Unknown until measured.
|
||||
- **Mitigation:** Narrow device matrix (§4) runs before production; budgets and fallbacks (overview-only, detail tile-split DD-9, Preact reconsider trigger) are predefined.
|
||||
- **Residual:** Unknown pre-device; zero post-device if protocol passes.
|
||||
- **Owner/category:** Architecture Validation / QA.
|
||||
- **Blocks implementation?** Blocks production declaration, not implementation start — staging work proceeds on provisional origin with fixtures (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:128`).
|
||||
|
||||
## W-5 — Emergency content correctness and sign-off
|
||||
|
||||
- **Risk:** Wrong security number, moved AED/muster point, or unapproved procedure text is safety-critical (`DISCOVERY.md:468` EM-1, `DISCOVERY.md:345` OQ-2).
|
||||
- **Impact:** Critical.
|
||||
- **Likelihood:** Low-medium without a gate.
|
||||
- **Mitigation:** Single emergency source sheet → both baseline and dataset section (no drift, `ARCHITECTURE-DESIGN.md:345`), ≤16 KB cap keeps scope life-safety-minimum, signed dataset section + provenance stamps on every screen (`SPIKE-06:60`), sign-off gate with named approver and legal review (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:62` O-02, `DISCOVERY.md:352` OQ-7), version/updatedAt in manifest.
|
||||
- **Residual:** Low with gate enforced; high without it.
|
||||
- **Owner/category:** Content/Ops (organizers) + Pipeline.
|
||||
- **Blocks implementation?** Blocks emergency *content* publish, not scaffolding (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:128`).
|
||||
|
||||
## W-6 — Map asset quality / organizer-provided artwork
|
||||
|
||||
- **Risk:** Organizers may not have raster art at required quality/dimensions; POI placement quality varies (`DISCOVERY.md:328` AMB-3, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:105` OQ-6).
|
||||
- **Impact:** Medium (map degrades to facilities list).
|
||||
- **Likelihood:** Medium (illustrated raster most likely, quality unknown).
|
||||
- **Mitigation:** Architecture accepts raster as-is; pipeline POI tap-tool/normalized coords (`ARCHITECTURE-DESIGN.md:560`), WebP budgets with margin (F-2), predefined fallbacks overview-only and 2×2 tile-split (`SPIKE-03:98`), SVG flip if vector supplied, facilities list is first-class (`SPIKE-03:79`).
|
||||
- **Residual:** Medium — list view compensates but artwork quality caps experience.
|
||||
- **Owner/category:** Content/Ops + MapService.
|
||||
- **Blocks implementation?** No — placeholder art unblocks development (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:128`).
|
||||
|
||||
## W-7 — Vanilla-TS hand-rolled UI accretion
|
||||
|
||||
- **Risk:** No framework safety net; view-layer complexity could accrue bugs (`DISCOVERY.md:863` RK-7, `ARCHITECTURE-DESIGN.md:898` W-5).
|
||||
- **Impact:** Medium.
|
||||
- **Likelihood:** Medium as feature count grows.
|
||||
- **Mitigation:** Small surface (4 destinations + status), strict layering B-1…B-7 (`ARCHITECTURE-DESIGN.md:195`), small store/router, unit tests, reconsider trigger → Preact (`ARCHITECTURE-DECISIONS.md:117`) at first sign of sustained interdependence.
|
||||
- **Residual:** Low with discipline.
|
||||
- **Owner/category:** Architecture/Implementation.
|
||||
- **Blocks implementation?** No.
|
||||
|
||||
## W-8 — Bundled pure-JS Ed25519 verifier supply chain
|
||||
|
||||
- **Risk:** Verifier library must be audited and stay tiny; WebCrypto Ed25519 not baseline on iOS 16.4 (`ARCHITECTURE-DESIGN.md:14`, `DISCOVERY.md:401` TQ-9).
|
||||
- **Impact:** High if verifier is wrong/pulled; medium on bundle.
|
||||
- **Likelihood:** Low with pinned audited dep.
|
||||
- **Mitigation:** Pin, audit, bundle-budget gate (≤150 KB gz JS), WebCrypto SHA-256 + pure-JS Ed25519 chosen correctly per spikes; CSP restricts to self.
|
||||
- **Residual:** Low pending audit (AQ-06).
|
||||
- **Owner/category:** Security + Build.
|
||||
- **Blocks implementation?** Blocks production signing declaration, not scaffolding (staging uses test key).
|
||||
|
||||
## W-9 — Signing key custody / rotation
|
||||
|
||||
- **Risk:** Loss of the offline festival signing key or compromised publish credentials push bad data (`ARCHITECTURE-DESIGN.md:863` RK-6, `DISCOVERY.md:616` SE-1/SE-2, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:85` S-01, AQ-21).
|
||||
- **Impact:** High.
|
||||
- **Likelihood:** Low with process.
|
||||
- **Mitigation:** Offline key, sealed backup, singular publisher + deputy (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:60` O-01), fingerprinted key set in shell for rotation, repo audit trail, rollback via new higher version, rotation drill pre-festival.
|
||||
- **Residual:** Low with runbook + drill.
|
||||
- **Owner/category:** Ops/Security.
|
||||
- **Blocks implementation?** Blocks production publishing, not dev.
|
||||
|
||||
## W-10 — Single-origin availability
|
||||
|
||||
- **Risk:** All shell+data on one HTTPS origin (`DISCOVERY.md:261` C-20); CDN outage concentrates risk (`ARCHITECTURE-DESIGN.md:863` RK-10, `ARCHITECTURE-DESIGN.md:898` W-6).
|
||||
- **Impact:** Low (updates are enhancement-only; last-good datasets already on devices).
|
||||
- **Likelihood:** Low (static CDN).
|
||||
- **Mitigation:** Static files trivially mirrorable; Transport seam also future-mitigates via LAN mirror (`ARCHITECTURE-DESIGN.md:920` DD-8); origin chosen deliberately before public link (AQ-18).
|
||||
- **Residual:** Low.
|
||||
- **Owner/category:** Ops/Deployment.
|
||||
- **Blocks implementation?** No.
|
||||
|
||||
---
|
||||
|
||||
# 6. Architecture freeze test
|
||||
|
||||
20 adversarial scenarios — each: what happens, what remains functional, acceptability, any required architecture change, V1 impact. All answers are after incorporating spike changes.
|
||||
|
||||
1. **User never installs Lumen.** *Tab-only on iOS.* Shell works while open; storage subject to 7-day ITP eviction (`DISCOVERY.md:429` PW-1). Coach re-surfaces; degraded-persistence warning shown (`SPIKE-07:48`). Emergency L0 floor always works after any shell load; L1/L2 are available until evicted then RECOVERY. *Acceptable:* no technical fix inside web platform — install is the fix (`ARCHITECTURE-DESIGN.md:880`). *No new architecture.* V1: tab path stays.
|
||||
|
||||
2. **User opens Lumen once and never connects again.** *Never stages dataset.* After first shell load: L0 floor only; NOT_READY state honest, not blank (`SPIKE-07:66`). Map/info show "not downloaded" cards. *Acceptable:* expectation management is product/ops (distribution before arrival + physical fallback, `SPIKE-07:66` W-1). *No new architecture.* V1 unchanged.
|
||||
|
||||
3. **iOS evicts storage.** *Per-origin all-or-nothing, silent.* Boot light check (sizes + verification-record match, `SPIKE-05:83`) fails → RECOVERY with reason `missing`; emergency floor intact (`SPIKE-06:81`); one-tap re-prep when online; favourites in user DB survive unless user DB was also evicted (then RECOVERY+baseline, `SPIKE-02:69`). *Acceptable:* eviction can never be prevented (`ARCHITECTURE-DESIGN.md:898` W-4), only detected/recovered. *No new architecture beyond implemented detection.*
|
||||
|
||||
4. **Browser kills the service worker during an update.** SW holds no dataset state (C-21, P8). Staging runs in page context (`SPIKE-01:83`). Page persists per-file progress; resume path defined (`SPIKE-02:50` S03). SW kill cannot corrupt staging (`ARCHITECTURE-DESIGN.md:882`). *Acceptable. No change.*
|
||||
|
||||
5. **Page is killed during a dataset transaction.** IDB transactions are atomic per database (spec CONFIRMED, `SPIKE-01:60`); an uncommitted write leaves last committed intact (`SPIKE-01:111`). Single short txn per file (P1, F-1); activation single txn on `lumen-system` (P2, F-2). Kill before flip → old stands; during flip → uncommitted, old stands; after flip before readback → new stands but readbackPending guard (`SPIKE-02:97`) covers next-boot recheck. *Acceptable pending iOS jetsam atomicity UNVERIFIED (`SPIKE-02:111`).* V1: document and verify on device. No new architecture if device tests pass; if fails, architecture fallback is re-prep path plus retry-once behaviour (`ARCHITECTURE-DESIGN.md:672`).
|
||||
|
||||
6. **Device reboots during a dataset update.** Same as #5 at OS level. Committed IDB survives browser/device reboot; SW/Caches rehydrate (`SPIKE-01:90`). Cold-start path is budgets-only (`ARCHITECTURE-DESIGN.md:848` §27). *Acceptable. No change beyond device-measured cold-start timing.*
|
||||
|
||||
7. **Festival dataset is corrupt (bits wrong).** Per-file SHA-256 fails → discard staging, quarantine version, keep active (`SPIKE-02:60` S06, `ARCHITECTURE-DESIGN.md:667`). Corruption discovered post-activation by readback → automatic rollback when fallback slot intact, else RECOVERY+baseline (`SPIKE-02:50` S14/X1/X2). Never exposes partial dataset (invariant 4). *Acceptable. No change.*
|
||||
|
||||
8. **Dataset has valid hash but invalid signature (MITM / compromised hosting, DISCOVERY SE-1).** Signature checked over exact manifest bytes before parsing or downloading anything else (`SPIKE-02:29` step 3). Ed25519 verifier rejects → quarantine, never fetch files (`SPIKE-02:60` S07), diag entry. Organizer rollback via new higher signed version. *Acceptable. No change.* Future transports inherit the same verifier (`SPIKE-02:26` composition).
|
||||
|
||||
9. **Dataset incompatible with shell (schema or appCompatibility).** Checked before staging and at boot (`ARCHITECTURE-DESIGN.md:688`). Rejected + "please update app" when `minAppVersion > current`; boot prefers compatible slot else limited mode (floor+status only). Publishing rule C-23 (shell range first, dataset after, freeze 7 days before festival) prevents most cases. *Acceptable. No change.*
|
||||
|
||||
10. **User has incorrect device clock.** If any online sync occurred, `ClockService.now()` uses persisted skew (`SPIKE-08:34`); mid-session jumps flagged by monotonic anchor (`SPIKE-08:35`). Sanity window warning is suppressed until near festival or evidence of skew (F-3) so early prep is quiet (`SPIKE-08:97`). READY is time-independent (`SPIKE-05:47`), so wrong clock never bricks availability. Never-online wrong clock is *accepted residual* — absolute times remain readable (`SPIKE-08:124`). *Acceptable. No new architecture; NTP endpoint rejected (C-19).*
|
||||
|
||||
11. **GPS denied.** No permission requested in V1 (`ARCHITECTURE-DESIGN.md:222`). All map functionality is GPS-free; Facilities list is the primary non-visual path (`SPIKE-03:79`). *Acceptable. No change.*
|
||||
|
||||
12. **GPS unavailable.** Identical to #11. Hook `lat/lng` on POIs preserved for future blue dot without touching features (`ARCHITECTURE-DESIGN.md:540`). *No change.*
|
||||
|
||||
13. **Festival changes schedule after preparation.** New packageVersion published, monotonic. Device fetches on next open-while-online opportunistically (`SPIKE-02:29`); moved/cancelled markers render distinctly (`ARCHITECTURE-DESIGN.md:509`); dataset `generatedAt`/`fetchedAt` shows staleness. Users who never come online keep old schedule — *accepted and mitigated by organizer PA/boards* (`DISCOVERY.md:460` OF-8). *No new architecture.* Urgency beyond pull would be DD-1 annoucements (reserved, not required for core promise).
|
||||
|
||||
14. **Emergency information changes after preparation.** Tier-2 dataset emergency section updates via new signed package; floor frozen at shell build remains as fallback (`SPIKE-06:93`). Provenance labels show age; organizer physical channels remain urgent path (A-16, `ARCHITECTURE-DESIGN.md:891` W-3). *Acceptable. No change.*
|
||||
|
||||
15. **User arrives with no usable festival dataset (NOT_READY).** Shell + floor guarantee L0; PARTIAL levels each fully functional (`SPIKE-07:66`); Status enumerates exactly what's missing; one-tap prep if any connectivity appears; physical fallback at venue. *Acceptable — distribution is product/ops problem (W-1). No new architecture.*
|
||||
|
||||
16. **User has low storage.** `storage.estimate()` pre-check refuses staging if free < 2× package with guidance; QuotaExceeded aborts and keeps active (P3/P4); L0/L1 partial still possible when full package won't fit (`SPIKE-07:52` FP-4). Baseline always works. *Acceptable. No change.*
|
||||
|
||||
17. **User has low-end Android phone.** Vanilla TS + GPU-transform map + ≤1600/3072 caps + windowed lists + WebP keep within budgets (`ARCHITECTURE-DESIGN.md:848` §27, `SPIKE-03:58` F-1). Fallback overview-only if decode/transform janks (`SPIKE-03:98`). Perf is YELLOW until device protocol in `SPIKE-03:116` passes. *Acceptable pending device proof. No speculative complexity added.*
|
||||
|
||||
18. **User uses iOS Safari without installing (tab).** Same as #1. Works while open; eviction risk; no background sync; `tel:` still standard but verify on standalone (`SPIKE-01:171`). *Acceptable with honest messaging. No change.*
|
||||
|
||||
19. **User's browser clears site storage (Settings → Clear).** Identical to eviction path (FA-5/`DISCOVERY.md:460` OF-13). Next boot → RECOVERY; baseline intact; re-prep path exists. Only shell reloads from network need connectivity. *Acceptable. No change.*
|
||||
|
||||
20. **Future mesh proves impossible inside a browser.** Expected per feasibility analysis (`DISCOVERY.md:581`, `ARCHITECTURE-DESIGN.md:717` §20) — no BLE peripheral on iOS, no ad-hoc Wi-Fi, no background sockets. The seam is transport-agnostic: any future delivery (native companion, hardware relay, LAN mirror implementing Transport) would attach as a bridge; V1 loses nothing because HTTP transport is *complete product functionality*, not a placeholder (`ARCHITECTURE-DESIGN.md:895` 16). *Acceptable. No V1 architecture stranded.*
|
||||
|
||||
No scenario requires new architecture. Two scenarios expose device-dependent behaviour already tracked as UNVERIFIED (IDB atomicity under kill, map/GPU performance) — both have defined fallbacks.
|
||||
|
||||
---
|
||||
|
||||
# 7. Final architecture status
|
||||
|
||||
Incorporates all spike addenda. Status values per directive: GREEN = validated and suitable for implementation; YELLOW = architecturally acceptable but requires physical-device or operational validation; RED = must change before implementation (none).
|
||||
|
||||
| Decision | Status | Evidence | Remaining validation | Implementation impact |
|
||||
|---|---|---|---|---|
|
||||
| ADR-001 Offline-first | **GREEN** | SPIKE-05 time independence + SPIKE-07 L0–L2 + SPIKE-02 crash suite | None | Builds proceed as designed |
|
||||
| ADR-002 PWA / install | **GREEN (functional) / YELLOW (persistence)** | Correct: install-first + single origin; L0–L2 formalised (`SPIKE-07:31`) | `persist()` grants + 7-day tab vs installed timing on D1/D2 (`SPIKE-01:159`) | Coach + honest tab warning required; no spec change |
|
||||
| ADR-003 Frontend vanilla TS | **GREEN** (with trigger) | Chosen correctly; small surface; EXP-3 no disqualifying JS cost; boundaries B-1…B-7 keep data layer safe | Low-end render cost on D3; first implementation spike is the real proof (watch for sustained store→render complexity → Preact per `ARCHITECTURE-DECISIONS.md:117`) | Hand-rolled store/router + hashed assets + precache manifest |
|
||||
| ADR-004 Storage (IDB + Cache) | **YELLOW** (device-conditional) | Design-logic valid: transactions atomic (spec), A/B ordering proven 16/16 (`SPIKE-02:50`), P1–P8 normative | Device tests `SPIKE-01:159` §5: iOS jetsam atomicity, quota-near-full writes, Blob read-back on D1/D3 | Must implement P1–P8 exactly; production-ready only after device pass; fallback = re-prep path already designed |
|
||||
| ADR-005 Package (JSON + manifest) | **GREEN** (schema) / **YELLOW** (content) | JSON+hash+Ed25519 sufficient (F-5); required flag, no-expiration, emergency sub-versioning, user-data schema split added (`SPIKE-04:203`) | Real content must pass 5 pipeline gates + budget enforcement (`SPIKE-04:189`); stable IDs pending source sample (AQ-20) | Pipeline tooling + fixture packages; `dayKey` validated at publish |
|
||||
| ADR-006 Atomic A/B updates | **YELLOW** (device-conditional) | State machine proven 14+3 scenarios PASS with F-1…F-4 (`SPIKE-02:50` + X1–X3) | IDB transaction atomicity under real kills is the sole platform assumption (`SPIKE-01:75`) | Activation = one txn + verification record + readbackPending; rollback depth 1 is accepted trade-off |
|
||||
| ADR-007 Emergency baseline (3 tiers) | **GREEN** | Fixed tier-1 contents + resolution/merge + zero-IDB path + same-source generation (`SPIKE-06:12`, `SPIKE-06:60`, `SPIKE-06:75`) | Organizer content sign-off gate and real `tel:` on standalone (`SPIKE-06:81`) | Generate floor + section from same sheet at build; 16 KB cap enforced |
|
||||
| ADR-008 Map | **YELLOW** (device-conditional) | Raster+DOM correct choice; memory caps tightened 1600/3072 ≤~35 MB (`SPIKE-03:58`); object-URL (F-3) + dark filter (F-4) added | Device protocol `SPIKE-03:116` §7: fps ≥30, decode, no leaks, a11y, texture-cap fallback | Lazy object URLs; one detail resident; facilities list first-class; no GPS/blue dot |
|
||||
| ADR-009 Time model | **GREEN** (logic) / **YELLOW** (JSC parity) | 24/24 PASS on V8/ICU (DST, unusual zones, skew, Now/Next); F-3 sanity suppression until near (`SPIKE-08:97`); clarification F-4 (only Intl) | JSC parity at DST edges + CDN Date observation + low-end timing on D1/D3 (`SPIKE-08:152`) | `ClockService.now()` pure computation; no timers; read `Date` header opportunistically |
|
||||
| ADR-010 Sync pull-only | **GREEN** | Pull on open/online/manual + monotonic + no background is the only viable model (C-19, B-06) | Online hint best-effort nature (not a bug) | `SyncService` + staging ordering; announcements schema reserved (DD-1) |
|
||||
| ADR-011 Transport seam | **GREEN** | Minimal `isAvailable/fetchPointer/fetchBytes` succeeds for HTTP and any future byte-pipe; verifier-only truth | None — deliberately tiny | One interface + HttpTransport; tests inject fault transport |
|
||||
| ADR-012 Mesh strategy | **GREEN** (strategy) | Future-only; costs zero in V1; out-of-browser assumption documented; no mesh fails V1 | None — feasibility study deferred until concrete opportunity (DD-7) | Nothing built; docs only |
|
||||
| ADR-013 Security | **GREEN (model) / YELLOW (library audit)** | Threat model TH-1…TH-11 covered with minimal parts; WebCrypto-SHA256 + pure-JS Ed25519 correct per TQ-9; CSP `self` only; privacy by design | Bundled verifier audit + bundle gate (AQ-06); key custody/rotation drill (AQ-21, S-01) | Pin audited verifier; HSTS; no inline/eval; no secrets client-side (C-24) |
|
||||
| ADR-014 Deployment (static CDN) | **GREEN (topology) / YELLOW (provider)** | Static immutable URLs + `latest.json` pointer + staging/production origins correct; cache headers per `ARCHITECTURE-DESIGN.md:807` | Provider + production origin choice (AQ-14, AQ-18) — not blocking dev; swap is cheap pre-users, costly post-users | Content repo + sign/publish pipeline + smoke re-verify |
|
||||
|
||||
No RED. All YELLOW are device-dependent or content/ops-dependent — no architecture must change before implementation. The only path that could turn a YELLOW to RED is a device test failure for which fallbacks are already defined (overview-only, tile-split, re-prep, Preact trigger).
|
||||
|
||||
---
|
||||
|
||||
# 8. Implementation gate
|
||||
|
||||
## Recommendation: **B. READY FOR IMPLEMENTATION AFTER SPECIFIED BLOCKERS**
|
||||
|
||||
Base: actual remaining risks after incorporating every spike's accepted change. Physical-device testing is the only architectural-level remaining blocker for a production festival deployment; it does not block starting implementation against fixtures.
|
||||
|
||||
### Blockers, distinguished
|
||||
|
||||
#### ARCHITECTURAL BLOCKERS — none.
|
||||
|
||||
No ADR requires redesign before code starts. The architecture is frozen at the design-logic level with all spike addenda incorporated. Starting implementation now will not incur speculative rework.
|
||||
|
||||
#### PHYSICAL-DEVICE VALIDATION — required before a production festival, not before implementation start
|
||||
|
||||
- **P-D-1 — IDB atomicity under real kills on iOS (D1/D2).** Verifies `SPIKE-01:75` / `SPIKE-02:111`. BLOCKING for production. Runnable immediately on any iPhone once a staging shell exists.
|
||||
- **P-D-2 — IDB quota/Blob behaviour near-full + persistence signals on D1–D4.** `SPIKE-01:159` items 4–6, 8–10. BLOCKING (quota path) / HIGH (persistence heuristics).
|
||||
- **P-D-3 — Map fps, decode, memory, leaks, texture-cap fallback on D3 (+ sanity D1/D2).** `SPIKE-03:116`. BLOCKING for map production claim; facilities list already covers degraded path.
|
||||
- **P-D-4 — Intl/JSC parity at DST edges, dayKey grouping, ClockService skew + monotonic drift, F-3 sanity suppression on D1/D2 + D3.** `SPIKE-08:152`. HIGH; early-prep vs near-festival distinction must read correctly.
|
||||
- **P-D-5 — SW lifecycle (kill mid-staging) on iOS.** `SPIKE-01:82` C-21 + `ARCHITECTURE-DESIGN.md:243`. BLOCKING for invariant proof.
|
||||
- **P-D-6 — `tel:` from standalone on D1/D2 + D3.** `SPIKE-06:81`, `SPIKE-01:171`. HIGH for safety.
|
||||
- **P-D-7 — Cold-start / boot light-check timing on D3 (and iOS sanity).** `ARCHITECTURE-DESIGN.md:851`; `SPIKE-05:83`. MEDIUM/HIGH.
|
||||
|
||||
All are listed in §4 with severity. None invents new architecture if they pass; each has a documented fallback if they fail.
|
||||
|
||||
#### PRODUCT / OPERATIONS BLOCKERS — required before content/production, not before code
|
||||
|
||||
- **O-1 — Emergency content sign-off + legal review (`DISCOVERY.md:345` OQ-2/OQ-7, `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:62` O-02, W-5).** BLOCKING for emergency *content* publish.
|
||||
- **O-2 — Distribution plan + on-site physical fallback (`DISCOVERY.md:346` OQ-3, W-1).** HIGH — determines unprepared cohort size.
|
||||
- **O-3 — Production origin choice (AQ-18, AQ-14, A-11) + hosting provider/budget (OQ-8).** Must be chosen deliberately before any public staging link because storage is origin-keyed; not blocking for development on provisional origin (`ASSUMPTIONS-AND-OPEN-QUESTIONS.md:116` AQ-18).
|
||||
- **O-4 — Publisher/deputy + signing key custody + rotation drill (O-01, O-02, S-01, AQ-16/AQ-17/AQ-21).** BLOCKING for production signing; staging uses test key.
|
||||
|
||||
#### CONTENT / DEPLOYMENT REQUIREMENTS — parallelizable with implementation
|
||||
|
||||
- **C-1 — Real map art at ≥1600 px (and optionally ≥3072) + POI sheet to prove budgets (`SPIKE-03:58`, `SPIKE-04:189`).** HIGH.
|
||||
- **C-2 — Schedule sample to confirm dayKey, stable IDs / mint mapping, and changed-event handling (D-01, AQ-20, `SPIKE-04:189`).** HIGH.
|
||||
- **C-3 — Staging dry-run festival with test edition + field devices (AQ-23).** Strongly recommended pre-festival.
|
||||
|
||||
### Can implementation begin?
|
||||
|
||||
**Yes — immediately, on a provisional staging origin with fixtures.** `ASSUMPTIONS-AND-OPEN-QUESTIONS.md:138` explicitly states nothing above blocks shell, data-layer, sync/verifier, and feature modules against staging fixtures — by design. The only work that must complete *before* the festival is the physical-device protocol above plus the ops gates O-1…O-3; the architecture itself needs no further change.
|
||||
|
||||
---
|
||||
|
||||
*End of Architecture Validation.*
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue