Lumen/SPIKE-05-OFFLINE-READY.md
Lumen Stage1 c0bfd413ff 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
2026-08-30 23:25:35 -05:00

115 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SPIKE-05 — Offline Ready Model
- **Phase:** Architecture Validation
- **Date:** 2026-08-30
- **Decision under test:** the readiness state machine in ARCH §12 (R1–R7).
- **Outcome:** model confirmed; predicate formalized, tightened (time
independence, optional sections), and failure-state taxonomy completed.
## 1. What READY must actually guarantee
Deriving from the product promise ("everything attendees need works with zero
connectivity"), READY must guarantee, **from local evidence only**:
1. The app can launch and render without network (shell complete).
2. Emergency information is available (floor always; dataset section if present).
3. The schedule is browsable and Now/Next computable (schedule section present;
note: computable even with a wrong clock — a clock problem is a warning,
never a readiness failure).
4. The map is usable (map section + its required assets present).
5. Festival info is readable (info section present).
6. The dataset is authentic and untampered (signature + hashes verified).
7. The dataset is compatible with this shell (schema envelope).
8. The claim itself is evidenced (a stored verification record matching the
active version, not a memory of success).
## 2. The exact predicate (normative)
```
OFFLINE_READY ⟺
C1 shell_valid: every precache entry present in the current shell cache
∧ C2 dataset_present: active slot exists for the active edition
∧ C3 authenticity: manifest signature verified against embedded key set
(recorded verification matches active packageVersion)
∧ C4 integrity: all manifest-listed files present, sizes match;
hashes verified at staging (recorded), spot-check at boot
∧ C5 schema_compatible: package schemaVersion ∈ shell.supportedRange
∧ package.appCompatibility satisfied by APP_VERSION
∧ C6 required_sections: every section with required=true present and parseable:
{emergency, schedule, map, info, assets-inventory}
∧ C7 required_assets: every asset referenced by a required section present
(size-verified; hash verified at staging)
∧ C8 emergency_floor: embedded baseline present (trivially true; asserted
so a broken build cannot silently lose the floor)
```
**Rules of evaluation:**
- **Time independence (new, F-1):** the predicate must not consult any clock.
A device with a wildly wrong clock must still be able to know it is READY.
(No expiration fields exist in the package — SPIKE-04 F-2 — so nothing tempts
a time check.)
- **Optional sections never gate READY (new, F-2):** sections with
`required=false` (future announcements, i18n packs) report their own presence
in the status detail but cannot demote READY.
- **Evidence-based:** C3/C4 rely on the stored verification record (SPIKE-02
F-2) keyed to the active `packageVersion`; if the record is absent or
mismatched, a full re-verification is required before READY can be claimed.
- **All-or-nothing for READY; everything else is enumerated.**
## 3. State taxonomy (complete)
| State | Definition | User sees | Primary action offered |
|---|---|---|---|
| `READY` | C1–C8 all true | Green chip "OFFLINE READY ✓" | None (status detail on tap) |
| `PARTIAL(list)` | Shell valid; one or more required sections/assets missing, none corrupt; e.g., prep interrupted | Amber chip; status screen enumerates exactly what is missing and what still works | "Continue setup" (resumes staging) |
| `NOT_READY` | Shell valid; no dataset staged or active (fresh install) | Neutral chip "Get festival data" | Preparation flow |
| `RECOVERY(reason)` | Previously-verified state now fails checks: storage evicted (`missing`), verification mismatch (`corrupt`), incompatibility after app/dataset skew (`incompatible`) | Red chip; honest explanation of what happened | "Restore festival data" (re-prep; needs connectivity) — emergency floor works meanwhile |
| `BASELINE_ONLY` | Storage unavailable (private mode, blocked website data, quota 0) and app cannot persist anything | Distinct notice: offline saving unavailable; emergency info still here | Install / normal-mode / free-space guidance |
| `FAILED(op)` | A specific operation failed and no automatic recovery applied (activation transaction repeatedly failing, IDB errors) — distinct from RECOVERY because the *cause is an error, not missing data* | Error screen with plain cause + diagnostics entry | Retry; if persistent, re-prep; diagnostics share option |
Notes:
- The brief's example predicate listed `schedule_present`, `map_present`,
`festival_info_present`, `emergency_baseline_present` individually. This
model keeps them (C6/C8) but wraps them in the required-section mechanism so
future optional sections don't require predicate surgery.
- `FAILED` is the added state: RECOVERY means "data is gone/wrong"; FAILED
means "an operation errored". Keeping them separate keeps user messaging and
diagnostics accurate.
## 4. Evaluation cadence and cost
| When | What | Cost target |
|---|---|---|
| Every boot | Light check: C1 (cache list), C2 (slot existence), C4-light (sizes + verification-record match), C5 | ≤ ~150 ms; no hashing |
| After staging completes | Full: all hashes + schema (C3–C7 full) | Seconds; once per update |
| Activation | Record written atomically with the flip; readback spot-check (pending flag, SPIKE-02 F-3) | ms |
| User-initiated "Check my data" | Full re-verification | Seconds, with progress |
| After any IDB error | Full re-verify of active slot; quarantine on failure | Seconds |
## 5. Edge cases adjudicated
| Case | State | Why |
|---|---|---|
| Eviction between install and festival | RECOVERY(missing) | Light check finds no slot; floor still works |
| Verification record present but files gone | RECOVERY(missing) | Sizes check fails |
| Files present but record missing (e.g., partial restore) | PARTIAL → full re-verify path | READY cannot be claimed without evidence |
| New shell, dataset schema too old | RECOVERY(incompatible) with "update data when online" guidance; floor + status usable | C5 false |
| Wrong device clock | **READY unaffected**; separate clock warning (SPIKE-08) | Time independence |
| Optional announcements section missing | READY | F-2 |
| Private browsing, nothing persistable | BASELINE_ONLY | Storage probes fail early |
| Activation failed twice | FAILED(activation) | Distinct from data loss |
## 6. Cross-checks
- Consistent with SPIKE-02 (verification record + readback pending flag).
- Consistent with SPIKE-04 (`required` flag, no expiration).
- Consistent with SPIKE-06 (floor assertion C8; emergency never depends on READY).
- Consistent with SPIKE-07 (prep levels map onto PARTIAL/READY transitions).
## 7. Verdict
**ACCEPT (with formalization).** The ARCH §12 model was directionally correct;
it is now normative: predicate C1–C8, time independence, optional-section rule,
and the full six-state taxonomy including `FAILED`. Changes are refinements of
the design document, not a direction change; no ADR-level reversal.