Skip to content

Contain linked project skill roots inside the repository - #2990

Open
ylcn91 wants to merge 1 commit into
get-bb:mainfrom
ylcn91:fix/skill-root-containment
Open

Contain linked project skill roots inside the repository#2990
ylcn91 wants to merge 1 commit into
get-bb:mainfrom
ylcn91:fix/skill-root-containment

Conversation

@ylcn91

@ylcn91 ylcn91 commented Sep 3, 2026

Copy link
Copy Markdown

Human comments

What was wrong

scanSkillRootFiles opened a non-recursive project skill root before
resolving it against the workspace boundary, so a root that is itself a
symlink was scanned wherever it pointed. A checked-in .claude/skills
link to a directory outside the repository put every skill under it into
the project's skill and command lists, with origin: "project". The
recursive shape already resolved the same plumbed boundaryPath before
walking, and docs/provider-plugin-api.md already states the rule for
both shapes: a symlink out of a project root is followed within the
workspace for a plain root and within the repository root for a root
that walks ancestors or that the plugin resolved.

Issue: #2769. Investigation report: https://get-bb.github.io/reports/issues/2769.html

What changed

resolveRecursiveRootPath becomes resolveBoundedRootPath and the flat
skill root scan consults it, so both shapes enforce one containment rule
against the same boundaryPath. A root link that stays inside the
repository still resolves and is still scanned, and user-origin roots,
which carry no boundary, are unaffected.

No field or message on the server/host-daemon wire changed, so
HOST_DAEMON_PROTOCOL_VERSION stays at 178.

How you verified

  • Added two command-discovery regressions: a project .claude/skills
    root symlinked outside the workspace discovers nothing, and one
    symlinked to .agents/skills inside the workspace still discovers its
    skill. The first fails on the parent commit
    (expected [ { name: 'leaked', …(4) } ] to deeply equal []) and
    passes here; the second passes on both.
  • pnpm exec turbo run test --filter=@bb/host-daemon --force: 46 files,
    574 tests passed (1 failed before the fix).
  • pnpm exec turbo run typecheck --filter=@bb/host-daemon: passed.
  • BB_TEST_TIMEOUT_SCALE=3 pnpm exec turbo run test --filter=@bb/integration-tests --force --env-mode=loose -- native-roots-golden: 22 provider golden fixtures passed unchanged,
    including the cursor symlink-boundary and grok symlink variants.
  • Drove listHostCommands with the shipping claude-code declaration
    (.claude/skills, ancestors: true) over the issue's fixture: the
    escaping root reported ["leaked"] before and [] after, while the
    in-repository link reported ["in-repo"] in both.

Fixes #2769

AGENT GENERATED

## What was wrong

`scanSkillRootFiles` opened a non-recursive project skill root before
resolving it against the workspace boundary, so a root that is itself a
symlink was scanned wherever it pointed. A checked-in `.claude/skills`
link to a directory outside the repository put every skill under it into
the project's skill and command lists, with `origin: "project"`. The
recursive shape already resolved the same plumbed `boundaryPath` before
walking, and `docs/provider-plugin-api.md` already states the rule for
both shapes: a symlink out of a project root is followed within the
workspace for a plain root and within the repository root for a root
that walks ancestors or that the plugin resolved.

## What changed

`resolveRecursiveRootPath` becomes `resolveBoundedRootPath` and the flat
skill root scan consults it, so both shapes enforce one containment rule
against the same `boundaryPath`. A root link that stays inside the
repository still resolves and is still scanned, and user-origin roots,
which carry no boundary, are unaffected.

No field or message on the server/host-daemon wire changed, so
`HOST_DAEMON_PROTOCOL_VERSION` stays at 178.

## How you verified

- Added two `command-discovery` regressions: a project `.claude/skills`
  root symlinked outside the workspace discovers nothing, and one
  symlinked to `.agents/skills` inside the workspace still discovers its
  skill. The first fails on the parent commit
  (`expected [ { name: 'leaked', …(4) } ] to deeply equal []`) and
  passes here; the second passes on both.
- `pnpm exec turbo run test --filter=@bb/host-daemon --force`: 46 files,
  574 tests passed (1 failed before the fix).
- `pnpm exec turbo run typecheck --filter=@bb/host-daemon`: passed.
- `BB_TEST_TIMEOUT_SCALE=3 pnpm exec turbo run test
  --filter=@bb/integration-tests --force --env-mode=loose --
  native-roots-golden`: 22 provider golden fixtures passed unchanged,
  including the cursor symlink-boundary and grok symlink variants.
- Drove `listHostCommands` with the shipping claude-code declaration
  (`.claude/skills`, `ancestors: true`) over the issue's fixture: the
  escaping root reported `["leaked"]` before and `[]` after, while the
  in-repository link reported `["in-repo"]` in both.

Fixes get-bb#2769
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.

A linked project skill root is scanned outside the repository

1 participant