refactor: simplify run lifecycle and wrap dispatch#280
Merged
mwiebe merged 2 commits intoJul 24, 2026
Conversation
mwiebe
force-pushed
the
fix-wrap-actions-followup
branch
from
July 24, 2026 17:28
d99cc34 to
2cac5b5
Compare
Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
mwiebe
force-pushed
the
fix-wrap-actions-followup
branch
from
July 24, 2026 17:41
2cac5b5 to
fe674ec
Compare
crowecawcaw
previously approved these changes
Jul 24, 2026
Comment on lines
+142
to
+143
| the outer execution phase always unwinds the remaining environments. Thus a | ||
| `Session::run_task()` error cannot bypass environment cleanup. |
Contributor
There was a problem hiding this comment.
I think (?) this case is missing a test.
Contributor
Author
There was a problem hiding this comment.
Yes, this was missing, and in the process of fixing it I identified that the Windows implementation was missing some parts. Addressed in a fresh commit.
The Windows branch of the run command's signal handler only awaited ctrl_c(). A process spawned with CREATE_NEW_PROCESS_GROUP — as worker tooling typically does — cannot receive Ctrl+C, and CTRL_BREAK_EVENT is the only console event another process can deliver to the CLI's group alone, so the CLI was uninterruptible in that scenario. The handler now listens for both Ctrl+C and Ctrl+Break on Windows. Add an end-to-end test for the RESULTS-phase rule "treat any observed interruption as a failed run" (specs/cli/run.md), which previously had only a unit test of the RunContext flag mechanics. The test runs the CLI with a long-sleeping task, delivers a real SIGINT (Unix) or CTRL_BREAK_EVENT (Windows) once the task signals readiness, and asserts the task is canceled, "Interruption signal received." is printed, the session is reported failed, and the exit code is 1. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
crowecawcaw
approved these changes
Jul 24, 2026
mwiebe
enabled auto-merge (squash)
July 24, 2026 22:26
leongdl
approved these changes
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was the problem/requirement? (What/Why)
This is a follow-up to #277.
At a high level,
openjd runfollows a checklist:Most of that checklist had grown into one 779-line
execute()function. It mixedvalidation, session setup, environment tracking, task execution, interruption
handling, cleanup, and reporting. That made an important question unnecessarily
hard to answer: "If something fails here, will every environment still be exited?"
Review also found a performance issue in wrapped actions. To work around a Rust
borrowing conflict, the sessions crate cloned the complete wrapper environment
before running a wrap hook. An environment can contain large embedded script
files, so a worker could copy megabytes of data for every wrapped task even though
the hook only needs the environment name, symbol table, and
letbindings.What was the solution? (How)
The run command is now organized around the checklist above:
run/mod.rsdefines aRunContextthat owns the session, entered-environmentledger, interruption state, failure state, task count, and timing state.
run/execution.rsseparates preparation, preflight, session creation,environment lifecycle, step/task execution, adaptive chunking, and reporting
into focused private functions.
in
RunContextmethods. Step environments are unwound before a task error isreturned, and the outer execution path always unwinds remaining environments.
a temporary session directory or print session-start messages.
an interruption received between environment actions.
--task-paramand--tasksnow report an error when the selected step has noparameterSpace, instead of silently ignoring the supplied task values.previous timing behavior.
For wrapped actions,
WrapEnvironmentScopecopies only the three small pieces ofwrapper state needed after the environment-stack borrow is released. Embedded
files and the rest of the environment are no longer cloned for each wrapped task.
Environment exit also takes ownership of the environment already being removed
from the session instead of cloning it first.
The change also consolidates filename-to-document-type detection and reuses the
CLI's shared timestamp formatter.
What is the impact of this change?
The main impact is maintainability and auditability: the run lifecycle is split
into named phases, while one context owns the state needed for reliable cleanup.
This makes failure and interruption paths easier to follow and reduces the chance
that a future early return skips environment teardown.
Wrapped task execution avoids copying potentially large embedded files on every
task. This is most relevant to worker-agent library users that run many wrapped
tasks in one session.
There are a few intentional user-visible corrections:
No public Rust API was added or changed.
How was this change tested?
cargo fmt --all -- --checkcargo clippy --all-features --all-targets --workspace -- -D warningsvalidation, failed-task environment cleanup, and wrapped-action setup failures
cargo test --workspace --exclude openjd-sessionscargo test -p openjd-sessions -- --skip test_with_password_nonexistent_user_returns_logon_failure --skip test_tempdir_windows_nonvalid_principal_raises_errorYes, the unit and integration tests were run. The full unfiltered
cargo test --workspacerun passed all affected tests but failed two existingWindows account tests on this machine with
LogonFailure.Was this change documented?
Yes. The CLI architecture, run pipeline, parameter parsing, logging, and sessions
runtime specifications were updated to match the implementation. Stale references
to the old monolithic
run.rsflow and obsolete helper APIs were also corrected.No public API documentation changes were required because this does not change a
crate's public API.
Is this a breaking change?
No. This is an internal refactor and performance improvement with correctness
fixes to CLI error and interruption handling.
Does this change impact security?
No impact.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.