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:
Lumen Stage1 2026-08-30 23:25:35 -05:00
commit c0bfd413ff
100 changed files with 9863 additions and 0 deletions

115
SPIKE-05-OFFLINE-READY.md Normal file
View file

@ -0,0 +1,115 @@
# 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.