Skip to content

feat(sessions): per-loadout follow symlinks setting - #713

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

feat(sessions): per-loadout follow symlinks setting#713
evanspearman merged 1 commit into
mainfrom
evan/sym

Conversation

@evanspearman

@evanspearman evanspearman commented Jul 10, 2026

Copy link
Copy Markdown
Member

Adds a per-loadout follow_symlinks field to loadout TOML that overrides
the client config's [loadouts].follow_symlinks default for one loadout's
patches. None (the default) inherits the global setting; Some(bool)
wins.

name = "dotfiles"
follow_symlinks = true    # optional; falls through to config default

The override travels through the compose pipeline as a field on
ProvenancedPatch (Option), stamped by Loadout::contribute and
read once by expand_patch_sources (follow_override.unwrap_or(default)).
Non-loadout contributors (packages, projects) leave it None and inherit
the default. enumerate_patch_files reads the field per-item, so a single
call can walk two patches with different follow behavior. No wire-format
change — the setting resolves to a walk decision on the client before the
wire contribution is built.

Also in this PR: UserComposer::add rejects duplicate loadout names so
Source::UserLoadout { name } unambiguously identifies one loadout in
logs and conflict messages.

Summary by CodeRabbit

  • New Features
    • Added per-loadout control over whether patch processing follows symbolic links.
    • Individual patches can now use different symbolic-link handling settings within the same composition.
  • Bug Fixes
    • Duplicate loadout names are now rejected with a clear error instead of being added multiple times.
    • Symbolic-link behavior is consistently applied during patch expansion and file enumeration.
  • Tests
    • Added coverage for duplicate loadouts, configuration round-tripping, and mixed symbolic-link behavior.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Loadouts can specify symlink-following overrides that propagate through patch composition and enumeration. Patch enumeration now resolves this setting per patch. UserComposer also rejects duplicate loadout names.

Changes

Loadout composition and patch metadata

Layer / File(s) Summary
Override contracts and contribution stamping
crates/sessions/src/core/loadout.rs, crates/sessions/src/core/source.rs, crates/sessions/src/core/compose.rs
Loadouts and provenance records carry optional symlink overrides, which are applied to contributed patches and covered by serialization and builder tests.
Patch expansion and enumeration
crates/sessions/src/core/compose.rs, crates/sessions/src/core/enumerate.rs, crates/sessions/src/client/handler.rs
Patch expansion resolves per-patch symlink behavior from overrides or defaults, and enumeration uses each patch’s resolved value.
Composer duplicate-name enforcement
crates/sessions/src/client/composer.rs, crates/sessions/src/core/compose.rs
UserComposer tracks accepted names and returns DuplicateLoadout for duplicates, with tests for duplicate rejection and per-loadout metadata.
Estimated code review effort: 3 (Moderate) ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Loadout
  participant Contribution
  participant expand_patch_sources
  participant enumerate_patch_files
  Loadout->>Contribution: apply optional symlink override
  Contribution->>expand_patch_sources: provide contributed patches
  expand_patch_sources->>enumerate_patch_files: provide resolved per-patch flags
  enumerate_patch_files->>enumerate_patch_files: follow symlinks per patch
Loading

Suggested reviewers: twitchyliquid64, norrietaylor

Poem

A rabbit hops through patches bright,
With links that follow left or right.
No duplicate names pass the gate,
Each loadout’s choice determines fate.
The composer thumps: “All set!”

🚥 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 clearly summarizes the main change: adding a per-loadout follow-symlinks setting in sessions.
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.
✨ 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 (1)
crates/sessions/src/core/loadout.rs (1)

382-423: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add an end-to-end test for contribute() stamping.

The new tests cover TOML round-trip and the with_follow_symlinks builder, but nothing exercises the actual propagation path: a Loadout with follow_symlinks = Some(v) and at least one patch, calling contribute(), then asserting the resulting Contribution's ProvenancedPatch::follow_symlinks() reflects the override. This is the core new behavior (set_follow_symlinks_on_patches stamping), and it's currently only verified indirectly via enumerate.rs's walker test, which doesn't go through Loadout::contribute.

✅ Suggested additional test
#[test]
fn contribute_stamps_follow_symlinks_override_onto_patches() {
    use crate::core::compose::Composable;
    use crate::core::primitives::PatchDest;

    let patch = Patch::new("a", PatchDest::try_new("a").unwrap());
    let loadout = Loadout::new(LoadoutName::try_new("test").unwrap())
        .with_patch(patch)
        .with_follow_symlinks(true);

    let env: &dyn Fn(&str) -> Result<String, std::env::VarError> =
        &|_| Err(std::env::VarError::NotPresent);
    let contribution = loadout.contribute(env).unwrap();

    assert_eq!(contribution.patches().len(), 1);
    assert_eq!(contribution.patches()[0].follow_symlinks(), Some(true));
}
🤖 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/core/loadout.rs` around lines 382 - 423, Add an
end-to-end test near with_follow_symlinks_builder_sets_the_field that constructs
a Loadout with a Patch, applies with_follow_symlinks(true), calls
Loadout::contribute with an environment lookup returning VarError::NotPresent,
and asserts the resulting Contribution contains one patch whose
ProvenancedPatch::follow_symlinks() is Some(true).
🤖 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/sessions/src/core/loadout.rs`:
- Around line 382-423: Add an end-to-end test near
with_follow_symlinks_builder_sets_the_field that constructs a Loadout with a
Patch, applies with_follow_symlinks(true), calls Loadout::contribute with an
environment lookup returning VarError::NotPresent, and asserts the resulting
Contribution contains one patch whose ProvenancedPatch::follow_symlinks() is
Some(true).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96bcd936-5cbe-418c-8153-8b23f137fd38

📥 Commits

Reviewing files that changed from the base of the PR and between 3324a84 and 480d197.

📒 Files selected for processing (6)
  • crates/sessions/src/client/composer.rs
  • crates/sessions/src/client/handler.rs
  • crates/sessions/src/core/compose.rs
  • crates/sessions/src/core/enumerate.rs
  • crates/sessions/src/core/loadout.rs
  • crates/sessions/src/core/source.rs

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