Finding
api::workspace_delete — the frozen wf surface — is the delete without the unsaved-work guard.
Removal is three exported functions that the caller must sequence correctly:
| function |
site |
params |
unsaved_work_in |
flows/lifecycle.rs:868 |
6 |
guard_removal |
flows/lifecycle.rs:806 |
3 |
workspace_delete |
flows/lifecycle.rs:972 |
8, #[allow(clippy::too_many_arguments)] |
17 parameters of interface. The sequence — probe for unsaved work, refuse unless --force, then delete — lives in the binary, at dl/src/commands.rs:664-740. dl gets it right. Nothing makes a second caller get it right, and devlaunch-core/src/lib.rs freezes only workspace_delete into api, so a wf that links the promised surface deletes a clone holding uncommitted work without ever asking.
The comment at dl/src/commands.rs:688 calls the guard "the one thing dl refuses on its own account". That is the problem stated as a feature: it is the one thing a caller refuses, and the frozen API has no caller.
Shape
One deep module. remove(context, target, insistence, notices) -> Result<Removal, NotRun>, absorbing target resolution, opening the records once, the unsaved-work probe, the guard, the volume sweep and the delete. Removal::Refused(RemovalRefused) is an arm, so a caller that ignores the guard does not compile.
Two ordering constraints move from prose into the module while this is open:
lifecycle.rs:980-988 — volumes must be named before devpod delete, because delete destroys the record holding their names. Currently a comment.
- The guard must run before the delete, currently a comment plus the binary's control flow.
Cost, deliberately
This changes public-api.api.txt, which CI diffs — a deliberate PR, not a refactor. That is the right weight for a change that decides whether wf can delete unsaved work, and #251 §7 already says an addition to api is a deliberate PR and a removal a breaking change.
Proving it
Red-first, and the red is reachable from the promised surface alone: call api::workspace_delete against a clone with an uncommitted change and watch it go. There is no such test today because the guard is not on that path.
Found by an architecture review.
Finding
api::workspace_delete— the frozen wf surface — is the delete without the unsaved-work guard.Removal is three exported functions that the caller must sequence correctly:
unsaved_work_inflows/lifecycle.rs:868guard_removalflows/lifecycle.rs:806workspace_deleteflows/lifecycle.rs:972#[allow(clippy::too_many_arguments)]17 parameters of interface. The sequence — probe for unsaved work, refuse unless
--force, then delete — lives in the binary, atdl/src/commands.rs:664-740.dlgets it right. Nothing makes a second caller get it right, anddevlaunch-core/src/lib.rsfreezes onlyworkspace_deleteintoapi, so awfthat links the promised surface deletes a clone holding uncommitted work without ever asking.The comment at
dl/src/commands.rs:688calls the guard "the one thing dl refuses on its own account". That is the problem stated as a feature: it is the one thing a caller refuses, and the frozen API has no caller.Shape
One deep module.
remove(context, target, insistence, notices) -> Result<Removal, NotRun>, absorbing target resolution, opening the records once, the unsaved-work probe, the guard, the volume sweep and the delete.Removal::Refused(RemovalRefused)is an arm, so a caller that ignores the guard does not compile.Two ordering constraints move from prose into the module while this is open:
lifecycle.rs:980-988— volumes must be named beforedevpod delete, because delete destroys the record holding their names. Currently a comment.Cost, deliberately
This changes
public-api.api.txt, which CI diffs — a deliberate PR, not a refactor. That is the right weight for a change that decides whetherwfcan delete unsaved work, and #251 §7 already says an addition toapiis a deliberate PR and a removal a breaking change.Proving it
Red-first, and the red is reachable from the promised surface alone: call
api::workspace_deleteagainst a clone with an uncommitted change and watch it go. There is no such test today because the guard is not on that path.Found by an architecture review.