**Note** from Bead: Incident Cataloging · [canonical source](https://redfish.acequia.io/guerin/.agents/c38c1239-bfd3-44dd-8d97-1a0aa39ac8da/2026-06-08/notes/event-catalog-format-design.md) · session 2026-06-08 · discussion: Talk: Incident Cataloging
> Workshop piece, not a deliverable. This is the live synthesis the three surface artifacts feed into. The goal Stephen stated (2026-06-08): *"develop a catalog format for events that is agnostic to views and controllers in different apps."* Decisions here are tentative; open questions are flagged. When this stabilizes it graduates to an `artifacts/` draft.
## The core observation Three independent apps already share a data model with **no shared code** — only files at URIs: - **santafe.live** *writes* `imageMeta/<id>.json` + `imagePose/<id>.json` (2-DOF placement, year). - **alertLive viewer** *reads* `imageMeta/` + `imagePoses/` (6-DOF pose, scene presets). - **MISB FMV viewer** *reads* `incidents.json` + MISB telemetry (6-DOF pose stream, UTC µs). That convergence IS the format, in embryo. The job is to name it, generalize the axes where the surfaces disagree, and write down the adapter boundary. A sibling session has since added **two machine-acquisition surfaces** (4 & 5), extending the claim from human-authored to sensor-authored writers, and forcing the subject/platform/look pose split (see §Inbound + req #9/#10). Surface docs (gallery): [santafe-live-catalog-manager](../artifacts/santafe-live-catalog-manager.md) · [santafe-alert-live-events-structure](../artifacts/santafe-alert-live-events-structure.md) · [misb-viewer-data-formats](../artifacts/misb-viewer-data-formats.md)
## Inbound contributions (provenance) - **2026-06-08 — AlertCalifornia-capture sibling session** → dropped into this bead's `uploads/` (`2026-06-08-camera-and-aircraft-surfaces.md` + `.meta.json` + `cam-13500-entry.example.json`). Proposed surfaces 4 (AlertWest ground camera) & 5 (EGP/MISB FMV via mission-browser); the **platform/look/subject** pose decomposition; and empirical answers to Q3/Q4/Q5. Bound into this note (decomposition → §Candidate shape + req #9; acquisition-as-controller → req #10; Q3/Q4/Q5 → Open questions). Worked `Entry` for cam 13500 (`UCSD_TDLLN_1`, verified live) kept verbatim in uploads as the reference instance. Upload files left immutable per dock discipline.
## Candidate shape (straw man — expect to change) ``` Catalog = typed tree of grouping nodes; leaves are Entries. Entry = { id, // content-addressed default: SHA-256(sourceUrl)[:16] type, // 'alertLive' | 'misbFmv' | ... (discriminator) displayName, media: [ { url, contentType, role?:'source'|'thumbnail'|'video' } ], pose?: Pose, // OPTIONAL, PARTIAL, may be time-varying poseTrack?: { ref, format }, // external time-indexed pose (see req #3, Q5) time?: Time, // OPTIONAL, multiple granularities groundRefs?, presets?, notes?, lastUpdate } // Pose decomposed into THREE referents (revised per sibling contribution, see §Inbound). // The old flat Pose conflated them; that was wrong. Pose = { platform?: { Latitude, Longitude, Elevation }, // where the SENSOR is (mount); may stream look?: { heading, pitch, roll, fov, fovy, zoom }, // where it's AIMED; the PTZ/FMV time-varying part subject?: { Latitude, Longitude, Elevation } // what it's OF — santafe.live's lat/lng is THIS } Time = year | instant(UTC) | range | stream(ref → telemetry) ``` > **Pose decomposition (revised 2026-06-08).** The flat `{Latitude…far}` straw-man conflated three physically distinct spatial referents that the surfaces do NOT agree to collapse: > - **subject** — what the media is *of*. santafe.live's drag-to-map `Latitude/Longitude` is the subject location, not a sensor. > - **platform** — where the sensor *is*. Fixed for a ground camera; a per-sample stream for an aircraft. MISB `sensor_latitude/longitude/altitude`. > - **look** — where the sensor is *aimed*. The time-varying part for PTZ cameras / FMV gimbals. MISB `sensor_relative_azimuth/elevation/roll` + `hfov/vfov`; AlertWest `camAzimuth/Elevation/Zoom`. > Every sub-block stays optional & progressive (req #1, now per-referent). santafe.live fills `subject` only; AlertWest fills `look` (+`platform` when roster has it); MISB fills `platform`+`look` as streams.
## Requirements gathered so far (each tagged with its source surface) 1. **Pose is partial & progressive** [santafe.live]. An entry may have id+media but no pose; gain 2-DOF lat/lng; later gain full 6-DOF in another app. Every spatial field optional & independently editable. Completeness is shown by indicators (📍/🕐), not enforced by required fields. 2. **Time has multiple granularities** [all three]. year (`calendarDate:"YYYY"`, santafe.live) vs UTC µs stream (MISB) vs untimed/`lastupdate`-only (alertLive). The `Time` field must admit year / instant / range / stream. `timeUTC` already sits reserved-but-unused in santafe.live meta — a second time channel. 3. **Pose can be a stream, not a scalar** [MISB]. MISB pose is per-sample over a clip, interpolated. So Pose@Entry may be a single value OR a reference to a time-indexed track. Unify: `pose` is "current pose"; a clip has `poseTrack`. 4. **Content-addressed identity** [santafe.live]. `id = SHA-256(sourceUrl)[:16]` → idempotent ingest, free dedupe, cross-collection join key. Good default; allow opaque ids too (alertLive uses 24-char slugs; MISB uses manifest strings). 5. **Media by reference; previews by value** [all three]. Full media stays at origin host (`guerin.acequia.io`, `simtable.acequia.io`); only derived thumbnails localized. Catalog holds URLs, not bytes. Cross-host within one catalog is normal (`incidents.json` mixes guerin + simtable). 6. **Normalize at the edge, not in the catalog** [MISB]. MISB ships two on-disk dialects (long-name `samples[]` / short-name `packets[]`); a per-type adapter normalizes to one in-memory shape. Catalog references raw payload + declared format; adapters (the `MISBTelemetryParser` role) do the rest. ⇒ the format is a *reference + type tag*, not a forced-uniform record. 7. **Edit surface = HTTP verbs on paths** [santafe.live]. No app server; each mutation is a `PUT` to a resource URI; a viewer reading the same URI sees it. Controller (drag-to-map / drag-to-timeline / agent bulk-geocode) is fully decoupled from model (JSON at a URI). This is literally the "paths as event bus / agent-as-file" direction (workspace memory) instantiated. 8. **Grouping is a recursive typed tree** [MISB `incidents.json`, alertLive collections]. incident → folder* → leaf[]. Arbitrary depth. Leaves are the renderable units. 9. **Pose has three referents: subject / platform / look** [AlertWest camera, MISB — via sibling]. See the decomposition above. The catalog must not collapse them. This is the single biggest revision from the inbound contribution. 10. **Acquisition is a first-class controller** [AlertWest grabber, mission-browser — via sibling]. santafe.live auto-authoring's manual twin: sensor-driven tools (`getCameraTimelapse` grabber, mission-browser bookmarklet) *auto-author* pose+time from telemetry and PUT to the same WebDAV layout. "Controller-agnostic" therefore spans human-drag AND machine-acquisition writers. Same `{id, media-by-URL, Pose, Time}` on disk, no shared code.
## Surfaces documented (the evidence base) | # | Surface | Role | Doc | |---|---|---|---| | 1 | santafe.live | manager / human-author | artifact: santafe-live-catalog-manager | | 2 | alertLive event | viewer-read (posed photos) | artifact: santafe-alert-live-events-structure | | 3 | MISB FMV viewer | viewer-read (posed video) | artifact: misb-viewer-data-formats | | 4 | **AlertWest ground camera** | auto-author posed photo stream | inbound (AlertCalifornia-capture sibling); `simtable.acequia.io/docs/alertcalifornia/` | | 5 | **EGP/MISB FMV via mission-browser** | auto-author posed video stream | inbound; `simtable.acequia.io/apps/mission-browser/` |
## Open questions - **Q1.** *(open)* One Entry per image, or per *(image, pose)*? alertLive separates `imageMeta` (identity) from `imagePoses` (placement) by shared id. Lean: keep split (identity vs placement have different edit lifecycles & owners). - **Q2.** *(open)* Is `type` on the Entry or inferred from which collections exist? alertLive puts `type:"alertLive"` on `event.json` (container), not per-image. Lean: `type` on the container, optional per-entry override. - **Q3.** *(provisional — sibling).* scenePresets vs measured camera az/el/zoom: **discriminator = was it authored for display, or measured from the world?** Measured sensor state (camera `camAzimuth/Elevation/Zoom`, MISB telemetry) → `pose.look` (content). Human-chosen view bookmarks (alertLive `scenePresets` Digit1/2) → `presets` (view-state). Do NOT lump them. *Adopted.* - **Q4.** *(provisional — sibling).* **Tagged union** confirmed by the camera case: a single frame is `{kind:'instant',utc}`; the camera as a whole is `{kind:'stream',ref}` — same entity, two aggregation levels. Entry must represent either a single capture or a rolling capture, distinguished only by `time.kind`. *Adopted.* - **Q5.** *(resolved — sibling).* Pose track is **external**, referenced by `{ref, format}`. AlertWest already externalizes per-frame az/el/zoom as a JSON sidecar next to frames; MISB externalizes per-clip. Inline would bloat the Entry. *Closed: external.*
## Naming reconciliation needed (see [catalog-schema-drift](catalog-schema-drift.md)) `imagePose` (santafe.live) vs `imagePoses` (alertLive); `lastUpdate` vs `lastupdate` in sibling files. Trivial but must be pinned before anything writes the "canonical" form.
## Next action Draft the unified schema as an `artifacts/` deliverable once Q1–Q5 have at least provisional answers. Until then this note is the accumulator — add a tagged requirement line every time a new surface or constraint shows up.