Skip to content

feat(sessions): loadouts, config, and debug commands - #686

Merged
evanspearman merged 1 commit into
mainfrom
evan/loadouts
Jul 10, 2026
Merged

feat(sessions): loadouts, config, and debug commands#686
evanspearman merged 1 commit into
mainfrom
evan/loadouts

Conversation

@evanspearman

@evanspearman evanspearman commented Jul 9, 2026

Copy link
Copy Markdown
Member

Resolves https://github.com/gominimal/inbox/issues/202 and resolves https://github.com/gominimal/inbox/issues/203

Summary

Ships end-to-end loadout support, a user client config file, and two debug
commands (minimal dirs, minimal loadout list). Adds a rolling
file-based daemon log for detached mode and per-session content logging.

User-facing (minimal):

  • --config-dir global flag (routes through the new
    paths::minimal_config_dir(), XDG on Linux, ~/.config/minimal on
    macOS for consistency with the state/cache dirs)
  • minimal loadout list — enumerate loadouts from
    <config>/minimal/loadouts/*.toml, mark defaults from
    [loadouts].default_loadouts in config.toml
  • minimal dirs — print all Config/State/Cache paths with existence
    markers (* = exists, - = missing, ? = unresolved)
  • minimal activate --loadout NAME (repeatable) / --no-loadouts to
    select which loadouts to apply; otherwise [loadouts].default_loadouts
    is used

Library side (sessions):

  • New sessions::client::config — TOML with deny_unknown_fields,
    [loadouts] block for default_loadouts and follow_symlinks
  • New sessions::client::disklist_loadouts / read_loadout_file
    with per-entry LoadoutEntry results so listing tolerates malformed
    files without aborting
  • Loadout Inherit vars pre-resolve from the process env;
    NotPresent → warn+drop rather than error
  • Patch enumeration treats walkdir NotFound as warn+drop, so a
    loadout that opportunistically patches ~/dotfiles/helix/ doesn't
    fail activation on a host without that tree
  • ComposeOptions::with_follow_symlinks owned builder

Daemon (minimald):

  • MINIMALD_DETACHED=1 routes tracing to a daily-rotated log file
    under <state>/logs/minimald.log.YYYY-MM-DD
  • Per-session content logging: packages, vars, patches, hooks with
    their Source provenance
  • WindowAdjusted SSH flow-control messages downgraded from warn to
    debug (they were spamming the log)

Structure:

  • crates/minimal/src/lib.rs (2313 → 1613 lines) with the new logic
    split into config, dirs, loadouts modules
  • paths::minimal_config_dir() alongside the existing state/cache
    helpers
  • just min *args recipe for quick local invocations

Summary by CodeRabbit

Summary

  • New Features

    • Added loadout and dirs commands to list loadouts and inspect resolved config/state/cache directories.
    • Added --config-dir plus loadout activation controls (--loadout, --no-loadouts), and minimald rotation-backed logging in detached mode.
    • Added min convenience command to build and run with the daemon available automatically.
  • Bug Fixes

    • Improved robustness when optional paths or referenced loadouts/patch sources are missing, with clearer warnings.
    • In loadouts, inherited variables now safely drop when the target value is unavailable.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f22f4cb9-5433-4a2b-8a5d-84944c2af24f

📥 Commits

Reviewing files that changed from the base of the PR and between 80c4d45 and fb43cc8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • Cargo.toml
  • crates/minimal/src/config.rs
  • crates/minimal/src/dirs.rs
  • crates/minimal/src/lib.rs
  • crates/minimal/src/loadouts.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimald/Cargo.toml
  • crates/minimald/src/main.rs
  • crates/minimald/src/session_host.rs
  • crates/paths/src/lib.rs
  • crates/remote-proto/build.rs
  • crates/sessions/Cargo.toml
  • crates/sessions/src/client/config.rs
  • crates/sessions/src/client/disk.rs
  • crates/sessions/src/client/mod.rs
  • crates/sessions/src/core/compose.rs
  • crates/sessions/src/core/enumerate.rs
  • crates/sessions/src/core/loadout.rs
  • justfile
✅ Files skipped from review due to trivial changes (2)
  • crates/sessions/Cargo.toml
  • crates/minimal/tests/common/mod.rs
🚧 Files skipped from review as they are similar to previous changes (18)
  • crates/sessions/src/client/mod.rs
  • crates/minimald/Cargo.toml
  • Cargo.toml
  • justfile
  • crates/minimal/src/config.rs
  • crates/sessions/src/core/enumerate.rs
  • crates/minimal/tests/cli.rs
  • crates/sessions/src/client/config.rs
  • crates/remote-proto/build.rs
  • crates/sessions/src/core/compose.rs
  • crates/sessions/src/client/disk.rs
  • crates/minimald/src/session_host.rs
  • crates/minimal/src/dirs.rs
  • crates/minimald/src/main.rs
  • crates/paths/src/lib.rs
  • crates/sessions/src/core/loadout.rs
  • crates/minimal/src/loadouts.rs
  • crates/minimal/src/lib.rs

📝 Walkthrough

Walkthrough

Adds minimal config resolution, persisted loadout discovery and activation, dirs and loadout list commands, detached daemon logging, session tracing, missing-path handling, configurable protobuf includes, and a workspace run recipe.

Changes

Minimal client config, loadouts, dirs, and daemon logging

Layer / File(s) Summary
Config path and client config
crates/paths/src/lib.rs, crates/sessions/src/client/*, crates/minimal/src/config.rs, crates/sessions/Cargo.toml
Adds minimal config directory resolution, TOML config parsing, and config loading with defaults.
Loadout storage and composition
crates/sessions/src/client/disk.rs, crates/sessions/src/core/compose.rs, crates/minimal/src/loadouts.rs
Adds loadout file validation and enumeration, selection resolution, composition options, and contribution construction.
CLI wiring and activation
crates/minimal/src/lib.rs, crates/minimal/src/loadouts.rs, crates/minimal/src/dirs.rs, crates/minimal/tests/*
Adds config and loadout flags, new commands, directory and loadout tables, activation integration, and tests.
Daemon logging and session tracing
crates/minimald/src/main.rs, crates/minimald/src/session_host.rs, crates/minimald/Cargo.toml, Cargo.toml
Adds detached rolling logs, launcher-content tracing, baseline constants, and explicit window-adjusted message handling.
Patch and inherited-variable handling
crates/sessions/src/core/compose.rs, crates/sessions/src/core/enumerate.rs, crates/sessions/src/core/loadout.rs
Warns and drops missing patch sources and resolves inherited variables from the host environment.
Build and workspace tooling
crates/remote-proto/build.rs, justfile
Adds configurable protobuf include paths and a recipe that builds and runs both minimal binaries.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Config
  participant LoadoutFiles
  participant Composer
  participant Daemon
  CLI->>Config: read client config
  CLI->>LoadoutFiles: resolve selected loadouts
  LoadoutFiles->>Composer: provide parsed loadouts
  Composer->>CLI: return WireContribution
  CLI->>Daemon: create session with contribution
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: norrietaylor, twitchyliquid64, bryan-minimal, msample

Poem

A rabbit tucked loadouts in a den,
Then found and chose them once again.
Logs now roll through moonlit night,
While missing patches fade from sight.
New dirs bloom where configs gleam. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds tracing/log rotation, session content logging, proto build changes, and a justfile recipe that are unrelated to issue #202. Split unrelated logging, build, and task-runner changes into separate PRs or remove them from this issue-focused patch.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: loadouts, config, and debug commands.
Linked Issues check ✅ Passed The PR adds on-disk loadout discovery, default-loadout config persistence, and distinct path-specific errors for missing or malformed files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
crates/minimal/src/dirs.rs (1)

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

Consider an enum instead of &'static str for mesh_group.

mesh_group is compared via string equality (== "Config" / == "State") to decide row placement. An enum (e.g. MeshGroup::Config | MeshGroup::State) would make the invariant compiler-checked instead of relying on matching literals.

Also applies to: 103-105, 145-150

🤖 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/minimal/src/dirs.rs` at line 61, The mesh_group field is using raw
&'static str values and string comparisons to decide placement, so replace it
with a compiler-checked enum such as MeshGroup with Config and State variants.
Update the affected code paths in the dirs.rs logic that currently compare
against "Config" and "State" to use the enum instead, including the related uses
in the surrounding structs/functions where mesh_group is passed through.
crates/minimald/src/main.rs (1)

296-319: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

No log retention/cleanup for daily-rotated files.

tracing_appender::rolling::daily is used without max_log_files, so <state>/logs/minimald.log.YYYY-MM-DD accumulates indefinitely for a long-running daemon. The RollingFileAppender::builder() API supports .max_log_files(n) to bound retention.

♻️ Proposed fix to bound log retention
-    let appender = tracing_appender::rolling::daily(&log_dir, "minimald.log");
+    let appender = tracing_appender::rolling::Builder::new()
+        .rotation(tracing_appender::rolling::Rotation::DAILY)
+        .filename_prefix("minimald.log")
+        .max_log_files(14)
+        .build(&log_dir)
+        .map_err(|e| MainError::Other(format!("initializing log appender: {e}")))?;
🤖 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 296 - 319, The daily rolling logger
in minimald does not cap retained files, so old logs can accumulate
indefinitely. Update the log setup in main() where
tracing_appender::rolling::daily is created to use the RollingFileAppender
builder API and set a max file count for retention, then keep the existing
writer/guard wiring unchanged so the daemon still routes tracing output to the
log file.
🤖 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/minimal/src/dirs.rs`:
- Line 61: The mesh_group field is using raw &'static str values and string
comparisons to decide placement, so replace it with a compiler-checked enum such
as MeshGroup with Config and State variants. Update the affected code paths in
the dirs.rs logic that currently compare against "Config" and "State" to use the
enum instead, including the related uses in the surrounding structs/functions
where mesh_group is passed through.

In `@crates/minimald/src/main.rs`:
- Around line 296-319: The daily rolling logger in minimald does not cap
retained files, so old logs can accumulate indefinitely. Update the log setup in
main() where tracing_appender::rolling::daily is created to use the
RollingFileAppender builder API and set a max file count for retention, then
keep the existing writer/guard wiring unchanged so the daemon still routes
tracing output to the log file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fae9a9a7-15e8-42c4-b93f-b9f1d8a4427c

📥 Commits

Reviewing files that changed from the base of the PR and between 15048a9 and 4ad23e3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • Cargo.toml
  • crates/minimal/src/config.rs
  • crates/minimal/src/dirs.rs
  • crates/minimal/src/lib.rs
  • crates/minimal/src/loadouts.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimald/Cargo.toml
  • crates/minimald/src/main.rs
  • crates/minimald/src/session_host.rs
  • crates/paths/src/lib.rs
  • crates/remote-proto/build.rs
  • crates/sessions/Cargo.toml
  • crates/sessions/src/client/config.rs
  • crates/sessions/src/client/disk.rs
  • crates/sessions/src/client/mod.rs
  • crates/sessions/src/core/compose.rs
  • crates/sessions/src/core/enumerate.rs
  • crates/sessions/src/core/loadout.rs
  • justfile

Add loadout support end-to-end, a user client config file, and a
`minimal dirs` debug command. Squashed after a rebase onto main
that restructured minimal into main.rs + lib.rs.

- sessions: client-side `Config` (toml, deny_unknown_fields) with
  `[loadouts]` block and `default_loadouts` / `follow_symlinks`;
  on-disk `list_loadouts` / `read_loadout_file` with per-entry
  `LoadoutEntry` result rows.
- sessions: pre-resolve loadout `Inherit` vars from the process
  env, downgrading `NotPresent` to a warn+drop.
- sessions: patch enumeration treats walkdir `NotFound` as a
  warn+drop so a missing dotfile tree doesn't fail activation.
- sessions: `ComposeOptions::with_follow_symlinks` owned builder.
- minimal: `--config-dir` global flag routes through the new
  `paths::minimal_config_dir()` (XDG on Linux, `~/.config` on
  macOS to match state/cache dirs).
- minimal: `loadout list` prints discovered loadouts with default
  markers; `dirs` prints Config/State/Cache paths with existence
  markers.
- minimal: `activate` applies `--loadout` / `--no-loadouts` /
  config defaults; composed contribution goes to the daemon.
- minimald: file-based rolling daily log when detached; content
  logging of packages/vars/patches/hooks per session.
- paths: `minimal_config_dir` helper; `xdg_config_home` fallback
  to `$HOME/.config`.
- justfile: `just min *args` recipe.
/// inspect when debugging. Grouped by role (config / state / cache)
/// with an "exists?" marker so unused paths don't look identical to
/// misconfigured ones.
pub fn cmd_dirs(global: &GlobalArgs) -> Result<(), anyhow::Error> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want minimald/minvmd stuff in here?

@twitchyliquid64 twitchyliquid64 Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Eg $STATE/minimal/providers/local-0/{ssh.sock,minvmd stuff, data-vol.raw} etc

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think we do actually because the client is the main way that users will interact with minimal. That said, it only really makes sense if the daemon they are connected to is running locally. When we add support for connecting to remote daemons we should revisit this I think.

@@ -1,2 +1,4 @@
pub mod composer;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Up to you but this might be a better candidate as a top-level crate, i.e. clientconf or clientstate or something

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think that's reasonable, but I'll do it in a separate PR.

@twitchyliquid64 twitchyliquid64 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A few nits but up to you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants