Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .codex/skills/release-oliphaunt/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ another repository mutation.
- Recover: inventory external state first. For ordinary single-identity
recovery, resume idempotently from the publication ledger. For an authorized
same-version control recovery, keep the original commit/tree, pinned complete
payload CI inventory, approved lock/capsule, and terminal ledger as the
publication source; require fresh full CI and approved control equivalence on
the later controller; require lock replay to be byte-identical including
`source` and `lockDigest`; keep tags/releases/assets source-bound; disable
bootstrap and continuations; and rerun root `publish` to reconcile missing
state. Never delete, overwrite, or republish a matching immutable public
version.
payload CI inventory, approved lock/capsule, recorded immutable boundary, and
any required terminal ledger as the publication source; require fresh
recovery-control CI and approved control equivalence on the later controller;
require lock replay to be byte-identical including `source` and `lockDigest`;
keep tags/releases/assets source-bound; disable bootstrap and continuations;
and rerun root `publish` to reconcile missing state. Never delete, overwrite,
or republish a matching immutable public version.
- History repair: use only before any affected product tag/package is public. Follow `references/recovery.md` and require explicit maintainer authorization for protection changes or force-push.

## Local gates
Expand Down
18 changes: 13 additions & 5 deletions .codex/skills/release-oliphaunt/references/invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@
qualified workflow controller. Never rewrite the lock source or relabel
product evidence as controller output.
- Same-version recovery selects the complete original payload CI inventory,
approved lock/capsule, and terminal bootstrap ledger by exact committed
run/artifact ID, digest, and size. Its replayed publication lock must be
byte-identical to the approved original, including `source` and `lockDigest`.
The current first-release recovery requires all 73 recorded CI artifacts.
approved lock/capsule, immutable recovery boundary, and any required terminal
bootstrap ledger by exact committed run/job/artifact ID, digest, and size.
The boundary is either a nonempty exact public-registry prefix or the complete
exact-source GitHub staged set backed by the failed run's recovery artifact.
Its replayed publication lock must be byte-identical to the approved original,
including `source` and `lockDigest`. The current first-release recovery
requires all 73 recorded CI artifacts.
- A recovery controller receives a fresh, exact-main recovery-control CI record
for its zero-owner control delta; it does not rebuild unchanged platform or
package payloads. The separately reverified, committed complete source CI
inventory remains the sole payload authority.
- Product tags/releases/assets, Swift source publication, registry receipts,
and consumer-facing provenance remain publication-source-bound. Workflow
code, the transport tag, OIDC claims, request journals, and pacing are
Expand Down Expand Up @@ -47,7 +54,8 @@
one byte of drift fails closed and requires a new version.
- Same-version recovery cannot run bootstrap or any continuation. Resume only
through an idempotent root `publish` rerun that verifies the original
terminal ledger and reconciles every exact immutable identity before writes.
terminal ledger when one was required, proves the recorded immutable recovery
boundary, and reconciles every exact immutable identity before writes.
- With a clean release state, a pure zero-owner control-plane, workflow,
validator, registry-transport, test, or documentation change creates no
release PR and performs no publication. Semantic ownership, not a `ci:`
Expand Down
40 changes: 26 additions & 14 deletions .codex/skills/release-oliphaunt/references/recovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@

### Same-version control recovery after partial publication

Use this path only when at least one immutable carrier is already public, no
product tag/release has been promoted, and the required repository fix has no
release-semantic product owner.
Use this path only after the original source crossed a recorded immutable
publication boundary: either at least one exact registry carrier is public, or
the complete selected GitHub tag/release/asset set was staged at the original
source. No product tag/release may have been moved or replaced, and the
required repository fix must have no release-semantic product owner.

1. Keep the original release-bump commit and public history immutable. Every
recovery commit must be a linear descendant with subject
Expand All @@ -34,31 +36,41 @@ release-semantic product owner.
change.
3. Name the identities explicitly. The trailer target is the immutable
**publication source**: it owns the original commit/tree, product bytes,
versions, approved publication lock/capsule, terminal bootstrap ledger,
versions, approved publication lock/capsule, any required terminal bootstrap ledger,
product tags/releases/assets, Swift source tag, registry receipts, and
consumer-facing provenance. The later current-main recovery head is only the
**controller**: it owns workflow code, its fresh CI/run identity, the release
transport tag, OIDC claims, request journals, and pacing.
4. Run fresh complete CI on the controller. Then run `publish-dry-run` on that
4. Run fresh recovery-control CI on the controller. It must execute the
controller delta's checks/tests/policy plus exact-main `Required` and
`Qualified`, but must not rebuild payload or platform matrices. The complete
successful original payload CI inventory remains independently pinned and
reverified as the sole payload authority. Then run `publish-dry-run` on the
controller to produce and approve recovery-control equivalence evidence.
This dry-run must not upload a replacement publication lock or bootstrap
capsule.
5. Resolve the committed immutable recovery record. Select the original source
SHA/tree, complete payload CI run, approved dry-run lock/capsule, and terminal
bootstrap ledger only by the exact recorded workflow run and artifact
ID/digest/size. For the current first-release recovery, compare the complete
observed CI inventory with all 73 recorded artifacts. Do not select “latest,”
fall back to artifact name alone, or accept a merely same-SHA run.
SHA/tree, complete payload CI run, approved dry-run lock/capsule, immutable
recovery boundary, and any required terminal bootstrap ledger only by the
exact recorded workflow run/job and artifact ID/digest/size. For the current
first-release recovery, compare the complete observed CI inventory with all
73 recorded artifacts and prove the failed staging run's recovery artifact
against the approved lock. Do not select “latest,” fall back to artifact
name alone, or accept a merely same-SHA run.
6. Reassemble only from those pinned original payload artifacts. Replay
publication-lock construction at the original source and require the
resulting file to be byte-identical to the approved original lock, including
the `source` object and `lockDigest`. Preserve the controller/source
equivalence receipt. A lock rebound to the controller is a provenance
mismatch even when every package-envelope byte is equal.
7. Verify the pinned terminal source-bound bootstrap ledger against the
original lock. Recovery bootstrap is disabled: do not create a new
controller-bound ledger, request bootstrap credentials, or invoke
`publish-bootstrap`.
7. If the pinned record contains a terminal source-bound bootstrap ledger,
verify it against the original lock. If the record explicitly contains no
ledger, require the live Cargo/npm bootstrap-state classification to prove
that no bootstrap ledger is needed, and independently require the exact
source product tags for a GitHub-staged boundary; never invent or select an
unrelated ledger. Recovery
bootstrap is disabled: do not create a new controller-bound ledger, request
bootstrap credentials, or invoke `publish-bootstrap`.
8. Derive the exhaustive Cargo/npm/Maven/JSR inventory from the original frozen
lock so generated payload-part carriers cannot disappear behind the static
catalog. A matching public identity is a read-only recovery skip and must
Expand Down
39 changes: 37 additions & 2 deletions .github/scripts/check-release-intent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,46 @@ fi
# A same-version partial-publication recovery is not a product release, but it
# must fail before expensive planning unless its exact original release,
# linear trailer chain, zero-product impact, and unchanged metadata all verify.
qualification_mode="full-payload"
recovery_release_sha=""
recovery_controller_sha=""
if git show -s --format=%B "${head_ref}^{commit}" |
grep -qi "^Oliphaunt-Release-Recovery-Of:"; then
tools/dev/bun.sh tools/release/verify-publication-candidate.mjs \
recovery_candidate_output="$(mktemp "${TMPDIR:-/tmp}/oliphaunt-recovery-candidate.XXXXXX")"
if ! tools/dev/bun.sh tools/release/verify-publication-candidate.mjs \
--derive-products \
--head-ref "${head_ref}"
--head-ref "${head_ref}" \
--github-output "${recovery_candidate_output}"; then
rm -f "${recovery_candidate_output}"
exit 1
fi
verified_recovery_mode="$(sed -n 's/^mode=//p' "${recovery_candidate_output}")"
recovery_release_sha="$(sed -n 's/^release_sha=//p' "${recovery_candidate_output}")"
recovery_controller_sha="$(sed -n 's/^publication_sha=//p' "${recovery_candidate_output}")"
recovery_mode_count="$(grep -c '^mode=' "${recovery_candidate_output}" || true)"
recovery_release_count="$(grep -c '^release_sha=' "${recovery_candidate_output}" || true)"
recovery_controller_count="$(grep -c '^publication_sha=' "${recovery_candidate_output}" || true)"
rm -f "${recovery_candidate_output}"
if [[ "${recovery_mode_count}" != "1" ]] ||
[[ "${recovery_release_count}" != "1" ]] ||
[[ "${recovery_controller_count}" != "1" ]] ||
[[ "${verified_recovery_mode}" != "release-recovery" ]] ||
[[ ! "${recovery_release_sha}" =~ ^[0-9a-f]{40}$ ]] ||
[[ ! "${recovery_controller_sha}" =~ ^[0-9a-f]{40}$ ]] ||
[[ "${recovery_controller_sha}" != "$(git rev-parse "${head_ref}^{commit}")" ]] ||
[[ "${recovery_release_sha}" == "${recovery_controller_sha}" ]]; then
echo "verified recovery lineage did not emit one exact release/controller binding" >&2
exit 1
fi
qualification_mode="recovery-control"
fi

if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
{
echo "qualification_mode=${qualification_mode}"
echo "recovery_release_sha=${recovery_release_sha}"
echo "recovery_controller_sha=${recovery_controller_sha}"
} >> "${GITHUB_OUTPUT}"
fi

release_plan="$(tools/dev/bun.sh tools/release/release_plan.mjs --base-ref "${base_ref}" --head-ref "${head_ref}" --format json)"
Expand Down
64 changes: 55 additions & 9 deletions .github/scripts/manage-release-drafts.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bun
import { existsSync, readFileSync } from "node:fs";
import { appendFileSync, existsSync, readFileSync } from "node:fs";
import path from "node:path";
import process from "node:process";

Expand Down Expand Up @@ -63,7 +63,7 @@ function error(message, options = {}) {

function usageError() {
return error(
"usage: manage-release-drafts.mjs <preflight|recovery-preflight|stage|verify|promote> "
"usage: manage-release-drafts.mjs <preflight|recovery-preflight|recovery-staged-preflight|stage|verify|promote> "
+ "--products-json JSON --head-ref SHA [--state draft|public|staged]",
);
}
Expand All @@ -89,7 +89,11 @@ function selectedPublicationLock(command, products, headRef, environment) {
?? DEFAULT_PUBLICATION_LOCK,
);
if (!existsSync(file)) {
if (command === "preflight" || command === "recovery-preflight") return null;
if (
command === "preflight"
|| command === "recovery-preflight"
|| command === "recovery-staged-preflight"
) return null;
throw error(`${command} requires the frozen publication lock: ${file}`);
}
const lock = loadPublicationLock(file);
Expand Down Expand Up @@ -731,7 +735,7 @@ export function reconcileSelectedReleasesSync(
});

let releasesByTag;
if (command === "verify") {
if (command === "verify" || command === "recovery-staged-preflight") {
releasesByTag = requiredReleaseMap(expectedState);
} else if (command === "promote") {
// No mutation has happened yet. A missing/stale precondition can fail and
Expand All @@ -750,10 +754,27 @@ export function reconcileSelectedReleasesSync(
return;
}
if (command === "recovery-preflight") {
const exactTagCount = selected.filter(({ tag }) => tagsByName.get(tag) !== null).length;
const exactReleaseCount = selected.filter(({ tag }) => releasesByTag.has(tag)).length;
console.log(
`${selected.length} selected product tag/release names are absent or exact-SHA resumable for same-version recovery`,
);
return;
return {
exactReleaseCount,
exactTagCount,
selectedCount: selected.length,
};
}
if (command === "recovery-staged-preflight") {
requireExactTagSnapshot(selected, tagsByName, headRef);
console.log(
`${selected.length} selected product tags and releases are exact-SHA staged for same-version recovery`,
);
return {
exactReleaseCount: selected.filter(({ tag }) => releasesByTag.has(tag)).length,
exactTagCount: selected.filter(({ tag }) => tagsByName.get(tag) !== null).length,
selectedCount: selected.length,
};
}

if (command === "stage") {
Expand Down Expand Up @@ -880,8 +901,17 @@ export function createReleaseDraftOperationBudget(

export function main(argv, { environment = process.env, now = Date.now } = {}) {
const { command, values } = parseArgs([...argv]);
if (!["preflight", "recovery-preflight", "stage", "verify", "promote"].includes(command)) {
throw error("command must be preflight, recovery-preflight, stage, verify, or promote");
if (![
"preflight",
"recovery-preflight",
"recovery-staged-preflight",
"stage",
"verify",
"promote",
].includes(command)) {
throw error(
"command must be preflight, recovery-preflight, recovery-staged-preflight, stage, verify, or promote",
);
}
const repo = environment.GITHUB_REPOSITORY?.trim();
if (!repo || !environment.GH_TOKEN) {
Expand All @@ -907,14 +937,15 @@ export function main(argv, { environment = process.env, now = Date.now } = {}) {
if (!headRef || !FULL_SHA.test(headRef)) {
throw error("--head-ref must be a full lowercase commit SHA");
}
const expectedState = values.get("state") ?? "draft";
const expectedState = values.get("state")
?? (command === "recovery-staged-preflight" ? "staged" : "draft");
if (!new Set(["draft", "public", "staged"]).has(expectedState)) {
throw error("--state must be draft, public, or staged");
}

const selected = selectedReleases(command, products, headRef, environment);
const budget = createReleaseDraftOperationBudget(command, { environment, now });
reconcileSelectedReleasesSync({
const result = reconcileSelectedReleasesSync({
budget,
command,
environment: budget.environment,
Expand All @@ -923,6 +954,21 @@ export function main(argv, { environment = process.env, now = Date.now } = {}) {
repo,
selected,
});
if (
(command === "recovery-preflight" || command === "recovery-staged-preflight")
&& environment.GITHUB_OUTPUT
) {
appendFileSync(
environment.GITHUB_OUTPUT,
[
`exact_release_count=${result.exactReleaseCount}`,
`exact_tag_count=${result.exactTagCount}`,
`selected_count=${result.selectedCount}`,
"",
].join("\n"),
"utf8",
);
}
}

if (import.meta.main) {
Expand Down
Loading
Loading