Skip to content

feat(cargo-each): add portable execution inputs - #176

Open
martin-kolinek wants to merge 42 commits into
mainfrom
feat/cargo-each-portable-execution
Open

martin-kolinek wants to merge 42 commits into
mainfrom
feat/cargo-each-portable-execution

Conversation

@martin-kolinek

@martin-kolinek martin-kolinek commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

🤖 Prepares cargo-each to replace shell-heavy workspace orchestration in generated tooling.

  • reads repeatable --package-file selections, including Windows-style leading UTF-8 BOMs
  • exposes lazy {workspace-rust-version} resolution and validation
  • adds bounded --jobs N execution plus machine-relative --jobs auto
  • preserves inherited standard streams when effective concurrency is one and uses deterministic plan-order output for genuinely parallel work
  • captures parallel stdout/stderr directly into finite temporary-file snapshots, with independent child-writer and parent-reader offsets and plan-contiguous waves that bound retained files
  • adds per-invocation --timeout using command-group Unix process groups and Windows Job objects
  • keeps bounded termination and a prestarted polling reaper local to cargo-each
  • documents Unix process-group escape limitations rather than claiming sealed containment
  • performs single-pass placeholder substitution so inserted paths are never rescanned

This PR contains no cargo-gamma source changes or dependencies. Its only non-cargo-each diffs are .spelling, workspace Cargo.toml, and Cargo.lock.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI lite review requested due to automatic review settings September 11, 2026 18:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical and moderate issues remain in execution cleanup, timeout handling, parsing, and placeholder expansion.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds portable package-file selection, workspace Rust-version substitution, bounded parallel execution, buffered output, timeouts, and process-tree cleanup to cargo-each.

Changes:

  • Adds repeatable --package-file inputs and lazy Rust-version validation.
  • Adds --jobs, deterministic buffered output, and per-invocation timeouts.
  • Updates tests, documentation, dependencies, and lockfile.
File summaries
File Summary
crates/cargo-each/tests/cli.rs Adds integration coverage.
crates/cargo-each/src/workspace.rs Resolves and validates workspace Rust versions.
crates/cargo-each/src/substitute.rs Adds workspace placeholder substitution; moderate issue: inserted values can be rescanned.
crates/cargo-each/src/select.rs Parses package-file selections; critical issue: malformed validation results are not propagated correctly.
crates/cargo-each/src/run.rs Implements execution and cleanup; critical issues remain around unbounded output draining and synchronous timeout termination, plus moderate issues with interrupted reads, descendant lifecycle, and placeholder ordering.
crates/cargo-each/src/plan.rs Propagates expanded invocation options.
crates/cargo-each/src/main.rs Updates CLI documentation.
crates/cargo-each/src/filter.rs Updates test fixtures.
crates/cargo-each/src/error.rs Adds typed input and configuration diagnostics.
crates/cargo-each/src/cli.rs Defines new selection and execution options.
crates/cargo-each/README.md Updates generated documentation; nit: differs from the source rustdoc artifact.
crates/cargo-each/docs/design/README.md Documents the expanded contract.
crates/cargo-each/Cargo.toml Adds required dependencies.
Cargo.lock Records dependency updates.
Review details

Suppressed comments (5)

crates/cargo-each/README.md:121

  • This generated README disagrees with its source rustdoc: crates/cargo-each/src/main.rs:111 uses member's, while this line uses member’s. Since the repository's anvil-readme-check validates generated crate READMEs, regenerate this file from the source rather than committing an artifact-only mismatch.
uses `{workspace-rust-version}`, then requires every member’s resolved

crates/cargo-each/src/run.rs:350

  • run_captured uses spawn_tree even when timeout is None, so the no-timeout parallel path reaches ProcessTree::observe, whose normal-exit contract sweeps descendants (crates/cargo-gamma-process/src/process_tree.rs:1120-1125). A command such as sh -c 'sleep 10 & exit 0' therefore has its background child killed under --jobs > 1, unlike the existing sequential Command::status path; the new --jobs contract only documents buffering. Please preserve the no-timeout child lifecycle or document this behavior explicitly.
    let mut tree = match spawn_tree(command) {
        Ok(tree) => tree,
        Err(error) => {

crates/cargo-each/src/run.rs:189

  • Workspace placeholder expansion happens after {manifest} is substituted. If the workspace/member path legally contains the literal {workspace-rust-version} text, this second pass rewrites the inserted manifest path as well, so a valid {manifest} becomes wrong. Expand the workspace token against the original command argument before inserting member-derived values; target mode needs the same ordering.
    let mut next_index = 0;

crates/cargo-each/src/run.rs:209

  • Target mode repeats the same replacement ordering: {manifest} is inserted before the workspace-token pass. A legal member path containing {workspace-rust-version} is therefore rewritten into an invalid path whenever both placeholders are used. Apply the workspace-token replacement to the original argument before inserting target/member values here as well.
        if !keep_going && outcome.outcome.result.failed() {

crates/cargo-each/src/substitute.rs:97

  • This helper is called after {manifest} and other per-package substitutions, so it also rewrites the replacement text. A workspace path such as /tmp/{workspace-rust-version}/Cargo.toml makes an original echo {manifest} invocation either fail as unresolved (when the token was not in the command) or silently alter the path. Replace this workspace token on the original argument before inserting other placeholder values, or otherwise keep inserted values from being rescanned.
fn replace_workspace_rust_version(arg: String, placeholders: &Placeholders) -> Result<String, EachError> {
    if !arg.contains(WORKSPACE_RUST_VERSION_TOKEN) {
        return Ok(arg);
    }
    let version = placeholders
        .workspace_rust_version()
        .ok_or_else(|| WorkspaceRustVersionError::new("the command uses the placeholder but its root value was not resolved".to_owned()))?;
    Ok(arg.replace(WORKSPACE_RUST_VERSION_TOKEN, version))
  • Files reviewed: 13/14 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread crates/cargo-each/src/run.rs Outdated
Comment thread crates/cargo-each/src/run.rs Outdated
Comment thread crates/cargo-each/src/select.rs
Comment thread crates/cargo-each/src/run.rs Outdated
@codecov-commenter

Codecov Comments Bot (codecov-commenter) commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.2%. Comparing base (9248a3b) to head (37504b6).
⚠️ Report is 2 commits behind head on main.

❌ Your project status has failed because the head coverage (98.2%) is below the target coverage (100.0%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@          Coverage Diff           @@
##            main    #176    +/-   ##
======================================
  Coverage   98.1%   98.2%            
======================================
  Files        309     310     +1     
  Lines      85048   85924   +876     
======================================
+ Hits       83505   84382   +877     
+ Misses      1543    1542     -1     
Flag Coverage Δ
linux 98.1% <100.0%> (+<0.1%) ⬆️
linux-arm 98.1% <100.0%> (+<0.1%) ⬆️
scheduled ?
windows 98.4% <100.0%> (+<0.1%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 11, 2026 21:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved process-execution and resource-handling issues block approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (5)

crates/cargo-each/src/run.rs:361

  • The parallel runner unconditionally sets child stdin to Stdio::null(), whereas the existing sequential Command::status() path inherits the caller's stdin. Consequently, opting into --jobs > 1 changes commands that consume input to see EOF immediately, which can silently break otherwise valid commands. Preserve the prior stdin behavior or make this non-interactive contract explicit and reject/document input-consuming commands.
    let _ = command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());

crates/cargo-each/src/run.rs:213

  • The scheduler retains every BufferedOutcome in outcomes until the entire plan has finished, and only emits them afterward. With --keep-going this makes captured stdout/stderr grow with the total output of every selected package rather than with --jobs; a workspace-wide test run can therefore exhaust cargo-each's memory even though concurrency is bounded. Spool completed output to per-invocation temporary storage, or otherwise add a bounded retention strategy that still preserves plan-order emission.
    let mut outcomes = Vec::with_capacity(invocations.len());
    while let Some(outcome) = wait_for_worker(&mut workers) {
        if failure_stops_launching(keep_going, outcome.outcome.result.failed()) {
            stop_launching = true;
        }
        outcomes.push(outcome);

crates/cargo-each/src/run.rs:574

  • The captured reader treats every read error as fatal, including ErrorKind::Interrupted. A signal can transiently interrupt a pipe read, and the repository's sibling process-tree reader explicitly retries this condition (crates/cargo-gamma-process/src/process_tree.rs:1359-1362); here it instead turns a successful invocation into an infrastructure failure and drops the remaining output. Retry interrupted reads before returning other errors.
            let read = match stream.read(&mut chunk)? {
                0 => return Ok(()),
                read => read,
            };

crates/cargo-each/src/run.rs:414

  • This no-timeout branch still waits through ProcessTree::observe, whose contract kills surviving descendants when the leader exits. Consequently, requesting --jobs > 1 changes a successful command's child-process semantics: a command that intentionally leaves a background child running is terminated, whereas the default sequential path uses Command::status() and leaves it running. The documented process-tree termination is scoped to timed-out commands; either preserve the no-timeout behavior or document and test this additional ownership of descendants.
    let tree_outcome = match timeout {
        Some(timeout) => wait_for_tree(&mut tree, timeout),
        None => wait_for_tree_without_timeout(&mut tree),
    };

crates/cargo-each/src/substitute.rs:213

  • This comment is no longer accurate: {workspace-rust-version} is a valid placeholder in this branch and is substituted on line 217. Narrow the statement to the {packages} token so it describes the actual validation guarantee.
            Placeholders::Once { packages, .. } => {
                // Validation above guarantees each arg is either exactly
                // `{packages}` or contains no placeholder token at all.
  • Files reviewed: 13/14 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread crates/cargo-each/src/run.rs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 11, 2026 22:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved process cleanup, scheduling, reader-lifecycle, and capacity-handling issues block approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

crates/cargo-each/src/run.rs:228

  • With keep_going enabled, a worker-thread creation error sets stop_launching, so pending invocations are never attempted and the function returns an AppError instead of an invocation outcome. That contradicts the documented complete-plan/exit-1 contract and differs from ordinary command-spawn failures, which are recorded per invocation; record this index as an infrastructure outcome and continue scheduling under keep_going (while retaining the fail-fast abort).
            Err(error) => {
                launch_error = Some(error);
                stop_launching = true;
            }

crates/cargo-each/src/run.rs:759

  • When the drain deadline expires, retaining is set false, but the reader may already be blocked in stream.read; dropping the JoinHandle only detaches that thread and leaves its pipe handle alive. A descendant that keeps the pipe open indefinitely therefore leaks one blocked thread and read handle per stream, so repeated --jobs/--keep-going invocations can eventually exhaust process resources. Close/interrupt the stream before detaching, or use a reader ownership design with a cancellable read.
    drop(thread);

crates/cargo-each/src/run.rs:191

  • cargo_gamma_process::capacity() is the Unix limit for watched process-tree slots, but this branch applies it even when timeout is None; in that mode run_captured creates an ordinary Child and never allocates a ProcessTree. On Unix, requesting --jobs above that unrelated watcher capacity is therefore silently throttled. Apply this cap only when timed containment is actually selected.
    let worker_count = jobs.get().min(invocations.len()).min(cargo_gamma_process::capacity().max(1));
  • Files reviewed: 17/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread crates/cargo-each/src/run.rs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 11, 2026 23:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Parallel output capture and process cleanup require fixes, and stdin behavior needs a defined contract.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

crates/cargo-each/src/run.rs:559

  • This error branch drops the local Child immediately and returns; unlike the setup-failure paths above, it never calls terminate_ordinary_child. If a wait error occurs before the leader is reaped, the no-timeout parallel path can report an infrastructure failure while the invocation is still running. Run the same bounded cleanup here and retain the wait error, plus any cleanup error, in the result.
                    Err(error) => TreeOutcome::new(InvocationResult::Infrastructure(format!(
                        "failed to wait for child process: {error}"
                    ))),

crates/cargo-each/src/run.rs:191

  • cargo_gamma_process::capacity() is the Unix interrupt-watch limit, but the timeout == None branch launches ordinary Childs directly at lines 364-369 and never consumes those slots. Capping every parallel run by this value unnecessarily throttles ordinary --jobs N executions (and limits them to the registry capacity on Unix); apply the cap only when timeout-based ProcessTrees are used.
    let worker_count = jobs.get().min(invocations.len()).min(cargo_gamma_process::capacity().max(1));

crates/cargo-each/src/run.rs:774

  • On the drain deadline this only flips a flag and drops the JoinHandle; it cannot interrupt the reader's already-blocked stream.read, because the stream is owned by that detached thread. A descendant that keeps a pipe open indefinitely therefore leaves one live thread and file descriptor per timed-out stream, so repeated --keep-going invocations can accumulate resources despite the one-second bound. The reader needs a cancellable/closable ownership path, or the resource lifetime must be bounded another way.
        Err(mpsc::RecvTimeoutError::Timeout) => {
            retaining.store(false, Ordering::Release);
            Some(format!(
                "child {stream} remained open for more than {} ms after the {boundary} completed; partial output was retained",
                grace.as_millis()

crates/cargo-each/src/run.rs:363

  • Parallel workers unconditionally replace stdin with Stdio::null(), while the sequential run_streamed path leaves stdin inherited. Consequently the same arbitrary command receives caller input with --jobs 1 but EOF with --jobs > 1, and the new CLI/docs do not state this change in input semantics. Please define/document the stdin contract or provide an explicit handling policy for commands that read stdin.
    let _ = command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
  • Files reviewed: 17/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread crates/cargo-each/src/run.rs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Four unresolved moderate issues in run.rs affect stdin behavior, parallelism limits, child cleanup, and reader-thread resources.

Review details

Suppressed comments (4)

crates/cargo-each/src/run.rs:371

  • The parallel runner unconditionally replaces the caller's stdin with Stdio::null(). A command run with --jobs > 1 therefore receives EOF even when the plan contains only one invocation, whereas the sequential path leaves stdin inherited (run_streamed at crates/cargo-each/src/run.rs:327-335). This is an observable behavior change for commands that read stdin; either preserve stdin for the single-invocation case and define/reject it for multiple workers, or document and test the new contract.
    let _ = command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());

crates/cargo-each/src/run.rs:191

  • This clamps every parallel run to the process-tree interrupt registry capacity, even when timeout is None. In the no-timeout branch below, workers use ordinary Command::spawn (crates/cargo-each/src/run.rs:372-377) and do not consume process-tree watch slots, so an explicit --jobs larger than that capacity is silently serialized to the platform ceiling for no reason. Apply the capacity limit only to timed/contained workers so ordinary parallel runs can use the requested bound.
    let worker_count = jobs.get().min(invocations.len()).min(cargo_gamma_process::capacity().max(1));

crates/cargo-each/src/run.rs:567

  • If Child::wait() returns an error, this arm has already taken the child out of CapturedProcess and returns without terminating it. The subsequent drop(process) therefore sees Ordinary(None), so a still-running child (and its descendants) can outlive cargo-each; the injected CaptureFault::WaitFailure exercises this path. Attempt bounded termination here and preserve any cleanup error alongside the wait error.
                match waited {
                    Ok(status) => TreeOutcome::new(InvocationResult::Exited(status)),
                    Err(error) => TreeOutcome::new(InvocationResult::Infrastructure(format!(
                        "failed to wait for child process: {error}"
                    ))),

crates/cargo-each/src/run.rs:783

  • This only flips retaining; if a background or escaped descendant keeps the pipe open, the reader can remain blocked in stream.read, and the JoinHandle is then dropped below without stopping that thread. Each such invocation can therefore leave a live reader thread and pipe handle behind even though the scheduler returns, so repeated parallel runs can exhaust process resources. Please make the pipe read cancellable/closeable from the owning side, or use a bounded reusable reader strategy, before detaching.
        Err(mpsc::RecvTimeoutError::Timeout) => {
            retaining.store(false, Ordering::Release);
            Some(format!(
                "child {stream} remained open for more than {} ms after the {boundary} completed; partial output was retained",
                grace.as_millis()
            ))
  • Files reviewed: 17/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 12, 2026 02:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

One critical and four moderate unresolved findings affect process reaping, timeout semantics, memory bounds, stdin behavior, and reader cleanup.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

crates/cargo-each/src/run.rs:215

  • The 1 MiB limit is only per stream, while every BufferedOutcome is retained in outcomes until all workers finish and deterministic emission begins. A large plan whose commands each produce less than 1 MiB can therefore accumulate output proportional to the entire plan, defeating the bounded-memory expectation in the design (crates/cargo-each/docs/design/README.md:305-320) and potentially exhausting the runner. Please drain completed outcomes in plan order as soon as the next contiguous result is available, or enforce a global output budget/spill policy.
    let mut outcomes = Vec::with_capacity(invocations.len());
    while let Some(outcome) = wait_for_worker(&mut workers) {
        if failure_stops_launching(keep_going, outcome.outcome.result.failed()) {
            stop_launching = true;
        }

crates/cargo-each/src/run.rs:372

  • Parallel execution unconditionally replaces the caller's stdin with Stdio::null(), so a command that reads stdin receives EOF when --jobs > 1; the sequential path still inherits stdin through Command::status(). That makes execution depend on the concurrency setting, while the documented parallel behavior says commands retain ordinary direct-child semantics (src/main.rs:117-124). Preserve an explicit stdin policy across modes or document/reject this incompatible behavior.
    let _ = command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());

crates/cargo-gamma-process/src/process_tree.rs:1357

  • At the deadline, a failed kill returns an error with error.kind() (for example, PermissionDenied) instead of io::ErrorKind::TimedOut, even though this method documents deadline results as timed-out errors. Preserve TimedOut as the error kind while retaining the kill failure in the message so callers can distinguish a bounded deadline from an immediate cleanup failure.
                return Err(kill_error.take().map_or_else(
                    || io::Error::new(io::ErrorKind::TimedOut, deadline_error.clone()),
                    |error| io::Error::new(error.kind(), format!("{error}; {deadline_error}")),
                ));
  • Files reviewed: 17/18 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread crates/cargo-gamma-process/src/process_tree.rs Outdated
Comment thread crates/cargo-each/src/run.rs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 12, 2026 04:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

A critical unbounded wait and a moderate --keep-going test mismatch remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 17/18 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread crates/cargo-each/src/run.rs Outdated
Comment thread crates/cargo-each/tests/cli.rs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 22, 2026 14:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Unresolved reaper retention and fail-fast state issues remain in run.rs.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 High severity

Open (3)

Comment thread crates/cargo-each/src/run.rs Outdated
Use static test storage instead of leaked allocations and exclude only the filesystem-backed snapshot regression under isolated Miri.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 22, 2026 16:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Unresolved reaper, failure-propagation, and descendant-output resource issues remain in run.rs.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 4 High severity

Open (4)

Comment thread crates/cargo-each/src/run.rs
Propagate capture-read failures into scheduler state, observe the direct leader during timeout cleanup, stop retaining terminal reaper errors, and document storage retained by preserved descendants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Exercise the completed, pending, interrupted, and terminal observation truth table directly so an always-retain mutation fails immediately.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Use a timed completion channel before joining the fake reaper so an always-retain mutation fails promptly instead of timing out the test binary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 22, 2026 18:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Critical and moderate execution issues remain in run.rs, along with a documentation nit in workspace.rs.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (4)

Comment thread crates/cargo-each/src/run.rs Outdated
Launch untimed streamed commands in command groups and route leader observation errors through bounded termination and the local reaper while preserving inherited standard streams.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 22, 2026 20:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

A critical unresolved process-group and signal-handling issue remains in the sequential execution path.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Comment thread crates/cargo-each/src/run.rs
Keep untimed effective-one commands as ordinary children while separating spawn from wait, applying bounded direct-child cleanup, and prestarting a child-handle reaper for failed waits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Exercise the bounded direct-child terminator and both process-reaper startup/handoff channels; isolate only the unforceable OS wait-error ownership adapter from coverage and mutation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Verify a completed ordinary child is observed as exited so an always-pending observation mutation fails immediately.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 22, 2026 22:56
//! output already collected at earlier plan indices. Parallel work runs in
//! plan-contiguous waves capped by the effective worker count; each completed
//! wave is emitted and dropped before the next wave starts, bounding retained
//! temporary-file storage. Untimed effective-one execution uses an ordinary

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Pato''s Pull Request Agent: [language/CI] anvil-spellcheck is failing on "Untimed" — add it to .spelling

This PR''s Fast Checks (linux, linux-arm) are red because anvil-spellcheck (Hunspell) rejects the newly added word Untimed:

error: spellcheck(Hunspell)
    --> crates/cargo-each/src/main.rs:134
134 |  temporary-file storage. Untimed effective-one
     |                          ^^^^^^^  - Untied, Untamed, or Runtime
     |   Possible spelling mistake found.

Untimed is a coined term used in this new rustdoc (and mirrored in README.md and docs/design/README.md), but it was never added to the workspace dictionary — this PR only added nonblocking to .spelling. Add Untimed there (keeping the file''s ordering convention) so the spellcheck recipe passes. Until then Fast Checks stay red on every platform.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Broad execution and process-reaping changes include an unresolved moderate handling issue and warrant human review.

Review effort: Lite
Findings: 1 Low severity

Open (1)
Resolved since last review (1)

.spawn(move || {
let _ = writeln!(
io::stderr().lock(),
"cargo each: process-group reaper failed to observe a retained group: {message}"
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9fc919b-99f7-4134-aad1-2116321b4e0c
Copilot AI review requested due to automatic review settings September 23, 2026 00:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Broad concurrency, timeout, and process-management changes warrant final human review.

Review effort: Lite
Findings: 1 Low severity

Open (1)
Resolved since last review (1)

@github-actions

Copy link
Copy Markdown

⚠️ SemVer check advisory

Inconclusive comparisons

cargo semver-checks could not complete the following comparisons. These failures are informational because an unbuildable baseline is not evidence of a breaking API change.

cargo-aprz-lib (exit 101)

     Cloning f4e32d05a1767bf083e5b65055e6a1c9673b7689
    Building cargo-aprz-lib v1.1.2 (current)
error: running cargo-doc on crate 'cargo-aprz-lib' failed with output:
-----
   Compiling proc-macro2 v1.0.107
   Compiling quote v1.0.47
   Compiling unicode-ident v1.0.26
   Compiling libc v0.2.189
    Checking memchr v2.8.3
    Checking cfg-if v1.0.5
    Checking smallvec v1.16.1
    Checking bytes v1.12.1
    Checking once_cell v1.21.4
    Checking regex-syntax v0.8.11
   Compiling thiserror v2.0.20
   Compiling syn v3.0.6
    Checking stable_deref_trait v1.2.1
   Compiling crc32fast v1.5.2
   Compiling crossbeam-utils v0.8.23
    Checking scopeguard v1.2.0
    Checking regex-automata v0.4.18
    Checking zlib-rs v0.6.8
   Compiling getrandom v0.4.3
   Compiling parking_lot_core v0.9.12
    Checking fastrand v2.5.0
    Checking bitflags v2.13.2
    Checking gix-trace v0.1.21
    Checking lock_api v0.4.14
   Compiling serde_core v1.0.229
    Checking tinyvec v1.13.3
    Checking parking_lot v0.12.5
    Checking itoa v1.0.18
    Checking bstr v1.13.1
    Checking unicode-normalization v0.1.25
    Checking gix-utils v0.3.6
    Checking gix-validate v0.11.4
    Checking crossbeam-channel v0.5.17
    Checking same-file v1.0.6
    Checking walkdir v2.5.0
   Compiling shlex v2.0.1
    Checking foldhash v0.2.0
    Checking byteorder v1.5.0
    Checking equivalent v1.0.2
   Compiling find-msvc-tools v0.1.13
    Checking allocator-api2 v0.2.21
    Checking prodash v31.0.0
   Compiling jobserver v0.1.35
    Checking gix-error v0.2.5
   Compiling pkg-config v0.3.34
   Compiling cc v1.4.7
   Compiling version_check v0.9.5
   Compiling generic-array v0.14.7
    Checking hashbrown v0.17.1
    Checking typenum v1.20.1
   Compiling thiserror-impl v2.0.20
   Compiling heapless v0.8.0
    Checking hash32 v0.3.1
    Checking faster-hex v0.10.0
   Compiling serde v1.0.229
    Checking block-buffer v0.10.4
    Checking crypto-common v0.1.7
    Checking cpufeatures v0.2.17
    Checking digest v0.10.7
   Compiling serde_derive v1.0.229
    Checking pin-project-lite v0.2.17
    Checking sha1 v0.10.7
    Checking jiff-core v0.1.1
    Checking sha1-checked v0.10.0
    Checking gix-path v0.12.6
   Compiling synstructure v0.14.0
    Checking gix-features v0.48.1
   Compiling rustix v1.1.5
    Checking jiff v0.2.37
    Checking gix-hash v0.25.1
    Checking linux-raw-sys v0.12.1
   Compiling zerofrom-derive v0.1.8
    Checking zerofrom v0.1.8
   Compiling yoke-derive v0.8.3
    Checking gix-date v0.15.6
   Compiling autocfg v1.5.1
    Checking gix-actor v0.41.2
    Checking yoke v0.8.3
    Checking gix-hashtable v0.15.2
    Checking futures-core v0.3.34
    Checking gix-object v0.61.0
   Compiling zerovec-derive v0.11.6
    Checking errno v0.3.14
    Checking signal-hook-registry v1.4.8
   Compiling tokio-macros v2.7.2
   Compiling displaydoc v0.2.7
    Checking mio v1.2.3
    Checking socket2 v0.6.5
    Checking memmap2 v0.9.11
   Compiling cmake v0.1.58
   Compiling dunce v1.0.5
    Checking tokio v1.53.1
   Compiling rustversion v1.0.23
    Checking zerovec v0.11.8
   Compiling fs_extra v1.3.0
    Checking futures-sink v0.3.34
   Compiling aws-lc-sys v0.45.0
    Checking tinystr v0.8.4
    Checking tempfile v3.27.0
    Checking gix-fs v0.21.2
    Checking gix-chunk v0.7.3
    Checking writeable v0.6.4
    Checking percent-encoding v2.3.2
   Compiling vcpkg v0.2.15
    Checking litemap v0.8.3
    Checking icu_locale_core v2.3.0
   Compiling libz-sys v1.1.29
    Checking gix-tempfile v23.0.2
    Checking potential_utf v0.1.6
    Checking zerotrie v0.2.5
    Checking gix-quote v0.7.2
    Checking tracing-core v0.1.36
   Compiling aws-lc-rs v1.18.1
    Checking simd-adler32 v0.3.10
   Compiling icu_properties_data v2.3.0
    Checking nonempty v0.12.0
   Compiling icu_normalizer_data v2.3.0
    Checking utf8_iter v1.0.4
    Checking zeroize v1.9.0
    Checking slab v0.4.12
    Checking icu_collections v2.3.0
    Checking tracing v0.1.44
    Checking icu_provider v2.3.1
    Checking http v1.5.0
    Checking adler2 v2.0.1
    Checking fnv v1.0.7
    Checking miniz_oxide v0.9.1
    Checking rustls-pki-types v1.15.1
    Checking gix-commitgraph v0.37.1
    Checking gix-glob v0.26.1
    Checking indexmap v2.14.2
    Checking log v0.4.34
    Checking futures-io v0.3.34
    Checking futures-task v0.3.34
    Checking tokio-util v0.7.19
    Checking futures-util v0.3.34
    Checking gix-revwalk v0.32.0
    Checking icu_properties v2.3.0
    Checking icu_normalizer v2.3.0
    Checking flate2 v1.1.10
    Checking http-body v1.1.0
    Checking gix-lock v23.0.1
   Compiling num-traits v0.2.19
   Compiling syn v2.0.119
   Compiling zmij v1.0.23
   Compiling rustls v0.23.45
    Checking untrusted v0.9.0
   Compiling httparse v1.10.1
    Checking idna_adapter v1.2.2
   Compiling encoding_rs v0.8.41
    Checking futures-channel v0.3.34
    Checking atomic-waker v1.1.2
    Checking static_assertions v1.1.0
    Checking subtle v2.6.1
    Checking tower-layer v0.3.3
    Checking tower-service v0.3.3
    Checking try-lock v0.2.5
    Checking unicode-bom v2.0.3
    Checking want v0.3.1
    Checking h2 v0.4.19
    Checking idna v1.1.0
    Checking form_urlencoded v1.2.2
    Checking gix-config-value v0.18.1
   Compiling serde_json v1.0.151
   Compiling multiversion_no_op v1.0.0
    Checking core_detect v1.0.0
    Checking shell-words v1.1.1
    Checking simdutf8 v0.1.5
    Checking compression-core v0.4.33
    Checking compression-codecs v0.4.43
    Checking hyper v1.11.1
    Checking gix-command v0.9.2
    Checking url v2.5.8
    Checking kstring v2.0.5
    Checking sync_wrapper v1.0.2
    Checking gix-sec v0.14.2
    Checking base64 v0.22.1
    Checking openssl-probe v0.2.1
    Checking ipnet v2.12.2
    Checking rustls-native-certs v0.8.4
    Checking gix-attributes v0.33.2
    Checking tower v0.5.3
    Checking hyper-util v0.1.20
    Checking async-compression v0.4.48
    Checking http-body-util v0.1.5
   Compiling heck v0.5.0
   Compiling crossbeam-epoch v0.9.21
    Checking tower-http v0.6.11
    Checking gix-ref v0.64.0
    Checking gix-url v0.36.2
    Checking gix-packetline v0.21.5
    Checking filetime v0.2.29
    Checking either v1.18.0
   Compiling crossbeam-deque v0.8.8
    Checking base64 v0.23.1
    Checking mime v0.3.17
    Checking gix-prompt v0.15.1
    Checking gix-traverse v0.58.0
    Checking gix-revision v0.46.0
   Compiling zstd-sys v2.1.0+zstd.1.5.7
    Checking gix-bitmap v0.3.3
    Checking hashbrown v0.16.1
    Checking fast_time v0.1.32
    Checking arrayvec v0.7.8
   Compiling camino v1.2.6
   Compiling rayon-core v1.13.0
    Checking uluru v3.1.0
    Checking nm_impl v0.1.50
    Checking gix-refspec v0.42.0
    Checking clru v0.6.3
    Checking gix-index v0.52.0
    Checking gix-credentials v0.38.2
    Checking gix-ignore v0.21.1
   Compiling memoffset v0.9.1
    Checking semver v1.0.28
   Compiling cfg_aliases v0.2.2
    Checking winnow v1.0.4
    Checking utf8parse v0.2.2
   Compiling thiserror v1.0.69
    Checking toml_parser v1.1.3+spec-1.1.0
    Checking anstyle-parse v1.0.0
   Compiling nix v0.30.1
    Checking gix-worktree v0.53.0
    Checking gix-pack v0.71.0
    Checking nm v0.1.50
    Checking gix-config v0.57.0
    Checking gix-filter v0.31.0
    Checking gix-pathspec v0.18.1
   Compiling thiserror-impl v1.0.69
   Compiling maybe-async v0.2.11
    Checking gix-shallow v0.12.1
    Checking gix-negotiate v0.32.0
    Checking arc-swap v1.9.2
   Compiling fs-err v3.3.1
    Checking smol_str v0.3.6
    Checking toml_datetime v1.1.1+spec-1.1.0
    Checking serde_spanned v1.1.1
    Checking io-close v0.3.7
    Checking unicode-properties v0.1.4
    Checking is_terminal_polyfill v1.70.2
    Checking colorchoice v1.0.5
   Compiling zstd-safe v8.0.0
   Compiling typeid v1.0.3
    Checking bit-vec v0.8.0
    Checking anstyle-query v1.1.5
    Checking toml_writer v1.1.2+spec-1.1.0
    Checking bumpalo v3.20.3
   Compiling portable-atomic v1.15.0
    Checking anstyle v1.0.14
    Checking anstream v1.0.0
    Checking zopfli v0.8.3
    Checking toml v1.1.6+spec-1.1.0
    Checking bit-set v0.8.0
    Checking ra-ap-rustc_lexer v0.160.0
error[E0080]: evaluation panicked: unicode-properties and unicode-ident must use the same Unicode version, `unicode_properties::UNICODE_VERSION` and `unicode_ident::UNICODE_VERSION` are different.
  --> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ra-ap-rustc_lexer-0.160.0/src/lib.rs:47:9
   |
47 | /         panic!(
48 | |             "unicode-properties and unicode-ident must use the same Unicode version, \
49 | |             `unicode_properties::UNICODE_VERSION` and `unicode_ident::UNICODE_VERSION` are \
50 | |             different."
51 | |         );
   | |_________^ evaluation of `_` failed here

For more information about this error, try `rustc --explain E0080`.
error: could not compile `ra-ap-rustc_lexer` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

-----

error: failed to build rustdoc for crate cargo-aprz-lib v1.1.2
note: this is usually due to a compilation error in the crate,
      and is unlikely to be a bug in cargo-semver-checks
note: the following command can be used to reproduce the error:
      cargo new --lib example &&
          cd example &&
          echo '[workspace]' >> Cargo.toml &&
          cargo add --path /home/runner/work/ox-tools/ox-tools/crates/cargo-aprz-lib --features internals &&
          cargo check &&
          cargo doc

error: aborting due to failure to build rustdoc for crate cargo-aprz-lib v1.1.2

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.

5 participants