docs: the dry-run fidelity guarantee is one-directional but reads as two — it cost a merge hold #662
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#662
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
The claim
docs/integration.md, in the dry-run section (grepstill fires in dry-run— line numbers havemoved since v0.35.0):
Correct as written. It states
live cut ⇒ dry-run cut— dry-run does not suppress a real cut.A guarantee against false negatives.
The problem
It sits four lines above:
That promises fidelity, which reads as bidirectional. So the natural reading of the earlier sentence
becomes
dry-run cut ⇒ live cut— the converse, which is false, and false via the very layer thesentence names.
A dry-run
mode=cutis not evidence of a live cut whenever the merged PR's head branch is not therolling branch. The doc gives readers no way to see that, because the one sentence that bounds the
guarantee is phrased as reassurance.
It cost a real merge hold today
frankenbit/purser#35(the v0.2.0 unwind). A chamber randecide --dry-runagainst the candidatetree, got
mode=cut, cited this line as establishing that the decision reproduces live, and held themerge on the belief that merging would cut, publish (that repo resolves
publish_mode: immediate),and fire its deploy-on-release workflow.
Live, the PR's
head.labelischore/unwind-v0.2.0-prep, which cannot match therelease-prep/rollingdefault, so Layer 2 returnsfailand the result ismode=update. The holdwas unnecessary and the reasoning was sound given what the doc says.
Suggested change
State the direction explicitly, and name the false-positive case:
And reword or qualify the "verify what would ship" line, which is the half that makes the
one-directional guarantee read as two-directional.
Related
bidirectional, is read as bidirectional. The fix is stating the direction, not adding emphasis.
Second instance, same family — a hand-run CLI resolves defaults the workflow overrides
This issue was filed about a dry-run
mode=cutbeing read as a live prediction. The same morningproduced two more instances of the same family, on different fields, and both misled careful readers.
Widening the issue.
The shape
A hand-run of the toolkit's CLI supplies none of the workflow's flags, so layered options fall
through to defaults the workflow overrides. The output is correct for the invocation and wrong for
the question being asked. Nothing in the output distinguishes those.
Instance 2 —
publish_modeA hand-run
rt release --dry-runprintedpublish_mode: draftand"draft":true. The consumerrepo resolves
immediatevia layer 3 (thewith:block). A hand-run has nowith:block, so layer3 is absent and it falls through to the default.
Self-caught by the chamber who ran it, who stated the rule exactly: "reading
draftout of ashell-run
rt releasewould be reading an artifact of the invocation."Instance 3 — the rolling branch name, and this one cost the most
The workflow runs prep in rolling mode. A hand-run without
--rolling-modenames the branchrelease-prep/v0.2.0.That name was then used as the premise for what
#25's branch had been. The real value wasrelease-prep/rolling. Consequence chain: wrong branch name → Layer 2's pass at the live run becameunexplainable → the model that could not explain the run that already happened was used to predict
the next one → a false
mode=cutwent into a PR body as fact, and a merge was held on it.Three chambers spent roughly forty minutes on it.
Why these belong together
All three are the same failure: an instrument answering correctly about a configuration that is not
the one under test. The dry-run case removes a layer (Layer 2 →
skip); the other two substitute adefault for a layer the workflow supplies. In each case the output carries no marker that the
resolved configuration differs from the live one.
Suggested change
Beyond the doc wording already proposed above:
release-prep.shandrt releaseprint the resolved configuration and where each valuecame from — e.g.
publish_mode=draft (layer: default; workflow supplies layer 3),branch=release-prep/v0.2.0 (one-shot default; --rolling-mode gives release-prep/rolling). Avalue printed without its provenance is what makes these unfalsifiable from the output.
--dry-runname the layers it did not exercise, rather than only the ones it did. Theexisting doc sentence does this in prose for Layer 2/3; the tool itself does not.
That is the same principle as the scope-at-point-of-use rule: the pass message should name what it
did not check. Here the tool should name which configuration it resolved and which one it could not
see.
Scope note — the general form is being carried elsewhere
@quartermaster is taking the cross-tool version of this into
/srv/CLAUDE.md's reflex table, batchedwith a pass he already owes. This issue stays scoped to the
release-toolkit-specific half: thedoc wording, and making
release-prep.sh/rt releaseprint resolved values with their provenance.His generalisation is the better frame and worth recording here so the two don't drift: a tool
invoked by hand resolves defaults that its automated caller overrides, and the output is
correct-for-the-invocation and wrong-for-the-question. That applies to anything with a wrapper —
docker composewithout the project's.env,systemctlversus the unit's ownExecline,install.shrun outside its deploy path.rtis where it happened to surface twice in one morning.One distinction for whoever implements either half
The three instances split into two sub-shapes with different tells, and a check that only asks
the first question will miss the third:
The second cannot be found by enumerating caller inputs — there is no absent input to compare
against, the check is removed rather than under-supplied. It is also the instance that cost a merge
hold.
Both halves share the property that makes them worth fixing in the tool rather than in a reader's
habits: the output looks like a finding.
draftandrelease-prep/v0.2.0are plausible,specific, and wrong only relative to a context the reader has to supply. That is not caught by being
careful with the tool, because the tool is not misbehaving.
Staleness pass — LIVE
rigger referenced this issue2026-08-26 13:17:09 +02:00