# Phase 18 — Plan Index & Coverage Audit **Created:** 2026-06-15 **Plans:** 4 (3 waves) **Mode:** standard · TDD mode ON · MVP off --- ## Wave Structure | Wave | Plans | Autonomous | Files (disjoint per wave) | |------|-------|------------|----------------------------| | 1 | 18-01 (accessor + IANA validator, TDD) | yes | `lib/householdTimezone.ts` (+test) | | 2 | 18-02 (admin endpoints, TDD), 18-03 (broker wiring, TDD) | yes, yes | 02: `routes/admin.ts` (+test); 03: `broker/reminderScheduler.ts` + `broker/outboxWorker.ts` (+tests) — no overlap with 02 | | 3 | 18-04 (PWA picker UI) | no (human-verify checkpoint) | `pwa/api/client.ts`, `pwa/routes/AdminPage.tsx` | Dependency rationale: 02 and 03 both depend only on the Wave-1 accessor and touch disjoint files → parallel in Wave 2. 04 depends on the 02 endpoint contract → Wave 3. --- ## Artifacts This Phase Produces (MANDATORY) | Symbol / Artifact | Kind | File | Plan | |-------------------|------|------|------| | `getHouseholdTimezone(db)` | function (async, → Promise) | `apps/api/src/lib/householdTimezone.ts` | 01 | | `isValidIanaTimezone(tz)` | function (→ boolean) | `apps/api/src/lib/householdTimezone.ts` | 01 | | `'household_timezone'` | app_config key string (new additive row, no migration) | `apps/api/src/db/schema.ts` appConfig (existing table) | 01/02 | | `GET /api/admin/config/timezone` | route | `apps/api/src/routes/admin.ts` | 02 | | `PUT /api/admin/config/timezone` | route | `apps/api/src/routes/admin.ts` | 02 | | `POST /api/admin/config/timezone/seed` | route (no-overwrite seed, D-02/D-03) | `apps/api/src/routes/admin.ts` | 02 | | `timezoneSchema` | Zod schema | `apps/api/src/routes/admin.ts` | 02 | | `fetchAdminTimezone()` | client fn | `apps/pwa/src/api/client.ts` | 04 | | `setAdminTimezone(timezone)` | client fn | `apps/pwa/src/api/client.ts` | 04 | | `AdminTimezoneResponse` | interface | `apps/pwa/src/api/client.ts` | 04 | | Timezone section (`aria-label="Timezone"`) | UI component/section | `apps/pwa/src/routes/AdminPage.tsx` | 04 | | `apps/api/tests/lib/householdTimezone.test.ts` | new test file | — | 01 | Test files extended (not new): `apps/api/tests/routes/admin.test.ts` (02), `apps/api/tests/broker/reminderScheduler.test.ts` + `apps/api/tests/broker/outboxWorker.test.ts` (03). --- ## Multi-Source Coverage Audit ### GOAL (ROADMAP Phase 18 goal) "Auto-detect the household timezone and allow changing it" → COVERED: detection seed (D-02, Plans 02 seed endpoint + 04 detected-zone affordance); change (D-04, Plans 02 PUT + 04 picker); behavioral wiring (D-05, Plan 03). ### REQ (phase_req_ids from REQUIREMENTS.md) No REQ-IDs are mapped to Phase 18 in ROADMAP.md (confirmed: "Requirements: TBD"). Per the planning context, the D-01..D-07 decision set is the coverage contract — see CONTEXT below. Not a gap. ### RESEARCH (18-RESEARCH.md features/constraints) - Single accessor `getHouseholdTimezone` + IANA validator → Plan 01. - GET/PUT endpoints on adminRouter, no new router → Plan 02. - Seed endpoint, additive/non-blocking (Phase 12 not yet executed) → Plan 02. - Rewire reminderScheduler:247 + outboxWorker:501,607 → Plan 03. - PWA picker, no new package, Intl-based → Plan 04. - No new npm installs (Package Legitimacy Audit: zero) → honored across all plans; T-18-SC = no-op. ### CONTEXT (D-01..D-07 — the trackable decision contract) | Decision | Covered by | Where | |----------|-----------|-------| | D-01 single household app_config key `household_timezone` | Plan 01 (key literal), Plan 02 (upsert) | accessor + endpoints | | D-02 browser-detect + seed at first run | Plan 02 (seed endpoint), Plan 04 (detected-zone affordance) | endpoints + UI | | D-03 no auto-overwrite after seed | Plan 02 (seed writes only if unset) | seed endpoint | | D-04 change via role-gated /admin Settings | Plan 02 (requireAdmin endpoints), Plan 04 (Timezone section) | endpoints + UI | | D-05 stored TZ = source of truth, single accessor at both scheduler sites | Plan 01 (accessor), Plan 03 (both sites route through it) | accessor + broker | | D-06 fallback chain when unset | Plan 01 (verbatim fallback), Plan 03 (existing tests stay green) | accessor + broker | | D-07 do not touch display/timed-write path | Plan 03 + Plan 04 (boundary as acceptance criterion) | broker + UI | **No unplanned items.** Deferred Ideas (per-member timezones; driving display/timed off stored TZ) are correctly absent from all plans.