Feature design: exact-SHA release candidate evidence gate¶
- Status: APPROVED
- Owner: dpone maintainers
- Issue: corrective release follow-up after PR #525
- Target release: 0.74.0
- Approval: explicit maintainer authorization in the 2026-08-13 release task
- Last verified: 2026-08-14
Executive summary¶
The tag workflows can currently publish Python packages and a runtime image
without consuming the real-live release evidence required by
docs/release-evidence.md. The existing behavioral evidence assemblers also
accept caller-selected inputs, so their JSON alone is not an authorization
boundary.
This change adds one manual, exact-master release-candidate workflow and one
read-only provider verifier. The workflow runs the reviewed live campaign,
derives a closed manifest from observed outputs, and publishes one
attempt-specific artifact. The verifier first authenticates the paired tag
runs, freezes exact-SHA dispatch eligibility at their earliest provider
created_at, and selects the unique maximum provider created_at within that
pre-cutoff set before evaluating the run's current attempt and result. It binds
the terminal job/check to the exact commit and workflow attempt, downloads the
unique retained artifact under hard limits, and validates every inner identity
and digest. Both PyPI and GHCR tag workflows invoke the same verifier in
preflight and again at their mutation boundaries.
Personas and customer journey¶
| Persona | Goal | Current pain | Success signal |
|---|---|---|---|
| Release maintainer | Publish one reviewed commit | A tag can bypass required live evidence | Both publication workflows reject a tag without a current exact-SHA PASS artifact |
| Reviewer | Audit what really ran | Generic JSON can self-assert PASS | Provider run, job, attempt, artifact, source files, and digests form one closed chain |
| Operator | Recover safely | A rerun can accidentally reuse stale green evidence | Before tagging, the newest dispatch must pass; publication selects the unique newest eligible dispatch created by the paired-run cutoff, and equal eligible creation times, a red current attempt, or missing evidence block |
The maintainer merges the corrective PR, waits for exact-commit CI and merge
closure, dispatches Release candidate evidence on master with the full
commit SHA and proposed tag, and waits for the fixed job and attempt-specific
artifact to pass. Only then may the annotated tag be pushed. Both tag consumers
independently re-read provider state and artifact bytes. The tag push must
create exactly one release.yml run and one runtime-image.yml run; the
earlier provider created_at becomes their shared evidence cutoff. The
publication verifier ignores any evidence dispatch created after that frozen
cutoff.
Scope¶
In scope¶
- exact-
master,workflow_dispatchrelease evidence fornative_transfer, a strict superset ofreal_localfor this route-heavy release; - observed service, route, CDC/reconciliation, benchmark, exact-check, and merge-closure inputs;
- closed JSON receipt/manifest with deterministic SHA-256 projections;
- newest-eligible-dispatch provider selection and bounded artifact validation;
- exact paired tag-push authentication for
release.ymlandruntime-image.yml, using the earlier provider start as the cutoff; - fresh mutation-local verification before release attestation, PyPI, GitHub Release, GHCR digest/attestation publication, and alias promotion;
- an immediate PyPI absent-or-exact-candidate-subset gate and attempt-bound
receipt before the ordinary publisher may use
skip-existing; - self-service docs and failure recovery.
Non-goals¶
- enabling the synthetic
if: falserelease assemblers inlive-certification.yml; - treating local Docker evidence as production authorization for preview-only routes;
- creating a custom check run or granting
checks: write; - making Actions artifacts permanent. Missing, deleted, or expired bytes are
UNVERIFIED. Before tagging they require a new campaign; after the cutoff freezes they require a newly reviewed version and tag.
Assumptions and constraints¶
- Dispatch uses
--ref master; inputcommit_sha,GITHUB_SHA, checkoutHEAD, and currentrefs/heads/mastermust match at initial preflight. - Evidence remains bound to that frozen commit after
masteradvances. Tag consumers require the annotated tag to target that commit. The authority cutoff is the earlier GitHubcreated_atacross the two exact tag-push runs; only exact-SHA evidence dispatches created no later than that cutoff are eligible, and post-cutoff dispatches are ignored. An annotated tagger timestamp is never trusted as a security boundary. - The package release workflow has no manual dispatch. The runtime workflow's manual and pull-request modes remain non-publishing.
- Paired publication discovery is a semantic, bounded wait for provider
indexing only: default 10-second interval, initial sweep plus at most 30
sleeps (31 observations and about 300 seconds of sleep budget, excluding
request time). The caller detail and both fixed workflow lists are
read on every sweep. Other-tag runs are ignored. Only zero exact-tag matches
and mutable current-run
run_attempt/updated_atdetail/list projection drift are retryable; malformed exact matches, more than one exact match, and immutableid/created_atdrift fail immediately. Persistent mutable drift fails after the same bounded poll. - Only GitHub-hosted Linux execution is release authority.
- The live job has no publication token, OIDC authority, or protected environment.
- The compact authority archive stays within existing bounded artifact limits.
Public contract¶
Workflow¶
.github/workflows/release-candidate-evidence.yml is manual-only. Required
inputs are commit_sha and release; the authoritative profile is
native_transfer. The fixed terminal job/check name is
Release candidate evidence.
The job emits exactly one artifact named
release-candidate-evidence-<commit>-<run-id>-<attempt> with requested 90-day
retention and no overwrite. Its closed member set contains these top-level
files:
release_candidate_evidence_receipt.json;release_candidate_evidence_manifest.json;release_candidate_evidence_pack.json;release_candidate_evidence_exit_code.txt.
It also contains the exact observed source files below sources/ named by the
manifest. Unknown, missing, duplicate, or unlisted members fail validation.
The reusable live job separately retains
release-candidate-live-<commit>-<run-id>-<attempt> for diagnosis. That raw
artifact is not authority and is never selected by the tag verifier.
retention-days: 90 is a request, not durable storage: repository policy,
manual deletion, or expiry can make the artifact unavailable earlier. It is
authoritative only while GitHub reports it unexpired and its downloaded bytes
match provider size and SHA-256 digest.
Verifier CLI¶
python tools/agent_policy/release_candidate_evidence_gate.py \
--repository OWNER/REPO \
--commit-sha <full-lowercase-sha> \
--release vX.Y.Z \
--publication-workflow-path .github/workflows/release.yml \
--publication-run-id <current-github-run-id> \
--publication-run-attempt <current-github-run-attempt> \
--output <path>
Exit 0 writes status=PASS; every absent, nonterminal, red, malformed,
ambiguous, expired, oversized, stale, or mismatched condition exits nonzero and
writes status=FAIL. The token comes from GITHUB_TOKEN and is never
serialized. This is a workflow-internal contract, not an operator pre-tag
command: it deliberately requires the exact current in-progress tag-push run
and the paired provider run.
Evidence identity¶
The receipt binds repository, exact commit, release, profile, workflow path, run ID, run attempt, policy version, and manifest digest. The manifest is a closed, path-sorted list of source names, sizes, and SHA-256 digests. The pack repeats repository/commit/release/profile/run identity and the exact required evidence roles. A caller cannot replace or shrink the role set.
Every successful verification receipt is also self-describing for the tag
publication boundary. paired_publication_runs contains exactly two entries,
sorted by workflow_path, for .github/workflows/release.yml and
.github/workflows/runtime-image.yml. Each entry has only workflow_path,
positive run_id, positive run_attempt, and provider created_at fields.
The flattened current-caller workflow path, run ID, and attempt must match its
corresponding entry. publication_cutoff is the exact minimum of the two
creation times, and publication_pair_sha256 is the tagged SHA-256 of the
canonical JSON bytes of the complete ordered array. The receipt therefore
retains both inputs from which its cutoff decision was derived.
JSON is UTF-8, finite, duplicate-key-free, and mapping-only where specified.
Canonical bytes are json.dumps(..., ensure_ascii=False, separators=(",", ":"),
sort_keys=True) + "\\n". Digests use sha256: plus lowercase hex over exact
bytes. Self-digests exclude their own field.
The nested v1 exact_commit_checks.policy_sha256 source field preserves the
exact-commit GateReport producer's bare lowercase 64-hex value. As captured
source-schema data, it is validated without normalization. Other nested source
schemas may also use bare hashes; bundle, provider, manifest, archive, and
receipt digests remain tagged.
PyPI prepublication receipt¶
The package publisher consumes the original closed candidate inventory and
the exact local dist/ bytes, then observes all four exact-version PyPI JSON
endpoints. A PASS accepts only VERSION_ABSENT or
EXACT_CANDIDATE_SUBSET; every visible row must match a candidate filename,
SHA-256, size, and yanked=false. Network/HTTP errors other than exact 404,
malformed or duplicate-key JSON, extra public filenames, and identity conflicts
are terminal pre-upload failures.
pypi_prepublication_gate.json is deterministic and closed. It contains the
repository, 40-hex tagged commit, v<expected_version> tag, fixed
release.yml path, positive GitHub run ID/attempt, SHA-256 of the raw candidate
inventory, the four ordered endpoint observations, eight ordered candidate
classifications, and a digest over the observation/classification projection.
The CLI values must equal GitHub's environment values. A FAIL receipt is
written before returning nonzero whenever the validated boundary can be
represented; no FAIL can unlock the following action.
Compatibility and migration¶
Existing dpone ops performance-certification, live-state-reconciliation,
and release-evidence-pack remain behavioral/diagnostic APIs. No released
Python signature or schema changes. Existing raw live workflow use remains
supported.
Detailed algorithm¶
- Validate event, repository, full SHA, release, profile, and branch.
- Checkout exact
masterwithout persisted credentials and prove exact HEAD identity before repository Python runs. - Verify exact required checks and automatic merge closure.
- Run native-transfer as a strict live superset: service markers, MySQL route cells, native fixtures, both route refreshes, CDC update/delete/offset/ replay/reconciliation, and observed 25,000-row stress benchmark. Require each fixed phase metric to be at least 500 rows/s and require its rounded rate to be mathematically consistent with the rounded duration.
- Parse each source with closed schemas and exact identity; reject skips, failures, errors, missing cases, non-finite numbers, unknown keys, relaxed thresholds, or inconsistent row counts.
- Derive checklist, chain, and pack only from validated source bytes. No booleans or metrics are supplied by the caller.
- Write the manifest and receipt, then re-read the complete file set. Upload once. The terminal job passes only after upload succeeds.
- Authenticate exactly one tag-push execution for each of
release.ymlandruntime-image.yml, bound to the same repository, tag, commit, workflow path, andpushevent. Require the caller's exact positive run ID/attempt,status=in_progress, andconclusion=null. - Discover that pair with an initial provider sweep and at most 30
ten-second waits (31 observations and about 300 seconds of sleep budget,
excluding request time). On every sweep re-fetch
and authenticate the current caller detail and list both fixed paths.
Ignore other-tag runs. Retry when a path has zero exact-tag matches or the
current run's mutable
run_attempt/updated_atlist projection lags its authenticated detail; persistent drift fails after the bound. Fail immediately on a malformed exact match, more than one exact match, or immutableid/created_atdetail/list inconsistency. - Use the earlier of the two provider
created_atvalues as the shared cutoff. Serialize the closed, path-sorted two-run identity array, require the current caller to match its entry, and bind the array with its canonical SHA-256 in every successful verification receipt. From exact-SHA evidence dispatches created no later than that cutoff, select the unique maximum providercreated_atbefore examining the current attempt or status. Equal eligible creation times are ambiguous; post-cutoff dispatches are ignored. Rerunning an older dispatch never changes its creation order. Require the selected run's current attempt to becompleted/success; never fall back to an older eligible pass. - Bind the selected run's one terminal job through check-suite ID to its
provider-reported current
run_attempt. Require the run and check to have completed and the one unexpired matching artifact to have been created by the cutoff. Then verify provider digest, bounded ZIP, closed files, manifest, receipt, pack, and every cross-identity. - Preserve the provider cutoff across every repeated gate in the same tag
run: the paired runs identify the same immutable earliest
created_at, so a later evidence dispatch never changes authority for that tag. - Run a fresh gate inside every mutation-capable job immediately before its first external write. Only PASS unlocks artifact attestation, PyPI, GitHub Release, GHCR digest/attestation publication, or alias promotion. A multi-write block does not repeat the gate before each attestation; no checked-out repository code runs between the gate and its later attest writes, and no intervening step can supersede the frozen evidence cutoff.
- In the PyPI block, rehash the exact candidate set and classify each public
exact-version endpoint as absent or an exact immutable candidate subset.
Write the attempt-bound receipt, then permit the single Trusted Publishing
action only on PASS. Treat
skip-existingas exact-subset recovery mechanics; retain public-byte verification after the non-transactional file-by-file upload.
State machine¶
stateDiagram-v2
[*] --> Candidate
Candidate --> Running: exact master dispatch
Running --> Failed: missing, red, or ambiguous input
Running --> Passed: closed artifact uploaded and re-read
Passed --> Superseded: newer exact-SHA dispatch is created before cutoff
Superseded --> Failed: newer dispatch current attempt is not successful
Superseded --> Passed: newer dispatch current attempt succeeds
Passed --> Expired: artifact deleted or expires
Passed --> TagPushed: annotated tag targets C
TagPushed --> Discovering: one or both paths have zero exact match
Discovering --> Discovering: zero exact match and bounded wait remains
Discovering --> Blocked: timeout, invalid exact match, duplicate, or drift
TagPushed --> PairedRuns: exactly one release + runtime tag-push run
Discovering --> PairedRuns: both exact singleton runs become visible
PairedRuns --> Blocked: no valid eligible evidence at/before cutoff
PairedRuns --> PairedRuns: post-cutoff evidence dispatch is ignored
PairedRuns --> MutationGates: both preflights independently verify
MutationGates --> Blocked: any fresh mutation-local gate fails
MutationGates --> Published: every external write is gated
Failure and recovery¶
- Before tagging, a failed, cancelled, timed-out, or merely queued/running
newest dispatch invalidates an older PASS for the same commit. During
publication, that rule applies to the newest dispatch eligible at the
immutable paired-run cutoff; a post-cutoff dispatch is ignored. Equal
eligible provider
created_atvalues are ambiguous. A rerun of an older dispatch remains older, though its current attempt replaces prior attempts of that same dispatch and must still have completed by the cutoff. If the commit is still currentmaster, start a new complete dispatch before tagging; otherwise select the new tip as the candidate. Never reuse predecessor bytes. - A different proposed/tagged commit requires evidence for that exact commit.
Later
masteradvancement does not invalidate evidence for frozen commit C; it remains usable only when the annotated tag targets C and both exact tag-push workflows start after the evidence is complete. - A missing, duplicate, foreign, manually dispatched, or mismatched package or
runtime publication run blocks the pair. Never dispatch
release.ymlmanually; that trigger is intentionally absent. - Before tagging, missing or expired artifacts require a new full campaign on
the then-current
mastercandidate. After the paired-run cutoff freezes, post-cutoff evidence cannot repair the same tag; use a newly reviewed version. Never rebuild authority JSON by hand or copy it across attempts. - An unsafe tag must fail both publication DAGs. Never retarget an annotated version tag; recover with a new version.
Architecture¶
| Component | Responsibility |
|---|---|
| reusable live workflow | execute unprivileged observed integration work |
| release evidence builder | pure validation and deterministic composition |
| GitHub source adapter | read-only paginated provider transport |
| archive validator | bounded ZIP, strict JSON, exact inner binding |
| release evidence gate | compose provider selection and validation |
| PyPI prepublication gate | classify absent/exact public state before upload |
| tag preflights | independently enforce PASS before mutation |
Dependencies point inward. No product runtime imports agent-policy code.
Alternatives and tradeoffs¶
| Alternative | Decision |
|---|---|
| Enable disabled blocks | Rejected: literal placeholder metrics/state/checklist values. |
| Trust raw pack JSON | Rejected: no immutable provider and exact-run identity. |
| Custom check | Rejected: native checks need less privilege. |
| Validate only PyPI | Rejected: runtime-image is an independent tag/GHCR path. |
| Provider-bound job plus artifact | Adopted: provider outcome plus byte evidence. |
ADR 0049 records this cross-workflow trust boundary. Implementation is split into source selection, archive validation, and CLI composition modules, each within the global 400-SLOC limit; the product import graph is unchanged.
Market comparison¶
The ETL systems named by the feature standard are N/A: this change governs
dpone repository publication evidence, not connector or managed ELT behavior.
GitHub Actions is the hosting substrate. Official documentation confirms that
artifacts pass data between jobs, have bounded retention, and check-run listing
supports filter=all; checked 2026-08-13:
- https://docs.github.com/en/actions/concepts/workflows-and-actions/workflow-artifacts
- https://docs.github.com/en/rest/checks/runs
- https://docs.github.com/en/rest/actions/artifacts
- https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations
Measurable differentiation¶
axis: stale-green release rejection
scenario: PASS evidence is followed by a failed rerun on the same commit
baseline: tag workflow with no release-evidence verifier
metric: unauthorized publication jobs started
target: 0
procedure: provider-adapter mutation test plus both workflow contract tests
artifact: release_candidate_evidence_verification.json
limitations: repository publication gating, not production route readiness
Security, testing, docs, and rollout¶
Verification uses only actions: read, checks: read, contents: read, and
statuses: read. API and archive reads inherit existing hard limits. Tests
cover strict JSON/digests, observed metric/state projections, latest failed
rerun, provider drift, expired/ambiguous archives, reusable native superset,
and both publication DAGs. docs/release-evidence.md remains the canonical
operator guide; release, runtime-image, workflow, runbook, testing, and route
pages link to it.
Land through a reviewed corrective PR. After merge, run exact-commit CI and merge closure, dispatch evidence on new master, and do not tag until PASS. Rollback before tag is a normal corrective PR; after publication use a new patch version.
Agent execution and approval¶
The root integrator owns workflows, shared docs, verifier modules, and tests. Specialists performed read-only architecture, test/security, and docs audits.
- User problem and CJM are clear.
- Algorithm and failure semantics are implementable.
- Public contracts, compatibility, architecture, and alternatives are explicit.
- Provider facts use current official sources; irrelevant comparators are N/A.
- Tests, evidence, docs, rollout, and rollback are defined.
- Maintainer explicitly authorized implementation and completion.