Repository navigation
Tell whether prod has converged on main from the published artifact's signed provenance #3846
Description
🤖 Generated by the Agentic Engineer
Part of #2838. Also resolves the race #2828 described, which was closed as its duplicate.
Evidence
- Every production deploy promotes one digest to
ghcr.io/devantler-tech/platform/manifests:latest, and that digest carries a signed SLSA provenance attestation that names the commit it was built from.- Measured 2026-09-15 on the merge-group deploy for chore(deps): update dependency devantler-tech/ksail to v7.183.3 #3845:
latest@sha256:6cb49cc4…attests commiteb8b5d0386a8…, built fromgh-readonly-queue/main/pr-3845-…. - That commit is exactly
main's tip after the merge (the compare readsidentical).
- Measured 2026-09-15 on the merge-group deploy for chore(deps): update dependency devantler-tech/ksail to v7.183.3 #3845:
- So the Git state prod is running is already recorded and signed. Nothing reads it.
- heal-prod-on-failure can overwrite a newer deploy that has succeeded but not yet merged #2828's proposed guard, "skip the heal when the published revision is reachable from
main", cannot close the race. When the older group's heal runs, the newer group has not merged yet, so its artifact is not reachable frommaineither and the two look identical. The gap only becomes decidable after the newer group lands, which is what A merge-group heal can leave prod behind a merge that lands just after it #2838 describes.
Audience and impact
This affects the platform operator and every service on the cluster. After that interleaving, prod silently runs an older revision than main until some unrelated change triggers another deploy. Nothing reports it, so the platform's GitOps promise ("prod is what main says") is quietly false.
Smallest useful change
Build a read-only resolver. Given :latest's digest, it verifies the provenance against the platform's own deploy workflow identity, takes the source commit, and reports one verdict:
| Verdict | Meaning |
|---|---|
CONVERGED |
the commit is main's tip, or an ancestor of it with no deploy-input change since |
BEHIND |
an ancestor of main, but deploy inputs changed since (the #2838 state) |
DIVERGED |
not reachable from main: an ejected merge-group artifact (the #2026 state the heal already handles) |
UNKNOWN |
any read or verification failure; never reported as CONVERGED |
Deploy inputs are the k8s path filter in the changes job of .github/workflows/ci.yaml. The resolver reads them from that file rather than copying the list, so the two cannot drift.
This child is not wired into any workflow and changes nothing in production. The follow-up child uses the verdict to run a convergence deploy after a merge.
Acceptance criteria
- A test proves each verdict against a fixture repository, including ablations that fail when the ancestry check or the deploy-input check is removed.
- A test proves that an unverifiable, unreadable, or wrong-identity attestation yields
UNKNOWN. - Reading the deploy-input paths from
ci.yamlfails closed (UNKNOWN) when the filter block cannot be found or is empty. - The one-command invocation is documented, and a read against the current
:latestdigest andmainreportsCONVERGED.
Size
S–M: one Go command under scripts/ with tests, no workflow wiring.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Effort
Projects
- StatusShow more project fields✅ Done
Activity
chore: agent-claim #3846 nonce=811a7f23f220d2af90fe474db879add4
fix(ci): judge every attested commit and document what a heal attests
fix(ci): accept gh's legacy predicate_type field and check Fprintf er…
devantler commentedon Sep 15, 2026
Resolver merged in #3847 (merged at
a3bbe487; its merge-group deploy to prod succeeded).UNKNOWNUNKNOWNand exited 2.UNKNOWNci.yaml, so a filter change the resolver cannot parse fails CI.:latestdigest reportsCONVERGEDpackages: read, so it is proven by the wiring follow-up, #3848.Two findings from review changed what the verdicts mean, and the package documentation now says so:
DIVERGEDalthough it holdsmain. Redeployingmainis still the correct remedy, and it converges prod in one deploy.UNKNOWN.This issue stays open until #3848 records the live
CONVERGEDread.devantler commentedon Sep 16, 2026
Live acceptance point met. #3864 merged as
5e3049c9. Its first🧭 Observe Production Convergencerun onmain(run 35150608562, job 104977674658) ran the resolver against the real:latestdigest with the Actions token. The run annotatedCONVERGED prod runs main 5e3049c91ae599d5b215da404b20b0622cac4ae2. With that live read recorded, all four acceptance criteria are met.