Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0079

ADR-0079: The prod interlock requires a green test-env record of the commit being promoted

field value
Status Superseded — Superseded by ADR-0087 (2026-10-11)
Date 2026-10-08
Register factory (docs/adr, 4-digit ids)
Source docs/adr/0079-the-prod-interlock-requires-a-green-test-env-record-of-the-commit-being-promoted.md — permalink at 07aa3b3c6 (unchanged on master since that commit)
Wiki register entry ADR-0079 (wiki status: Superseded)

← ADR-0078 · ADR-0080 →

The record

Status: Superseded by ADR-0087 (2026-10-11) · Date: 2026-10-08

Context

scripts/promote_env.py promote --to prod is gated by Promoter.gate_for("prod") (src/orchestrata_ultimate/promotion/promoter.py), which reads the promotion ledger and refuses unless the test env's latest test record has result == "PASS". That record is written by promote_env.py test test and carries a timestamp and a return code — not the commit it tested. The gate therefore accepts ANY latest green record, whatever tree produced it.

Measured 2026-10-08, shipping feat/recorded-run-20261008-redesign (25 commits over origin/master 1d5f3e52c):

The promote record already carries sha; the test record is the one that does not. ship.sh's own run-evidence stamp (reports/.ship-verified, ADR-era fix 1f02eb894) solved the identical problem one layer up by binding the evidence to a content hash AND the commit — the ledger never caught up.

Decision (proposed)

  1. promote_env.py test <env> records, with the result, the commit it ran at (git rev-parse HEAD of the checkout it ran from) and ship's source id (ship_source_id, the content hash of every tracked file outside reports/), so the record names exactly the tree the suite saw.
  2. Promoter.gate_for("prod") refuses unless the latest test-env record is PASS and its sha equals the commit being promoted (the resolved target of the promote, which ship.sh makes origin/master after the fast-forward). A record with no sha (pre-ruling ledger) is treated as absent — the migration path is to run test test once.
  3. deploy/ship.sh's promote arm keeps its post-hoc 5a/5b checks; this ruling closes the pre-hoc gap only. ship.sh does not gain a test test step of its own: the recorded full run stays the explicit, observed command the RUL-032 procedure already names, and the interlock now proves it happened for this tree.
  4. run_tests.py's aborted: freshness outcome (observed the same day when swipl was absent from a minimal systemd-run PATH) prints the freshness guard's cause on stdout and in reports/run-tests-latest.json, so an abort is never a bare word.

Alternatives rejected

Consequences

Provenance

Relations of ADR-0079

Rulings cited

Specs cited

None (or no spec page exists for the ones named).