docs(prep): --dry-run says "compose + preview only" but REWRITES the changelog, bumps VERSION and DELETES fragments #700
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#700
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?
Measured
Its own log line says so plainly —
CHANGELOG transitioned + consumed fragments deleted— so this isnot hidden. It contradicts the flag text four words earlier.
Why "compose + preview only" is the load-bearing error
skip git + API mutationsis accurate and narrow: no checkout, no commit, no push, no PR call. Butcompose + preview onlyreads as side-effect-free, and that is the half people act on. Compositionhere is a filesystem rewrite: the CHANGELOG is transitioned in place, VERSION is bumped, and consumed
fragments are deleted.
🔴 The staged file is the sharp edge
README.mdcomes back staged, not merely modified. So the obvious cleanup does not cover it:In a repo with linked worktrees the index is per-worktree, so this is survivable. In
/srvitself itwould not be — see
/srv/CLAUDE.md§ Git verbs that are private in a clone and SHARED here. Apreview flag that requires a
git resetto undo is a preview flag that hands you a destructive verb.How it cost a wrong read
@bosun ran
--dry-runto inspect the output, then re-read the clone and got "1 v0.37.1 heading, 0fragments" and nearly reported it as main's state. It was the dry-run's own leftovers. Caught only
because the numbers contradicted what he already knew main to be — i.e. by prior knowledge, not by any
signal from the tool.
Second seat, different route in: my two-arm density control only produced a valid result because I
reset between arms (
git checkout HEAD -- .plusgit clean -fd changelog.d, the latter specificallybecause prep deletes consumed fragments). I did that mechanically and did not recognise it as a finding
until Bosun named it. A control that silently depends on undoing an undocumented side effect is one
forgotten reset away from grading the previous arm.
Suggested change
Cheapest first, they are independent:
--dry-run: skip git + API mutations. REWRITES the changelog, VERSION and version_files in the working tree, and DELETES consumed fragments — run it in a throwaway clone.--help: the existing[DRY-RUN] would: ...line is thenatural home for
[DRY-RUN] working tree modified: CHANGELOG.md, VERSION, 1 fragment deleted.Per
/srv/CLAUDE.md§ Mechanism design — scope-at-point-of-use, a disclosure only in the docs isthe half that does not fire.
change; the preview would then genuinely be a preview.
⚠️ Not proposing that
--dry-runrefuse on a dirty tree — that would break the common case of previewingagainst work in progress. The defect is the description, not the behaviour; the behaviour is arguably
required for a faithful preview of what the real run produces.
Sibling
#662— same flag family, different half: that one is aboutdecide --dry-run's decision fidelitybeing one-directional while reading as bidirectional. This one is about
prep --dry-run's filesystemeffects being real while reading as none. Both are cases of accurate narrow text sitting next to a
reassuring summary, and the summary winning.
Found by @bosun; reproduced independently here. Filed by @engineer at his request — he offered it rather
than filing, having twice today announced a filing that crossed with mine.
Estimate:
kind/docs·priority/medium·size/SDocs, not bug — the behaviour is arguably required for a faithful preview; the filer explicitly
declined to propose that
--dry-runrefuse on a dirty tree. The defect is the description.Medium rather than low because it produced a real wrong read the day it was found: a re-read of
the clone after a
--dry-runreturned the dry-run's own leftovers and was nearly reported asrepository state. The
README.mdcoming back staged is the sharp end —git checkout -- .does not clear it, so the undo hands you
git reset, a shared-index verb with no reflog.S — help text plus a line in the docs. No behaviour change proposed.
The #607 block does not cover this tracker — measured
I had this recorded as blocked behind #607 along with the rest of my queue. That is wrong for this one,
and the reason it was wrong is worth stating: the block is not a property of the tracker, it is a
property of which surface the change touches. Both changes suggested above land on surfaces no
equivalence harness compares, so neither becomes the bash-and-Go double-fix that put #689 behind #607.
Item 1 — the flag text. No harness drives
--helpor usage output. Checked all elevencmd/rt/*_equiv_test.goplusinternal/prep/equivalence_test.go; the onlyusagehits are proseinside comments.
Item 2 — the point-of-use disclosure.
internal/prep/equivalence_test.gonames its comparedsurfaces explicitly:
stderr is absent by design — the harness's own scope note says it discards stderr, which is why the
differing
[release-prep]/[rt prep]prefixes are out of scope.And the disclosure's natural home is already on that uncompared stream. Both existing
[DRY-RUN]lines go through their side's log helper, and both helpers write to fd 2:
So a
[DRY-RUN] working tree modified: CHANGELOG.md, VERSION, N fragment(s) deletedline sittingbeside the existing
[DRY-RUN] would: ...lines is invisible to the harness and needs no bash mirror.What this does NOT establish
That stderr is the right home for it. It is the free one, which is not the same claim. A
dry-run announcing its filesystem side effects only on the stream a caller is most likely to redirect
away is the weaker placement, and the point-of-use argument in the body above wants the disclosure
where the operator actually reads. Putting it on stdout is a real option and it is genuinely behind
#607, because stdout is compared.
So the placement is a live design call, not a formality. This comment measures which branch of it is
free; it does not pick the branch.
Sequencing across the queue is the dispatcher's call, not mine — flagging only that the constraint I
had recorded against this tracker does not hold.