Skip to content

fix(core): bound skill discovery and watches - #55597

Merged
rekram1-node merged 2 commits into
devfrom
bounded-skill-scan
Oct 18, 2026
Merged

rekram1-node merged 2 commits into
devfrom
bounded-skill-scan

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #55367
Closes #45961
Closes #51080

Supersedes #55479 and #55234. Both authors are credited as co-authors on the commit.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Skill discovery used {*.md,**/SKILL.md} with symlinks followed, no depth limit and no exclusions. It descended into node_modules, and symlink cycles crashed it. When a found SKILL.md resolved outside the watched sources, the plugin recursively watched the file's parent directory. home-manager links SKILL.md files straight into /nix/store, so that parent was the whole store: about 1.4M directories, enough to exhaust max_user_watches.

The skill and compatibility plugins now share SkillScan.scan, a breadth-first walk:

  • A directory containing SKILL.md is one skill, and the walk does not search inside it.
  • Loose *.md files still count only at the source root.
  • Hidden entries and node_modules are skipped.
  • The walk stops six directories below the source root, which also ends symlink cycles.

Watches are unchanged except for two things:

  • Recursive skill watches ignore hidden entries and node_modules.
  • A skill file outside the watched sources gets a file watch instead of a recursive watch on its parent directory.

A shared FSUtil.ignoredDirectories list (node_modules, .git, .venv, venv, __pycache__) now backs the skill scan, the skill and config directory watches, and the supporting-file sample Skill.prepare builds for a loaded skill, which previously globbed everything inside the skill directory. Glob.Options gains ignore for this.

Behavior changes: a SKILL.md inside another skill's directory, skills under hidden directories, and skills more than six directories deep no longer load. The skills docs describe the rules and fix a stale note about root SKILL.md IDs.

How did you verify your code works?

  • packages/core: bun typecheck and the config and skill tests pass (254). packages/util tests pass. Root bun run check passes.
  • Existing watch tests were updated for the shared ignore list and for the file watch on symlinked skills.
  • A throwaway test (not committed) confirmed the skill file sample skips the ignored directories.
  • A throwaway scan test (not committed) checked nested skills, hidden and node_modules skipping, the depth limit, a symlinked SKILL.md and symlink cycles. The cycles finished in a few milliseconds.
  • Checked against the native watcher that the ignore globs match relative to the watched root, including roots under hidden directories.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This fixes the store watch. I ran the #55367 reproduction from source against a fake store of about 5,000 directories with a symlinked SKILL.md pointing into it. On the base, the server watched the whole store recursively and held about 5,065 inotify watches. With this change, it holds a single file watch on the skill target, about 15 inotify watches in total, and the nix.expert skill still loads. The skill config tests and typecheck in packages/core pass.

Public API: no SDK, HTTP, plugin, config or CLI surface changes. Skill discovery rules do change: these skills no longer load:

  • SKILL.md files nested inside another skill
  • skills under hidden directories or node_modules
  • hidden root .md files
  • root .md files next to a root SKILL.md

The docs cover the new rules.

I have no blocking concerns.

Related: #53393, #54114, #51208

rekram1-node and others added 2 commits October 17, 2026 18:36
Replace the unbounded `{*.md,**/SKILL.md}` glob with a breadth-first skill
scan. A directory containing SKILL.md is one skill and is not searched
further, hidden entries and node_modules are skipped, and the scan stops six
directories below the source root.

Recursive skill watches ignore hidden entries and node_modules. A skill file
that resolves outside the watched sources gets a file watch instead of a
recursive watch on its parent, which could be all of /nix/store.

Co-authored-by: Dante <duanjl.china@gmail.com>
Co-authored-by: Kugesh Rajasekaran <47845948+kugesh-Rajasekaran@users.noreply.github.com>
@rekram1-node
rekram1-node merged commit 49e9786 into dev Oct 18, 2026
19 checks passed
@rekram1-node
rekram1-node deleted the bounded-skill-scan branch October 18, 2026 03:33
hts1238 added a commit to hts1238/opencode that referenced this pull request Oct 21, 2026
Merge anomalyco/opencode dev through 055d95b,
the final upstream revision before v2 promotion removed the legacy app and
server. Keep the fork history and its session, timeline, model, push and
OpenAI response-ID fixes.

Keep the legacy interface as the default without an expiry. Persist an
explicit layout preference so either interface remains selectable.

Backport bounded skill discovery from upstream anomalyco#55597 and linear snapshot
index updates from anomalyco#51996. Include upstream SSE cancellation handling and
bounded provider retries from the compatible merge. Apply the fork's
rotated response-ID SDK patch to OpenAI 3.0.88.

Validation: app/core/opencode and e2e types; focused app, core and legacy
server tests; regenerated clients; embedded local binary build and version
smoke test. Three time-limited tests passed on isolated rerun. The original
production streaming benchmark failed to complete, so no quantitative
before/after performance claim is made.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant