# ADR 0050 — Recorded Observatory first; portable profiles then onboard > 2026-09-03: [ADR 0051](0051-modular-observatory-profiles.md) now defines > modular Docker composition and grouped single-provider settings. Its UI and > packaging rules supersede the finite whole-profile selector/one-image target > below. Recorded-first, exact cache, preserved evidence and safety remain. Date: 2026-09-02. Decision accepted by the owner; implementation in progress. This supersedes ADR 0049's realtime-first product gate and execution order, not its live-stream integrity, ownership or safety contracts. ## Decision The primary operator workflow is a complete calculation of a compatible K1 recording with a selected immutable profile, followed by publication and cached review. Processing may be slower than acquisition. Network latency is evidence, not a prerequisite for using the laboratory. After this workflow works, compare Docker configurations; later qualify the selected configuration on actual onboard hardware. No vehicle or remote-realtime acceptance is claimed now. Keep the existing source admission, queue, artifact store, publication recovery, streaming runtime and common viewer. Do not introduce another per-LAB application. One Worker executes one active profile; alternative profiles remain sequential. M4.9T5 currently supplies CPU TGS; LAB V1 supplies sequential EoMT/DDRNet, not the complete future detector/distance/motion/TGS/policy profile. ## Recorded analysis versus realtime rehearsal Recorded analysis preserves source timestamps and causal ordering, uses bounded backpressure, and accounts for every required scheduled input/result. Its elapsed wall time need not match the source clock. Source sensor gaps remain unknown. Do not disguise live drop/resync as a complete recorded result. Introducing this execution mode requires an explicit versioned contract, not silently weakening the existing 1× live freshness, deadline or ownership gates. Realtime rehearsal remains an independent qualification. Historical network canaries retain their original FAIL results and thresholds. Neither cached video playback FPS nor end-to-end upload/export time is a Worker compute benchmark. Measure preparation, warmup, processing, delivery and publication separately; onboard estimates are conditional on matching hardware and effective config. ## Result identity and UI Only compatible, not-yet-calculated current profile versions belong in the calculation selector. Published results of every version remain below as evidence. All calculated means no Calculate action, an empty/disabled selector, and Refresh. Refresh, source selection and cached review must never start inference. Reuse requires the exact admitted source fingerprint and immutable definition (including model, image, configuration, adapter and result-contract identities). An old LAB name, a legacy overlay, or compute success without publication is not a matching cache. Missing/corrupt artifacts must not suppress a valid retry. Publication failure retries publication, not computation, while the sealed package is recoverable. Changed profile versions become new calculations. ## Implementation checkpoints The checkpoints below are chronological evidence, not a current TODO list. As of 2026-09-03, the queue/claim/cache/selector foundation is implemented and locally checked. Attempt-bound progress is implemented and active (`bee8552`, control-agent installation `54c8d82`). One real M49 run succeeded and published; exact cached document reopening survives restart after the no-op reindex fix `bd947b4`. Full recorded-analysis and synchronized saved visual replay remain unaccepted; the current four-stage route and next increment are maintained only in the linked ExecPlan. ## First implementation increment Portable queue admission now opts into an atomic duplicate-computation guard. It uses the existing job identity, not a second cache key ontology, and rejects a different request while the identical job is active, requires reconciliation, or has a sealed result pending/failed publication. The check and INSERT use one SQLite transaction. Exact retries retain the original idempotency semantics. A genuinely failed compute can be retried with a new request key. Legacy queue callers retain their existing policy; no schema migration or Worker/image change is introduced. Published-cache verification, catalog projection, selector changes, progress and complete recorded-analysis execution are still pending. This increment does not mark stage 1 or the full workflow complete. Tests cover concurrent admission, durability, changed source/profile, failure retry and HTTP 409 duplicate handling. ## Second increment: bounded non-binding idle claims Claim/v3 is active on the backend and both current Worker006 control agents. Idle polls do not persist receipts; actual grants are durable and share the request-ID namespace with preserved v2 history. Repeating a non-binding v3 idle request may later acquire work, unlike an old persisted v2 empty receipt. The 50,000-row legacy ledger remains intact; a separate 50,000-positive-grant budget does not change the shared 128 MiB database/WAL/SHM storage bound. Only the imported gateway's request schema literal changes in two pinned child images. Compute package identities and model images remain unchanged. The exact control-agent create declarations, separate from executor package declarations, are now the durable runtime source; old agents remain stopped for rollback. 126 focused tests and 16 real empty-capability Worker-to-backend probes passed. All pre-existing queue row hashes are unchanged. This repairs dispatch polling, not published-cache verification or full recorded-workflow acceptance. See the [repair evidence and rollback boundary](../../experiments/perception/OBSERVATORY_RECORDED_CLAIM_REPAIR_2026-09-02.md). ## Third increment: exact published-result cache (2026-09-03) Implemented and activated as a matching backend/frontend release on canonical8000 on 2026-09-03 after an owner-approved Docker VM restart recovered memory headroom. Full typecheck,714 frontend tests and production build passed sequentially; API catalogs and in-app UI were checked without creating inference jobs. All queue row hashes are preserved. This does not claim a completed recorded run. The portable catalog now joins successful **published** queue receipts to the current per-source catalog snapshot, immutable definition, Session publication binding, calculation profile, package contract, central manifest and every artifact. No matching by LAB name, operator label, or the newest 100-job page. The queue receives two indexes, with no deletion or rewriting of history. The existing computation identity remains the only computation key. An exact usable result projects `existing / open-existing`, its result ID and path-free source/definition/package identities. It disables new submission but does not require Worker dispatch or raw-source preparation for cached review. Missing/corrupt artifacts do not count as coverage; the older evidence remains. The newest verified exact candidate is returned; other versions are not hidden from the evidence catalog or mistaken for the current calculation. Large immutable result objects are hashed in 1 MiB chunks on first use or file metadata change. A bounded 4096-entry memo rechecks dev/inode/size/mtime/ctime; normal refresh does not reread unchanged large objects. This is result-integrity IO, not source upload, source decoding, model inference, or an FPS measurement. The queue INSERT fence rechecks the published candidate atomically. It never streams large files while holding the write lock: cold/concurrent/changed verification returns HTTP409 requesting Refresh. Exact idempotency retries still return the original job. Verified corruption permits a new request. The frontend's existing portable decoder now accepts only bound, internally consistent cached results. No per-LAB page, renderer or selector implementation was introduced. Full selector/progress/product acceptance remains stage 2. Browser QA confirms that source/setup-only job selection can still display a legacy completion status against a current definition. Exact-version status binding and separating archives from executable choices remain required; the activated backend cache does not prove those UI changes complete. See [verification and activation status](../../experiments/perception/OBSERVATORY_PUBLISHED_CACHE_2026-09-03.md). ## Selector contract clarification — 2026-09-03 The owner explicitly excludes completion buttons and status badges. A completed current profile is represented by its reviewable, profile-marked evidence below, not by a “calculation complete” control. The selector now contains compatible portable definitions with no verified exact published cache hit. An archived LAB or old definition never hides a new version. An empty selector has no Calculate. An in-flight computation has only an activity indicator; failed publication keeps a separate recovery action and never starts inference again. Removed the six-card presentation cap; the existing catalog window limit remains explicit. Queue reads now filter definition SHA before pagination. Hook snapshots are fenced by source/setup/definition so previous jobs cannot flash in the next selection. New synthetic tests exercise none/all/partially cached choices, unavailable executors, legacy/new-version separation and pre-effect selection changes. Sequential validation:720 frontend tests,52 queue/API tests, architecture, typecheck, Ruff/mypy and production build PASS. Full recorded-run/product acceptance and measured progress counters remain stage2. Historical terminal logs do not become a new selection's error. The existing responsive selector layout follows workspace width, including restored narrow windows, rather than only the browser viewport. ## Real progress, publication and restart proof — 2026-09-03 Worker-to-backend-to-UI progress is claim/generation/sequence-bound and stored as one bounded current row per job. It does not renew ownership or change live semantics. One UI-submitted M49 × RAVNOVES004TREE run published on its first attempt after 1416.763 s. All 6830 fragment-schedule rows and result artifacts were verified; 19 missing-LiDAR positions remain UNOBSERVED, point accounting has zero unaccounted contributions. The schedule spans 39.215–757.160 s, not the whole 808.779 s source session. This CPU-only result is not a full AI profile. An actual canonical restart exposed a missed case: unchanged archive discovery advanced the source row's update clock, changing its admitted catalog hash and hiding the exact cache hit. Reconciliation now preserves the **entire existing snapshot** when only that incidental clock differs. Real changes still alter identity; the fingerprint, package and computation schemas are not weakened. The affected source's one clock field was restored only after reconstructing its exact admitted hash, with backup and transactional before/after fences. No source bytes, result provenance or job identity were rewritten. Following activation/restart, the same catalog, job and cached result document reopened; all previous jobs and claim history remain. However, the portable review currently renders a checked JSON document/artifact inventory, not video and TGS. Therefore stage 2B stays open for a common schema-bound saved visual adapter, using this sealed result without recomputation or legacy overlays. See [real-run and restart evidence](../../experiments/perception/OBSERVATORY_RECORDED_PROGRESS_2026-09-03.md). ## Boundaries Owner clarification, 2026-09-03: Observatory's result list includes only admitted portable-profile publications, not every historical LAB linked to a source. Legacy contours, canonical archive projectors and stored evidence remain unchanged. Filter by executable replay/publication binding, never by LAB label, date, model name or equality to the currently installed definition. Older valid portable versions remain reviewable. Versioned opt-in cursor pages feed the existing source search; legacy catalog consumers retain their previous response. See [catalog boundary evidence](../../experiments/perception/OBSERVATORY_CATALOG_BOUNDARY_2026-09-03.md). Source-reuse increment (2026-09-03): both current control agents now share a Worker-local source CAS. Every generation still obtains its own authenticated, claim-bound manifest and checks exact digest/length before reusing bytes. Missing camera members are fetched individually instead of retransferring a partly cached epoch. Job/output/profile identities and model packages are unchanged. Cached visual results remain served by Core, not this Worker input cache. M49 now selects an exact current-producer LiDAR v2 pack before raw decoding; the legacy producer file and its identity hash remain unchanged. Full source hash and strict NPZ integrity validation still occur on reuse. Cold whole-source materialization, versioned recorded-analysis and full workflow acceptance remain open. Shared-cache synthetic Worker proof is not a model run or a throughput measurement. See [source-reuse evidence](../../experiments/perception/OBSERVATORY_SOURCE_REUSE_2026-09-03.md). No Synology deployment, hardware actuation, motor integration, new capture, silent model substitutions or deletion of recordings/results. A Docker image's portability does not promise that a CUDA build runs unchanged on Apple Silicon. Physical-live and onboard/vehicle acceptance require the actual target hardware. The only maintained execution plan is [the four-stage ExecPlan](../OBSERVATORY_REALTIME_PROFILES_EXECPLAN.md).