Skip to content

feat(embed): add configurable workspace discovery - #1169

Merged
jdx merged 6 commits into
mainfrom
agent/embed-configurable-workspace-discovery
Jul 28, 2026
Merged

jdx merged 6 commits into
mainfrom
agent/embed-configurable-workspace-discovery

Conversation

@jdx

@jdx jdx commented Jul 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • expose workspace-root detection and package discovery through aube::embed
  • add an opt-in ConfinedToRoot boundary for repository-scoped embedding hosts
  • support brace expansion in positive and negative workspace globs
  • keep workspace YAML authoritative even when its package list is empty

Why

Embedding hosts such as mise need Aube’s workspace semantics without allowing a workspace pattern to scan outside a host-selected repository root. Previously they had to duplicate discovery or call lower-level crates with incompatible boundary behavior.

The default remains pnpm-compatible and continues to allow parent-relative patterns such as ../**; only hosts that explicitly select the confined option get the stricter policy.

Stack

Validation

  • cargo test -p aube-workspace
  • cargo test -p aube embed::tests
  • cargo test -p aube --test embed
  • cargo clippy -p aube-workspace -p aube --all-targets -- -D warnings
  • cargo fmt --check

AI-assisted — Tool: Codex; model: openai/gpt-5; version: unavailable.


Note

Medium Risk
Confined discovery changes security-sensitive path resolution for embed hosts; the empty-YAML precedence tweak can alter which packages are discovered for projects with both files.

Overview
Workspace discovery gains find_workspace_packages_with_options with WorkspaceBoundary::ConfinedToRoot, which rejects parent-relative or absolute globs, canonicalizes matched packages, and errors on symlinks that resolve outside the workspace root; the default remains pnpm-compatible (../** still allowed).

Discovery now expands brace alternates in positive and negative patterns (e.g. {apps,packages}/*), and treats an on-disk workspace YAML as authoritative even when packages: is empty, so package.json#workspaces is not used as a fallback in that case.

The aube::embed facade exposes is_workspace_project_root, discover_workspace_packages, and re-exports WorkspaceDiscoveryOptions, PackageJson, and related manifest types for embedding hosts (e.g. repository-scoped tooling).

Reviewed by Cursor Bugbot for commit 0ab2087. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added configurable workspace discovery with options to allow packages outside the project root or confine discovery to the root.
    • Added support for brace-expanded workspace patterns and canonical package paths.
    • Added embedding helpers to detect workspace roots and discover workspace packages.
    • Added public access to workspace and manifest types.
  • Bug Fixes

    • Prevented confined workspace discovery from following symlinks outside the workspace.
    • Preserved empty workspace configuration files as authoritative settings.
    • Improved tolerance when parsing unexpected manifest fields.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Workspace discovery now supports configurable boundary policies, brace-expanded patterns, canonical path validation, and authoritative workspace YAML handling. The embedding facade re-exports related types and exposes workspace detection and package discovery helpers with tests.

Changes

Workspace discovery and embedding facade

Layer / File(s) Summary
Boundary-aware workspace discovery
crates/aube-workspace/src/lib.rs
Adds configurable confinement, brace expansion, pattern validation, canonicalized package paths, negation handling, and coverage for boundary and workspace-definition behavior.
Embedding facade APIs and validation
crates/aube/src/embed.rs, crates/aube/tests/embed.rs
Re-exports manifest and workspace types, adds workspace root and package discovery helpers, and tests tolerant manifest parsing and confined discovery.

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

Sequence Diagram(s)

sequenceDiagram
  participant Embedder
  participant EmbedFacade
  participant WorkspaceDiscovery
  participant Filesystem
  Embedder->>EmbedFacade: discover_workspace_packages(project_dir, options)
  EmbedFacade->>WorkspaceDiscovery: find_workspace_packages_with_options(project_dir, options)
  WorkspaceDiscovery->>Filesystem: load workspace configuration and match patterns
  Filesystem-->>WorkspaceDiscovery: candidate package paths
  WorkspaceDiscovery->>Filesystem: canonicalize and validate paths
  WorkspaceDiscovery-->>EmbedFacade: validated package paths or error
  EmbedFacade-->>Embedder: package paths or report
Loading

Possibly related PRs

  • jdx/aube#1168: Both PRs update crates/aube/src/embed.rs with the same manifest facade re-exports.

Poem

A bunny found packages in a neat little row,
With braces that bloom and safe roots below.
YAML spoke clearly, paths turned bright,
The embedder hops through the workspace right.
“Canonical carrots for all!” cried the hare.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: configurable workspace discovery for embed hosts.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0c72216. Configure here.

Comment thread crates/aube-workspace/src/lib.rs Resolved
Comment thread crates/aube-workspace/src/lib.rs Resolved
@greptile-apps

greptile-apps Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds configurable workspace discovery for embedders.

  • Exposes workspace-root probing, package discovery options, and tolerant manifest types through aube::embed.
  • Adds opt-in confined discovery that validates patterns and returns canonical in-root package paths.
  • Expands braces in positive and negative workspace patterns.
  • Makes an existing workspace YAML file authoritative even when its package list is empty.
  • Adds unit and integration coverage for the new discovery behavior.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; confined discovery now checks canonical package paths against the canonical root and returns those validated paths, resolving both previously reported symlink issues.

Important Files Changed

Filename Overview
crates/aube-workspace/src/lib.rs Adds workspace boundary options, brace expansion, YAML precedence changes, and canonical containment enforcement that addresses both prior symlink findings.
crates/aube/src/embed.rs Exposes workspace discovery and manifest APIs to Rust embedding hosts.
crates/aube/tests/embed.rs Verifies confined workspace discovery through the public embedding facade.

Reviews (4): Last reviewed commit: "test(embed): compare canonical workspace..." | Re-trigger Greptile

Comment thread crates/aube-workspace/src/lib.rs Resolved
Base automatically changed from agent/embed-workspace-discovery to main July 28, 2026 19:05
@github-actions

Copy link
Copy Markdown

Instruction counts

benchmark trend instructions Δ wall (min) Δ
graph ▁▁▁▄▄▄▄▄▄▄▄▄▄▄▄▄████ 16,720,122 → 16,716,022 -0.02% 3.59 → 4.72ms +31.35%
install ▁▂▂▂▂▃▇▆▅▇▆▆▆▅▇▆█▇▇█ 103,091,347 → 103,109,281 +0.02% 36.22 → 31.08ms -14.18%
startup ▂▂▁▄▄▄▄▄▅▄▄▄▄▄▄▄████ 7,671,850 → 7,668,813 -0.04% 3.77 → 2.79ms -25.98%
tree ▁▁▁▄▄▄▄▄▄▄▄▄▄▄▄▄███▇ 17,738,526 → 17,732,092 -0.04% 4.46 → 5.02ms +12.52%

No instruction-count regression above 1%.

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

0ab2087aa723 vs 8e5716aed05a · measured on this runner, not pushed to the history.

@jdx
jdx enabled auto-merge (squash) July 28, 2026 19:39

@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 (1)
crates/aube-workspace/src/lib.rs (1)

489-613: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing confined-discovery test for negated patterns.

confined_discovery_rejects_parent_relative_patterns and confined_discovery_validates_expanded_brace_alternatives only cover positive patterns. validate_workspace_pattern is applied identically to negated patterns (Line 127 runs before the if negated branch), but that path has no direct test — worth a quick regression test (e.g. packages:\n - 'packages/*'\n - '!../outside/**'\n) to lock in the behavior.

🤖 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/aube-workspace/src/lib.rs` around lines 489 - 613, Add a
confined-discovery regression test alongside
confined_discovery_rejects_parent_relative_patterns that includes a valid
positive pattern and a negated parent-relative pattern such as !../outside/**.
Call find_workspace_packages_with_options with
WorkspaceDiscoveryOptions::confined_to_root(), assert Error::Parse, and verify
the error reports that the pattern cannot escape the workspace root.
🤖 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/aube-workspace/src/lib.rs`:
- Around line 219-266: Bound brace expansion in expand_braces by enforcing a
finite limit on recursion/groups, total generated alternatives, or pattern size,
and propagate limit violations as Error::Parse through its callers rather than
silently truncating results. Preserve normal expansion behavior below the
configured threshold while ensuring attacker-controlled workspace patterns
cannot cause exponential memory/CPU use or unbounded recursion.

---

Nitpick comments:
In `@crates/aube-workspace/src/lib.rs`:
- Around line 489-613: Add a confined-discovery regression test alongside
confined_discovery_rejects_parent_relative_patterns that includes a valid
positive pattern and a negated parent-relative pattern such as !../outside/**.
Call find_workspace_packages_with_options with
WorkspaceDiscoveryOptions::confined_to_root(), assert Error::Parse, and verify
the error reports that the pattern cannot escape the workspace root.
🪄 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: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: b0eeb79e-68e5-434e-b2d7-ae19dc5e63ba

📥 Commits

Reviewing files that changed from the base of the PR and between 2727166 and 0ab2087.

📒 Files selected for processing (3)
  • crates/aube-workspace/src/lib.rs
  • crates/aube/src/embed.rs
  • crates/aube/tests/embed.rs

Comment on lines +219 to +266
fn expand_braces(pattern: &str) -> Vec<String> {
let mut depth = 0;
let mut open = None;
let mut commas = Vec::new();
for (index, character) in pattern.char_indices() {
match character {
'{' => {
if depth == 0 {
open = Some(index);
commas.clear();
}
depth += 1;
}
',' if depth == 1 => commas.push(index),
'}' if depth > 0 => {
depth -= 1;
if depth == 0 {
let Some(open_index) = open else {
continue;
};
if commas.is_empty() {
open = None;
continue;
}
let mut boundaries = Vec::with_capacity(commas.len() + 2);
boundaries.push(open_index + 1);
boundaries.extend(commas.iter().map(|comma| comma + 1));
boundaries.push(index + 1);
return boundaries
.windows(2)
.flat_map(|window| {
let end = window[1] - 1;
let expanded = format!(
"{}{}{}",
&pattern[..open_index],
&pattern[window[0]..end],
&pattern[index + 1..]
);
expand_braces(&expanded)
})
.collect();
}
}
_ => {}
}
}
vec![pattern.to_string()]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unbounded brace expansion can blow up combinatorially / recurse deeply on attacker-controlled patterns.

expand_braces has no limit on the number of alternatives produced or on recursion depth. Sequential brace groups compound multiplicatively ({a,b}/{a,b}/{a,b}/... → 2^N results) and each group adds a recursive call, so a crafted pnpm-workspace.yaml/package.json#workspaces entry with many groups causes exponential memory/CPU use and/or a deep call stack. This directly undermines the security goal of WorkspaceBoundary::ConfinedToRoot, which exists specifically for "embedding hosts that discover projects inside a preselected repository" — i.e., repos whose contents (including this very config) the host doesn't necessarily trust.

Consider capping either the number of brace groups per pattern, the total expanded-alternative count (bail with Error::Parse past a threshold), or the pattern string length before expansion.

💡 Example guard
 fn expand_braces(pattern: &str) -> Vec<String> {
+    const MAX_ALTERNATIVES: usize = 64;
     let mut depth = 0;
     let mut open = None;
     let mut commas = Vec::new();
@@
                     return boundaries
                         .windows(2)
                         .flat_map(|window| {
                             let end = window[1] - 1;
                             let expanded = format!(
                                 "{}{}{}",
                                 &pattern[..open_index],
                                 &pattern[window[0]..end],
                                 &pattern[index + 1..]
                             );
                             expand_braces(&expanded)
                         })
-                        .collect();
+                        .collect::<Vec<_>>();
                 }

(Sketch only — the cap needs to be threaded through as a Result so callers can surface a validation error instead of silently truncating.)

🤖 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/aube-workspace/src/lib.rs` around lines 219 - 266, Bound brace
expansion in expand_braces by enforcing a finite limit on recursion/groups,
total generated alternatives, or pattern size, and propagate limit violations as
Error::Parse through its callers rather than silently truncating results.
Preserve normal expansion behavior below the configured threshold while ensuring
attacker-controlled workspace patterns cannot cause exponential memory/CPU use
or unbounded recursion.

@jdx
jdx merged commit 4ef1505 into main Jul 28, 2026
39 checks passed
@jdx
jdx deleted the agent/embed-configurable-workspace-discovery branch July 28, 2026 19:57
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.

1 participant