**Note** from Bead: Incident Viewer Epic 4 · [canonical source](https://redfish.acequia.io/guerin/.agents/be9c3efd-ed6e-440e-9e88-653db538931d/2026-06-14/notes/01-stac-manager.md) · session 2026-06-14 · discussion: Talk: Incident Viewer Epic 4
Stephen's directive (2026-06-15): the viewer must stop defaulting to Palisades, and grow a real catalog manager. His answers reframed "switch between STACs" → **multiple loaded at once**.
## The model (Stephen's words) - "you can have multiple stacs loaded." - "the **items** [are] a set which is the **union of the STACS**." - "they should probably **display twice** in their items panel but an item will be a **singleton referring to the same URI**." → URI-as-resource: an item's identity = its URI; the same URI under two STACs is ONE model, surfaced at two mount points. - "the **view properties are independent** but the **model is the same** if we change any properties in the future." → per-binding view state (visibility, future pose edits) vs shared resource model. This is the project_uri-bind-mount / bind-vs-mount pattern made concrete in the roster.
## Asks 1. Don't load Palisades by default. 2. Store previously-accessed STACs in **IndexedDB**. 3. **Load dialog**: paste a URL + **drag-and-drop** (incl. **local catalog.json files**). 4. Multiple STACs available; **remove** them but keep them **selectable in the load dialog** (history). 5. **Reload remembers the last state per domain** (keyed by `location.origin`).
## Design **Union without namespacing (v1).** Walk each active catalog into the SAME `registry`, keyed by its RAW incident id. Distinct catalogs (clouds / santafe / simtable) have distinct incident ids → no collision → the Sandy/AIR7 id-literals (`'2026SandyFire'`, `'2025Palisades'`) keep working untouched. Same-id-in-two-STACs = last wins (v1 limitation; full per-binding identity is the singleton-by-URI follow-up). Each `IncidentReg` carries `stacUrl`+`stacTitle`; the selector groups incidents by STAC (optgroup). Visibility config keyed by the active-set signature. **Boot resolution (no Palisades fallback):** `?stac=`/`?catalog=` (comma-separated, WINS) → else per-domain remembered active set (IndexedDB) → else same-origin `./stac/catalog.json` if it exists → else **empty** (open the Load dialog). The old `simtable` incident-catalog fallback is removed from the auto-path (kept only as a dialog suggestion). **Persistence (IndexedDB `incident-viewer`):** - store `catalogs` (key `url`): `{url, title, addedAt, lastUsed}` — full history, never auto-pruned. - store `domains` (key `origin`): `{origin, active: string[]}` — per-domain last active set. **Load / remove (in-place, additive).** Adding a catalog walks it into the registry and refreshes roster+selector (no navigate — required because local-file object URLs can't survive a navigation). Removing drops that STAC's incidents (+ their per-incident caches) and rebuilds. URL catalogs persist to the domain active set; **local files are session-only** (no durable URL across reload — browser limitation; the history keeps a non-auto-restorable stub).
## Ships - **A (foundation):** boot rework + no-Palisades default + IndexedDB store + per-domain memory + `walkStacInto(url)` union walk + selector grouped by STAC + `?stac=a,b` multi-load. - **B (dialog):** the modal — URL input, drag-drop (URLs + local .json), history list with toggle-active / remove-from-history, apply.
## Deferred (singleton-by-URI follow-up) Per-binding view state (independent visibility/pose per STAC display of the same item URI); true display-twice with a shared model node. v1 shares visibility by itemId.