Skip to content

--rm is docker's --rm: retire --stop, --autorm and the suffix override (0.9.0) - #369

Merged
blooop merged 5 commits into
mainfrom
autorm1
Aug 22, 2026
Merged

blooop merged 5 commits into
mainfrom
autorm1

Conversation

@blooop

@blooop blooop commented Aug 22, 2026 •

Copy link
Copy Markdown
Owner

Divergence row 32. --rm becomes docker's --rm — the workspace goes when the
session ends — and the rm verb stays the only way to delete one now. The whole
grammar is then docker rm beside docker run --rm, and neither spelling has to be
read twice to work out which was meant.

dl kinisi/repo@fix/x rm                            # delete it now
dl kinisi/repo@fix/x --rm                          # shell; it goes when you exit
dl kinisi/repo@fix/x --rm -- make test             # one command, then it goes
aid kinisi/repo@fix/x 'fix the flaky test' --rm    # the agent runs, then it goes

What is withdrawn

Three things, each recognised and refused at exit 1 with the spelling that
replaced it — never dropped to clap's unexpected argument at exit 2, which names
the spelling and not the replacement:

  • --autorm is a rename and says so. The behaviour above is what it always did.
  • --stop, whose only reason to exist was being a flag (rows 15 and 30). It
    cannot stay a cancelling suffix beside a --rm that runs the line: the two look
    like a pair and would act oppositely.
  • Row 30's suffix override. aid <ws> 'review this pr' --rm now runs the review
    and deletes afterwards, where it used to delete instead and print
    --rm overrode the rest of the line. dl prune <ws> --rm is row 31's refusal.

For "I am done with this workspace": dl <ws> rm names it, dl rm picks it and TAB
marks several — which for a long aid prompt line is fewer keystrokes than recalling
it to append to. What is genuinely gone is deleting a workspace without naming or
picking it, by appending to whatever the last line happened to be.

Notes

  • --force still does not compose with --rm; that is dl <ws> rm --force, which is
    where docker keeps its -f too.
  • dl <ws> rm --rm is refused as the two requests it is.
  • Retired flags are answered before anything else a line got wrong, since both moved
    on account of --rm's new meaning.
  • aid peels the retired spellings so dl refuses them by name rather than joining them
    into a prompt, and builds no agent command for such a line — nothing is booted on the
    way to exit 1.
  • Overridden, pick_target, verb_command and the override notice are gone with the
    override: 857 lines deleted against 737 added.

Version bumped to 0.8.0 — breaking CLI change on a 0.x line.

🤖 Generated with Claude Code

Summary by Sourcery

Adopt Docker-style --rm session cleanup while retiring the legacy automatic-removal and suffix-command flag behaviors.

New Features:

  • Make --rm delete a workspace after an attached shell, command, or agent session ends, matching Docker’s run --rm semantics.

Bug Fixes:

  • Ensure retired flag spellings fail clearly at exit 1 before launching workspaces or agents, rather than producing generic parser errors or unintended actions.

Enhancements:

  • Retire suffix-based command overrides so aid prompts continue running with trailing --rm, while immediate deletion remains available through the rm verb.
  • Retire --stop and rename --autorm with replacement guidance, refuse conflicting --rm combinations, and preserve safe cleanup behavior and session exit statuses.
  • Update CLI parsing, lifecycle handling, help text, documentation, completions, and tests for the revised command grammar.

Build:

  • Bump the Rust workspace version to 0.9.0.

Documentation:

  • Document the Docker-style distinction between immediate deletion with rm and session-end deletion with --rm, including retired spellings and migration guidance.

Tests:

  • Update unit and integration coverage for session-end removal, retired flags, refusal precedence, conflicting options, agent handling, interrupts, and completion refreshes.

blooop added 4 commits August 22, 2026 19:29
Divergence row 32. `dl <ws> --rm` and `dl <ws> --rm -- <cmd>` now hand over a
session and delete the workspace once it ends -- what `--autorm` did, under the
name docker gives it. The `rm` verb is unchanged and is the only way to delete one
now, so the grammar is `docker rm` beside `docker run --rm` and neither spelling
has to be read twice.

Three withdrawals pay for it, each recognised and refused at exit 1 rather than
reinterpreted or dropped to clap's exit 2:

- `--autorm`, which is a rename and says so.
- `--stop`, whose only reason to exist was being a flag (rows 15 and 30). It cannot
  stay a *cancelling* suffix beside a `--rm` that runs the line: the two look like
  a pair and would act oppositely.
- row 30's suffix override itself, so `aid <ws> <prompt> --rm` runs the prompt and
  deletes afterwards, and `dl prune <ws> --force --rm` is row 31's refusal. That
  takes `Overridden`, `pick_target`, `verb_command` and the override notice with it.

`--force` still does not compose with `--rm` -- docker keeps `-f` on `rm` too --
and `dl <ws> rm --rm` is refused as the two requests it is. Retired flags are
answered before anything else a line got wrong, since both moved on account of
`--rm`'s new meaning. aid peels the retired spellings so dl refuses them by name
instead of joining them into a prompt, and builds no agent command for such a
line, so nothing is booted on the way to exit 1.
Deleting `overridden_notice` left its `// The suffix verbs' notice` banner
standing over nothing, immediately above the next section's banner.
`Suffix { options, retired }` let `retired: true` sit beside options holding no
retired spelling, and the reverse -- a state nothing constructs but the type
allowed. Derived at the one place that asks instead, which takes the struct with
it.
… is RmForced

The pair is checked before the words are read, so that line answers
`--force does not apply to --rm` and not the prune retirement. Without --force it
is the retirement. Both readings now pinned by a test, since the claim was prose
nothing enforced.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @blooop, your pull request is larger than the review limit of 150000 diff characters

@sourcery-ai

sourcery-ai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Aligns devlaunch’s --rm behavior with Docker’s docker run --rm, retires the flag-spelled verbs (--stop, --autorm) and the suffix override, and updates aid/dl parsing, help text, completions, tests, and versioning to reflect the new grammar.

Sequence diagram for --rm session cleanup

sequenceDiagram
    participant User
    participant dl
    participant Workspace
    participant Session
    User->>dl: invoke --rm
    dl->>Workspace: launch or attach
    dl->>Session: hand over shell or command
    Session-->>dl: session ends
    dl->>Workspace: rm_on_exit_removing
    dl->>Workspace: remove workspace and clone
Loading

Sequence diagram for aid handling --rm and retired flags

sequenceDiagram
    participant User
    participant aid
    participant dl
    participant Agent
    User->>aid: prompt with --rm
    aid->>dl: prompt and --rm
    dl->>Agent: run agent session
    Agent-->>dl: session ends
    dl->>dl: remove workspace
    User->>aid: prompt with --autorm or --stop
    aid->>dl: retired spelling without agent command
    dl-->>User: refusal at exit 1
Loading

Flow diagram for the new dl workspace removal grammar

flowchart LR
    Input["dl workspace arguments"] --> Decision{"Request type"}
    Decision -->|"rm verb"| Now["Delete workspace now"]
    Decision -->|"--rm on attach or command"| Session["Run session or command"]
    Session --> Exit["Session ends"]
    Exit --> Later["Delete workspace"]
    Decision -->|"--force with rm"| Forced["Delete now despite unsaved work"]
    Decision -->|"--force with --rm"| Refuse["Refuse at exit 1"]
Loading

File-Level Changes

Change Details Files
Reinterpret --rm as a session-scoped removal flag (Docker-style) and retire --autorm/--stop as command flags.
  • Change CLI grammar so dl <ws> --rm / dl <ws> --rm -- <cmd> delete the workspace when the session ends while dl <ws> rm remains the only way to delete immediately.
  • Introduce RetiredFlag in the CLI layer and return GrammarError::RetiredFlag for --stop and --autorm, with detailed refusal messages naming the new spellings.
  • Remove the flag-first verbs (dl --rm, dl --stop) and the suffix override mechanism so no flag cancels a line; dl <ws> rm --rm and --force+--rm combinations are explicitly refused.
  • Rename Autorm to RmOnExit and thread the new semantics through command dispatch, launch family selection, and post-session cleanup behavior.
rust/dl/src/cli.rs
rust/dl/src/lib.rs
rust/dl/src/commands.rs
rust/dl/src/launch.rs
Update aid’s argument rewriting to treat --rm as a suffix option that preserves the prompt and to peel retired spellings for dl to refuse.
  • Replace suffix verb handling with SUFFIX_OPTIONS for --rm and SUFFIX_RETIRED for --stop/--autorm, so aid <ws> <prompt> --rm [--force] still runs the agent and passes flags through to dl.
  • Simplify Task to Agent vs Retired, eliminating the verb override arm and ensuring retired flags start no agent and only produce dl refusals.
  • Adjust build_dl_args and build_boot_args so suffix options land between the spec and -- for agent commands, and retired spellings travel without an agent command.
  • Remove the overridden-notice printing from aid and its associated tests, since suffix override semantics are gone.
rust/aid/src/main.rs
rust/aid/src/rewrite.rs
Refresh documentation, help text, and completion scripts to describe the new --rm behavior and the retired flags, and bump version to 0.8.0.
  • Update README and rust-rewrite-plan divergence rows (15, 30, 31, new 32) to document Docker-style --rm, retirement of --stop/--autorm, removal of suffix override, and the pruning behavior changes.
  • Revise dl and aid long help text to describe --rm as the throwaway workspace, point users at dl <workspace> rm for immediate deletion, and mention that --stop/--autorm are retired.
  • Adjust bash completion to suggest --rm instead of --autorm in workspace subcommands.
  • Bump Cargo workspace version from 0.7.3 to 0.8.0 and add a 0.8.0 entry in CHANGELOG with the breaking CLI change notes.
CHANGELOG.md
README.md
docs/rust-rewrite-plan.md
rust/Cargo.toml
rust/devlaunch-core/completions/dl.bash
rust/dl/src/cli.rs
rust/aid/src/main.rs
Update tests to cover the new grammar, retirement behavior, and --rm lifecycle semantics, and remove tests for the old suffix override.
  • Replace tests around suffix verbs, overridden notices, and --autorm behavior with tests that assert --rm’s on-exit removal semantics, guard behavior on unsaved work, and no-op behavior when no workspace/session exists.
  • Add tests that --stop and --autorm are refused as retired flags in all positions, that retirement diagnostics are emitted before other grammar errors, and that the word-spelled verbs (stop, rm) continue to work.
  • Update interrupt and lifecycle tests to assert the new --rm wording and behavior, including completion cache refresh after removal and collection of workspaces whose up failed or sessions never opened.
  • Adjust read-side/selector tests and help-text assertions to match the new invitations and documentation (e.g., selector messages for rm instead of --rm).
rust/dl/src/cli.rs
rust/dl/tests/interrupt.rs
rust/dl/tests/launch.rs
rust/dl/tests/lifecycle.rs
rust/dl/tests/read_side.rs
rust/aid/src/rewrite.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

main released 0.8.0 while this branch was being written, so this release is 0.9.0
-- still the 0.x minor bump that signals a breaking CLI change.

Two conflicts, both taken as this branch's semantics plus main's feature:

- `build_dl_args` gained main's `herdr: Option<&herdr::Session>` parameter, and
  every call site this branch wrote now passes it. `build_agent_command` had
  already merged cleanly with the same parameter.
- CHANGELOG keeps both entries under their own versions.

The tests main deleted with the suffix override -- `a_verb_flag_before_the_spec_is
_the_same_request` and the `Task::Verb` arms -- stay deleted; main only reformatted
them for the new argument.
@blooop blooop changed the title --rm is docker's --rm: retire --stop, --autorm and the suffix override (0.8.0) --rm is docker's --rm: retire --stop, --autorm and the suffix override (0.9.0) Aug 22, 2026
@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.07843% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.12%. Comparing base (8c70516) to head (ce62130).

Files with missing lines Patch % Lines
rust/dl/src/lib.rs 71.42% 4 Missing ⚠️
rust/aid/src/rewrite.rs 96.77% 2 Missing ⚠️
rust/dl/src/cli.rs 98.18% 2 Missing ⚠️
Additional details and impacted files
Flag Coverage Δ
rust 95.48% <96.07%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
shipped code (rust) 95.48% <96.07%> (+0.04%) ⬆️
harness and tooling (python) 42.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@blooop blooop left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during review.

Retrospective review of a merged PR. #369 landed with no review (Sourcery was rate-limited, no wf-review ran), so this is a wf-review run after the fact. Every finding below was re-checked against current origin/main (a5f7ed8) and is reported only if it still reproduces there; anything a later PR fixed was dropped.

  • Fixed point: git diff 8c70516...ce62130 (base = the merge commit's first parent, head = ce62130).
  • Spec: no ticket — closingIssuesReferences is empty and the branch is autorm1. The written spec is divergence row 32 of docs/rust-rewrite-plan.md, which the repo's own rule makes normative ("Additions require a PR that updates this table"), restated by the PR body, CHANGELOG [0.9.0], and README's --rm sections.

Standards

Repo-documented standards first (CLAUDE.md, AGENTS.md, docs/rust-rewrite-plan.md, README), then the Fowler baseline. Clippy/fmt-enforced items skipped.

S1 — non-blocking — rust/aid/src/rewrite.rs:236 (build_boot_args). The function's docstring states an invariant the function does not hold:

"This is what the interactive flow runs in the background while the prompt is being typed, so it deliberately carries no suffix options: --rm beside up is a pair dl refuses by name…"

It filters spec_options but copies dl_options verbatim (rewrite.rs:237), and parse_aid_args puts a leading --rm into dl_options (rewrite.rs:367-370) — the spelling rm_before_the_spec_is_the_same_request (rewrite.rs:817) pins as "the same request". Reproduced end-to-end on main under a pty:

$ aid --rm owner/repo
Booting owner/repo in the background. Type the prompt for claude and press Enter to launch…
> fix the bug
aid -> dl --rm owner/repo -- 'IS_SANDBOX=1 claude --dangerously-skip-permissions '\''fix the bug'\'''
--rm deletes the workspace when a session ends, so it applies to 'dl <workspace>' and
'dl <workspace> -- <command>', not to 'up'. Use 'dl <workspace> rm' to delete a workspace now.
aid: the background boot exited 1; launching in the foreground...

The banner claims a boot that never happened, the whole prewarm-while-you-type overlap is silently lost, and the user gets a refusal line for a line that is entirely valid. The trailing spelling (aid owner/repo --rm) boots correctly — verified side by side. Two rules collide here and the collision is unguarded: an_unknown_leading_flag_boots_with_the_same_flag (rewrite.rs:948) says a leading flag goes to both the boot and the attach, and build_boot_args's docstring says --rm must reach neither up. Fix: classify a leading --rm/--stop/--autorm/--force at rewrite.rs:367-370 into spec_options / Task::Retired the way the trailing peel does, so leading and trailing really are the same request; rm_before_the_spec_is_the_same_request then needs its built[0] assertion updated to the (correct) behind-the-spec order.

S2 — non-blocking — rust/aid/src/rewrite.rs:928 (the_boot_line_is_options_then_spec_then_up_and_nothing_else). The one test guarding the boot line drives it with --autorm, which now yields Task::Retired; build_boot_args is only ever reached from interactive::collect_prompt, which requires Task::Agent (interactive.rs:163-171). So the test can never exercise the path it is named for, and its comment ("--autorm beside up is a pair dl refuses") names a spelling dl now refuses as retired, not as an up/--rm pair. Fix: use --rm --force in the argv and reword the comment. (Making that change is also what would have caught S1.)

S3 — non-blocking — rust/aid/src/rewrite.rs:281 (peel_suffix) and rewrite.rs:880. The "options come out ahead of modifiers, and that is not cosmetic" rationale is now false. resolve answers retired flags first (cli.rs:616) and workspace_command returns RmForced before force_placement (cli.rs:771), so every peelable run — the only words that can be in one are --rm, --stop, --autorm, --force — gets the same dl answer in either order. Verified: dl ws --force --rm and dl ws --rm --force both print the --force does not apply to --rm sentence. The comment also still names --autorm as the worked example. Fix: keep the reorder if a stable emitted line is wanted, but say that is the reason; or drop it with its test.

S4 — non-blocking — rust/dl/src/commands.rs:1065. render_select's doc still offers dl --stop as the live example of the selector honouring its verb; that line is now an exit-1 retirement refusal. The PR swept the same doc block 35 lines lower. Fix: dl stop.

S5 — non-blocking — rust/dl/src/cli.rs:1670-1672. a_workspace_called_force_still_reads_that_way_without_autorm, and its comment "for every line that does not also say --autorm", name the retired spelling; the guard is about --rm. Fix: rename to …_without_rm and update the comment.

S6 — non-blocking — rust/aid/src/rewrite.rs:172. [Suffix::options] is a dangling intra-doc link: the Suffix struct was deleted by this PR's own follow-up commit (38eae67) and the reference was not updated. cargo doc -p aid --no-deps warns unresolved link to 'Suffix::options' — no item named 'Suffix' in scope. Same class at rewrite.rs:177 ("--autorm would survive either side") and rewrite.rs:294 ("Divergence row 30, aid's half" — row 30 is the row this PR superseded). Fix: point at peel_suffix/SUFFIX_OPTIONS and row 32.

S7 — non-blocking — docs/rust-rewrite-plan.md:194 (row 30). The row's own rule is that a row cites the tests that pin it. Row 30 still says "Pinned by the suffix_verb tests in dl/src/cli.rs and aid/src/rewrite.rs" — this PR deleted every one of them (three existed at 8c70516, none exist now), so a superseded row asserts a pin that no longer exists. Fix: amend row 30's pinning clause to say the tests went with the behaviour, and point at row 32's list.

Nothing else: README, --help, dl.bash and row 32's cited tests are in step with the grammar, and the Fowler baseline turned up no smell this diff introduced — the RmOnExit / Verb::Attach{rm} / Family::Launch{verb, rm} shape is a genuine de-primitivisation of the old Overridden/Suffix{options, retired} pair.


Spec

Row 32 read as acceptance criteria, each checked against the built binary on main.

The dl grammar honours row 32 in full. Every spelling and position of --autorm, --stop, prune, rm, --force and --rm refuses at exit 1 with the right sentence; retired flags are answered first (dl --ls --stop, dl --force --autorm, dl --install x --stop, dl -y --autorm all name the retirement); dl prune <ws> --rm is row 31's refusal and dl prune <ws> --force --rm is the pair; dl <ws> rm --rm is refused; Overridden, pick_target and the override notice are gone. Beyond the hand-named cases I ran #344's argv-space property as a temporary matrix over {ws, other, rm, stop, prune, --force, --rm, --autorm, --stop, --devcontainer, v, --, -y, --prune} to length 4 (and {ws, rm, --force, --rm, --devcontainer, v} to length 5, 2 122 lines): no escapes for "a forced delete implies nothing named follows --force", "--rm and --force together always yield RmForced", "a retired spelling always refuses", and "--rm never produces an immediate delete and rm never defers". The matrix bites — disabling force_placement produces 66 escapes, and removing the cli.rm && cli.force guard produces 284 — so those are real exercises, not vacuous passes. The --rm-and-signals claim is honoured too and is pinned end to end by a_ctrl_c_that_reaches_dl_mid_session_leaves_a_throwaway_workspace_standing (dl/tests/interrupt.rs:424); README's "How you exit decides whether it fires" agrees, and edd59e3 corrected the last of that prose after the fact.

Every finding is in aid, and all three share one root cause: a retired or session spelling is recognised only by the trailing peel, so any other position falls through to Task::Agent.

P1 — non-blocking — rust/aid/src/rewrite.rs:367-370 + :381. Row 32:

"aid peels the retired spellings so dl refuses them by name instead of joining them into a prompt, and builds no agent command for such a line, so nothing is booted on the way to exit 1."

A leading retired spelling is classified as an ordinary dl option, so Task::Agent is chosen and a full agent command is built. On main, under a pty:

$ aid --autorm owner/repo
Booting owner/repo in the background. Type the prompt for claude and press Enter to launch…
> fix the bug
aid -> dl --autorm owner/repo -- 'IS_SANDBOX=1 claude --dangerously-skip-permissions '\''fix the bug'\'''
--autorm is now spelled --rm: … Use 'dl <workspace> rm' to delete one now.
aid: the background boot exited 1; launching in the foreground...
--autorm is now spelled --rm: … Use 'dl <workspace> rm' to delete one now.

A boot child is spawned, the "Booting …" banner is printed, the user is made to type a prompt that will never be used, and the refusal is then printed twice. The trailing spelling gets the intended one-line refusal with no ceremony. The final answer is right — dl refuses by name at exit 1 — but the three things the sentence promises about the way there are all false. Row 32's own named pin, a_retired_spelling_starts_no_agent_and_is_handed_to_dl_to_refuse (rewrite.rs:600), covers only the trailing position. Fix: as S1 — classify at the leading branch too, and extend that test to the leading position.

P2 — non-blocking — rust/aid/src/rewrite.rs:381. Same sentence, "instead of joining them into a prompt". A retired spelling anywhere that is not the trailing run is joined into the prompt and the launch proceeds:

$ aid owner/repo --autorm fix the bug
aid -> dl owner/repo -- 'IS_SANDBOX=1 claude --dangerously-skip-permissions '\''--autorm fix the bug'\'''
Cloning repository git@github.com:owner/repo.git …

No refusal; the workspace is cloned and the agent would be handed --autorm fix the bug as its prompt. This one is arguably by design — peel_suffix's docstring bounds the exception to "only at the very end of the line", and a_prompt_that_merely_mentions_a_suffix_flag_is_still_a_prompt deliberately pins that aid <ws> explain --rm please stays prompt text. But the bound that is right for a live flag is not obviously right for a retired one: --rm inside a prompt can be prose, --autorm cannot be anything but the flag. Either widen the retired-spelling scan to the whole argv, or narrow row 32's sentence to say the peel is trailing-only.

P3 — non-blocking — rust/dl/src/commands.rs:1065. Independently flagged by both axes: the doc comment still advertises dl --stop as a working selector line, which row 32 ("--stop" … retired) made an exit-1 refusal.

Minor, not worth a fix: a clap-level error preempts the retirement (dl --autorm a b c, dl --refresh --ls --autorm → exit 2), so row 32's "answered before anything else the line got wrong" holds for dl's own diagnostics but not for clap's.


Issue #303

#303 (Close the --force flag-verb bypass) is dead on main and can be closed. verb_command — the function the issue names at cli.rs:756 — was deleted entirely by this PR, and --rm is no longer a delete verb, so neither of the issue's two repros can force anything: dl --force <ws> --rm and dl --rm --force <ws> both exit 1 with --force does not apply to --rm and delete nothing. So does dl <ws> --rm --force, the line #344 wanted to keep force-deleting, and so does dl ws --rm --force other, the case #344's matrix found. #354's sub-item "aid's leading --rm --force form still loses force" is also dead — all three orderings now reach RmForced.

What #369 did not bring across is the guard: the property lives only as five hand-named orderings at cli.rs:1645-1662, and #344's exhaustive matrix never landed. The property does hold today — I verified it over the argv space as described above — but nothing in the repo would notice if it stopped. That residue is exactly #354, which is open and should stay open; closing #303 should reference it.


Verdict

Comment. No blocking findings. The safety properties this PR exists to establish all hold on current main and are exercised, not merely asserted: --rm never deletes immediately, rm never defers, --force never composes with --rm in any argv ordering I could construct, every retired spelling refuses at exit 1 by name from every position dl can see it, and the removal is genuinely absent from the signal path.

The one real defect is in aid, and it is a single root cause with a single fix: retired and session spellings are recognised only by the trailing peel, so a leading one is passed through as an opaque dl option. That breaks the interactive prewarm for aid --rm <ws> (S1) and makes aid --autorm <ws> boot, banner and prompt on its way to a refusal row 32 says it reaches with nothing booted (P1). Worth one ticket covering S1 + P1 + S2's test, with P2 settled as either a widening or a wording change.

The remaining items are documentation and test-name drift left by the rename (S3-S7, P3) — cheap, and each one is the kind of second copy of a fact that CLAUDE.md's own doc rule exists to prevent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant