Skip to content

feat(minimald): implement check session side-op - #1000

Merged
twitchyliquid64 merged 1 commit into
mainfrom
tom/check-sop
Jul 28, 2026
Merged

feat(minimald): implement check session side-op#1000
twitchyliquid64 merged 1 commit into
mainfrom
tom/check-sop

Conversation

@twitchyliquid64

@twitchyliquid64 twitchyliquid64 commented Jul 28, 2026

Copy link
Copy Markdown
Member

Implements check session side op, workable via:

  • min check <args> in the session
  • min check <args> over ssh exec

Summary by CodeRabbit

  • New Features
    • Added min check support end-to-end (SSH exec and daemon/session streaming), including incremental output and a single final success/failure summary.
    • Added check cancellation support to stop in-progress work early and report cancellation cleanly.
  • Bug Fixes
    • Ensured check session streams close with exactly one terminal outcome, even for empty workspaces.
  • Documentation
    • Updated min attach -c documentation to reflect the accepted min check and min package build forms.
  • Tests
    • Added/expanded SSH end-to-end and unit tests for min check, rendering/summarization, and cancellation behavior.

Note

Implement min check as a session side-op in minimald

  • Adds SideOp::spawn_check in session_sop.rs to run checks as background side-ops with cooperative cancellation, streaming per-object CheckUpdate results and a terminal CheckOutcome to subscribers.
  • Adds CheckOpts argument parsing for min check, rejecting unknown flags with an error rather than misinterpreting them as name filters.
  • Wires min check into the SSH exec handler (exec.rs) and the env socket handler (env.rs), streaming progress and exiting with status 0 (success), 1 (failure), or 130 (cancelled).
  • Adds a CancellationToken to CheckCtx in the check crate so individual checkers, semaphore waits, and sandboxed standalone tests all stop promptly when cancelled.
  • SessionHandle::start_check sends a StartCheck message to the session actor, which tracks the run and cancels it on shutdown.

Macroscope summarized 6b2fbd6.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR adds cancellation-aware checker execution and introduces min check as a streamed session side operation. It adds check progress and terminal outcome types, session wiring, exec/channel routing, and end-to-end coverage.

Check side operation

Layer / File(s) Summary
Cancellation propagation and execution stopping
crates/check/..., crates/op/standalone_test.rs, crates/mctx/...
Checker futures, blocking checks, standalone tests, and sandbox execution observe cancellation and report cancelled runs.
Check side-operation model and execution
crates/minimald/src/session_sop.rs
Check options, streamed updates, terminal outcomes, generic sinks, and the check side operation are implemented.
Session streaming integration
crates/minimald/src/session.rs, crates/minimald/src/env.rs, crates/minimald/src/sessions.rs
Session messages start checks, channel handling streams updates, and session tests verify terminal stream closure.
Exec routing and validation
AGENTS.md, crates/minimald/src/exec.rs
Exec handling accepts min check, renders results, reports status, and tests accepted and rejected commands.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SessionHandle
  participant SessionSideOp
  participant Checker
  Client->>SessionHandle: Start min check with arguments
  SessionHandle->>SessionSideOp: Start check operation
  SessionSideOp->>Checker: Run checks with cancellation
  Checker-->>SessionSideOp: Stream checked results
  SessionSideOp-->>Client: Render updates and terminal outcome
Loading

Possibly related PRs

  • gominimal/minimal#960: Introduced the session side-operation infrastructure extended here for background checks.
  • gominimal/minimal#984: Modified the SSH min command routing that is extended here for min check.

Suggested reviewers: norrietaylor, bryan-minimal

Poem

A rabbit checks the graph at dawn,
Streams each result across the lawn.
If cancellation rings its bell,
The busy checks all pause as well.
“Min check” hops through channels bright—
One clean outcome ends the night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the feature summary, but it omits the required Testing and Checklist sections from the template. Add a Summary section, include testing commands and output, and fill the Checklist items (docs update and BREAKING CHANGE if applicable).
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely summarizes the main change: implementing the check session side-op in minimald.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (4)
crates/minimald/src/sessions.rs (1)

1607-1647: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider bounding the drain loop with a timeout.

If the terminal update is ever lost the loop blocks forever and CI hangs instead of failing. Wrapping the drain in tokio::time::timeout turns a regression into a fast, readable failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minimald/src/sessions.rs` around lines 1607 - 1647, Bound the
update-draining loop in start_check_closes_with_exactly_one_terminal_outcome
with a tokio::time::timeout. Preserve the existing collection and assertion of
CheckOutcome values, but make expiration fail the test with a clear timeout
message instead of allowing CI to hang when the terminal update is missing.
crates/minimald/src/session.rs (1)

1015-1029: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

sops never sheds completed side-ops.

Every start_check/start_build pushes onto SessionInner::Active::sops and nothing removes finished entries until stop_running, so a long-lived session that runs min check repeatedly accumulates SideOps (each holding a JoinHandle plus the op's Arc<Mutex<Inner<_>>>). Pruning finished ops on push would bound it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minimald/src/session.rs` around lines 1015 - 1029, Update the
side-operation registration used by start_check (and the corresponding
start_build path) so completed entries in Active::sops are pruned before pushing
a newly spawned SideOp. Retain only running operations, then append the new
operation while preserving existing shutdown and stop_running behavior.
crates/check/src/lib.rs (1)

1415-1424: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tests share a fixed temp dir and leak it on failure.

make_project derives the path from pid+suffix only, so a re-run in the same process (or a panic before remove_dir_all) leaves a populated dir behind that the next run silently reuses. tempfile::TempDir would give per-run isolation and drop-time cleanup, matching the rest of the workspace's tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/check/src/lib.rs` around lines 1415 - 1424, Update the test helper
make_project to create and return a tempfile::TempDir-backed project instead of
a pid-and-suffix PathBuf, preserving the existing packages, profiles, and stacks
layout while relying on TempDir for unique per-run isolation and drop-time
cleanup. Adjust callers to use the TempDir path and retain the directory handle
for the test lifetime.
crates/check/src/outputs.rs (1)

163-167: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Error::Cancelled is flattened to Error::Other on this path.

The blocking closure's error is stringified at Line 369 and re-wrapped at Line 373, so a cancelled run here reports Other("cancelled"). It's currently masked because check_package calls ctx.bail_if_cancelled() before consuming this checker's result, but the variant is lost if that ordering ever changes. Returning the cancellation out-of-band (e.g. a bool/Option<Error> alongside the string) would keep the classification.

Also applies to: 206-208, 278-279

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/check/src/outputs.rs` around lines 163 - 167, Preserve
Error::Cancelled when errors from the blocking checker closures are converted to
strings and re-wrapped, rather than flattening cancellation into Error::Other.
Update the spawn_blocking error-handling paths at the shown locations, including
the analogous sections around the other reported ranges, to return cancellation
metadata out-of-band (such as an optional cancellation error) and reconstruct
the original variant at the caller.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/minimald/src/session_sop.rs`:
- Around line 172-201: Update SopCheckArgs::from_args to reject any unrecognized
token beginning with "--" instead of adding it to filter_names. Preserve
existing handling for recognized flags and ordinary object-name filters, and
make the invalid-flag failure visible through the established argument-error
mechanism.
- Around line 460-504: Cancel the run token whenever the drain loop in the
outcome handling exits early with CheckOutcome::Failed or
CheckOutcome::Cancelled, before dropping the stream or reporting Finished. Reuse
the existing cancel flag/token associated with CheckCtx and ensure normal
CheckOutcome::Completed remains unchanged, so detached StandaloneTestCheck work
observes cancellation during terminal error paths.

---

Nitpick comments:
In `@crates/check/src/lib.rs`:
- Around line 1415-1424: Update the test helper make_project to create and
return a tempfile::TempDir-backed project instead of a pid-and-suffix PathBuf,
preserving the existing packages, profiles, and stacks layout while relying on
TempDir for unique per-run isolation and drop-time cleanup. Adjust callers to
use the TempDir path and retain the directory handle for the test lifetime.

In `@crates/check/src/outputs.rs`:
- Around line 163-167: Preserve Error::Cancelled when errors from the blocking
checker closures are converted to strings and re-wrapped, rather than flattening
cancellation into Error::Other. Update the spawn_blocking error-handling paths
at the shown locations, including the analogous sections around the other
reported ranges, to return cancellation metadata out-of-band (such as an
optional cancellation error) and reconstruct the original variant at the caller.

In `@crates/minimald/src/session.rs`:
- Around line 1015-1029: Update the side-operation registration used by
start_check (and the corresponding start_build path) so completed entries in
Active::sops are pruned before pushing a newly spawned SideOp. Retain only
running operations, then append the new operation while preserving existing
shutdown and stop_running behavior.

In `@crates/minimald/src/sessions.rs`:
- Around line 1607-1647: Bound the update-draining loop in
start_check_closes_with_exactly_one_terminal_outcome with a
tokio::time::timeout. Preserve the existing collection and assertion of
CheckOutcome values, but make expiration fail the test with a clear timeout
message instead of allowing CI to hang when the terminal update is missing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a5dfae44-fd0f-4368-88f5-7a45bd05f6d4

📥 Commits

Reviewing files that changed from the base of the PR and between 6b30427 and 1666b4c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • AGENTS.md
  • crates/check/Cargo.toml
  • crates/check/src/lib.rs
  • crates/check/src/outputs.rs
  • crates/mctx/src/error.rs
  • crates/mctx/src/lib.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/exec.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_sop.rs
  • crates/minimald/src/sessions.rs
  • crates/op/src/standalone_test.rs

Comment thread crates/minimald/src/session_sop.rs Outdated
Comment thread crates/minimald/src/session_sop.rs Outdated

@bryan-minimal bryan-minimal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, one issue Claude found,

Unknown --flags silently become name filters → a typo'd flag exits 0 (medium — the one I'd fix pre-merge). CheckOpts::from_args routes any unrecognized token into filter_names. So min check --packags (typo) filters for a name that matches
  nothing → zero objects checked → Completed{failed: false} → exit 0. On the SSH path, whose entire contract is the exit status for scripted use (their own test says so), a typo'd flag is a silent pass. Suggest rejecting unknown --* tokens (bare
  words stay filters). Inherited from the old inline parser, but the old parser didn't feed CI scripts.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/minimald/src/sessions.rs`:
- Around line 1631-1638: Update the loop consuming updates in the test around
updates.recv().await to apply a timeout to each receive or the overall wait,
causing the test to fail promptly when the check side-op stalls or the sender
remains open. Preserve the existing CheckUpdate::Checked panic and Finished
outcome handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3cf21887-127f-40e7-8a1d-3acf222f5858

📥 Commits

Reviewing files that changed from the base of the PR and between 1666b4c and a8ad007.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • AGENTS.md
  • crates/check/Cargo.toml
  • crates/check/src/lib.rs
  • crates/check/src/outputs.rs
  • crates/mctx/src/error.rs
  • crates/mctx/src/lib.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/exec.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_sop.rs
  • crates/minimald/src/sessions.rs
  • crates/op/src/standalone_test.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/op/src/standalone_test.rs
  • crates/check/Cargo.toml
  • crates/mctx/src/error.rs
  • crates/mctx/src/lib.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/session.rs
  • crates/check/src/outputs.rs
  • crates/check/src/lib.rs
  • crates/minimald/src/session_sop.rs

Comment thread crates/minimald/src/sessions.rs

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/check/src/lib.rs`:
- Around line 526-536: Update the profile and stack checking flows around the
permit acquisition and the `profile::check_profile`/`check_stack` calls so
cancellation is checked between each parse, typecheck, compile, and checker
stage after work begins. When `ctx.cancel` is triggered, stop before the next
stage and return `Error::Cancelled`, while preserving the existing stage order
and results for non-cancelled checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7fba3e0-35ff-4a37-82f3-61f1b4b748ad

📥 Commits

Reviewing files that changed from the base of the PR and between a8ad007 and 6b2fbd6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • AGENTS.md
  • crates/check/Cargo.toml
  • crates/check/src/lib.rs
  • crates/check/src/outputs.rs
  • crates/mctx/src/error.rs
  • crates/mctx/src/lib.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/exec.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_sop.rs
  • crates/minimald/src/sessions.rs
  • crates/op/src/standalone_test.rs
🚧 Files skipped from review as they are similar to previous changes (11)
  • crates/check/Cargo.toml
  • crates/mctx/src/error.rs
  • AGENTS.md
  • crates/minimald/src/env.rs
  • crates/op/src/standalone_test.rs
  • crates/mctx/src/lib.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/exec.rs
  • crates/check/src/outputs.rs
  • crates/minimald/src/session_sop.rs
  • crates/minimald/src/sessions.rs

Comment thread crates/check/src/lib.rs
Comment on lines +526 to +536
let name = pd.to_str().unwrap().to_string();
let _permit = tokio::select! {
biased;
_ = ctx.cancel.cancelled() => {
return (CheckObj::Profile(name), Err(Error::Cancelled));
}
permit = ctx.semaphore.acquire() => permit.unwrap(),
};
(
CheckObj::Profile(pd.to_str().unwrap().to_string()),
profile::check_profile(pd.to_str().unwrap().to_string(), &ctx, profiles_dir)
.await,
CheckObj::Profile(name.clone()),
profile::check_profile(name, &ctx, profiles_dir).await,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Observe cancellation after profile/stack checks start.

These select! blocks only cancel queued work. After acquiring a permit, check_profile and check_stack run their full parse/typecheck/compile and checker sequences without observing ctx.cancel, so cancellation cannot prevent remaining stages. Add cancellation checks between those stages (and return Error::Cancelled).

Also applies to: 581-591

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/check/src/lib.rs` around lines 526 - 536, Update the profile and stack
checking flows around the permit acquisition and the
`profile::check_profile`/`check_stack` calls so cancellation is checked between
each parse, typecheck, compile, and checker stage after work begins. When
`ctx.cancel` is triggered, stop before the next stage and return
`Error::Cancelled`, while preserving the existing stage order and results for
non-cancelled checks.

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