- 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
7.1 KiB
7.1 KiB
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:
- The app can launch and render without network (shell complete).
- Emergency information is available (floor always; dataset section if present).
- 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).
- The map is usable (map section + its required assets present).
- Festival info is readable (info section present).
- The dataset is authentic and untampered (signature + hashes verified).
- The dataset is compatible with this shell (schema envelope).
- 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_presentindividually. This model keeps them (C6/C8) but wraps them in the required-section mechanism so future optional sections don't require predicate surgery. FAILEDis 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 (
requiredflag, 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.