Minimald scaffolding, including sessions actor, session actor, session store + generics, and RPC tests - #253
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughAdds Linux renameat2 syscall wrapper and workspace libc wiring, implements a workspace sessions crate with disk-backed loader and path helpers, introduces session actor/manager in minimald, refactors RPCs to async typed handlers with dispatcher, integrates sessions into ServerState, and adds an end-to-end RPC test harness. ChangesSessions system and renameat2 abstraction
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/minimald/src/main.rs (1)
204-209:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPropagate server error instead of unwrapping.
The
.unwrap()on line 209 will panic on server errors without context. Consider propagating viaMainErroror at minimum usingexpect().🛡️ Suggested fix
match cli.command { Command::Completions(_) => unreachable!(), Command::Run(_) => Server::run_on_uds(config, listener), } .await - .unwrap(); + .expect("server exited unexpectedly");Or better, extend
MainErrorto wrap server errors and use?.🤖 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/main.rs` around lines 204 - 209, The match arm currently awaiting Server::run_on_uds(config, listener).await.unwrap() will panic on errors; replace the unwrap with proper error propagation by returning the Result from Server::run_on_uds and using the ? operator or mapping the error into your MainError type (e.g., Server::run_on_uds(...).await.map_err(MainError::from)?), so that failures from Server::run_on_uds are converted into MainError and propagated up instead of panicking; update the call site handling of cli.command / Command::Run accordingly.
🧹 Nitpick comments (13)
crates/minimald/src/main.rs (3)
28-49: ⚡ Quick winReplace
unwrap()calls withexpect("reason")to document invariants.Multiple
unwrap()calls in path construction can obscure why they're safe. Per guidelines, useexpect("why the invariant holds")to document the assumptions:
- Line 32:
try_newon an absolute path (validated byis_absolute()check)- Line 35:
from_cwd()can fail if CWD is invalid or non-UTF8- Line 36:
try_newon a relative path (validated by!is_absolute()branch)- Lines 40-47:
from_path_buffails on non-UTF8 paths;try_newvalidates absoluteness♻️ Suggested improvements
Some(d) => { if d.is_absolute() { - DaemonAbsPath::try_new(d.clone()).unwrap() + DaemonAbsPath::try_new(d.clone()).expect("validated absolute above") } else { DaemonAbsPath::from_cwd() - .unwrap() - .join(&DaemonRelPath::try_new(d).unwrap()) + .expect("CWD should be valid UTF-8 path") + .join(&DaemonRelPath::try_new(d).expect("validated relative above")) } } None => DaemonAbsPath::try_new( Utf8PathBuf::from_path_buf( dirs::state_dir() .unwrap_or_else(|| PathBuf::from("~/.local/state")) .join("minimal"), ) - .unwrap(), + .expect("XDG state dir should be valid UTF-8"), ) - .unwrap(), + .expect("XDG state dir is absolute"),As per coding guidelines: "Only use
unwrap()andpanic!()for broken invariants; in production code useexpect("why the invariant holds")instead of unwrap".🤖 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/main.rs` around lines 28 - 49, In minimal_state_dir, replace the bare unwrap() calls with expect("...") that document the invariant for each use: for DaemonAbsPath::try_new(d.clone()) inside the is_absolute() branch use expect("absolute path validated by is_absolute()"), for DaemonAbsPath::from_cwd() use expect("current working directory must be valid UTF-8"), for DaemonRelPath::try_new(d) in the relative branch use expect("relative path validated by !is_absolute()"), and for Utf8PathBuf::from_path_buf(...) and the outer DaemonAbsPath::try_new(...) in the None branch add expect messages like expect("state dir path is valid UTF-8") and expect("constructed state dir is absolute") respectively so each panic documents the assumed invariant.
53-63: 💤 Low valueSame
unwrap()→expect()improvement needed here.Apply the same pattern as
minimal_state_dir()to document why these invariants hold.♻️ Suggested fix
pub fn minimal_cache_dir(&self) -> DaemonAbsPath { DaemonAbsPath::try_new( Utf8PathBuf::from_path_buf( dirs::cache_dir() .unwrap_or_else(|| PathBuf::from("~/.local/cache")) .join("minimal"), ) - .unwrap(), + .expect("XDG cache dir should be valid UTF-8"), ) - .unwrap() + .expect("XDG cache dir is 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/main.rs` around lines 53 - 63, Replace the chained unwraps in minimal_cache_dir with explicit expect messages mirroring minimal_state_dir: when calling dirs::cache_dir().unwrap_or_else(...) and when converting to Utf8PathBuf::from_path_buf(...).unwrap() and DaemonAbsPath::try_new(...).unwrap(), use expect(...) with short explanations (e.g., why a fallback path exists and why the path must be valid UTF‑8 / absolute) so each invariant is documented; locate the minimal_cache_dir function and update the unwraps on dirs::cache_dir()/Utf8PathBuf::from_path_buf(...) and DaemonAbsPath::try_new(...) to expect(...) with descriptive messages.
182-186: 💤 Low valueUse structured logging fields instead of string interpolation.
Line 182 should use structured fields for machine-parseable logs. The multi-line debug message on lines 183-186 is fine as-is since it's user-facing guidance.
♻️ Suggested fix
- tracing::info!("Started listening on {}", cli.listen_on()); + tracing::info!(path = %cli.listen_on(), "Started listening");As per coding guidelines: "Use structured logging with tracing:
tracing::info!(pkg = %name, "building")instead of interpolated strings".🤖 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/main.rs` around lines 182 - 186, Replace the interpolated log with a structured tracing field: change the call that uses tracing::info!("Started listening on {}", cli.listen_on()) to use a named field such as tracing::info!(listen_on = %cli.listen_on(), "Started listening on"); leave the multi-line ssh guidance block (the tracing::info! that prints the debug command) unchanged.crates/sessions/src/lib.rs (1)
19-38: ⚡ Quick winMark
Recordas#[non_exhaustive]to keep API evolution non-breaking.Given this is a public record type, future field additions are likely.
Suggested patch
#[derive(Debug, Clone, Serialize, Deserialize)] +#[non_exhaustive] pub struct Record {As per coding guidelines, "Apply
#[non_exhaustive]to public enums and structs that may grow".🤖 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/lib.rs` around lines 19 - 38, Public struct Record should be marked #[non_exhaustive] so adding fields later won't be a breaking change; add the #[non_exhaustive] attribute immediately above the Record definition (e.g., above #[derive(...)] for Record) and ensure any external pattern matches using Record are updated to use a wildcard (_) or the struct update pattern where appropriate (since consumers will now need to account for possible future fields).crates/common/src/renameat2.rs (1)
75-84: ⚡ Quick win
test_rename_noreplacedoes not exercise the NOREPLACE failure path.The current test only validates rename-to-missing-target. To verify
RENAME_NOREPLACE, pre-createnewand assertAlreadyExists.Suggested patch
fn test_rename_noreplace() { let tmp = TempDir::new().unwrap(); let old = tmp.path().join("old"); let new = tmp.path().join("new"); fs::write(&old, "test").unwrap(); + fs::write(&new, "existing").unwrap(); - renameat2_cwd(&old, &new, RENAME_NOREPLACE).unwrap(); - assert!(new.exists()); + let err = renameat2_cwd(&old, &new, RENAME_NOREPLACE).unwrap_err(); + assert_eq!(err.kind(), std::io::ErrorKind::AlreadyExists); + assert_eq!(fs::read_to_string(&new).unwrap(), "existing"); }As per coding guidelines, "Write tests that verify behavior, not implementation; one concept per 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/common/src/renameat2.rs` around lines 75 - 84, The test test_rename_noreplace currently only verifies a successful rename to a missing target; update it to also exercise the NOREPLACE failure path by pre-creating the destination path (new) before calling renameat2_cwd(&old, &new, RENAME_NOREPLACE) and assert that the call returns an AlreadyExists (or equivalent) error; ensure the test explicitly checks the error kind rather than implementation details so both success and failure behaviors of RENAME_NOREPLACE are covered by separate assertions in the same test.crates/sessions/src/store.rs (1)
113-118: ⚡ Quick winUse
expectwith invariant explanation instead ofunwrap.Per coding guidelines,
unwrap()should be replaced withexpect("why the invariant holds")for broken invariants.Proposed fix
fn workspace_path(&self) -> DaemonAbsPath { self.minimal_state_dir .sub_path("sessions") - .join(&DaemonRelPath::try_new(&self.key.dir_key).unwrap()) + .join(&DaemonRelPath::try_new(&self.key.dir_key) + .expect("dir_key is always a valid relative path component")) .sub_path("tree") }As per coding guidelines: "Only use
unwrap()andpanic!()for broken invariants; in production code useexpect("why the invariant holds")instead of unwrap".🤖 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 113 - 118, In workspace_path(), replace the call to DaemonRelPath::try_new(&self.key.dir_key).unwrap() with .expect("explain why dir_key must be a valid DaemonRelPath here") so that the invariant failure produces a clear message; update the expect string to succinctly state the invariant (e.g., "dir_key was validated on construction and must be a valid DaemonRelPath") referencing workspace_path and DaemonRelPath::try_new to locate the change.crates/sessions/src/paths.rs (1)
197-218: 💤 Low valueCross-platform path separator check may be incomplete.
On Windows, both
\and/are valid path separators, butMAIN_SEPARATOR_STRis only\. A caller could pass"foo/bar"on Windows and bypass the assertion. If this crate is Linux-only (implied by therenameat2usage elsewhere), consider documenting that assumption or adding a/check for defense-in-depth.Proposed fix to check both separators
pub fn sub_path(&self, dir: &'static str) -> AbsPath<R> { assert!( - !dir.contains(std::path::MAIN_SEPARATOR_STR), - ".subdir(\"{dir}\") contains path separators" + !dir.contains(std::path::MAIN_SEPARATOR_STR) && !dir.contains('/'), + ".sub_path(\"{dir}\") contains path separators" ); assert!(dir != "..", ".subdir(\"..\") attempts path traversal");🤖 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/paths.rs` around lines 197 - 218, The assertion in sub_path does not catch Windows-style '/' separators because it only checks std::path::MAIN_SEPARATOR_STR; update the validation in fn sub_path to reject both '/' and the platform MAIN_SEPARATOR (or explicitly check dir.contains('/') || dir.contains(std::path::MAIN_SEPARATOR_STR)) and keep the existing ".." check and panic message; make sure the error message remains clear and references sub_path/subdir usage so callers see why the input was rejected.crates/minimald/src/rpc.rs (4)
92-94: 💤 Low valueUse structured logging fields instead of format interpolation.
As per coding guidelines, use structured fields with tracing:
tracing::warn!(rpc = Self::NAME, error = %e, "RPC handler failed")instead of interpolated format strings.♻️ Suggested fix
- tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e); + tracing::warn!(rpc = Self::NAME, error = %e, "RPC handler failed");🤖 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/rpc.rs` around lines 92 - 94, Replace the interpolated log in the error branch with structured tracing fields: in the RPC error handling where you currently use tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e), change it to use structured fields (e.g. tracing::warn!(rpc = Self::NAME, error = %e, "RPC handler failed")) so the RPC name and error are separate fields; update the call in the same block that references Self::NAME and variable e.
236-241: 💤 Low valueSpawned task handles are silently dropped.
The
JoinHandlereturned byspawnis discarded. If a handler panics, it won't be observed. Consider either:
- Adding a brief comment explaining this is intentional fire-and-forget
- Collecting handles for observability
Since handlers log their own errors, this is likely intentional but warrants a comment.
🤖 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/rpc.rs` around lines 236 - 241, The spawned JoinHandle returned by spawn for GetVersion.handle, ListSessions.handle and GetSessionRecord.handle is currently discarded which hides panics; either explicitly mark this as intentional fire-and-forget by adding a concise comment above the match stating that handlers own their error logging and panics are acceptable/ignored, or collect the JoinHandles (e.g., push them into a Vec<JoinHandle<...>> or an observability task registry) so panics can be awaited/inspected; update the match site accordingly and reference the spawn calls and the individual handler entry points (GetVersion.handle, ListSessions.handle, GetSessionRecord.handle) when making the change.
138-140: 💤 Low valueSame structured logging improvement applies here.
♻️ Suggested fix
- tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e); + tracing::warn!(rpc = Self::NAME, error = %e, "RPC handler failed");🤖 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/rpc.rs` around lines 138 - 140, Replace the string-formatted warning in the RPC error branch with structured tracing fields: when checking the result (if let Err(e) = res) log using tracing::warn! with named fields for the handler name (Self::NAME) and the error (e) instead of interpolating them into the message so the log system can index them; update the log invocation in the block that currently contains tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e) to use structured fields like handler = Self::NAME and error = ?e (or %e) and a short message such as "RPC handler failed".
188-190: 💤 Low valueSame structured logging improvement applies here.
♻️ Suggested fix
- tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e); + tracing::warn!(rpc = Self::NAME, error = %e, "RPC handler failed");🤖 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/rpc.rs` around lines 188 - 190, The current warn log in the error branch of the RPC handler uses string interpolation which loses structured fields; change the logging in the block that checks if let Err(e) = res (the code referencing Self::NAME) to use structured tracing fields instead — e.g., pass the handler name and the error as separate fields (handler = Self::NAME, error = %e) in the tracing::warn! call so the error is recorded as a structured field rather than embedded in a formatted message.crates/minimald/src/connection.rs (1)
364-367: ⚡ Quick winInconsistent lock access pattern.
This method still uses
self.0.0.lock()while other methods now useself.0.lock(). Use the new helper for consistency.♻️ Suggested fix
async fn channel_close(&mut self, id: ChannelId, _: &mut Session) -> Result<(), Self::Error> { protocol_trace!("Got channel_close on channel {id}"); - self.0.0.lock().await.handle_channel_close(id) + self.0.lock().await.handle_channel_close(id) }🤖 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/connection.rs` around lines 364 - 367, In channel_close, the lock is acquired via the old field path self.0.0.lock().await; update it to use the new helper field by calling self.0.lock().await and then invoke handle_channel_close(id) on the locked value so the method mirrors the other methods' access pattern (refer to function channel_close and the helper accessor self.0).crates/minimald/src/session.rs (1)
50-57: 💤 Low valueImprove error discard justification placement.
The comment on line 54 explains the discard but would be clearer directly on line 55 where the discard occurs.
♻️ Suggested fix
pub async fn workspace_path(&self) -> DaemonAbsPath { let (send, recv) = oneshot::channel(); - // Ignore send errors - the recv will also fail. - let _ = self.0.send(SessionMessage::GetWorkspacePath(send)).await; + let _ = self.0.send(SessionMessage::GetWorkspacePath(send)).await; // If send fails, recv below will also fail. recv.await.expect("corresponding session is dead") }🤖 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 50 - 57, Move the explanatory comment about ignoring send errors so it sits directly above the discard statement in SessionHandle::workspace_path (i.e., immediately before the `_ = self.0.send(SessionMessage::GetWorkspacePath(send)).await;` line) rather than earlier; keep the comment text but relocate it to clarify that we intentionally ignore send errors because the corresponding recv will also fail when the session is dead.
🤖 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/common/src/renameat2.rs`:
- Around line 39-48: Add a `// SAFETY:` comment immediately before the unsafe
syscall block that calls libc::syscall(SYS_RENAMEAT2, olddirfd,
oldpath_c.as_ptr(), newdirfd, newpath_c.as_ptr(), flags) documenting that the C
strings (oldpath_c, newpath_c) are valid NUL-terminated pointers,
olddirfd/newdirfd are valid fds or AT_FDCWD, flags are valid bitflags for
renameat2, and that the syscall returns -1 with errno on failure or a
non-negative value on success; also update the `test_rename_noreplace` test to
create the destination file (`new`) before invoking renameat2 with
RENAME_NOREPLACE, assert the syscall fails (checks returned error/errno) and
verify that `new` was not overwritten (its contents remain unchanged) so the
test covers the NOREPLACE behavior.
In `@crates/lcache/src/lib.rs`:
- Line 4: The import and use of renameat2 (along with AT_FDCWD and
RENAME_EXCHANGE) must be guarded by #[cfg(target_os = "linux")] to match the
export in crates/common; wrap the use statement for
common::renameat2::{AT_FDCWD, RENAME_EXCHANGE, renameat2} in #[cfg(target_os =
"linux")] and likewise wrap the call site that invokes renameat2 (the call
around line ~103) in a #[cfg(target_os = "linux")] block; provide a non-Linux
alternative branch (e.g., #[cfg(not(target_os = "linux"))] that returns a clear
error, no-op, or uses a fallback implementation) so non-Linux builds compile and
behavior is well-defined.
In `@crates/minimald/src/connection.rs`:
- Line 228: The Error impl for ConnectionError should expose its inner cause via
source() so downstream code can traverse the error chain; update the impl
std::error::Error for ConnectionError to implement fn source(&self) ->
Option<&(dyn std::error::Error + 'static)> that matches on ConnectionError's
variants/fields and returns a reference to the wrapped error (or None for
variants without an inner error), ensuring the wrapped error types are returned
as trait objects to preserve chaining.
- Around line 181-185: The return type impl Future in ConnectionHandle::lock
isn't in scope; add the missing import by either adding use std::future::Future;
at the top of the file or fully-qualifying the return type (e.g., ->
std::future::Future<Output = MutexGuard<'_, Connection>>) so the compiler can
resolve Future for the ConnectionHandle::lock function.
In `@crates/minimald/src/rpc.rs`:
- Around line 38-41: The generic bound on handle_channel uses the nightly-only
AsyncFnOnce trait; change its handler bound to a stable Future-based form:
replace F: for<'a> AsyncFnOnce(Self::Request<'a>) -> Result<Self::Response,
ConnectionError> with a FnOnce that returns an associated future (e.g. F:
for<'a> FnOnce(Self::Request<'a>) -> Fut where Fut: Future<Output =
Result<Self::Response, ConnectionError>>), or accept a boxed future to simplify
types; update the function signature and any call-sites using handle_channel /
the handler to await the returned Fut accordingly (keeping types RuChannel<Msg>,
Self::Request, Self::Response and ConnectionError).
In `@crates/minimald/src/session.rs`:
- Around line 35-43: The async handler handle_message matches
SessionMessage::GetWorkspacePath and currently calls the blocking
std::fs::create_dir_all(...).unwrap(); replace that call with the non-blocking
tokio::fs::create_dir_all(self.session.workspace_path()).await and propagate or
log the error instead of unwrapping (e.g., map_err/log and return or respond
with an error). Also leave the r.send(wsp) call but add a brief comment noting
it's safe to ignore the Result because the receiver may have been dropped, or
handle the Err path if you want to log it; reference the handle_message
function, SessionMessage::GetWorkspacePath branch, session.workspace_path(), and
r.send for locating the change.
In `@crates/minimald/src/sessions.rs`:
- Around line 115-125: When starting a session in the GetSession path you call
Session::run(...) and return its SessionHandle but never cache it in
self.running, so subsequent GetSession requests spawn duplicates; fix by
inserting the newly created SessionHandle into self.running (using the same key
`k`) immediately after Session::run(...) completes (before sending via
`r.send`), updating the map entry used by the match that checks
`self.running.get(&k)` so future lookups return the running handle.
- Around line 73-128: Replace all fallible unwrap() calls on storage and session
start operations in the ManagerMessage handlers (ManagerMessage::List,
ManagerMessage::GetRecord, ManagerMessage::GetSession) with proper error
handling: handle Result/Option from self.store.list(), self.store.get(),
self.store.find_by_uuid(), self.store.find_by_name() and Session::run() by
matching errors and sending an appropriate response over the oneshot channel
(r.send), e.g., send an Err variant or None/empty collection after logging the
storage error instead of panicking; update the signatures/types used with the
channel if needed to carry Result<T, E> so callers can observe failures, and
ensure all places referencing get(&k), find_by_* and Session::run use matches or
? propagation rather than unwrap().
In `@crates/sessions/src/paths.rs`:
- Line 268: Fix the doc comment that currently reads "The current directory
contains utf8 characters" to correctly describe the error condition (e.g.,
"contains non-UTF8 characters" or "contains invalid UTF-8") in the comment in
paths.rs so the documentation matches the actual check; update the wording in
the doc comment near the path/CWD validation (the comment line shown) to use
"non-UTF8" or "invalid UTF-8".
In `@crates/sessions/src/store.rs`:
- Around line 72-88: DiskSessionKey has mismatched equality and ordering: Eq
(derived) compares session_uuid and dir_key while Ord::cmp only compares
session_uuid; update the implementations so equality and ordering are
consistent. Either derive Ord (remove manual impls of Ord/PartialOrd) so both
fields are compared consistently with Eq, or change Eq/PartialEq to be manual to
only compare session_uuid to match the current Ord::cmp behavior; modify the
DiskSessionKey implementations (DiskSessionKey, impl Ord, impl PartialOrd, and
Eq/PartialEq usage) accordingly to ensure a == b implies cmp == Ordering::Equal.
---
Outside diff comments:
In `@crates/minimald/src/main.rs`:
- Around line 204-209: The match arm currently awaiting
Server::run_on_uds(config, listener).await.unwrap() will panic on errors;
replace the unwrap with proper error propagation by returning the Result from
Server::run_on_uds and using the ? operator or mapping the error into your
MainError type (e.g., Server::run_on_uds(...).await.map_err(MainError::from)?),
so that failures from Server::run_on_uds are converted into MainError and
propagated up instead of panicking; update the call site handling of cli.command
/ Command::Run accordingly.
---
Nitpick comments:
In `@crates/common/src/renameat2.rs`:
- Around line 75-84: The test test_rename_noreplace currently only verifies a
successful rename to a missing target; update it to also exercise the NOREPLACE
failure path by pre-creating the destination path (new) before calling
renameat2_cwd(&old, &new, RENAME_NOREPLACE) and assert that the call returns an
AlreadyExists (or equivalent) error; ensure the test explicitly checks the error
kind rather than implementation details so both success and failure behaviors of
RENAME_NOREPLACE are covered by separate assertions in the same test.
In `@crates/minimald/src/connection.rs`:
- Around line 364-367: In channel_close, the lock is acquired via the old field
path self.0.0.lock().await; update it to use the new helper field by calling
self.0.lock().await and then invoke handle_channel_close(id) on the locked value
so the method mirrors the other methods' access pattern (refer to function
channel_close and the helper accessor self.0).
In `@crates/minimald/src/main.rs`:
- Around line 28-49: In minimal_state_dir, replace the bare unwrap() calls with
expect("...") that document the invariant for each use: for
DaemonAbsPath::try_new(d.clone()) inside the is_absolute() branch use
expect("absolute path validated by is_absolute()"), for
DaemonAbsPath::from_cwd() use expect("current working directory must be valid
UTF-8"), for DaemonRelPath::try_new(d) in the relative branch use
expect("relative path validated by !is_absolute()"), and for
Utf8PathBuf::from_path_buf(...) and the outer DaemonAbsPath::try_new(...) in the
None branch add expect messages like expect("state dir path is valid UTF-8") and
expect("constructed state dir is absolute") respectively so each panic documents
the assumed invariant.
- Around line 53-63: Replace the chained unwraps in minimal_cache_dir with
explicit expect messages mirroring minimal_state_dir: when calling
dirs::cache_dir().unwrap_or_else(...) and when converting to
Utf8PathBuf::from_path_buf(...).unwrap() and
DaemonAbsPath::try_new(...).unwrap(), use expect(...) with short explanations
(e.g., why a fallback path exists and why the path must be valid UTF‑8 /
absolute) so each invariant is documented; locate the minimal_cache_dir function
and update the unwraps on dirs::cache_dir()/Utf8PathBuf::from_path_buf(...) and
DaemonAbsPath::try_new(...) to expect(...) with descriptive messages.
- Around line 182-186: Replace the interpolated log with a structured tracing
field: change the call that uses tracing::info!("Started listening on {}",
cli.listen_on()) to use a named field such as tracing::info!(listen_on =
%cli.listen_on(), "Started listening on"); leave the multi-line ssh guidance
block (the tracing::info! that prints the debug command) unchanged.
In `@crates/minimald/src/rpc.rs`:
- Around line 92-94: Replace the interpolated log in the error branch with
structured tracing fields: in the RPC error handling where you currently use
tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e), change it to use
structured fields (e.g. tracing::warn!(rpc = Self::NAME, error = %e, "RPC
handler failed")) so the RPC name and error are separate fields; update the call
in the same block that references Self::NAME and variable e.
- Around line 236-241: The spawned JoinHandle returned by spawn for
GetVersion.handle, ListSessions.handle and GetSessionRecord.handle is currently
discarded which hides panics; either explicitly mark this as intentional
fire-and-forget by adding a concise comment above the match stating that
handlers own their error logging and panics are acceptable/ignored, or collect
the JoinHandles (e.g., push them into a Vec<JoinHandle<...>> or an observability
task registry) so panics can be awaited/inspected; update the match site
accordingly and reference the spawn calls and the individual handler entry
points (GetVersion.handle, ListSessions.handle, GetSessionRecord.handle) when
making the change.
- Around line 138-140: Replace the string-formatted warning in the RPC error
branch with structured tracing fields: when checking the result (if let Err(e) =
res) log using tracing::warn! with named fields for the handler name
(Self::NAME) and the error (e) instead of interpolating them into the message so
the log system can index them; update the log invocation in the block that
currently contains tracing::warn!("RPC handler for {} failed: {}", Self::NAME,
e) to use structured fields like handler = Self::NAME and error = ?e (or %e) and
a short message such as "RPC handler failed".
- Around line 188-190: The current warn log in the error branch of the RPC
handler uses string interpolation which loses structured fields; change the
logging in the block that checks if let Err(e) = res (the code referencing
Self::NAME) to use structured tracing fields instead — e.g., pass the handler
name and the error as separate fields (handler = Self::NAME, error = %e) in the
tracing::warn! call so the error is recorded as a structured field rather than
embedded in a formatted message.
In `@crates/minimald/src/session.rs`:
- Around line 50-57: Move the explanatory comment about ignoring send errors so
it sits directly above the discard statement in SessionHandle::workspace_path
(i.e., immediately before the `_ =
self.0.send(SessionMessage::GetWorkspacePath(send)).await;` line) rather than
earlier; keep the comment text but relocate it to clarify that we intentionally
ignore send errors because the corresponding recv will also fail when the
session is dead.
In `@crates/sessions/src/lib.rs`:
- Around line 19-38: Public struct Record should be marked #[non_exhaustive] so
adding fields later won't be a breaking change; add the #[non_exhaustive]
attribute immediately above the Record definition (e.g., above #[derive(...)]
for Record) and ensure any external pattern matches using Record are updated to
use a wildcard (_) or the struct update pattern where appropriate (since
consumers will now need to account for possible future fields).
In `@crates/sessions/src/paths.rs`:
- Around line 197-218: The assertion in sub_path does not catch Windows-style
'/' separators because it only checks std::path::MAIN_SEPARATOR_STR; update the
validation in fn sub_path to reject both '/' and the platform MAIN_SEPARATOR (or
explicitly check dir.contains('/') ||
dir.contains(std::path::MAIN_SEPARATOR_STR)) and keep the existing ".." check
and panic message; make sure the error message remains clear and references
sub_path/subdir usage so callers see why the input was rejected.
In `@crates/sessions/src/store.rs`:
- Around line 113-118: In workspace_path(), replace the call to
DaemonRelPath::try_new(&self.key.dir_key).unwrap() with .expect("explain why
dir_key must be a valid DaemonRelPath here") so that the invariant failure
produces a clear message; update the expect string to succinctly state the
invariant (e.g., "dir_key was validated on construction and must be a valid
DaemonRelPath") referencing workspace_path and DaemonRelPath::try_new to locate
the change.
🪄 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: 272e62d6-b4d8-4146-bff1-2dbb85a7e49b
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (19)
Cargo.tomlcrates/common/Cargo.tomlcrates/common/src/lib.rscrates/common/src/renameat2.rscrates/lcache/Cargo.tomlcrates/lcache/src/lib.rscrates/minimald/Cargo.tomlcrates/minimald/src/connection.rscrates/minimald/src/lib.rscrates/minimald/src/main.rscrates/minimald/src/rpc.rscrates/minimald/src/server.rscrates/minimald/src/session.rscrates/minimald/src/sessions.rscrates/minimald/src/test_harness.rscrates/sessions/Cargo.tomlcrates/sessions/src/lib.rscrates/sessions/src/paths.rscrates/sessions/src/store.rs
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/sessions/src/paths.rs (1)
207-218:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix method name in panic messages.
The panic messages reference
.subdir()but the method is namedsub_path(). This inconsistency will confuse developers when they encounter these panics.🐛 Proposed fix
pub fn sub_path(&self, dir: &'static str) -> AbsPath<R> { assert!( !dir.contains(std::path::MAIN_SEPARATOR_STR), - ".subdir(\"{dir}\") contains path separators" + ".sub_path(\"{dir}\") contains path separators" ); - assert!(dir != "..", ".subdir(\"..\") attempts path traversal"); + assert!(dir != "..", ".sub_path(\"..\") attempts path traversal"); AbsPath { inner: self.inner.join(dir),🤖 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/paths.rs` around lines 207 - 218, Panic messages in AbsPath::sub_path refer to the wrong method name ".subdir()", which is inconsistent with the actual method name sub_path; update the assert message strings in sub_path (the two assertions checking MAIN_SEPARATOR_STR and dir != "..") to reference ".sub_path()" (or a neutral message) so the panic output correctly names the method where the error occurred and avoids confusion for developers.
🧹 Nitpick comments (1)
crates/sessions/src/paths.rs (1)
269-277: 💤 Low valueConsider harmonizing error message phrasing with doc comment.
The doc comment (line 268) says "non-UTF8" while the error message uses "not-utf8". For consistency, consider using the same phrasing and capitalization in both places.
♻️ Proposed fix
pub fn from_cwd() -> Result<Self, std::io::Error> { let Ok(cwd) = Utf8PathBuf::from_path_buf(std::env::current_dir()?) else { - return Err(std::io::Error::other("cwd contains not-utf8 characters")); + return Err(std::io::Error::other("cwd contains non-UTF-8 characters")); };🤖 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/paths.rs` around lines 269 - 277, The error message in from_cwd (which uses Utf8PathBuf::from_path_buf and returns std::io::Error::other) uses "cwd contains not-utf8 characters" but the doc comment uses "non-UTF8"; update the returned error string to match the doc comment phrasing and capitalization (e.g., "cwd contains non-UTF8 characters") so the message is consistent with the documentation.
🤖 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.
Outside diff comments:
In `@crates/sessions/src/paths.rs`:
- Around line 207-218: Panic messages in AbsPath::sub_path refer to the wrong
method name ".subdir()", which is inconsistent with the actual method name
sub_path; update the assert message strings in sub_path (the two assertions
checking MAIN_SEPARATOR_STR and dir != "..") to reference ".sub_path()" (or a
neutral message) so the panic output correctly names the method where the error
occurred and avoids confusion for developers.
---
Nitpick comments:
In `@crates/sessions/src/paths.rs`:
- Around line 269-277: The error message in from_cwd (which uses
Utf8PathBuf::from_path_buf and returns std::io::Error::other) uses "cwd contains
not-utf8 characters" but the doc comment uses "non-UTF8"; update the returned
error string to match the doc comment phrasing and capitalization (e.g., "cwd
contains non-UTF8 characters") so the message is consistent with the
documentation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f3deaf3f-21ba-49b0-887d-0790fb3c5d88
📒 Files selected for processing (5)
crates/common/src/renameat2.rscrates/minimald/src/connection.rscrates/minimald/src/sessions.rscrates/sessions/src/paths.rscrates/sessions/src/store.rs
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/common/src/renameat2.rs
- crates/sessions/src/store.rs
- crates/minimald/src/sessions.rs
| SessionKeyPredicate::Id(id) => self | ||
| .store | ||
| .find_by_uuid(&id) | ||
| .unwrap() |
There was a problem hiding this comment.
If the unwraps in here are due to an invariant enforced elsewhere we should document that using expect() rather than unwrap. It's not clear to me that these are invariants though, so it might make more sense to do something like:
fn key_for(&self, pred: &SessionKeyPredicate) -> Result<Option<Key>, StoreError> {
match pred {
SessionKeyPredicate::Id(id) => self.store.find_by_uuid(id),
SessionKeyPredicate::Name(name) => self.store.find_by_name(name),
}
}
//...
ManagerMessage::FindRecord(pred, r) => {
let record = self
.key_for(&pred)?
.map(|k| self.store.get(&k).map(|e| e.record().clone()))
.transpose()?;
let _ = r.send(record);
}
ManagerMessage::GetSession(pred, r) => {
let handle = match self.key_for(&pred)? {
None => None,
Some(k) => Some(match self.running.get(&k) {
Some(h) => h.clone(),
None => {
let entry = self.store.get(&k)?;
let h = Session::run(entry).await?;
self.running.insert(k, h.clone());
h
}
}),
};
let _ = r.send(handle);
}There was a problem hiding this comment.
I was just being lazy - will properly handle those cases.
There was a problem hiding this comment.
I ended up revamping the model a bit by having a generic type that can carry these errors, ptal.
829cf33 to
8733788
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
crates/minimald/src/main.rs (1)
30-57: ⚡ Quick winReplace bare
.unwrap()calls with.expect("reason")to document invariants.Multiple
.unwrap()calls without context make debugging difficult if they fail. Per coding guidelines, use.expect("why the invariant holds")for assumptions about the environment.♻️ Proposed refactor with explicit reasons
pub fn minimal_state_dir(&self) -> DaemonAbsPath { match &self.global_args.minimal_dir { - Some(p) => p.resolve().unwrap(), + Some(p) => p.resolve().expect("--minimal-dir should resolve to absolute path"), None => DaemonAbsPath::try_new( Utf8PathBuf::from_path_buf( dirs::state_dir() - .unwrap_or_else(|| dirs::home_dir().unwrap().join(".local/state")) + .unwrap_or_else(|| { + dirs::home_dir() + .expect("home directory must exist for default state dir") + .join(".local/state") + }) .join("minimal"), ) - .unwrap(), + .expect("state directory path must be valid UTF-8"), ) - .unwrap(), + .expect("state directory must be absolute"), } }Apply the same pattern to
minimal_cache_dir().🤖 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/main.rs` around lines 30 - 57, The code in minimal_state_dir and minimal_cache_dir uses multiple bare .unwrap() calls; replace each .unwrap() with .expect("...") giving a short reason why the value must exist (e.g., "state dir fallback must exist", "UTF-8 conversion should succeed", or "DaemonAbsPath creation must succeed") so failures emit clear diagnostics. Update the unwraps on dirs::state_dir()/dirs::cache_dir() fallbacks, Utf8PathBuf::from_path_buf(...).unwrap(), and DaemonAbsPath::try_new(...).unwrap() within the minimal_state_dir() and minimal_cache_dir() functions to use .expect(...) with context strings referencing the specific invariant being assumed.crates/minimald/src/rpc.rs (1)
92-94: 💤 Low valueUse structured logging fields instead of interpolated strings.
Per coding guidelines, prefer structured tracing fields for machine-parseable logs.
♻️ Example fix (apply to lines 140, 191 as well)
- tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e); + tracing::warn!(rpc = %Self::NAME, error = %e, "RPC handler failed");As per coding guidelines: "Use structured logging with tracing:
tracing::info!(pkg = %name, "building")instead of interpolated strings likeinfo!("building {name}")"🤖 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/rpc.rs` around lines 92 - 94, Replace the interpolated string in the tracing::warn call with structured fields: pass the handler name and the error as named fields (e.g., name = %Self::NAME, error = %e) to tracing::warn! so the log is machine-parseable; update the tracing calls in this block (the current tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e)) and make the same structured-field change for the other similar tracing calls referenced in the review (lines shown around the other occurrences, e.g., the calls at the locations you noted earlier).crates/minimald/src/sessions.rs (1)
39-44: 💤 Low valueAdd justification comment for discarded send result.
Line 43 discards the oneshot send result without a comment. The
ManagerHandlemethods (lines 171, 182, 193) include comments like// Ignore send errors - the recv will also fail.for the same pattern. Adding a similar comment here maintains consistency and satisfies the guideline against silent error discards.Suggested fix
pub async fn handle<F>(self, fut: F) where F: Future<Output = Result<T, ResponseError>>, { + // If the receiver dropped, the caller no longer cares about the result. let _ = self.0.send(fut.await); }🤖 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 39 - 44, The oneshot send result in the async function handle (the call self.0.send(fut.await)) is being discarded without explanation; add a brief comment above or inline with that send — e.g. mirroring other ManagerHandle methods — such as "// Ignore send errors - the recv will also fail." to justify swallowing the send error and maintain consistency with ManagerHandle's other methods (which use the same rationale).
🤖 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/rpc.rs`:
- Line 186: The use of expect("TODO error handling") on the call to
mngr.get_record() is swallowing a recoverable I/O error; update the surrounding
function to return a Result and propagate the error instead of panicking—mirror
the fix applied to list(): replace the expect with the ? operator (or convert
the error via map_err into the function's error type) so mngr.get_record()
failures are propagated to the caller; ensure the function signature and any
callers are adjusted to handle the propagated Result.
- Line 129: The call to mngr.list() currently uses .expect("TODO error
handling") which will panic on I/O errors; change it to propagate the error with
? so failures bubble up to handle_channel for proper logging and client
notification. Update the surrounding function to return a Result if needed, and
if ConnectionError lacks a variant for loader/store errors, add one (e.g.,
SessionStore(std::io::Error) or LoaderIo(std::io::Error)) and convert the error
into that variant when returning, ensuring mngr.list() errors are mapped into
ConnectionError and returned instead of panicking.
---
Nitpick comments:
In `@crates/minimald/src/main.rs`:
- Around line 30-57: The code in minimal_state_dir and minimal_cache_dir uses
multiple bare .unwrap() calls; replace each .unwrap() with .expect("...") giving
a short reason why the value must exist (e.g., "state dir fallback must exist",
"UTF-8 conversion should succeed", or "DaemonAbsPath creation must succeed") so
failures emit clear diagnostics. Update the unwraps on
dirs::state_dir()/dirs::cache_dir() fallbacks,
Utf8PathBuf::from_path_buf(...).unwrap(), and
DaemonAbsPath::try_new(...).unwrap() within the minimal_state_dir() and
minimal_cache_dir() functions to use .expect(...) with context strings
referencing the specific invariant being assumed.
In `@crates/minimald/src/rpc.rs`:
- Around line 92-94: Replace the interpolated string in the tracing::warn call
with structured fields: pass the handler name and the error as named fields
(e.g., name = %Self::NAME, error = %e) to tracing::warn! so the log is
machine-parseable; update the tracing calls in this block (the current
tracing::warn!("RPC handler for {} failed: {}", Self::NAME, e)) and make the
same structured-field change for the other similar tracing calls referenced in
the review (lines shown around the other occurrences, e.g., the calls at the
locations you noted earlier).
In `@crates/minimald/src/sessions.rs`:
- Around line 39-44: The oneshot send result in the async function handle (the
call self.0.send(fut.await)) is being discarded without explanation; add a brief
comment above or inline with that send — e.g. mirroring other ManagerHandle
methods — such as "// Ignore send errors - the recv will also fail." to justify
swallowing the send error and maintain consistency with ManagerHandle's other
methods (which use the same rationale).
🪄 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: b681f557-443b-4d67-80c6-c2f9c85c9561
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (19)
Cargo.tomlcrates/common/Cargo.tomlcrates/common/src/lib.rscrates/common/src/renameat2.rscrates/lcache/Cargo.tomlcrates/lcache/src/lib.rscrates/minimald/Cargo.tomlcrates/minimald/src/connection.rscrates/minimald/src/lib.rscrates/minimald/src/main.rscrates/minimald/src/rpc.rscrates/minimald/src/server.rscrates/minimald/src/session.rscrates/minimald/src/sessions.rscrates/minimald/src/test_harness.rscrates/sessions/Cargo.tomlcrates/sessions/src/lib.rscrates/sessions/src/paths.rscrates/sessions/src/store.rs
✅ Files skipped from review due to trivial changes (2)
- crates/common/Cargo.toml
- crates/minimald/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (11)
- Cargo.toml
- crates/sessions/src/lib.rs
- crates/common/src/lib.rs
- crates/minimald/Cargo.toml
- crates/sessions/Cargo.toml
- crates/minimald/src/session.rs
- crates/common/src/renameat2.rs
- crates/minimald/src/connection.rs
- crates/minimald/src/test_harness.rs
- crates/minimald/src/server.rs
- crates/sessions/src/store.rs
8733788 to
74a5c13
Compare
74a5c13 to
809c719
Compare
Summary by CodeRabbit
New Features
Infrastructure