- 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
119 lines
5.6 KiB
Markdown
119 lines
5.6 KiB
Markdown
# SPIKE-06 — Emergency Baseline Architecture
|
||
|
||
- **Phase:** Architecture Validation
|
||
- **Date:** 2026-08-30
|
||
- **Decision under test:** ADR-007 (three-tier emergency architecture with an
|
||
embedded floor).
|
||
- **Outcome:** decision confirmed; tier contents fixed; version/compatibility
|
||
matrix resolved; one hardening rule added.
|
||
|
||
## 1. What belongs in each tier (fixed)
|
||
|
||
### Tier 1 — Emergency Floor (embedded in app shell, immutable per build)
|
||
|
||
Must fit ≤ 16 KB and cover "what saves a life or prevents panic in the first
|
||
minutes":
|
||
|
||
```json
|
||
{
|
||
"floor": true,
|
||
"floorVersion": "1.0.0+2026.08.20",
|
||
"emergencySchemaVersion": 1,
|
||
"generatedAt": "2026-08-20T12:00:00Z",
|
||
"sourceContentVersion": 5,
|
||
"services": {
|
||
"emergencyNumber": "911",
|
||
"security": { "phone": "+1-555-0142" },
|
||
"firstAid": { "summary": "Behind Stage B, 10:00–02:00" }
|
||
},
|
||
"address": { "lines": ["…"], "coordinates": { "lat": 41.88, "lon": -87.63 } },
|
||
"musterPoints": [ { "name": "North Field", "directions": "…" } ],
|
||
"exits": [ { "name": "East Gate" }, { "name": "West Gate" } ],
|
||
"aedSummary": "AEDs at Info Tent and Main Gate (see map when available)",
|
||
"procedures": [
|
||
{ "id": "medical", "title": "Medical emergency", "steps": ["Call 911", "Alert security: +1-555-0142", "…"] },
|
||
{ "id": "weather", "title": "Severe weather", "steps": ["…"] },
|
||
{ "id": "fire", "title": "Fire", "steps": ["…"] },
|
||
{ "id": "lost", "title": "Lost person", "steps": ["…"] }
|
||
]
|
||
}
|
||
```
|
||
|
||
Excluded from the floor (by policy): full POI lists, per-location detail,
|
||
vendor/operational info, anything that changes frequently. The floor is a
|
||
**life-safety minimum**, not a small copy of the dataset section.
|
||
|
||
### Tier 2 — Festival Emergency Dataset (signed section of the package)
|
||
|
||
Full detail: all emergency-relevant POIs with map links, complete procedure
|
||
text, per-role contacts, hours, notices, `contentVersion`/`updatedAt`
|
||
(SPIKE-04 fragment). Updateable by publishing a new package version.
|
||
|
||
### Tier 3 — Optional live emergency notices (future, reserved)
|
||
|
||
Shape reserved: `{ id, severity, title, body, publishedAtUtc, expiresAtUtc,
|
||
signature }`, delivered with a package or via the transport seam; rendered
|
||
only when signed and unexpired; **display-only, additive, never a replacement
|
||
for Tier 1/2**. Not implemented in V1.
|
||
|
||
## 2. Resolution rules (normative)
|
||
|
||
```
|
||
render_emergency():
|
||
floor = embedded floor # always exists; parses from frozen schema
|
||
section = active dataset emergency section
|
||
if section exists
|
||
and section.emergencySchemaVersion ∈ shell.supportedEmergencySchemas
|
||
and section passes integrity (already guaranteed by activation):
|
||
render section (full detail), labeled "FESTIVAL DATA v<package> · <generatedAt>"
|
||
render floor-only fields if section omits any (defensive merge)
|
||
else:
|
||
render floor, labeled "BASELINE v<floorVersion>"
|
||
append Tier-3 notices if any signed+unexpired (future)
|
||
```
|
||
|
||
Hardening rule **(new, F-1)**: the floor renderer is **forward-tolerant** —
|
||
it ignores unknown fields and never throws on dataset content; and the floor
|
||
path must be reachable with **zero IDB access**, because BASELINE_ONLY and
|
||
eviction scenarios must still render emergency info.
|
||
|
||
## 3. The cases the spike must answer
|
||
|
||
| Case | What renders | Why the floor never disappears |
|
||
|---|---|---|
|
||
| Shell old, dataset newer (section schema v2 vs floor/shell supports v1) | Floor + "update app when online" note; known-v1 fields of the section may still render if backward-compatible | Floor is compiled into the shell; dataset incompatibility can only *demote to floor*, never remove it |
|
||
| Shell new, dataset old | Dataset section (old schema is within shell's supported range) or floor | Backward range support (C-23) |
|
||
| Dataset unavailable (never prepared) | Floor | Floor ships with shell bytes |
|
||
| Dataset corrupt (verification fails at staging — never activated) | Previous dataset section if active slot intact, else floor | Corruption blocks activation (SPIKE-02); floor unaffected |
|
||
| Storage evicted | Floor | Floor is shell bytes, not IDB; Ring-0 survival |
|
||
| Network unavailable | Whatever is local: active dataset section or floor | No network call exists in the emergency render path |
|
||
| Update fails mid-flight | Old active dataset (or floor) | A/B invariant (SPIKE-02); staging never touches shell or active slot |
|
||
|
||
## 4. Tradeoffs (re-stated with verdicts)
|
||
|
||
- **Baseline staleness** (floor frozen at shell build): accepted. Mitigations:
|
||
same-source generation (no drift), floor version stamp on screen, Tier 2
|
||
updates for anything less-than-critical, organizer physical channels for
|
||
urgency.
|
||
- **Baseline scope creep**: resisted. 16 KB cap + content policy (life-safety
|
||
minimum only). Everything else belongs in Tier 2.
|
||
- **Two copies drift**: eliminated by generating floor + section from the same
|
||
emergency source sheet in the pipeline (ADR-007/§10.3).
|
||
- **Separate localStorage copy of emergency data**: rejected again — it dies in
|
||
eviction too; only shell bytes survive everything.
|
||
|
||
## 5. Cross-checks
|
||
|
||
- SPIKE-05 C8 asserts floor presence in the readiness predicate (defense
|
||
against a broken build silently dropping the floor).
|
||
- SPIKE-04 F-3 gives the emergency section independent versioning used by the
|
||
resolution rules above.
|
||
- ADR-013: the floor's provenance is the shell build itself (signed app
|
||
delivery); Tier 2 inherits package signatures; Tier 3 will require
|
||
per-notice signatures.
|
||
|
||
## 6. Verdict
|
||
|
||
**ACCEPT.** ADR-007 stands unchanged in direction; addendum records the fixed
|
||
tier contents, the resolution/merge rules, the forward-tolerant floor renderer
|
||
(F-1), and the zero-IDB requirement for the floor path.
|