Skip to content

Keep command output and Git checkpoints reliable on Windows - #23

Open
LeZergan wants to merge 3 commits into
devnoname120:mainfrom
LeZergan:pr/exec-reliability
Open

LeZergan wants to merge 3 commits into
devnoname120:mainfrom
LeZergan:pr/exec-reliability

Conversation

@LeZergan

@LeZergan LeZergan commented Nov 12, 2026 •

Copy link
Copy Markdown

Summary

Split out of #20, as requested. This PR has the command-execution and Windows-service fixes, which don't depend on the other split PRs. Based on current main (v2.3.29).

Changes

exec_command / write_stdin

  • A command that exits right after a snapshot keeps its session addressable until the next poll drains its final output and exit code. Late stdout is no longer discarded.
  • Final results are kept until they have been polled. Idle expiry only applies to sessions with a configured non-zero idle timeout, and running expired sessions are terminated outside the map lock.
  • An interrupted output wait ends only the wait. It never signals the child, resends stdin, consumes owner text or changes the session ID.

Git checkpoints (diff.rs)

  • Each edit or command no longer repeats the legacy-namespace Git probes. Windows pays for every Git process.
  • Git's stderr is drained under the command deadline, so per-path warnings (CRLF conversion, for example) can't stall a checkpoint through pipe backpressure. Diagnostics stay bounded.

Windows paths and service

  • worktrees::strip_verbatim_prefix removes \\?\ prefixes before passing paths to Git and project clones.
  • The native server starts at Normal priority. Task Scheduler can otherwise start the supervisor at BelowNormal and the server inherits it.
  • The supervisor holds an OS lock for its lifetime, so a lingering older supervisor can't race a new one for ports or the shared log. A leftover lock file doesn't block recovery.

Testing

  • cargo check --all-targets and cargo fmt --check on this branch alone: pass.
  • On Windows 11, the exec_command, exec_sessions, diff, markdown_chat::wait, worktrees and service unit tests pass, along with tests/tools_core, tests/worktree_selection and tests/starvation.
  • One exception: requested_output_budget_cannot_exceed_server_policy fails, but it fails the same way on unmodified main on Windows. Its fixture command assumes /usr/bin.
  • This branch adds markdown_chat/wait.rs so an owner message can end a command's output wait.

🤖 Generated with Claude Code

exec_command keeps a session addressable until its final output and exit code
are polled, retains final results until read, and lets an owner message end
only the output wait. Git checkpoints skip repeated legacy-namespace probes and
drain stderr under the command deadline. Paths drop the \?\ verbatim prefix
before reaching Git, the native server runs at Normal priority, and the
supervisor holds an OS lock for its lifetime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@devnoname120 devnoname120 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Reviewed 44bd419, including output/exit-status snapshots, session expiry and cancellation, owner-message wakeups, Git diagnostic draining, path normalization and the supervisor lock.

The core changes look sound and this is the closest of the four PRs to merging. The snapshot fix retains an addressable session for a later final drain; completed quiet sessions keep their exit receipt; owner messages end the output wait without resending stdin; Git diagnostics stay bounded while being fully drained. I found no additional functional blocker in these reviewed paths.

Validation: all 863 library tests passed, with 1 ignored, on macOS at this head. cargo clippy --all-targets -- -D warnings fails on introduced dead code (inline). CI independently reports that unused function, along with a needless_bool diagnostic in unchanged store.rs; the latter should be handled as baseline/toolchain drift, not blamed on this change.

Recommendation: request changes for the standalone branch's lint regression, then rerun its checks. Subject to that and the remaining CI results, I would prioritize this PR for merging first.

Comment thread src/exec_sessions.rs Outdated
StdMutex<HashMap<String, Vec<Weak<ExecSessionState>>>>,
> = OnceLock::new();

pub(crate) fn conversation_has_running_commands(identity: &ConversationIdentity) -> bool {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

[P2] Keep the standalone execution PR warning-free

conversation_has_running_commands has no caller anywhere in this branch; its consumers arrive only in #24. Removing the opportunistic reaper's empty-buffer check also leaves PendingBuffer::is_empty unused in non-test builds. Both now fail cargo clippy --all-targets -- -D warnings locally, and CI independently reports the unused function. Move the unused activity helper/registry into the commit that consumes it and remove or test-gate the obsolete is_empty helper, so this independently based PR can pass its required lint gate. The passing library tests do not cover this all-target build failure.

Move the conversation command-activity registry to the agent-teams change
that consumes it, and compile PendingBuffer::is_empty only for tests now
that the reaper no longer treats an empty buffer as an acknowledgement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LeZergan

Copy link
Copy Markdown
Author

Thanks for the review. Fixed in c319e39:

cargo clippy --target x86_64-unknown-linux-gnu --all-targets -- -D warnings passes on this branch. Before the change it reported exactly the two dead-code errors. The exec_sessions unit tests still pass, and cargo fmt --check passes.

Admission counted every retained session against maxSessions, so a client
that never drained final results could hold all slots for the idle timeout.
Only running processes now block admission: when the map is full, the least
recently used finished-but-unread results are evicted (and logged) to admit
the new command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LeZergan

Copy link
Copy Markdown
Author

Session admission fixed in 78ca93d:

  • Only running processes now count against maxSessions. When the map is full, the least recently used finished-but-unread results are evicted (logged at info) to admit the new command. Final output is still kept for clients that do poll.
  • If every slot holds a live process, the command is refused as before.
  • New test unread_finished_sessions_do_not_block_admission_but_running_ones_do. It fails on the previous code.

Exec session tests: 26/26 pass. Linux-target clippy --all-targets -D warnings is clean.

This branch has not been deployed

No deployments
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.

2 participants