Imported from anthony-chaudhary/dos-kernel (
.claude/skills/stable-release/SKILL.md). Install upstream withnpx skills add anthony-chaudhary/dos-kernel --skill stable-release. Copyright stays with the author.
Stable Release — a named channel on top of rolling /release (DOS)
/release ships a rolling tag (vX.Y.Z) whenever DOS lands user-visible change.
That tag promises "this commit merged" — it does not promise "the substrate
was provably trustworthy here." This skill adds a second, less-frequent channel
that does make that promise.
A stable release is a promotion of an already-shipped commit, not a new
build. No new version is minted. No new wheel or zip is produced. The gate is
read at promote time, frozen into an evidence file, and a second annotated tag
(stable/<codename>) is pinned on the same commit.
This is the DOS adaptation of
job's stable-release skill. job gates on apply-loop hero metrics (silent-failure share, PSV verified-success rate, funnel-stage regressions) and KEEP-slot baselines fromexecution-state.yaml+baselines.yaml. None of that exists in DOS — DOS is a domain-free trust substrate, not an apply pipeline; it has no runtime funnel, no hero metric, no baseline ledger. So the gate is re-grounded on the only "known-good" signals DOS actually has: a green kernel suite, a clean truth syscall, and a soak window. See "The gate" below.
Trunk is master. The stable tag is pinned on a commit reachable from
master; the push targets master's remote.
The kernel-vs-tooling boundary. The gate context script
(scripts/stable_release_context.py) and this skill are dev/release tooling
that operates ON the package — never imported BY dos.*. The
"kernel imports no host, no scripts/" litmus in CLAUDE.md stays intact.
Relationship to other systems
| System | Owns | Stable-release reads | Stable-release writes |
|---|---|---|---|
/release (scripts/release_bump.py) |
rolling vX.Y.Z tag, version markers |
the latest vX.Y.Z as promotion candidate |
nothing — never re-bumps |
the kernel suite (pytest) |
substrate correctness | exit code (green == eligible) | nothing |
the truth syscall (dos verify) |
ground-truth adjudication | exit + verdict (well-formed == eligible) | nothing |
| git tags | the channel itself | stable/* collision check + previous-stable |
one new stable/<codename> tag |
The spec deliberately introduces no new state machine, database table, or pipeline event. It reuses git tags + the suite + the syscall.
When to invoke
The user (or a scheduled cron) invokes this skill. Typical triggers:
- After a
/releasethat landed a meaningful kernel change and you want a rollback anchor that says "the substrate was provably good here." - Quarterly hygiene — pick the highest
vX.Y.Zthat satisfies the gate. - After a regression rollback, to mark the recovered state.
Cadence — the two-channel contract. Rolling vX.Y.Z tags ship continuously:
/release is deliberately cheap, and a coherent unit that clears the mechanical
gates (suite, leak scan, CI, the publish ci-green witness) just ships — several
per week is healthy. The stable channel is the deliberate counterweight: an
infrequent, gate-driven promotion that answers "which version do I pin in
production." There is still no fixed calendar — the gate either passes or it
doesn't — but the working rhythm is: promote the highest soaked-green tag
(a) after a milestone (a new syscall family, a major/ABI bump, the first
PyPI-published era), or (b) when a meaningful run of rolling tags has accumulated
since the last stable — every few weeks, not every few days. Rolling moves fast
because stable exists to absorb the trust question; stable can afford its soak
window because rolling carries the urgency.
Codename convention
Operator picks one short word at promote time, prefixed by year-month:
stable/2026-06-aardvark. Memorable, sortable, and rollback becomes meaningful
(git checkout stable/2026-06-aardvark says "the substrate was good here" in a
way git checkout v0.3.0 never claimed).
Validate: ^\d{4}-\d{2}-[a-z][a-z0-9-]{2,20}$ (the context script enforces this).
Reject ambiguous reuses by checking git tag -l 'stable/*-<codename>'.
Step 1 — Pre-digest the gate context
One helper collapses every gate read into one JSON payload:
python scripts/stable_release_context.py --codename <codename>
# --from vX.Y.Z pin the candidate (default: latest semver tag)
# --window-days N min soak age for the candidate tag (default 3)
# --skip-pytest smoke / dry inspection only (do NOT use for a real promote)
# --force-promote report blockers but mark all_green-eligible (records rationale)
The script returns:
{
"candidate_tag": "v0.3.0",
"candidate_sha": "abc123…",
"codename": "2026-06-aardvark",
"window_days": 3,
"previous_stable": {"tag": "stable/2026-05-zephyr", "sha": "…"},
"tag_collision": false,
"idempotency": {
"tag_exists": false, "tag_sha": null, "tag_matches_candidate": null,
"evidence_file_exists": false, "evidence_path": "docs/stable-releases/2026-06-aardvark.md"
},
"gate": {
"pytest_suite_green": {"name": "...", "pass": true, "exit_code": 0, "summary_tail": "..."},
"ci_green": {"name": "...", "pass": true, "verdict": "GREEN", "advisory": false, "reason": "..."},
"dos_verify_clean": {"name": "...", "pass": true, "exit_code": 1, "verdict": {...}},
"tag_age": {"name": "...", "pass": true, "age_days": 5.2, "window_days": 3}
},
"summary": {"all_green": true, "blockers": [], "forced": false}
}
If summary.all_green is false, the skill stops and reports the failing
rows — exactly which gate row blocked promotion and its reading. This is the
entire point of the skill; never paper over a red gate.
dos_verify_cleanexits 1 on a healthy no-plan repo — that is a PASS. The truth syscall's exit code carries the ship verdict (0 shipped / 1 not), not execution health. The gate probes with a sentinel(plan, phase)that never shipped, so a healthy syscall returnsshipped=false, source="none"and exit 1; the script treats a well-formed verdict dict + exit ∈ {0,1} as the pass. A crash (non-{0,1} exit, traceback, no JSON) is the only failure. Don't "fix" this by demanding exit 0.
Step 2 — Decide the promotion candidate
By default, promote the most recent vX.Y.Z tag that has soaked ≥ window_days
(so the gate had time to observe it). If --from vX.Y.Z is passed, use that.
Refuse to promote (the context script flags these as blockers):
- A tag already promoted (a
stable/*tag points at the same SHA →tag_collision). - A tag younger than
window_daysunless--force-promoteis set with a written rationale in the evidence file.
If candidate_tag is null, there is no vX.Y.Z tag yet — DOS has not cut
a rolling release. Stop and tell the operator to run /release first; a stable
promotion has nothing to promote.
Advisory check (PyPI live since 2026-06-10): did the candidate actually
publish? The stable promise includes "pin this in production," and the pin is
pip install dos-kernel==<underlying> — which only resolves if the candidate
tag's publish.yml run was approved and uploaded (the tag push triggers the
pipeline, but the upload waits on the ci-green witness + the pypi-environment
approval, so a tag can exist unpublished). Check pip index versions dos-kernel
(or the publish run on the tag). If unpublished: prefer approving the pending
publish first; promoting anyway is allowed (the git tag still anchors the
rollback) but the evidence file must note the pin does not resolve. Hand-checked
for now — not a script gate row yet.
Step 2.5 — Idempotent re-run (a partial-failure replay is SAFE)
A promotion writes two durable side-effects in sequence — the stable/<codename>
tag (Step 5) and the docs/stable-releases/<codename>.md evidence file (Step 4 +
its commit in Step 6). If a prior run was interrupted between them (operator
Ctrl-C, a git push that failed), re-running with the same codename must be
safe — detect what already exists and skip it, never error or write a duplicate.
This is the idempotency property the userland app bakes into its promote
orchestrator; DOS reads it from the context script's idempotency block and the
skill body acts on it (DOS keeps the promotion script-light, so this is data the
skill obeys, not a wrapper).
Read idempotency from Step 1 and branch:
tag_matches_candidate: true(thestable/<codename>tag already exists and points at the candidate SHA) → the tag step already succeeded. Skip Step 5 (do not re-tag — a stable tag is never re-pointed); record it on anidempotent_skipslist for the final summary. This is the clean replay case, NOT atag_collisionblocker (the context script already excludes a same-codename tag from the collision check).evidence_file_exists: true→ the evidence file is already written. Re-read it; if its gate snapshot matches today's, skip re-writing Step 4 and its Step 6 commit (note the skip). If it's stale/partial, finish writing it and commit — the file is the artifact, completing it is the point.tag_exists: trueANDtag_matches_candidate: false→ the codename already tags a different commit (the word was reused). This is a hard blocker the context script already raised — stop; a stable tag is immutable, so pick a fresh codename and add a## Supersedesnote in the new evidence file.- All false → a normal first run; proceed through Steps 4–6 as written.
The load-bearing property: every side-effect detects its own already-done state, so
the skill can be re-run after any partial failure and converges to one tag + one
evidence file. Report every skip under idempotent_skips in the final summary so
the operator sees what the replay reused versus wrote.
Step 3 — The gate (the load-bearing step)
The gate is four rows, all from the context script. Each is a real check, not a remembered number:
| Row | Source | Pass condition |
|---|---|---|
pytest_suite_green |
python -m pytest -q |
exit 0 — the kernel suite is green on the promoting machine. The substrate's entire value is deterministic, correct verdicts; a red suite means it can't anchor a stable channel. |
ci_green |
the CI/Checks oracle (dos.drivers.ci_status) on the candidate commit |
not RED — the third-party CI record, not a local self-report. GREEN passes; RED blocks (a real, accountable failure, e.g. a Windows/py3.11 matrix leg the promoter's box never ran); PENDING/NO_SIGNAL are advisory passes (the local suite row stays load-bearing). See the asymmetry note below. |
dos_verify_clean |
dos verify (sentinel probe) |
well-formed verdict dict + exit ∈ {0,1} — the truth syscall executes cleanly (see the exit-code note above). |
tag_age |
committer date of the candidate tag | age ≥ window_days — the candidate soaked, not a just-cut tag promoted in the same breath. |
ci_greenis asymmetric — it can only ADD a blocker, never remove one. It is the substrate eating its own distrust (docs/93):pytest_suite_greenis a self-report from the very machine cutting the promotion, soci_greenreads the verdict the CI system recorded on infrastructure the promoter doesn't control. Only RED blocks (CI proves the build is broken — the row's whole reason to exist, catching matrix-leg failures invisible locally). PENDING (CI not finished) and NO_SIGNAL (ghunwired/unauthed, or the candidate was never pushed) are advisory passes — hard-blocking on them would make the gate unrunnable offline, contradicting the oracle's fail-safe "never fabricate a verdict" discipline. The local pytest row remains the load-bearing suite gate; CI is purely additive signal. Use--skip-cifor an offline smoke. Ifci_greenis PENDING and you want CI-verified promotion, wait for the run to finish and re-run the gate rather than--force-promote.
This row is itself the "richer known-good history across consumers" the gate was
always designed to grow into: as more signals appear (an N-day clean-dos verify
soak, a consumer-side loop reporting back), additional rows thread into
stable_release_context.py the same way — without touching the kernel. That is
the package's own modularity rule: policy (the gate) is data + a driver-shaped
script; mechanism (the kernel) stays untouched.
Step 4 — Write the evidence file
Single markdown file at docs/stable-releases/<codename>.md (create the dir on
the first promotion). Frontmatter is the gate JSON snapshot; body is
human-readable.
---
codename: 2026-06-aardvark
underlying_version: v0.3.0
candidate_sha: abc123…
promoted_at: 2026-06-15
window_days: 3
previous_stable: stable/2026-05-zephyr
gate:
pytest_suite_green: {pass: true, exit_code: 0}
ci_green: {pass: true, verdict: GREEN, advisory: false, repo: anthony-chaudhary/dos-kernel}
dos_verify_clean: {pass: true, exit_code: 1, source: none}
tag_age: {pass: true, age_days: 5.2, window_days: 3}
---
# Stable promotion — 2026-06-aardvark
Promotes `v0.3.0` (commit `abc123…`).
## What's known-good here
- Kernel suite green (`pytest -q`, exit 0) at this commit.
- Truth syscall executes clean (`dos verify` → well-formed verdict).
- Candidate soaked 5.2 days (≥ 3-day window).
- Pinned install: `pip install dos-kernel==0.3.0` (published on PyPI — or note
here if the candidate's publish run was never approved).
## Rollback target
`git checkout stable/2026-06-aardvark` returns to this exact commit + a
provably-green substrate.
## Gate evidence (mechanically generated; do not edit)
[full gate table from the context JSON]
This file is the artifact /stable-release exists to produce. Everything else is
bookkeeping. Use the date from the operator / the system clock provided in
context — do not invent a timestamp.
Step 5 — Tag
git tag -a stable/<codename> <candidate_sha> -m "stable/<codename> — promoted from v0.3.0"
git push origin stable/<codename>
Idempotent skip (Step 2.5): if idempotency.tag_matches_candidate was true,
the tag already exists on the right commit — do not run these commands; record
idempotent_skips: stable/<codename> tag (already present) and move on. If only the
local tag exists but the push didn't land (a rare interrupt), re-running
git push origin stable/<codename> is safe and idempotent.
Never delete or re-tag a stable tag. Mistakes get a new codename + a
## Supersedes section in the next evidence file.
Step 6 — Commit the evidence file
The evidence file is a real doc; commit it (it is not the promoted commit — the
tag points at the candidate, the evidence lands as a fresh commit on master):
Stage and commit by explicit pathspec (-- <path>) — master here is a
concurrently-written tree, and a bare git commit would sweep another session's
staged content under this message (the same hot-tree discipline /release Step 5
follows):
git add docs/stable-releases/<codename>.md
git commit -m "docs/stable: promote v0.3.0 to stable/<codename>" -- docs/stable-releases/<codename>.md
git push origin master
Idempotent skip (Step 2.5): if idempotency.evidence_file_exists was true
and the file's gate snapshot already matches today's, the evidence commit likely
already landed — check git log for the docs/stable: promote … <codename> commit;
if present, skip this step and record idempotent_skips: evidence file + commit (already present). Only write + commit if the file is missing or stale.
Step 7 — Final summary
Print:
- Codename + underlying tag + commit SHA (short)
- Gate result table (one row per check, reading vs pass/fail)
- Evidence file path
- Stable tag + push status
idempotent_skips(if any) — each side-effect a re-run reused rather than re-created (the tag, the evidence file, the evidence commit), so the operator sees this was a replay, not a fresh promotion.- Previous stable, for the rollback hint
Supporting script
scripts/stable_release_context.py produces the Step 1 JSON. It runs the
suite, probes the truth syscall, computes tag age, checks for a stable/*
collision, and reports the idempotency block (Step 2.5 — whether this codename's
tag + evidence file already exist) so a partial-failure re-run is safe. It is
dev/release tooling — never imported by dos.*. There is no heavier orchestrator
(job's stable_release_promote.py / agents.sr.*): DOS's gate is small enough
that the skill body drives the tag + evidence-file steps directly, and keeping it
script-light avoids adding surface the kernel ethos discourages. The idempotency is
therefore data the skill obeys, not a wrapper that enforces it — the same
mechanism/policy split the kernel keeps everywhere.
Anti-patterns (don't do these)
- Don't bump versions. This promotes an existing tag. If you're editing
pyproject.tomlorsrc/dos/__init__.py, you're in the wrong skill (use/release). - Don't build new artifacts. No new wheel, no new zip. The git tag IS the artifact in DOS.
- Don't
--skip-pytestfor a real promotion. That flag is for dry inspection only; the green suite is the load-bearing gate row. - Don't
--skip-cito dodge a RED CI run. The flag is for offline smokes. Ifci_greenis RED, the build is broken on a leg your machine didn't run — fix it, don't skip it. (PENDING/NO_SIGNAL already don't block; you only reach for--skip-cito silence them, which is fine for a dry run and wrong for a real one.) - Don't override a red gate without writing why.
--force-promoteis allowed, but the evidence file must contain a## Force-promote rationalesection explaining which row failed, why it was overridden, and who approved it. - Don't promote two stable tags in one window without a
--force-promoterationale. - Don't fold the rolling channel into the stable channel. Two tags, two audiences: rolling = "latest code," stable = "last provably-good substrate."
What this skill consciously punts
- A stable install pointer. job flips
stable_current.txtviainstall_versioned.py; DOS has no versioned-install snapshot at all (it's a pip package), so there is no pointer to flip.pip install dos-kernel==<underlying>pins the promoted version directly (live on PyPI since 2026-06-10) — note the dist name isdos-kernel, not the baredos(an unrelated PyPI package). - GitHub Releases entry for stable tags. The evidence file + annotated tag are the canonical artifacts. Revisit if operators ask.
- Auto-promotion via cron. Easy to add later (
/loop+ this skill), but the first few promotions should be operator-driven so the gate's calibration gets validated by hand. - Richer gate rows (multi-day soak, consumer feedback). Threaded into the context script when DOS has the history to support them — not before.