docs: document Phase 3 schedule endpoint, model, and resolved open items

This commit is contained in:
NeilClack
2026-09-29 10:35:44 -05:00
parent da0b6ffd5e
commit fc4ac4b107
2 changed files with 153 additions and 31 deletions
+40 -23
View File
@@ -495,45 +495,55 @@ later phase.
Phase 1 (Kiosk) and Phase 2 (System Name) are assumed here.
2. **`blocks` for weekly.** Chosen as an empty array (server cannot know the
Kiosk timezone). Consequence: old Kiosks see weekly schedules as "no
schedule".
3. **Block cap.** The existing `MAX_BLOCKS = 4` per day list is retained; the
brief does not state a cap. Whether weekly allows 4/day or 4 total is
unresolved.
schedule". Implemented in Phase 3.
3. **Block cap.** The existing `MAX_BLOCKS = 4` per day list is retained.
Phase 3 applies it **per day list** (each of the seven weekly days gets its
own 4-block budget). Whether the cap should be lower is unresolved.
4. **Field naming.** The brief's `start`/`end` were mapped to the existing
`startTime`/`endTime`; `startMinutes`/`endMinutes` retained for the Kiosk.
5. **`version` derivation.** Algorithm (content hash vs max `updatedAt` +
shape) deferred to implementation; it must be stable for equal content and
change on mutation.
5. **`version` derivation.** Resolved in Phase 3: a SHA-1 content hash of the
serialized `{type, daily, weekly}` shape. Stable for equal content, changes
on any mutation.
6. **Unauthenticated key-only access.** The new endpoints authenticate by
License Key alone (per glossary). Anyone who knows a key could read the
schedule or set the name. Mitigated only by the 10/min/IP rate limit. Known
limitation.
7. **`dynamicAudio` default flip.** DB default changes `false` → `true` for
**new** Blocks; existing Blocks are left at their stored values (no
backfill assumed).
8. **Per-user → per-License-Key migration.** Accounts with multiple License
Keys: which key inherits the existing per-user Schedule is unresolved;
conservative default is the account's first/oldest key.
9. **Cross-day spillover validation.** Weekly `sun` spillover into `mon` (and
daily same-list spillover) must be validated; exact error messaging is
deferred.
7. **`dynamicAudio` default flip.** DB default changed `false` → `true` for
**new** Blocks in Phase 3; existing Blocks keep their stored values.
8. **Per-user → per-License-Key migration.** Resolved in Phase 3: each existing
per-user Schedule is mapped to that account's **oldest** License Key
(`createdAt ASC`), as a daily schedule. Schedules with no owning
user/license are retained with `licenseKeyId NULL` and reported via a
migration `NOTICE` (not dropped).
9. **Cross-day spillover validation.** Implemented in Phase 3: weekly
`sun`→`mon` and daily self-wrap spillover are rejected as overlaps; the
shared `validateSchedule` is authoritative and mirrored client-side.
10. **Account scope for name uniqueness.** "Per account" = the owning User.
Legacy License Keys with only an `email` and no `userId` are an edge case.
11. **Geoip timezone display.** Informational only, not implemented in the
Portal today.
Legacy License Keys with only an `email` and no `userId` are an edge case
(written without a per-account uniqueness check).
11. **Geoip timezone display.** Phase 3 shows an informational note that times
run in the **Kiosk's** timezone and uses the **browser-resolved** timezone
as a fallback; no geoip dependency (paid or external) was added. The
Portal still stores no timezone.
12. **ETag on the legacy endpoint.** The legacy `GET /api/v2/schedule` emits no
ETag and never returns 304, to preserve old behaviour.
13. **`systemNameStatus` on v1.** Not added; v1 is unchanged.
14. **Trim/case representation.** Names are trimmed; uniqueness is
case-insensitive; the stored casing is preserved as provided.
15. **Sync cadence.** The brief's boot/network-up/5-minute defaults are
targets; the current Kiosk has no such timer.
16. **Atomic cache file.** Path and format deferred; SQLite is the current
durable store.
15. **Sync cadence.** Resolved in Phase 3: the Kiosk loads its on-disk cache on
boot, then pulls on boot, on a 5-minute timer, and on demand.
16. **Atomic cache file.** Resolved in Phase 3:
`~/.local/radiantwave/schedule_cache.json`, written atomically (temp file +
rename); a corrupt cache is ignored.
17. **`type` encoding for no schedule.** `type: null`; `daily`/`weekly` absent
or empty.
18. **Legacy alias.** `key` remains accepted as a `licenseKey` alias on the
legacy schedule endpoint.
19. **Kiosk local toggle.** `proclamation.schedule_enabled` in `config.yaml`,
default **on**, surfaced as System Settings → Schedule. Not synced. When
off, the engine plays local selections but the cache keeps updating. The
older `proclamation.schedule_active` flag is retained in config but is not
consulted by the Phase 3 engine.
---
@@ -544,3 +554,10 @@ later phase.
(`/api/v2/licenses/...`, additive license-validation fields), sync behavior,
System Name rules, dynamic audio, backward compatibility, touchpoints,
open items, and fixtures.
- 2026-09-29 — Phase 3. Schedule is now per License Key with daily/weekly and
`Block.day`; the shared `validateSchedule` is authoritative; the key-only
`GET /api/v2/licenses/{licenseKey}/schedule` (ETag/304) ships and the legacy
endpoint is extended additively; the Portal editor is per-system with a
browser-fallback timezone note; the Kiosk adds the conditional client,
atomic cache, schedule engine, and a System Settings On/Off toggle. Open
items 2, 5, 7, 8, 9, 11, 15, and 16 are updated to reflect the changes.