**Bead document** from Bead: Firewatch Camera Grid · [canonical source](https://redfish.acequia.io/guerin/.agents/65783732-7907-4a36-983a-3b015e75e80b/WORK-QUEUE.md) · discussion: Talk: Firewatch Camera Grid
> devops/epic-step queue (per [plan-firewatch-manager](2026-06-13/artifacts/plan-firewatch-manager.md)). One step per run: claim it, do exactly that step, build + CDP-verify + deploy to `…/app/`, git-commit in `repo/`, append to `log.md`, mark done. Read `about.md` + notes 00/01 first.
## Epic 1 — Fork + data layer - [x] **S1** Fork santafe app → `repo/`, git init, deploy to `…/app/`, CDP-verify load — **DONE 2026-06-13** (`repo/` `2e1422d`; renders, 0 errors, anon 200; `repo/deploy.sh`) - [x] **S2** Telemetry → per-frame pose/time sidecar skill (Sandy); convention verify — **DONE 2026-06-13** (`skills/derive-alertwildfire-sidecars.{mjs,md}`; 15801: 666 frames → `artifacts/sidecars/frames-15801.json`; 666/666 URLs+ids valid; heading CW-from-N confirmed by fire-bearing-in-pan-range) - [~] **S3** **Camera WALL off the ONLINE source** (model corrected: grid = cameras × time, not one camera's frames; remote-first, qualified URLs). - [x] S3a — wall index `repo/cameras-sandy.json` (10 cams, positions/az from STAC, qualified telemetry URLs, co-located pairs) — **DONE 2026-06-13** - [x] S3b — wall UI — **DONE 2026-06-13** (`repo/` `ad6a2d1`, deployed): 10 camera tiles; app loads manifest, fetches each cam's telemetry live (qualified) → frame index; scrub swaps every tile to its nearest frame (night→day verified, all 10 live); UTC scrubber on the canvas (per-cam density lanes + playhead, zoomed to 2026-05-18T07:23→05-19T20:51Z extent); 10 map markers; 0 exceptions. Ripped santafe's relative photo-placement loader; `window.__wall` debug handle. - [ ] S3c — 2D map: 10 markers + az wedges from the manifest
## Epic 2 — Interface - [ ] **S4** Scrub = the wall updates (all tiles swap to nearest frame); sampling for many-camera feasibility; brushable range - [ ] **S4-cache** Scrubber caching (note 06): cache-first **service worker** for the simtable frames (Cache Storage supersedes simtable's `no-cache`; cross-origin in-scope; CORS* non-opaque; immutable→cache-first w/ LRU MB/count cap) + neighbor prefetch + small in-memory decoded hot set. rAF-coalesce the scrub. Lift to a shared **acequia-cdn** SW (browser-as-CDN substrate). Origin-side `Cache-Control: public, max-age, immutable` on simtable = separate offer. - [ ] **S5** Grid ⇄ geospatial morph under spatial+temporal bound (FLIP layout — tiles fly to map positions); bound = filter = selection - [ ] **S6** 3D map view (TaosEngine): posed billboards + wedges + terrain; 2D↔3D toggle/morph
## Epic 3 — Curation + sources - [ ] **S7** Bound-select → add-to-STAC (emit Collection/Items from selection; offered) - [ ] **S8** Multi-source intake (santafe photos / MISB frames / uploads) into the same grid+pose model **Status:** plan drafted 2026-06-13, awaiting Stephen's go before S1.
## Queued (2026-06-14) - **alertwest CORS proxy** — the non-timelapse camera items' `latest` snapshot asset is on `alertwest.live`, which is CORS-blocked (403 cross-origin) so it can't be used as a WebGPU texture. Stand up a **proxy server** that re-serves alertwest images with `Access-Control-Allow-Origin`; then re-enable the alertwest still in `loadCamFrames` (drop the `alertwest.live` skip, route via the proxy). Until then those ~20 cameras show as markers only. (Timelapse cams use the CORS-OK simtable `frames_base`, unaffected.)