feat(observatory): add installed package dispatch and durable publication

Checkpoint existing backend lifecycle changes. Focused verification found nine legacy fixture failures in portable LAB V1 executor/runtime tests; repair follows separately without rewriting this snapshot. ADR date retains its intentional Markdown hard break.
This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 00:58:37 +03:00
parent d655d6998d
commit a945d665dd
47 changed files with 6008 additions and 1034 deletions
@@ -5,6 +5,11 @@ Status: accepted; portable LAB V1 admission and Worker pull foundations implemen
production executor, claim lease, result publication, Worker deployment and
live-trigger wiring pending
Amended on 2026-09-01 by ADR 0048. Capability-aware claims, renewable leases,
verified result transport and the durable publication lifecycle are now
implemented; physical Worker installation and live-trigger wiring remain outside
that source increment.
## Context
Observatory must let an operator apply an admitted laboratory setup to a saved
@@ -4,6 +4,11 @@ Date: 2026-08-31
Status: accepted as a backend foundation; production validators, transport and
executor wiring remain blocked
Amended on 2026-09-01 by ADR 0048. Exact built-in validators, package transport,
application publication wiring, durable publication status and a typed portable
result viewer are now implemented. Installed generic package executors and a
physical Worker 006 cutover remain separate acceptance work.
## Context
The durable Observatory queue deliberately treats a Worker `succeed` call as a
@@ -0,0 +1,138 @@
# ADR 0048: Independent installed LAB packages and portable result lifecycle
Date: 2026-09-01
Status: accepted and implemented as source contracts; no Worker deployment or image migration
## Context
The first portable Observatory path still composed M4.9 and LAB V1 through
profile-specific builders and a combined Worker release. A Worker could claim a
job whose exact executor was not installed, execution success could become
terminal before catalog publication, and the UI could open only one hard-coded
legacy replay family. Adding another LAB therefore still risked changes across
the Worker coordinator, backend publication and frontend viewer.
The target remains observation-only. This decision does not authorize a build,
deployment, Docker installation, Worker 006 mutation, K1 command or safety use.
## Decision
### Installed package boundary
A Worker-local `InstalledLabPackageRegistry` binds one independently installed
package to the exact RunDefinition, RuntimeCandidate and four-digest executor
identity. Its manifest contains only reviewed container images, argv, dependency
topology, fixed in-container mount targets and immutable asset IDs. It cannot
contain a host path, secret, environment, Docker socket or job-provided command.
Every package uses one stable container I/O contract:
- read-only source at `/missioncore/input/source`;
- read-only canonical plan at `/missioncore/input/run-plan.json`;
- result package at `/missioncore/output`;
- ephemeral work at `/missioncore/work`.
`single-container` and `fixed-stack` are package properties. Exactly one
container owns the portable result. All images must already be admitted by the
bound RuntimeCandidate, and the package asset inventory must exactly equal that
candidate's reusable asset inventory.
Worker composition now exposes one generic package executor factory. A Worker
may install any non-empty subset of server-ready definitions. It advertises only
the resulting four-digest identities; it is not required to implement every LAB
known by the backend. Existing profile-specific builders remain a compatibility
path until their images adopt the common package I/O contract.
The generic executor runs a deterministic dependency graph of hardened one-shot
containers through the local Docker Engine API. Every container is pinned by
image SHA-256, has no network, a read-only root filesystem, no Linux
capabilities or privilege escalation, and receives only the declared read-only
inputs plus the single shared result output. Memory, CPU, PID, shared-memory,
tmpfs, GPU-count and timeout limits are explicit package fields. Exactly one
`result-writer` step must transitively depend on every compute step, after which
the Worker verifies the declared result manifest, file lengths, digests and the
absence of links or undeclared files before publication can begin.
Worker-local asset bindings translate reviewed controller paths to Docker-host
paths. Those host paths never enter the package manifest or queued job. The
current package contract intentionally admits ordered offline steps only; a
future LAB that genuinely requires simultaneous services needs an explicit new
contract instead of silently weakening the isolation boundary.
### Capability-aware dispatch
Worker claim protocol v2 sends a bounded, canonical snapshot of installed
four-digest executor identities. The queue selects the oldest queued job that
matches one of those identities inside the same transaction that creates the
claim. An empty snapshot claims nothing. The snapshot is included in the claim
request digest, so an idempotency key cannot be replayed with different Worker
capabilities.
Claim v1 remains accepted only as a rolling compatibility path and cannot send a
capability field. New Worker code always uses v2.
### Execution and publication are separate durable lifecycles
Verified upload completion atomically seals execution as `succeeded` and creates
a publication outbox entry. Publication has its own state:
- `not-required` for the compatibility path;
- `pending` after verified execution completion;
- `failed` with bounded error evidence and attempt count;
- `published` with timestamp.
A publisher failure no longer rewrites or loses the successful execution. The
authenticated Worker API returns the durable job with HTTP 202 and exposes an
idempotent publication retry endpoint. Exact retries reuse content-addressed
artifacts and the immutable SessionStore projection; they do not rerun the model.
### Contract-driven result viewing
Portable publications receive an explicit v2 viewer capability
`portable-result-review / portable-result / result-defined`. The public viewer
service resolves that capability, immutable publication provenance and central
artifact manifest, then rechecks the JSON result document's length and SHA-256.
The Control Station selects either the legacy canonical replay adapter or the
portable result adapter from the typed capability. No portable setup ID, LAB ID,
source session ID or result-name family selects the viewer.
The UI polls while execution or publication is pending, refreshes the catalog
after publication, and keeps legacy and portable setup catalogs independently
usable if either endpoint is temporarily unavailable. Catalog merge identity is
`setup_id`, never a display name.
### Validator extension
Result validators are registered and selected by exact result-contract SHA-256.
The built-in LAB V1 and M4.9 functions remain compatibility registrations. The
composition core no longer requires both setup IDs or branches on them, so a
future contract can supply another server-owned registration without changing
the queue or Worker router.
## Compatibility and migration boundary
This source increment does not claim that the currently installed M4.9 or LAB V1
images implement the new common container I/O surface. No synthetic package
manifest is checked in for an image that has not been rebuilt and smoked against
that surface. The old builders continue to work, and M4.9 startup no longer
requires LAB V1 receipt/release environment values.
The next physical migration is deliberately per profile:
1. make one image emit the existing portable result package through the common I/O contract;
2. seal its independent installed package manifest and local asset bindings;
3. run contract composition and a short offline smoke;
4. advertise only that executor identity in claim v2;
5. perform a reversible canary without changing another profile.
## Consequences
- A Worker cannot take an unsupported job merely because it is earlier in the queue.
- Adding a conforming LAB changes its definition, runtime candidate, validator
registration and installed package, not the queue protocol or frontend routing.
- Successful compute and successful catalog publication are both visible and
recoverable facts.
- Portable result review is generic JSON/artifact evidence; richer visual viewers
can be added as new typed capabilities without adding setup-name conditionals.
- The generic package boundary is implemented, while image conformance and
physical Worker 006 acceptance remain explicit, unclaimed work.