Skip to content

feat: session-specific sandbox wiring - #425

Merged
twitchyliquid64 merged 3 commits into
mainfrom
tom/sandbox
Jun 17, 2026
Merged

feat: session-specific sandbox wiring#425
twitchyliquid64 merged 3 commits into
mainfrom
tom/sandbox

Conversation

@twitchyliquid64

@twitchyliquid64 twitchyliquid64 commented Jun 16, 2026

Copy link
Copy Markdown
Member
  • Sandbox env:

    • Session-specific home at /home
    • CWD / 'repo-dir' at /workbench, configurable
    • XDG_* variables point into /home except cache, which points into /state (we should probably rename that /cache at this point lol)
  • Duplicated mctx::Env into minimald and started specializing it for sessions, as well as removed some of the lifetime crimes we were doing with &mut Context / &mut Graph.

NB: The duplication is intentional: we want to support the old minimal run <task> semantics as long as possible, so duplicating will let us cook on the sessions stuff separately.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added a new “Session” sandbox layout with configurable home and working directories, including optional working-directory naming.
    • Introduced a session-owned runtime environment and a new min helper to drive session actions (add/search/run/check/build and related operations).
  • Improvements

    • Updated session path handling so exec, SFTP, and workspace file unpacking consistently use the session working directory.
    • Session attachment now propagates the authenticated SSH username for the launched session.
  • Bug Fixes

    • Fixed command execution, repository receive operations, and unpacked file locations to match the new session working-directory behavior.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c829c1e3-bfbb-4481-a244-170ca6670034

📥 Commits

Reviewing files that changed from the base of the PR and between 09677fe and e6974b5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/minimald/Cargo.toml
  • crates/minimald/src/connection.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/env_min_helper.sh
  • crates/minimald/src/exec.rs
  • crates/minimald/src/lib.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_host.rs
  • crates/minimald/src/sftp.rs
  • crates/sandbox2/src/config.rs
  • crates/sandbox2/src/lib.rs
  • crates/sessions/src/store.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/minimald/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/minimald/src/sftp.rs
  • crates/minimald/src/connection.rs
  • crates/minimald/src/exec.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/sandbox2/src/lib.rs
  • crates/minimald/src/session_host.rs
  • crates/sandbox2/src/config.rs
  • crates/minimald/src/env.rs

📝 Walkthrough

Walkthrough

Introduces a session-owned Env abstraction that constructs and manages a sandbox2 rootfs, spawns an async RPC handler, and exposes a min CLI helper script. Adds SessionPaths as a multi-path data model (working, home, cache), introduces WdSetup::Session layout for minimal filesystem isolation with /home and working directories, and updates all session-based execution paths to use the new paths API.

Changes

Session-owned environment for minimald

Layer / File(s) Summary
Session paths contract and accessors
crates/minimald/src/session.rs, crates/sessions/src/store.rs
Introduces SessionPaths { working, home, cache } struct. Adds Session::paths() helper and SessionMessage::GetPaths query. Replaces SessionHandle::workspace_path() with async SessionHandle::paths() returning SessionPaths. Extends SessionObject trait with home_path() and cache_path() methods, implemented in DiskSession. Updates Attach message to include conn_username. Creates working, home, and cache directories at session startup.
WdSetup::Session variant and container wiring
crates/sandbox2/src/config.rs, crates/sandbox2/src/lib.rs
Adds WdSetup::Session { home, working, working_name_override } variant. Introduces SESSION_DEFAULT_WD constant. In Sandbox::new, creates rootfs /home and working subdirectory; skips /state/home, /state/data, /state/state while retaining /state/cache. In Container::command_inner, sets HOME and XDG environment variables under /home for Session. In new_container, bind-mounts session home/home and working/{wd_name}. Updates program resolution to check Session.working for non-absolute commands.
EnvArgs and Env::build orchestration
crates/minimald/src/env.rs (module intro, types, helpers), crates/minimald/Cargo.toml, crates/minimald/src/lib.rs
Introduces EnvArgs builder with methods for packages, patches, env vars, op tracker, username. Env::build resolves/builds requested packages (ensuring bash/socat), constructs sandbox from SessionPaths, merges package filesystem/env, installs min helper, spawns async SessionChannel actor, and returns owned Env. Implements Drop to abort actor on cleanup. Provides container() and command() helper methods. Adds workspace dependencies to Cargo.toml and exports env module.
RPC bridge, request handler, and streaming
crates/minimald/src/env.rs (BridgeChannel, SessionChannel, StreamWriter sections)
BridgeChannel adapts synchronous sandbox2::Channel to forward requests to async SessionChannel actor. SessionChannel::handle dispatches add-*, search, check, patched-pkg, run requests, installs packages by rebuilding graph and hardlinking into rootfs, executes tasks with streamed output. StreamWriter frames stdout/stderr into newline-delimited msg: lines on shared UnixStream. Includes unit tests verifying protocol output.
min helper shell script
crates/minimald/src/env_min_helper.sh
Bash script implementing min CLI with __min_rpc to send requests via socat over Unix socket, parse streamed messages/env directives/errors. Provides subcommands add (with --session, --build, --runtime, --task flags), search, patched-pkg, run, check. Collects environment exports and returns exit code on errors.
SessionLauncher refactoring and Env integration
crates/minimald/src/session_host.rs
SessionLauncher::launch signature extended to accept name, username, and paths: SessionPaths. SandboxLauncher guard type changes from custom SessionEnv to crate::env::Env. launch implementation now builds Env via Env::build(...), derives container, sets session leader, opens PTY, starts /bin/bash. MockLauncher updated to accept new parameters. Host::spawn and Host::build route SessionPaths and identity into launcher. Unit tests updated to construct SessionPaths.
Connection and session attachment
crates/minimald/src/connection.rs, crates/minimald/src/session.rs
ConnectionHandler::shell_request captures authenticated SSH username and passes it to session_handle.attach(). SessionHandle::attach signature updated to accept conn_username parameter. Session actor threads username through attach flow for session host minting with user context.
Execution path migration to SessionPaths
crates/minimald/src/exec.rs, crates/minimald/src/rpc.rs, crates/minimald/src/sftp.rs
Updates handle_exec, handle_git_receive to use paths.working instead of workspace_path(). RPC unpack_workspace_files unpacks tarballs into paths.working. SFTP constructs SftpSession with paths.working. Tests updated to read/verify files from new paths location.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SSH as SSH Connection
  participant Handler as ConnectionHandler
  participant SessionHandle
  participant SessionActor
  participant Host as SessionHost
  participant Env as Env
  SSH->>Handler: shell_request with auth username
  Handler->>SessionHandle: paths() to get SessionPaths
  SessionHandle->>SessionActor: GetPaths message
  SessionActor-->>SessionHandle: SessionPaths { working, home, cache }
  Handler->>SessionHandle: attach(conn_username, channel, config)
  SessionHandle->>SessionActor: Attach(username, handle, channel, config)
  SessionActor->>Host: spawn(name, username, paths, sz, channel)
  Host->>Env: build(ctx, graph, EnvArgs)
  Env->>Env: resolve packages, build dependencies, create sandbox
  Env-->>Host: Env { sandbox, actor }
  Host-->>SessionActor: Launched session with PTY and bash
Loading

Possibly related issues

  • gominimal/inbox#151: The main issue implements the Session layout abstraction and session-owned runtime environment (Env) in minimald that directly support the session primitive and architectural goals described in the retrieved tracking issue.

Possibly related PRs

  • gominimal/minimal#375: Attachable-session session_host scaffolding that the main PR refactors to use Env and route SessionPaths through.
  • gominimal/minimal#383: Session attachment error-plumbing changes adjacent to the main PR's attach signature updates in session.rs and connection.rs.
  • gominimal/minimal#423: RPC tarball-streaming subsystem that the main PR's unpack_workspace_files migration to paths.working complements.

Suggested reviewers

  • 0chroma
  • evanspearman
  • norrietaylor

Poem

🐇 In session's home, the rabbit springs,
A working place, a cache to keep,
The min command whispers, brings
New packages in while systems sleep.
Bound mounts hold firm—the sandbox blooms! 🌱

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: session-specific sandbox wiring' directly and clearly describes the main change: introducing session-specific sandbox configuration with isolated file systems, home directories, and working directories.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

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

🧹 Nitpick comments (1)
crates/sandbox2/src/lib.rs (1)

224-240: 💤 Low value

Consider extracting a helper to derive the session working-directory name.

The pattern working_name_override.as_ref().cloned().unwrap_or_else(|| SESSION_DEFAULT_WD.to_string()) is repeated in three places (rootfs init, command_inner, and new_container). A small helper on WdSetup would centralize this logic and simplify call sites.

♻️ Example helper method
impl WdSetup {
    /// Returns the working directory name for Session layouts.
    /// Panics if called on non-Session variants.
    pub(crate) fn session_wd_name(&self) -> String {
        match self {
            Self::Session { working_name_override, .. } => {
                working_name_override
                    .clone()
                    .unwrap_or_else(|| SESSION_DEFAULT_WD.to_string())
            }
            _ => panic!("session_wd_name called on non-Session variant"),
        }
    }
}
🤖 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/sandbox2/src/lib.rs` around lines 224 - 240, The pattern for deriving
the session working-directory name
(working_name_override.as_ref().cloned().unwrap_or_else(||
SESSION_DEFAULT_WD.to_string())) is repeated in three places. Create a helper
method called session_wd_name on the WdSetup enum that encapsulates this logic
by matching on the Session variant and returning the working directory name.
Then replace all three occurrences of this pattern (in the rootfs init section
shown, in command_inner, and in new_container) with calls to this new helper
method to centralize and simplify the logic.
🤖 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.

Nitpick comments:
In `@crates/sandbox2/src/lib.rs`:
- Around line 224-240: The pattern for deriving the session working-directory
name (working_name_override.as_ref().cloned().unwrap_or_else(||
SESSION_DEFAULT_WD.to_string())) is repeated in three places. Create a helper
method called session_wd_name on the WdSetup enum that encapsulates this logic
by matching on the Session variant and returning the working directory name.
Then replace all three occurrences of this pattern (in the rootfs init section
shown, in command_inner, and in new_container) with calls to this new helper
method to centralize and simplify the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a35b5a84-e111-47e1-b312-20c9fb84c585

📥 Commits

Reviewing files that changed from the base of the PR and between ba1f04b and bbb79d1.

📒 Files selected for processing (2)
  • crates/sandbox2/src/config.rs
  • crates/sandbox2/src/lib.rs

Comment thread crates/sandbox2/src/config.rs
@twitchyliquid64 twitchyliquid64 changed the title feat(sandbox2): introduce sandboxing mode for sessions with own fs layout feat: session-specific sandbox wirigin Jun 16, 2026
@twitchyliquid64 twitchyliquid64 changed the title feat: session-specific sandbox wirigin feat: session-specific sandbox wiring Jun 16, 2026

@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: 8

🧹 Nitpick comments (5)
crates/minimald/src/session.rs (1)

34-38: 💤 Low value

Add common derives to SessionPaths.

The struct is public and would benefit from Debug and Clone derives for debugging and flexible usage patterns.

♻️ Suggested change
+#[derive(Debug, Clone)]
 pub struct SessionPaths {
     pub working: DaemonAbsPath,
     pub cache: DaemonAbsPath,
     pub home: DaemonAbsPath,
 }
🤖 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 34 - 38, Add the `Debug` and
`Clone` derives to the `SessionPaths` struct definition. Place a
`#[derive(Debug, Clone)]` attribute above the struct declaration to enable
debugging output and cloning of struct instances, which improves usability for a
public struct.
crates/sessions/src/store.rs (1)

106-120: 💤 Low value

Consider extracting the common base path computation.

All three path methods (workspace_path, home_path, cache_path) duplicate the base path computation. Additionally, DaemonRelPath::try_new(&self.key.dir_key) is called on self.key.dir_key which is already a DaemonRelPath (line 80), making the conversion redundant.

♻️ Suggested refactor to reduce duplication
 impl SessionObject for DiskSession {
     type Key = DiskSessionKey;

+    fn session_base(&self) -> DaemonAbsPath {
+        sub_path!(self.minimal_state_dir, "sessions").join(&self.key.dir_key)
+    }
+
     fn record(&self) -> &Record {
         &self.record
     }
     fn key(&self) -> &DiskSessionKey {
         &self.key
     }
     fn workspace_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "tree")
+        sub_path!(self.session_base(), "tree")
     }
     fn home_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "home")
+        sub_path!(self.session_base(), "home")
     }
     fn cache_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "cache")
+        sub_path!(self.session_base(), "cache")
     }
 }
🤖 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/sessions/src/store.rs` around lines 106 - 120, The methods
workspace_path, home_path, and cache_path contain duplicated code for computing
the base path using sub_path and DaemonRelPath::try_new. Extract this common
base path computation into a private helper method to eliminate duplication.
Additionally, since self.key.dir_key is already a DaemonRelPath (as shown on
line 80), remove the redundant DaemonRelPath::try_new wrapper call and directly
use self.key.dir_key in the path computation. Update all three methods to call
the new helper method instead of repeating the base path logic.
crates/minimald/src/env_min_helper.sh (1)

131-136: 💤 Low value

build and test subcommands ignore additional arguments.

min build foo and min test bar will ignore foo and bar respectively, running only min run build or min run test. If this is intentional for simplicity, consider adding a comment. Otherwise, forward the arguments.

Suggested fix if arguments should be forwarded
         build)
-            min_run build
+            min_run build "$@"
             ;;
         test)
-            min_run test
+            min_run test "$@"
             ;;
🤖 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/env_min_helper.sh` around lines 131 - 136, The build and
test case statements in the switch block are not forwarding additional
command-line arguments to the min_run function. In the build case and test case
blocks, modify the min_run calls to append the remaining arguments (using the
appropriate shell variable for all arguments passed after the subcommand) so
that commands like `min build foo` will properly forward `foo` to min_run
instead of discarding it.
crates/minimald/src/env.rs (2)

410-455: ⚖️ Poor tradeoff

Consider reducing repetition in add- command handlers.*

The four add-* branches share nearly identical structure: parse packages, handle errors, call install(), then return the mode. This could be extracted into a helper, though the current implementation is clear and functional.

🤖 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/env.rs` around lines 410 - 455, Extract the repeated
logic from the four add-* command handlers (add-session, add-build, add-runtime,
add-task) into a helper method. The common pattern in each branch is: parse
packages using parse_pkgs_line, handle errors with the same error message
format, call install, and return a result. Create a helper method that accepts
the package string and a closure or enum variant to construct the appropriate
AddDepMode result, then replace each of the four match arms with a call to this
helper to eliminate the duplication while preserving the unique AddDepMode
construction logic for each command type.

254-258: 💤 Low value

Chained .unwrap() calls on path conversion could panic.

If sandbox.rootfs() returns a path that isn't valid UTF-8, the Utf8PathBuf::try_from(...).unwrap() will panic. While sandbox rootfs paths are typically UTF-8, consider using expect() with a descriptive message or propagating the error for robustness.

Suggested improvement
         let channel = SessionChannel {
-            rootfs: DaemonAbsPath::try_new(
-                Utf8PathBuf::try_from(sandbox.rootfs().to_path_buf()).unwrap(),
-            )
-            .unwrap(),
+            rootfs: DaemonAbsPath::try_new(
+                Utf8PathBuf::try_from(sandbox.rootfs().to_path_buf())
+                    .expect("sandbox rootfs must be valid UTF-8"),
+            )
+            .expect("sandbox rootfs must be absolute"),
🤖 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/env.rs` around lines 254 - 258, Replace the chained
`.unwrap()` calls in the SessionChannel initialization (specifically in the
rootfs field where DaemonAbsPath::try_new and Utf8PathBuf::try_from are used)
with `.expect()` calls that include descriptive error messages. The inner
`.unwrap()` from Utf8PathBuf::try_from should explain that the sandbox rootfs
path must be valid UTF-8, and the outer `.unwrap()` from DaemonAbsPath::try_new
should explain that the rootfs path must be an absolute path. This provides
better debugging information if the conversions fail instead of a generic panic
message.
🤖 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/env_min_helper.sh`:
- Around line 143-153: The usage text in the default case block contains a
duplicate echo statement for "Check minimal configuration: min check". Remove
the second occurrence of this duplicate echo line that appears after the "min
run <task name>" line to eliminate the redundant instruction text in the usage
output.
- Line 69: The variable assignment in the packages variable declaration uses
"$@" which will cause word splitting when assigning to a string variable.
Replace "$@" with "$*" in the local packages assignment to properly concatenate
all arguments into a single string variable, ensuring the arguments are treated
as a single concatenated value rather than being split into separate elements.
- Line 58: The condition in the if statement is checking the same variable
`$pkg` twice with `[[ -z "$pkg" || -z "$pkg" ]]`, which is redundant. Replace
the second occurrence of `$pkg` with the appropriate second variable that should
be validated in this condition, so that the logic properly checks if either of
two distinct variables are empty rather than repeating the same check.
- Line 48: The conditional check at line 48 in env_min_helper.sh contains a
redundant condition where the variable $term is checked twice with -z "$term" ||
-z "$term". Remove the duplicate check by keeping only a single -z "$term"
condition, or if the second condition was intended to check a different
variable, replace the second -z "$term" with the correct variable that should be
validated. This appears to be a copy-paste artifact that should be corrected.
- Line 6: In the env_min_helper.sh file, the variable assignment for data is
using "$@" which loses word boundaries when assigned to a scalar variable.
Change the assignment from local data="$@" to local data="$*" so that the
arguments are properly concatenated with spaces as a single string, which is
what is needed when the data variable is used as a single string sent to the
RPC.

In `@crates/minimald/src/env.rs`:
- Around line 869-874: The poll_shutdown method currently returns
Poll::Ready(Ok(())) without flushing any remaining data in self.buf, causing
data without a trailing newline to be silently discarded on shutdown. Modify the
poll_shutdown method to check if self.buf contains any remaining data and flush
it to the output before returning Poll::Ready(Ok(())), ensuring no buffered
content is lost during shutdown even if it lacks a trailing newline.

In `@crates/minimald/src/rpc.rs`:
- Around line 193-199: The async_tar::Archive::unpack() method is vulnerable to
CVE-2025-62518 "TARmageddon" which allows path traversal attacks on untrusted
tarball data from SSH clients. Replace the async_tar crate with a maintained,
vulnerability-free tar library alternative, or if async_tar must remain,
implement strict multi-layered validation before unpacking that includes path
allowlisting, rejecting symlinks, verifying file ownership and permissions, and
isolating the extraction directory at the OS level to prevent arbitrary file
writes.

In `@crates/minimald/src/session.rs`:
- Around line 72-74: The three create_dir_all() calls for
session.workspace_path(), session.home_path(), and session.cache_path() use
unwrap() which will panic if directory creation fails. Since Session::run
already returns a Result<SessionHandle, std::io::Error>, replace each unwrap()
with the ? operator to propagate the errors instead of panicking, allowing the
caller to handle them appropriately.

---

Nitpick comments:
In `@crates/minimald/src/env_min_helper.sh`:
- Around line 131-136: The build and test case statements in the switch block
are not forwarding additional command-line arguments to the min_run function. In
the build case and test case blocks, modify the min_run calls to append the
remaining arguments (using the appropriate shell variable for all arguments
passed after the subcommand) so that commands like `min build foo` will properly
forward `foo` to min_run instead of discarding it.

In `@crates/minimald/src/env.rs`:
- Around line 410-455: Extract the repeated logic from the four add-* command
handlers (add-session, add-build, add-runtime, add-task) into a helper method.
The common pattern in each branch is: parse packages using parse_pkgs_line,
handle errors with the same error message format, call install, and return a
result. Create a helper method that accepts the package string and a closure or
enum variant to construct the appropriate AddDepMode result, then replace each
of the four match arms with a call to this helper to eliminate the duplication
while preserving the unique AddDepMode construction logic for each command type.
- Around line 254-258: Replace the chained `.unwrap()` calls in the
SessionChannel initialization (specifically in the rootfs field where
DaemonAbsPath::try_new and Utf8PathBuf::try_from are used) with `.expect()`
calls that include descriptive error messages. The inner `.unwrap()` from
Utf8PathBuf::try_from should explain that the sandbox rootfs path must be valid
UTF-8, and the outer `.unwrap()` from DaemonAbsPath::try_new should explain that
the rootfs path must be an absolute path. This provides better debugging
information if the conversions fail instead of a generic panic message.

In `@crates/minimald/src/session.rs`:
- Around line 34-38: Add the `Debug` and `Clone` derives to the `SessionPaths`
struct definition. Place a `#[derive(Debug, Clone)]` attribute above the struct
declaration to enable debugging output and cloning of struct instances, which
improves usability for a public struct.

In `@crates/sessions/src/store.rs`:
- Around line 106-120: The methods workspace_path, home_path, and cache_path
contain duplicated code for computing the base path using sub_path and
DaemonRelPath::try_new. Extract this common base path computation into a private
helper method to eliminate duplication. Additionally, since self.key.dir_key is
already a DaemonRelPath (as shown on line 80), remove the redundant
DaemonRelPath::try_new wrapper call and directly use self.key.dir_key in the
path computation. Update all three methods to call the new helper method instead
of repeating the base path logic.
🪄 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: fecded67-7202-4c3a-854a-c142bf624a9a

📥 Commits

Reviewing files that changed from the base of the PR and between bbb79d1 and 5ea04ef.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • crates/minimald/Cargo.toml
  • crates/minimald/src/env.rs
  • crates/minimald/src/env_min_helper.sh
  • crates/minimald/src/exec.rs
  • crates/minimald/src/lib.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_host.rs
  • crates/minimald/src/sftp.rs
  • crates/sandbox2/src/config.rs
  • crates/sandbox2/src/lib.rs
  • crates/sessions/src/store.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/minimald/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/sandbox2/src/config.rs
  • crates/sandbox2/src/lib.rs

Comment thread crates/minimald/src/env_min_helper.sh
Comment thread crates/minimald/src/env_min_helper.sh Outdated
Comment thread crates/minimald/src/env_min_helper.sh Outdated
Comment thread crates/minimald/src/env_min_helper.sh
Comment thread crates/minimald/src/env_min_helper.sh
Comment on lines +869 to +874
fn poll_shutdown(
self: Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

poll_shutdown does not flush partial lines remaining in the buffer.

If the writer is shut down while self.buf contains data without a trailing newline, that data is silently discarded. Consider flushing any remaining buffer content (even without a trailing newline) in poll_shutdown.

Suggested fix
     fn poll_shutdown(
-        self: Pin<&mut Self>,
+        mut self: Pin<&mut Self>,
         _cx: &mut std::task::Context<'_>,
     ) -> Poll<Result<(), std::io::Error>> {
+        if !self.buf.is_empty() {
+            let line = String::from_utf8_lossy(&self.buf);
+            let _ = writeln!(self.stream.lock().unwrap(), "msg:{line}");
+            self.buf.clear();
+        }
         Poll::Ready(Ok(()))
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn poll_shutdown(
self: Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
if !self.buf.is_empty() {
let line = String::from_utf8_lossy(&self.buf);
let _ = writeln!(self.stream.lock().unwrap(), "msg:{line}");
self.buf.clear();
}
Poll::Ready(Ok(()))
}
🤖 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/env.rs` around lines 869 - 874, The poll_shutdown method
currently returns Poll::Ready(Ok(())) without flushing any remaining data in
self.buf, causing data without a trailing newline to be silently discarded on
shutdown. Modify the poll_shutdown method to check if self.buf contains any
remaining data and flush it to the output before returning Poll::Ready(Ok(())),
ensuring no buffered content is lost during shutdown even if it lacks a trailing
newline.

Comment thread crates/minimald/src/rpc.rs
Comment thread crates/minimald/src/session.rs Outdated
Comment thread crates/minimald/src/env.rs Outdated
Comment thread crates/minimald/src/env.rs Outdated
Comment thread crates/minimald/src/env.rs Outdated
Comment thread crates/minimald/src/env.rs
Comment thread crates/minimald/src/env_min_helper.sh Outdated

@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

🧹 Nitpick comments (2)
crates/sessions/src/store.rs (2)

123-137: 💤 Low value

Consider extracting the common base path computation.

The base path calculation is duplicated across workspace_path, home_path, and cache_path. A private helper would reduce duplication.

♻️ Suggested refactor
+    fn session_base_path(&self) -> DaemonAbsPath {
+        sub_path!(self.minimal_state_dir, "sessions")
+            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap())
+    }
+
     fn workspace_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "tree")
+        sub_path!(self.session_base_path(), "tree")
     }
     fn home_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "home")
+        sub_path!(self.session_base_path(), "home")
     }
     fn cache_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "cache")
+        sub_path!(self.session_base_path(), "cache")
     }
🤖 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/sessions/src/store.rs` around lines 123 - 137, The base path
computation is duplicated identically across the workspace_path, home_path, and
cache_path methods. Extract this common computation into a private helper method
that returns the base DaemonAbsPath (which combines self.minimal_state_dir,
"sessions", and the dir_key). Then refactor each of the three methods to call
this helper instead of duplicating the base path calculation logic.

240-240: 💤 Low value

Method signature could be tightened.

write_record doesn't mutate self, so &self would suffice. Also, short: &str is more idiomatic than short: &String.

♻️ Suggested signature
-    fn write_record(&mut self, short: &String, record: &Record) -> Result<(), std::io::Error> {
+    fn write_record(&self, short: &str, record: &Record) -> Result<(), std::io::Error> {
🤖 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/sessions/src/store.rs` at line 240, The write_record method signature
has two issues that can be tightened. First, change the receiver from &mut self
to &self since the method does not actually mutate self. Second, change the
parameter short from &String to &str, which is the more idiomatic Rust pattern
for accepting string data. These changes make the API clearer about the method's
intent and follow Rust conventions.
🤖 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/sessions/src/store.rs`:
- Around line 340-360: The in-memory index mutations are happening before the
disk writes are guaranteed to succeed, creating a risk of inconsistent state if
a write fails. In the rename function, the removal of the old name from
self.index.name_to_id (before write_record) and the insertion of the new name
into self.index.name_to_id (before flush_index) should be deferred until after
both write_record and flush_index have completed successfully. Reorder the
operations so that all in-memory index updates happen only after the disk
operations succeed to ensure consistency.

---

Nitpick comments:
In `@crates/sessions/src/store.rs`:
- Around line 123-137: The base path computation is duplicated identically
across the workspace_path, home_path, and cache_path methods. Extract this
common computation into a private helper method that returns the base
DaemonAbsPath (which combines self.minimal_state_dir, "sessions", and the
dir_key). Then refactor each of the three methods to call this helper instead of
duplicating the base path calculation logic.
- Line 240: The write_record method signature has two issues that can be
tightened. First, change the receiver from &mut self to &self since the method
does not actually mutate self. Second, change the parameter short from &String
to &str, which is the more idiomatic Rust pattern for accepting string data.
These changes make the API clearer about the method's intent and follow Rust
conventions.
🪄 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: c829c1e3-bfbb-4481-a244-170ca6670034

📥 Commits

Reviewing files that changed from the base of the PR and between 09677fe and e6974b5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/minimald/Cargo.toml
  • crates/minimald/src/connection.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/env_min_helper.sh
  • crates/minimald/src/exec.rs
  • crates/minimald/src/lib.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_host.rs
  • crates/minimald/src/sftp.rs
  • crates/sandbox2/src/config.rs
  • crates/sandbox2/src/lib.rs
  • crates/sessions/src/store.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/minimald/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/minimald/src/sftp.rs
  • crates/minimald/src/connection.rs
  • crates/minimald/src/exec.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/sandbox2/src/lib.rs
  • crates/minimald/src/session_host.rs
  • crates/sandbox2/src/config.rs
  • crates/minimald/src/env.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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/sessions/src/store.rs (2)

123-137: 💤 Low value

Consider extracting the common base path computation.

The base path calculation is duplicated across workspace_path, home_path, and cache_path. A private helper would reduce duplication.

♻️ Suggested refactor
+    fn session_base_path(&self) -> DaemonAbsPath {
+        sub_path!(self.minimal_state_dir, "sessions")
+            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap())
+    }
+
     fn workspace_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "tree")
+        sub_path!(self.session_base_path(), "tree")
     }
     fn home_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "home")
+        sub_path!(self.session_base_path(), "home")
     }
     fn cache_path(&self) -> DaemonAbsPath {
-        let base = sub_path!(self.minimal_state_dir, "sessions")
-            .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap());
-        sub_path!(base, "cache")
+        sub_path!(self.session_base_path(), "cache")
     }
🤖 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/sessions/src/store.rs` around lines 123 - 137, The base path
computation is duplicated identically across the workspace_path, home_path, and
cache_path methods. Extract this common computation into a private helper method
that returns the base DaemonAbsPath (which combines self.minimal_state_dir,
"sessions", and the dir_key). Then refactor each of the three methods to call
this helper instead of duplicating the base path calculation logic.

240-240: 💤 Low value

Method signature could be tightened.

write_record doesn't mutate self, so &self would suffice. Also, short: &str is more idiomatic than short: &String.

♻️ Suggested signature
-    fn write_record(&mut self, short: &String, record: &Record) -> Result<(), std::io::Error> {
+    fn write_record(&self, short: &str, record: &Record) -> Result<(), std::io::Error> {
🤖 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/sessions/src/store.rs` at line 240, The write_record method signature
has two issues that can be tightened. First, change the receiver from &mut self
to &self since the method does not actually mutate self. Second, change the
parameter short from &String to &str, which is the more idiomatic Rust pattern
for accepting string data. These changes make the API clearer about the method's
intent and follow Rust conventions.
🤖 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/sessions/src/store.rs`:
- Around line 340-360: The in-memory index mutations are happening before the
disk writes are guaranteed to succeed, creating a risk of inconsistent state if
a write fails. In the rename function, the removal of the old name from
self.index.name_to_id (before write_record) and the insertion of the new name
into self.index.name_to_id (before flush_index) should be deferred until after
both write_record and flush_index have completed successfully. Reorder the
operations so that all in-memory index updates happen only after the disk
operations succeed to ensure consistency.

---

Nitpick comments:
In `@crates/sessions/src/store.rs`:
- Around line 123-137: The base path computation is duplicated identically
across the workspace_path, home_path, and cache_path methods. Extract this
common computation into a private helper method that returns the base
DaemonAbsPath (which combines self.minimal_state_dir, "sessions", and the
dir_key). Then refactor each of the three methods to call this helper instead of
duplicating the base path calculation logic.
- Line 240: The write_record method signature has two issues that can be
tightened. First, change the receiver from &mut self to &self since the method
does not actually mutate self. Second, change the parameter short from &String
to &str, which is the more idiomatic Rust pattern for accepting string data.
These changes make the API clearer about the method's intent and follow Rust
conventions.
🪄 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: c829c1e3-bfbb-4481-a244-170ca6670034

📥 Commits

Reviewing files that changed from the base of the PR and between 09677fe and e6974b5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/minimald/Cargo.toml
  • crates/minimald/src/connection.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/env_min_helper.sh
  • crates/minimald/src/exec.rs
  • crates/minimald/src/lib.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/minimald/src/session_host.rs
  • crates/minimald/src/sftp.rs
  • crates/sandbox2/src/config.rs
  • crates/sandbox2/src/lib.rs
  • crates/sessions/src/store.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/minimald/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/minimald/src/sftp.rs
  • crates/minimald/src/connection.rs
  • crates/minimald/src/exec.rs
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/session.rs
  • crates/sandbox2/src/lib.rs
  • crates/minimald/src/session_host.rs
  • crates/sandbox2/src/config.rs
  • crates/minimald/src/env.rs
🛑 Comments failed to post (1)
crates/sessions/src/store.rs (1)

340-360: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

In-memory index mutation before disk write risks inconsistent state on failure.

If write_record fails after line 351 removes the old name from the in-memory index, the process retains a corrupted view where the old name is gone but the rename never completed on disk. Defer in-memory mutations until after both disk writes succeed.

🐛 Proposed fix to reorder operations
 fn rename(&mut self, key: &Self::Key, new_name: String) -> Result<(), std::io::Error> {
     if self.index.find_by_name(&new_name).is_some() {
         return Err(std::io::Error::new(
             AlreadyExists,
             format!("a session with the name `{new_name}` already exists"),
         ));
     }

     let mut obj = self.get(key)?;
     let short = obj.key.dir_key.to_string();
-    if let Some(old_name) = &obj.record.name {
-        self.index.name_to_id.remove(old_name);
-    }
+    let old_name = obj.record.name.clone();

     obj.record.name = Some(new_name.clone());
     self.write_record(&short, &obj.record)?;

+    // Only mutate in-memory index after disk writes succeed
+    if let Some(old_name) = &old_name {
+        self.index.name_to_id.remove(old_name);
+    }
     self.index.name_to_id.insert(new_name, obj.record.id);
     self.flush_index()?;
     Ok(())
 }
🤖 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/sessions/src/store.rs` around lines 340 - 360, The in-memory index
mutations are happening before the disk writes are guaranteed to succeed,
creating a risk of inconsistent state if a write fails. In the rename function,
the removal of the old name from self.index.name_to_id (before write_record) and
the insertion of the new name into self.index.name_to_id (before flush_index)
should be deferred until after both write_record and flush_index have completed
successfully. Reorder the operations so that all in-memory index updates happen
only after the disk operations succeed to ensure consistency.

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.

3 participants