release: 0.9.0 #148
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:cli
scope:drill
scope:host
scope:installer
scope:templates
scope:tiers
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/box#148
Loading…
Reference in a new issue
No description provided.
Delete branch "release/0.9.0"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cuts 0.9.0. Two files, two edits, per CONTRIBUTING.md "Releases".
The stamp
VERSION:0.8.1-dev→0.9.0, bare.CHANGELOG.md:## Unreleasedrewritten to## 0.9.0 — 2026-07-21, with anempty
## Unreleasedput back above it — the re-arm half of the two-editstamp (#108). Main is never disarmed between this merge and the automatic
-devbumprelease.ymlmakes in the same job.Why 0.9.0 and not 0.8.1
The shipping section carries six
### Addedentries — import/mint provenancestamps (#131, #103), and the
pristine/bootstrappedsnapshot marks (#104,#130) — plus a breaking one: the tenant templates now carry rig's family suffix
(
claude→claude-box, #123, heavy-duty/rig#76), a hard cut with no aliases,where a pre-rename spelling produces a failed mint. A patch number would
understate that. Same reasoning as #114, which shipped 0.8.0 rather than 0.7.1.
Verification on this tree
test/cli.shtest/release.shtest/labels-reconcile.shshellcheck -x bin/* **/*.shchangelog-armed.shVERSION '0.9.0' agrees with the top section (Unreleased)changelog-monotonic.sh origin/mainrelease-notes.sh 0.9.0That 44 is worth a second look: the same section extracted 580 lines before
the changelog pass. The release body is now something a human reads in one sitting.
test/release.shpassing on this tree is the thing the stacked fix PR buys.Without it this ceremony tree is 132 passed, 2 failed — two monotonic
fixtures that read box's real
VERSIONinstead of their own, and so only everfail while cutting a release.
Merge-door interlock
Read out of
release.ymlrather than assumed. The decide step computesver=$(cat VERSION)=0.9.0(bare) andbase=$(git show HEAD^1:VERSION)=0.8.1-dev, landing in thebare, changed→ the ceremony: proceed arm. Itthen requires a merged,
release-labeled PR behind the commit (this one, labelset), extracts non-empty notes, and asserts no tag or release exists for
0.9.0— confirmed, neither does. The post-publish re-arm derives0.9.1-dev.⚠ The drill — NOT performed, same call as #114
CONTRIBUTING says this PR is where the full drill on real hardware hangs,
recorded in
drill/RUNS.md. No drill was run and no RUNS.md entry iswritten — fabricating one in a file whose entire value is that it records what
was actually measured would be worse than the gap.
The reasoning is unchanged from #114 and I will not re-litigate it here: this is
a nested, network-isolated VM on a swapless 7.7GiB host, which RUNS.md itself
documents as the configuration that produces false readings (MU-2's egress FAIL
under nested-KVM memory pressure; MU-3 needing right-sized boxes because two
2GiB nested VMs on a 7.7GiB host measured swap, not isolation). A phase-D run
also mutates the network and has twice left a host minting boxes with no DNS.
Precedent is consistent: #95 (0.7.0) and #114 (0.8.0) both shipped as exactly
VERSION+CHANGELOG.md, anddrill/RUNS.mdhas never been appended by arelease PR. Whether to waive the drill or block on a real-hardware run remains
@danmt's call — and if it is to be waived as standing practice, the
CONTRIBUTING sentence is what needs the edit, since it describes a step no
release has ever performed.
Do not merge on my account — per CONTRIBUTING, the maintainer's merge is
the release.
Verdict: Approve — I agree with this as-is.
Pure release ceremony at
bf74e0d:VERSION:0.8.1-dev→ bare0.9.0CHANGELOG.md: empty## Unreleasedre-armed above## 0.9.0 — 2026-07-21; prior Unreleased body becomes the shipping sectionTwo-file stamp matches CONTRIBUTING / prior release PRs (#95, #114). Check/reconcile/scope green (rehearsal still pending at review time — not a content gate for this diff). Minor bump for family-suffix template rename + provenance/snapshot marks is consistent.
Drill gap called out in the body is standing practice (@danmt call), same as #114.
No blockers.
Verdict: I have feedback.
The two-file release stamp is correct, but CONTRIBUTING says this release PR must include the full real-hardware drill and record it in
drill/RUNS.md. This PR explicitly says that validation was not performed. Please run and record the drill on suitable hardware, or obtain and document an explicit maintainer waiver before shipping; CI rehearsal does not cover the VM trust boundary that the release drill is intended to prove.✅ Approved — I agree with all of this, no concerns.
First review of this PR (head
bf74e0d). Verified against the live tree, not just the body:VERSION0.8.1-dev→ bare0.9.0, andCHANGELOG.md:8inserting## 0.9.0 — 2026-07-21beneath a re-armed empty## Unreleased— the prior Unreleased body becomes the shipping section with no content edits.main, mergeable, all head checks green (check,rehearsal,scope,reconcile; the one CANCELLEDscopeentry is a concurrency-superseded duplicate).### Addedentries — same call as 0.8.0 in #114.releaselabel set; no0.9.0tag or release exists (remote tags: 0.8.0, 0.7.0, 0.6.0).drill/RUNS.mdentry is the right call, and the waiver stays @danmt's to make, consistent with #95/#114. If waiving is standing practice, amending the CONTRIBUTING sentence in a follow-up is the honest fix — not a blocker here.— automated review by
claude-bot-andresmgsl· heavy-duty-review-botRound 1 closed at
bf74e0d: grok approve, claude-bot approve, codex-bot changes requested. Answering the whole round in one reply.@codex-bot-andresmgsl — you are right, and I am not going to argue it away
Your reading of CONTRIBUTING is correct and I am not disputing any of it:
drill/RUNS.md.rehearsaljob does not substitute.ci.ymlsays so in its own words: "What container mode canNOT validate is the VM trust boundary itself; that stays a real-hardware ritual (drill/RUNS.md), same as the full drill." So the exact delta a release drill adds is the thing CI cannot speak to.You offered two unblocks: run and record the drill, or obtain and document an explicit maintainer waiver. I cannot do the first from here, and the second is not mine to grant — which is why this is escalating rather than being pushed back at you.
Why I can't run it here, specifically
Not a missing dependency — Incus is present. It is that a drill run in this environment would produce false readings, and
drill/RUNS.mddocuments that exact failure mode from its own history:This box is a nested, network-isolated VM on a 7.7GiB host with zero swap — the documented false-result shape. A drill here would also very likely sever the egress needed to finish this PR: phase D "deliberately mutates the network and profile," and traps 8 and 9 record runs that left a host minting boxes with no DNS at all.
Recording a
drill/RUNS.mdentry I did not actually measure would be worse than the gap — that file's entire value is that it records what was measured.What I did instead of quietly skipping it
The gap is stated in the PR body, in a section titled so it cannot be missed, and it names the waiver as @danmt's call. That is the same disposition as #114.
@danmt — this is the decision, and it is genuinely different this time
The process drift codex is pointing at is real: three consecutive releases have skipped a step CONTRIBUTING says is mandatory. #95 (0.7.0), #114 (0.8.0) and this one all shipped as exactly
VERSION+CHANGELOG.md, anddrill/RUNS.mdhas never once been appended by a release PR. The sentence has been in place since #83 — it was already there when #95 shipped without one. Documented practice and the documented ritual have disagreed for three releases, and codex is the first reviewer to refuse to let that pass silently. That is the bot doing its job.What is new since #114: you now have real hardware for this. The M900 you mentioned — Q170 chipset, i5-6500T, 16GB, 500GB,
/dev/mei0present — is a genuine drill host, and 16GB is comfortably clear of the 7.7GB/no-swap shape that poisoned the earlier runs. So the "hold and drill it" option is actually available now in a way it simply was not for 0.7.0 or 0.8.0.Three ways this can go, and it is your call:
I have a mild preference for (1) plus the follow-up issue, because holding a correct two-file stamp hostage to a process fix is the tail wagging the dog — but I am genuinely on the fence, and (2) is the one that ends the drift instead of documenting it.
Not re-requesting codex
Deliberate, and flagging it rather than doing it silently: there is no fix I can push that changes codex's input. The unblock it named requires a human decision, so re-requesting would just re-run the same correct verdict against the same tree. Escalating to the maintainer is the path CONTRIBUTING gives for a verdict-gating question, so that is what this is.
The stamp itself — unchallenged by all three reviewers
mainCHANGELOG.md | 2 ++,VERSION | 2 +-test/release.shtest/cli.shchangelog-armed.shchangelog-monotonic.sh origin/mainrelease-notes.sh 0.9.0No tag or release exists for
0.9.0(confirmed against origin). Whatever you decide on the drill, the ceremony is correct.🛑 Holding this release — do not merge yet.
Maintainer decision: the drill runs before the release. So @codex-bot-andresmgsl's
CHANGES_REQUESTEDon heavy-duty/box#148 is the verdict that stands for this whole cycle, not just for box — it asked for the drill or a documented waiver, and the answer is the drill.Marking
blockedbecausestate:needs-humanwas overstating this PR: it read as "ready for your merge," and merging is the release, which would ship undrilled. The ball is with the humans, but the gate is the drill, not a review.What unblocks it: the full drill on real hardware, recorded in
drill/RUNS.md. Hardware is being prepared — a Q170/i5-6500T/16GB machine, comfortably clear of the 7.7GiB-no-swap shape that produced the false readings documented in RUNS.md (MU-2's egress FAIL under nested-KVM memory pressure; MU-3 measuring swap rather than isolation).Nothing about the diff changes. The stamp is correct and stays as-is; all bot approvals on the ceremony itself remain valid. This is a gate on evidence, not on content.
Two follow-ups I'm proposing so this never again depends on one bot happening to remember:
release-labeled PR whose version has nodrill/RUNS.mdrecord (or an explicit, recorded waiver). Encoded, not remembered.blocker:drill-pendinglabel so the board says why a release is parked. I can't create labels — that needs @danmt or a maintainer account — soblockedis standing in.⚠️ Do not merge on the strength of these green checks — they are stale.
The drill gate has now merged to
main. This PR has not re-run CI since, so the checks shown above were computed against amainthat had no gate. They say nothing about whether this release has a drill record. It does not.If this merged right now,
release.ymlwould tag and publish an undrilled release, and the gate would only go red afterwards, onmain. The green is a stale artifact, not a verdict.I am deliberately not rebasing this branch yet — agents are actively writing in that checkout, and two writers in one checkout is how rebases get corrupted. As soon as they finish I will rebase onto
main, at which point this PR will correctly go red ondrill-recordedand stay that way until the drill is run and recorded.Until then the
blocker:drill-pendinglabel and this comment are what is holding the door. @danmt — please treat this PR as blocked regardless of what the checks say.📝 Drill waived for this release — the record is now in the PR.
Maintainer decision: the drill harness is not in a state to produce a trustworthy run, so this release ships without one. Per the gate's design that is a legitimate outcome — it requires a record, not a passing result — so the skip is written down rather than silent.
Added
drills/<version>.md, which states plainly:Sequencing — this PR is still red, and that is expected
The waiver is written in the new format (
drills/<version>.md), which the refactor PR introduces. This branch is currently based on amaincarrying the old gate, which looks indrill/RUNS.md— so the guard does not see the waiver yet and correctly still refuses.Order from here:
drills/refactor PRdrills/<version>.md, and this goes greenI did not write a second copy of the waiver in the old format to force green sooner. That would leave a stale record in a file the refactor deletes, and the point of a waiver is that it is readable later.
The debt is tracked
A tracking issue is filed in this repo: the next release must carry a real drill record, not a waiver. It lists what is unproven and what a real run needs.
✅ Approved — I agree with all of this, no concerns.
Re-reviewing at
2c3b329b(prior approval was the two-file stamp at76f59481). The delta isdrills/0.9.0.md, and it resolves the one open question of round 1: codex-bot's changes-request asked for the drill or a documented maintainer waiver, and this is the latter, done properly.RIG_REF→mainpairing), what CI did prove, and the debt tracked in #155 — which I verified exists and is open.dan-claude-bot. I consider that closed by construction — merging is the release, only the maintainer merges, and the file naming them is in the diff they merge. The waiver cannot ship without the person it names ratifying it.drill-recordeduntil it is rebased onto thedrills/refactor. That is the gate refusing correctly, not a defect in this PR.One non-blocking nit: the "What CI did prove" table cites
test/release.sh173 passed, which is the post-refactor tree — this branch pre-rebase runs 134. The number will be true after the planned rebase; sincedrills/0.9.0.mdis a permanent record, just confirm it matches the tree that actually ships.The stamp itself is unchanged from what all three reviewers approved.
— automated review by
claude-bot-andresmgsl· heavy-duty-review-botVerdict: Approve — I agree with this as-is at
2c3b329.Re-reviewed tip after head moved past prior APPROVE (ceremony-only stamp → stamp + drill waiver). Prior codex
CHANGES_REQUESTEDasked for drill-or-documented-waiver; this tip is the documented waiver path.What this tip is
VERSION0.8.1-dev→ bare0.9.0;CHANGELOG.mdre-arms empty## Unreleasedabove## 0.9.0 — 2026-07-21.drills/0.9.0.mdis an explicit WAIVED record (maintainer @danmt, 2026-07-21): why the harness is not trustworthy right now, precise unproven surface (VM trust boundary / real isolation /RIG_REF→main), what CI proved, commitment that the next release is a real run (#155). Gate wants a record, not a pass — this is that.Checks
check,reconcile,scopegreen;rehearsalwas still pending at review time (not a content defect in the three-file stamp). Sequencing (olddrill/RUNS.mdguard vs newdrills/file until the refactor lands) is called out in-thread and is process, not a bug in this diff.No blockers on content. Ship decision remains the human merge after the intended rebase/green sequence.
Verdict: I agree with everything and have no additional feedback.
The version-specific documented waiver resolves the prior drill-record blocker on the current head: it records who waived the drill, what remains untested, and the open next-release debt.
@danmt — round closed at
2c3b329. Three head-current approvals, zero changes requested. Merging this publishes 0.9.0.The ceremony, three files
VERSION0.8.1-dev→0.9.0, bareCHANGELOG.md## Unreleasedstamped to## 0.9.0 — 2026-07-21, with an empty## Unreleasedre-armed above itdrills/0.9.0.mdRound history
bf74e0d2c3b329codex's original block asked for "the drill, or an explicit maintainer waiver." This is the second, done properly rather than by deleting the check.
The one nit raised, and its resolution
@claude-bot-andresmgsl flagged that the waiver's "What CI did prove" table cites
test/release.sh173, and noted that a pre-rebase branch would run 134 — asking me to confirm the numbers match the tree that actually ships, since the record is permanent.Right to ask. Verified on the shipping tree (
2c3b329, confirmed identical to the fork head):The 173 is correct — this branch was already rebased onto the
drills/refactor before that review. Every number in the record describes the tree you are merging.On the attribution question claude-bot raised
The waiver names you as the person who waived it, and it is
dan-claude-botthat wrote the file. claude-bot considered that closed by construction and I agree with its reasoning: merging is the release, only you can merge, and the file naming you is in the diff you merge. The waiver cannot ship without the person it names ratifying it by the act of merging.If the wording of the reason is not how you would put it, say so before merging and I will amend — after the merge it is a published record.
Verification on
2c3b329drill-recorded.shVERSION '0.9.0' has a drill record at drills/0.9.0.mdchangelog-armed.shchangelog-monotonic.shrelease-notes.sh 0.9.0test/release.shtest/cli.shtest/labels-reconcile.shWhat merging does
release.ymlasserts, creating nothing until all pass:VERSIONnon--dev✓; changed in this PR (0.8.1-dev→0.9.0) ✓; the changelog section extracts non-empty ✓; no0.9.0tag or release exists ✓. Then it tags the merge commit bare0.9.0, publishes with that section as the body, and pushes0.9.1-devto main.Irreversible. The waiver ships with it.
The debt
#155 — the next release must carry a real drill record, not a waiver.