Give a point to the segment a tag begins #344

Open
PerishFire wants to merge 1 commit from task/a-point-is-the-releases-to-make into main
Owner

plumb stable stamp --version v0.27.0-beta.1 contradicts itself in its own words. The fix is not a better name for that verb.

The seam it exposed

The verb families are organised by who runs themstable takes arguments from an operator, release and ship take PLUMB_RELEASE_* from a run — while the model they implement (decision rank 1's three segments) is organised by what they act on. stable therefore held both the line and one of its points. A line is always stable; its points are not.

The separation

stable   prepare · pick · freeze · rejoin        ← the line, and no point
release  stamp · retract · reference · channel…  ← the point, and the contract
ship     per medium                              ← the projection

Both point verbs take either channel, because a point is what a tag means and a tag is where the release segment begins — release reference already reads a version out of a ref, so the family owned that boundary from the far side already.

The invariant that had to be restated

freeze no longer stamps, which removes something it carried by construction: the line was protected and the stable name bound in one call. It is now checked rather than assumed:

$ plumb ship binary dispatch --version v0.27.0 --dry-run
v0.27.0 has no release point; run plumb release stamp --version v0.27.0

A stable release is now declared before it runs, in a step that says so.

Two things that fell out

  • retract stops being stable-only. The seal it reads is already stored per channel (v1/releases/{channel}/{version}/seal.json), so removing an exact point needed nothing but the channel it already knew.
  • stamp needs no Forgejo client — the mechanism resolves the head from git, not the API — so the verb takes no --repo.
`plumb stable stamp --version v0.27.0-beta.1` contradicts itself in its own words. The fix is not a better name for that verb. ## The seam it exposed The verb families are organised by **who runs them** — `stable` takes arguments from an operator, `release` and `ship` take `PLUMB_RELEASE_*` from a run — while the model they implement (decision rank 1's three segments) is organised by **what they act on**. `stable` therefore held both the line and one of its points. **A line is always stable; its points are not.** ## The separation ``` stable prepare · pick · freeze · rejoin ← the line, and no point release stamp · retract · reference · channel… ← the point, and the contract ship per medium ← the projection ``` Both point verbs take either channel, because a point is what a tag means and a tag is where the release segment begins — `release reference` already reads a version out of a ref, so the family owned that boundary from the far side already. ## The invariant that had to be restated `freeze` no longer stamps, which removes something it carried by construction: the line was protected and the stable name bound in one call. It is now checked rather than assumed: ``` $ plumb ship binary dispatch --version v0.27.0 --dry-run v0.27.0 has no release point; run plumb release stamp --version v0.27.0 ``` **A stable release is now declared before it runs, in a step that says so.** ## Two things that fell out - `retract` stops being stable-only. The seal it reads is already stored per channel (`v1/releases/{channel}/{version}/seal.json`), so removing an exact point needed nothing but the channel it already knew. - `stamp` needs no Forgejo client — the mechanism resolves the head from git, not the API — so the verb takes no `--repo`.
Give a point to the segment a tag begins
Some checks failed
guard / guard (pull_request) Failing after 1m31s
7eeb195099
The verb added a release ago put stamping under `plumb stable`, which reads
`stable stamp --version v0.27.0-beta.1` — a command that contradicts itself in
its own words. The fix is not a better name for that verb. Naming it exposed a
seam older than it: the verb families are organised by who runs them — `stable`
takes arguments from an operator, `release` and `ship` take `PLUMB_RELEASE_*`
from a run — while the model they implement is organised by what they act on.
`stable` therefore held both the line and one of its points, and a line is
always stable while its points are not.

So the objects are separated along the model's own seam. `plumb stable` opens,
picks onto, freezes and rejoins the line, and touches no point. `plumb release
stamp` fixes a version name to the head of the line it names; `plumb release
retract` removes one. Both take either channel, because a point is what a tag
means, and a tag is where the release segment begins — `release reference`
already reads a version out of a ref, so the family already owned that boundary
from the far side.

`freeze` no longer stamps, which removes an invariant it carried by
construction: the line was protected and the stable name bound in one call. It
is restated where it can be checked rather than assumed — dispatching a stable
release refuses unless its point already stands at the line head, so a stable
release is declared before it runs, and the declaration is a step that says so.

Two things follow. `retract` stops being stable-only: the seal it reads is
already stored per channel, so removing an exact point needed nothing but the
channel it already knew. And `stamp` needs no Forgejo client at all, because the
mechanism resolves the head from git rather than the API, which is why the verb
takes no `--repo`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PerishFire force-pushed task/a-point-is-the-releases-to-make from 7eeb195099
Some checks failed
guard / guard (pull_request) Failing after 1m31s
to 0c27285fe2
All checks were successful
guard / guard (pull_request) Successful in 3m19s
2026-08-18 17:00:40 +00:00
Compare
All checks were successful
guard / guard (pull_request) Successful in 3m19s
This pull request has changes conflicting with the target branch.
  • AGENTS.md
  • crates/cli/src/command/operator/line/mod.rs
  • crates/cli/src/dispatch/operator.rs
  • crates/cli/src/dispatch/operator/mark.rs
  • crates/cli/src/dispatch/operator/trigger.rs
  • crates/cli/src/dispatch/release/mod.rs
  • crates/cli/tests/operator/retract.rs
  • docs/CHANGELOG/v0.27.0/en/INDEX.md
  • docs/CHANGELOG/v0.27.0/en/MIGRATION.md
  • docs/CHANGELOG/v0.27.0/zh/INDEX.md
  • docs/CHANGELOG/v0.27.0/zh/MIGRATION.md
  • plumb.toml
  • skills/plumb/SKILL.md
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin task/a-point-is-the-releases-to-make:task/a-point-is-the-releases-to-make
git switch task/a-point-is-the-releases-to-make
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
PerishLab/plumb!344
No description provided.