Cory Policht
← All references
REF · ADR-032-RETIRE-DATS-UMBRELLA · ADR

Adr 032 Retire Dats Umbrella

source: AI-Dev-Platform/ADR/ADR-032-Retire-DATS-Umbrella-Independent-Per-Repo-Deployment.mdscrubbed extract

Scrubbed extract of AI-Dev-Platform/ADR/ADR-032-Retire-DATS-Umbrella-Independent-Per-Repo-Deployment.md — cited by the Chronicle (06-extraction-and-divergence). Redactions: phone / service detail / secrets / clearance / personal / internal hosts.


category: Work project: AI-Dev-Platform status: accepted created: 2026-08-10 number: ADR-032 title: Retire the DATS Umbrella; Each Repo Deploys Independently supersedes:

  • ADR-025
  • ADR-026 tags:
  • adr
  • architecture
  • decision-record
  • dats
  • release-of-record
  • tag-model
  • deployment
  • supersedes-adr-025
  • supersedes-adr-026

ADR-032: Retire the DATS Umbrella; Each Repo Deploys Independently

Status

Accepted 2026-08-10 (user-directed). Supersedes [[ADR-025-DATS-Demoted-To-Release-Of-Record|ADR-025]] and [[ADR-026-Component-Independent-Versioning-DATS-Umbrella|ADR-026]], and retires the umbrella half of [[ADR-008-Artifact-And-Delivery-Automation-Strategy|ADR-008]]’s delivery model.

✅ Executed and verified 2026-08-11. The disposition below is done, not planned: adp-orchestration-e2e#53 adopted the suite, DeveloperAutomationToolsServices#51 removed the umbrella (+134/−5188), and run 9039 is the first green system-integration run since the ADR-025 split — bring-up, smoke, all five sandbox scenarios, digest artifact. Two defects surfaced and were fixed in the process (#54 seccomp path resolution, #55 concurrent-run collision).

This decision is closed. Do not reopen it without new evidence of an actual consumer. The disconfirming evidence is recorded below precisely so this is not relitigated.

Context

ADR-025 demoted DATS to a thin release-of-record repo. ADR-026 then made the model explicit: components version independently, and DATS carries a single umbrella tag plus a digest manifest pinning the composing component versions, such that — in ADR-026’s words — “what component versions are in platform v1.0.0?” is answerable only via that manifest.

The model has now run for a full release cycle. It did not work, and investigation on 2026-08-10 established that it has no consumer to work for.

Evidence

1. Nothing consumes the manifest. A grep across homelab-stacks, shared-workflows, adp-orchestration-e2e, AIService and MCPServer for emit_manifest, release-metadata.schema, and catalog/releases returns zero hits. No deploy, workflow, or test has ever read the pin set.

2. Deploys already record composition elsewhere, truthfully. homelab-stacks deploys ADP from per-chart values — charts/aiservice/values.yaml pins tag: v0.0.59, charts/mcpserver pins v0.1.5 — and helm-deploy-adp.yml bumps them on main. The git history of those files is a per-environment composition record that is always current because it is what actually ran. There are zero @sha256: pins anywhere in that repo, so the umbrella was never the source of deploy truth even in principle.

3. The capability never once worked. DATS produced exactly one manifest, v1.0.0.json. Every digest in it was fabricated — the git commit SHA zero-padded — and its own notes field said so. Two of the four services (ADP-Orchestration-UI, IDEExtension) never had a container image to pin at all. The two that did (aiservice:v0.0.29, mcpserver:v0.0.7) have been purged from the registry, so those digests are unrecoverable. The defect went unnoticed for two weeks, and the attempted remediation substituted current :latest digests — asserting that v1.0.0 shipped images built weeks after the release was cut — and merged despite [[IP-E12-12-V1-Manifest-Placeholder-Digests|IP-E12-12]] stating in bold that it must not.

A mechanism that has never held a correct value, whose failures nobody detected, and which no consumer reads, is not providing the guarantee it nominally exists for.

4. The economics do not apply. An umbrella pays off when an external consumer installs “the platform” as a versioned unit and needs correlated rollback across it. There is no such consumer. This is a single-operator homelab, and every component already ships from its own repo with its own remote, tags, and CI.

Decision

Retire the DATS umbrella. Each repo builds, versions, and deploys independently, with no cross-repo release artifact.

  • No umbrella tag is cut again. v0.1.0/v0.2.0/v0.5.0/v1.0.0 are kept as historical tags — they cost nothing — but they no longer assert platform composition, and nothing may cite them as evidence that a component version shipped.
  • catalog/releases/v1.0.0.json is retired as-is. Its digests are not corrected, not re-emitted, and not replaced by a v1.0.1. The tag-decision options enumerated in IP-E12-12 are all withdrawn — see “Withdrawn options” below.
  • The manifest machinery is deleted: catalog/ (schemas, service/environment entries, releases), tests/system-integration/emit_manifest.py and its test suite, and scripts/validate-catalog.py.
  • Composition is recorded at the deploy point, not at tag time. homelab-stacks charts/*/values.yaml plus its git history is the composition record of record.
  • The genuinely valuable contents of DATS move to the repos that own them, per the disposition below. DATS itself is repurposed as a dev-tooling repo.

Disposition of DATS contents

Content Destination Rationale
tests/system-integration/smoke.py adp-orchestration-e2e cross-boundary smoke; e2e owns this
tests/system-integration/sandbox_e2e/ (5 tests, _harness.py, run_all.py) adp-orchestration-e2e kept intact as one suite (see note)
mint_ci_token.py, requirements.txt adp-orchestration-e2e dependencies of the above
docker-compose.ci.yml, scripts/ci/resolve-image-pins.py adp-orchestration-e2e the harness that stands the stack up for those tests
.gitea/workflows/system-integration.yml adp-orchestration-e2e revived where it runs, instead of dead where it doesn’t
dev-tools/Scripts/ (Start-LocalSpine.ps1, Stop-LocalSpine.ps1, agent-config switchers) stays in DATS the repurposed repo’s new reason to exist
catalog/, emit_manifest.py, test_emit_manifest.py, validate-catalog.py deleted umbrella machinery

The sandbox suite moves whole to adp-orchestration-e2e. Four of its five tests (test_run_command_seccomp, test_write_file_trust_gate, test_read_file_no_trust_gate, test_git_sequence) exercise MCPServer sandbox behavior and could defensibly live in MCPServer instead. They are kept together because they share _harness.py and a running-stack fixture, and splitting them would duplicate that harness across two repos. Revisit only if MCPServer grows its own equivalent harness.

Note for Phase K: sandbox_e2e/test_correlation_id_propagation.py already exists and is substantially the K3 gate (“correlation IDs continuous across Hub → AIService → MCPServer”). Relocating it to adp-orchestration-e2e also places it where K4 wants it (“repeatable E2E spec in adp-orchestration-e2e”). Both gates are partly implemented already.

Withdrawn options — do not revisit

IP-E12-12 enumerated three paths for the v1.0.0 manifest. All three are withdrawn, because they all presuppose the umbrella is worth maintaining:

  1. Re-emit and re-tag v1.0.0 with real digests — impossible; the RC images are purged.
  2. Publish a corrected v1.0.1 — would pin today’s images, documenting current composition rather than release history.
  3. Accept v1.0.0 as a lineage anchor and downgrade the vault’s language — superseded by this ADR, which retires the artifact rather than reinterpreting it.

A fourth question raised on 2026-08-10 — whether to restore the original zero-padded placeholders or substitute the sha256:0*64 sentinel — is likewise moot. The file is deleted; the digest values are not a live question.

Note also that the emitter’s fail-closed guard makes the file unregenerable regardless: _is_placeholder() rejects any 64-char hex ending in 24 zeros (which both candidate values satisfy) for an artifact_type: image entry, and omitting the digest hard-fails the same entries. That guard was correct work; it dies with the mechanism it guarded.

Consequences

Positive

  • One less cross-repo coupling, and one less artifact that can silently be wrong. The failure mode that produced this ADR — a signed, schema-valid manifest asserting a composition that never existed — is structurally impossible once there is no manifest.
  • Release attention goes to the repos that actually deploy.
  • [[IP-E12-17-DATS-Integration-CI-Dead-Since-ADR-025|IP-E12-17]] (DATS integration CI dead since ADR-025) closes by relocation rather than won’t-fix: the workflow moves to a repo where it runs.
  • [[IP-E12-12-V1-Manifest-Placeholder-Digests|IP-E12-12]] closes as superseded.

Negative / Risks

  • Loss of a single machine-readable answer to “what composed platform vX”. Mitigated by homelab-stacks chart values + git history, which answer it per environment and are true by construction. In practice the question has never been asked by a tool.
  • Loss of digest immutability as a stated goal. Tags are mutable; v0.0.59 can be re-pushed at a different image. The umbrella nominally offered immutability and never delivered it. If this matters, the fix is digest-pinning homelab-stacks charts/*/values.yaml — enforced where deploys actually read — not a revived umbrella. Filed as a follow-up, not a blocker.
  • Correlated breaking changes across the spine (e.g. an AIService/OUI wire-contract bump) lose their nominal expression point. In practice an umbrella tag nobody consulted at deploy time never enforced this either; wire-contract versioning does, and a versioned WebSocket schema already shipped as a v0.5.0 gate. See [[IP-E8-21-Consolidate-WebSocket-Wire-Contract|IP-E8-21]].
  • Cosign attestation is unaffected — it signs per-image and never needed an umbrella.

Alternatives Considered

Alternative 1 — Keep the umbrella, fix the manifest properly

Correct the digests, add an explicit digest_status: unrecoverable field to release-metadata.schema.json, and hold the emitter guard. Rejected: it invests schema-breaking work (which per the DATS CLAUDE.md requires architecture-reviewer approval) in a mechanism with zero consumers, to answer a question nothing asks.

Alternative 2 — Keep the umbrella tag, drop the digest manifest

Retain v1.0.0 on DATS as a pure milestone marker with no pin set. Rejected: a tag on a repo with no source and no manifest carries no information the vault’s phase trackers and Master-Plan do not already carry better, and it preserves the misleading implication that DATS is a release authority.

Alternative 3 — Move the umbrella to homelab-stacks

Emit the digest manifest from the repo that actually deploys. Rejected as an umbrella, but its useful half is adopted: record composition at the deploy point. That is chart values plus git history, not a separate signed artifact.

  • [[ADR-025-DATS-Demoted-To-Release-Of-Record|ADR-025]] — superseded by this ADR
  • [[ADR-026-Component-Independent-Versioning-DATS-Umbrella|ADR-026]] — superseded by this ADR. Its component-independent-versioning half survives in substance (components do version independently); its umbrella-pin half is what is retired
  • [[ADR-008-Artifact-And-Delivery-Automation-Strategy|ADR-008]] — the digest-pinned release model the umbrella manifest served; its umbrella half is retired here
  • [[IP-E12-12-V1-Manifest-Placeholder-Digests|IP-E12-12]] — closed as superseded
  • [[IP-E12-17-DATS-Integration-CI-Dead-Since-ADR-025|IP-E12-17]] — closed by relocation
  • [[IP-E12-10-Demote-DATS-Release-Of-Record|IP-E12-10]] — done; implemented ADR-025, now history
  • [[IP-ADP-GAP-3-DATS-Docs-And-Pin-Cadence|IP-ADP-GAP-3]] — pin-cadence work, moot