Walk one course whether or not the writes happen #338

Open
PerishFire wants to merge 1 commit from task/one-course-both-ways into main
Owner

A dry run printed a second document about the code beside it. Two texts drift, and both directions were measured:

  • plumb land planned GET …/pulls?state=open, a query the client never sends.
  • plumb stable freeze printed two lines while doing six — the sixth being git tag, pushed. An irreversible action the preview never mentioned.

Dry runs are the only review a release gets before the irreversible part, so one that omits an action is worse than none.

The obstacle was self-imposed

A preview could not know what it had not done — the head a branch stands at, whether the line exists. Hence the placeholders, hence the second text.

But a read is not a mutation. A dry run can perform every read and skip only the writes, and then it knows everything the real run knows at the moment each write would happen.

So it does

Course records a step and performs it, or records it and does not. Reads stay ordinary code and run in both modes:

  • prepare now resolves whether the branch stands, instead of describing both outcomes.
  • freeze prints the commit it would tag, instead of <head>.

An action can only reach the remote through Course::step, whose first argument is the sentence describing it — adding a write without adding its line is not expressible.

Three things fell out

  1. rejoined::plan is gone. The check runs now; a preview lists what it will do, not what it has already verified.
  2. Refusal replaces confident output. A preview that cannot read the line says the line does not stand — where before it printed a plan to freeze a branch that was not there. published now names the URL it could not read.
  3. line.rs crossed the file limit, so it splits by concern: the verbs, the opening, the settlement.

Two tests changed because they encoded the old contract, and both now assert the new one: a preview that cannot verify refuses and prints no plan.

plumb land keeps its hand-written plan for now — it lives in the library behind a different shape and deserves its own pass.

A dry run printed a **second document** about the code beside it. Two texts drift, and both directions were measured: - `plumb land` planned `GET …/pulls?state=open`, a query the client never sends. - `plumb stable freeze` printed **two** lines while doing **six** — the sixth being `git tag`, pushed. An irreversible action the preview never mentioned. Dry runs are the only review a release gets before the irreversible part, so one that omits an action is worse than none. ## The obstacle was self-imposed A preview could not know what it had not done — the head a branch stands at, whether the line exists. Hence the placeholders, hence the second text. But **a read is not a mutation.** A dry run can perform every read and skip only the writes, and then it knows everything the real run knows at the moment each write would happen. ## So it does `Course` records a step and performs it, or records it and does not. Reads stay ordinary code and run in both modes: - `prepare` now **resolves** whether the branch stands, instead of describing both outcomes. - `freeze` prints the commit it would tag, instead of `<head>`. An action can only reach the remote through `Course::step`, whose first argument is the sentence describing it — **adding a write without adding its line is not expressible.** ## Three things fell out 1. `rejoined::plan` is gone. The check runs now; a preview lists what it will do, not what it has already verified. 2. **Refusal replaces confident output.** A preview that cannot read the line says the line does not stand — where before it printed a plan to freeze a branch that was not there. `published` now names the URL it could not read. 3. `line.rs` crossed the file limit, so it splits by concern: the verbs, the opening, the settlement. Two tests changed because they encoded the old contract, and both now assert the new one: a preview that cannot verify **refuses and prints no plan**. `plumb land` keeps its hand-written plan for now — it lives in the library behind a different shape and deserves its own pass.
Walk one course whether or not the writes happen
All checks were successful
guard / guard (pull_request) Successful in 3m23s
d652b1e07d
A dry run printed a second document about the code beside it. Two texts drift,
and both directions were measured: `plumb land` planned a `?state=open` query
the client never sends, and `plumb stable freeze` printed two lines while doing
six — the sixth being `git tag`, pushed. An irreversible action the preview had
never mentioned. Dry runs are the only review a release gets before the
irreversible part, so a dry run that omits an action is worse than none.

The obstacle to one path was that a preview could not know what it had not
done: the head a branch stands at, whether the line exists at all. Hence the
placeholders, and hence the second text. That obstacle was self-imposed. A read
is not a mutation. A dry run can perform every read and skip only the writes,
and then it knows everything the real run knows at the moment each write would
happen.

So it does. `Course` records a step and performs it, or records it and does
not. Reads stay ordinary code and run in both modes, which is why `prepare` now
resolves whether the branch stands rather than describing both outcomes, and
`freeze` prints the commit it would tag rather than `<head>`. An action can only
reach the remote through `Course::step`, whose first argument is the sentence
describing it, so adding a write without adding its line is not something the
code lets you express.

Three things fell out. `rejoined::plan` is gone: the check now runs, and a
preview should list what it will do, not what it has already verified. Refusal
replaces confident output — a preview that cannot read the line says the line
does not stand, where before it printed a plan to freeze a branch that was not
there, and `published` now names the URL it could not read. And `line.rs`
crossed the file limit, so it splits by concern: the verbs, the opening, the
settlement.

`plumb land` keeps its own hand-written plan for now; it lives in the library
behind a different shape and is worth one pass of its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All checks were successful
guard / guard (pull_request) Successful in 3m23s
This pull request has changes conflicting with the target branch.
  • AGENTS.md
  • crates/cli/src/command/version.rs
  • crates/cli/src/dispatch/operator/mark.rs
  • crates/cli/src/dispatch/operator/trigger.rs
  • crates/cli/tests/operator/line/stable/mod.rs
  • crates/cli/tests/operator/retract.rs
  • 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/one-course-both-ways:task/one-course-both-ways
git switch task/one-course-both-ways
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!338
No description provided.