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#53adopted the suite,DeveloperAutomationToolsServices#51removed 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 (#54seccomp path resolution,#55concurrent-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.0are 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.jsonis retired as-is. Its digests are not corrected, not re-emitted, and not replaced by av1.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.pyand its test suite, andscripts/validate-catalog.py. - Composition is recorded at the deploy point, not at tag time.
homelab-stackscharts/*/values.yamlplus 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:
Re-emit and re-tag— impossible; the RC images are purged.v1.0.0with real digestsPublish a corrected— would pin today’s images, documenting current composition rather than release history.v1.0.1Accept— superseded by this ADR, which retires the artifact rather than reinterpreting it.v1.0.0as a lineage anchor and downgrade the vault’s language
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-stackschart 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.59can be re-pushed at a different image. The umbrella nominally offered immutability and never delivered it. If this matters, the fix is digest-pinninghomelab-stackscharts/*/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.
Related
- [[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