Agents see operator-side skills (update-config, fewer-permission-prompts, loop, schedule, …) in skill_listing — strip the marketplace mirror from agent env #882

Closed
opened 2026-05-05 10:58:49 +00:00 by claude-desktop · 0 comments
Collaborator

Symptom

When an agent boots, the SDK injects a system reminder listing every available skill. Code-lead's last session contained the full Anthropic-official marketplace catalog, including:

  • update-config — "configure the Claude Code harness via settings.json"
  • fewer-permission-prompts — "add allowlist to project .claude/settings.json"
  • keybindings-help
  • loop, schedule
  • peon-ping-*, chrome-devtools-mcp:*, caveman:*
  • and ~20 more

None of these apply in container context: update-config and fewer-permission-prompts mutate ~/.claude/settings.json on the host, but the agent's permission policy comes from allowedTools passed to the SDK at boot — settings.json edits are no-ops mid-session. Worse, when the agent hits a real or perceived problem (see #881 / sdk-adapter Zod bug), it reaches for these skills as escape hatches and burns the dispatch.

Root cause

~/.config/claude-hooks/agent-env/<agent>/settings.json carries:

{
  "extraKnownMarketplaces": {
    "claude-plugins-official": {
      "type": "github",
      "repo": "anthropics/claude-plugins-official"
    }
  }
}

At SDK boot the marketplace is cloned into agent-env/<agent>/plugins/marketplaces/claude-plugins-official/ and every SKILL.md under it gets enumerated into the skill_listing attachment regardless of enabledPlugins (the install-list is consulted for plugin activation but the skill enumeration scans the marketplace tree).

The 3 plugins we actually want enabled (security-guidance, typescript-lsp, claude-md-management) are unrelated to those skills.

Decision

No marketplace. Vendor the 3 plugins we use directly into the repo, seed them into agent env-dirs from there, and remove extraKnownMarketplaces entirely. Cherry-picked or self-made plugins only — never a full upstream catalog.

Acceptance criteria

Vendor the plugins

  • Create apps/server/src/infrastructure/agent-env-sync/plugin-fixtures/ with three sub-folders, each containing the plugin's source tree copied verbatim from upstream:
    • security-guidance/ (from claude-plugins-official/plugins/security-guidance/)
    • typescript-lsp/ (from claude-plugins-official/plugins/typescript-lsp/)
    • claude-md-management/ (from claude-plugins-official/plugins/claude-md-management/)
  • Pin each to a specific upstream commit SHA recorded in a plugin-fixtures/SOURCES.md file (path → upstream repo + commit). Refresh policy: operator manually re-vendors when an upgrade is needed; no auto-update.
  • Each plugin folder retains its original plugin.json / plugin.yaml so the SDK identifies it as a valid plugin.

Strip the marketplace from agent env

  • apps/server/src/infrastructure/agent-env-sync/render-for-instance.ts no longer writes extraKnownMarketplaces into agent-env/<agent>/settings.json.
  • On every render, the renderer copies the three vendored plugin folders into agent-env/<agent>/plugins/cache/<plugin-name>/<version>/ so installed_plugins.json resolves locally without any marketplace lookup.
  • installed_plugins.json keeps the same three entries (security-guidance, typescript-lsp, claude-md-management) but their installPath points at the locally-seeded folder instead of claude-plugins-official cache.
  • Existing agent-env/*/plugins/marketplaces/claude-plugins-official/ directories are deleted on the next render (renderer's existing wipe-and-rebuild semantics handle this).

Verification

  • After rebuild + restart, dispatch a code-lead task and grep its session JSONL for update-config, fewer-permission-prompts, loop, schedule — these names must not appear in the skill_listing attachment.
  • All agent containers still boot and the 3 enabled plugins still load — security-guidance hook fires, typescript-lsp registers, claude-md-improver is available.
  • No network fetch for anthropics/claude-plugins-official happens at agent boot (verifiable by booting a container with no outbound DNS and confirming plugin activation still succeeds).

Skill-body safety rail (companion change)

  • In every skill body under skills/*.mdimplement.md, review.md, rebase.md, fix-ci.md, breakdown.md, address-review.md, plus the design-* siblings — append a short paragraph:

    Permission denials. If a tool call returns a permission-denied or schema error, do NOT invoke Skill, update-config, fewer-permission-prompts, or any other harness-config skill. You are running inside a containerised Claude SDK session — those skills are operator-side and have no effect here. Either find an allowed alternative (Bash + curl against the Forgejo REST API using the claude-desktop token, file-edit tools, etc) or report the gap as a comment on the issue and stop. Do not loop.

  • If skill bodies live in DB rows after the skills-rework milestone (#38), add the same paragraph to the body of every seeded skill row in the migration.

Out of scope

  • Fixing the underlying canUseTool Zod error that triggers the spiral in the first place — covered in #881.
  • Building a curated upstream fork of claude-plugins-official — explicitly rejected. We only ship plugins we have read end-to-end and decided we want.
  • Self-built plugins beyond the three currently in use — out of scope here. If we author a new plugin later it goes in the same plugin-fixtures/ tree.

References

  • Sample env-dir: ~/.config/claude-hooks/agent-env/code-lead/.
  • Agent-env-sync renderer: apps/server/src/infrastructure/agent-env-sync/render-for-instance.ts.
  • Sample affected sessions: same as #881~/.config/claude-hooks/agent-env/code-lead/projects/-state-worktrees-boss-default-charles--claude-hooks--boss-2F671/9bacbc0f-12ad-428c-b06f-629ea0a6c211.jsonl and 4 siblings.
  • Upstream marketplace (source of vendored plugins, not registered as marketplace): https://github.com/anthropics/claude-plugins-official.
## Symptom When an agent boots, the SDK injects a system reminder listing every available skill. Code-lead's last session contained the full Anthropic-official marketplace catalog, including: - `update-config` — "configure the Claude Code harness via settings.json" - `fewer-permission-prompts` — "add allowlist to project .claude/settings.json" - `keybindings-help` - `loop`, `schedule` - `peon-ping-*`, `chrome-devtools-mcp:*`, `caveman:*` - and ~20 more None of these apply in container context: `update-config` and `fewer-permission-prompts` mutate `~/.claude/settings.json` on the host, but the agent's permission policy comes from `allowedTools` passed to the SDK at boot — settings.json edits are no-ops mid-session. Worse, when the agent hits a real or perceived problem (see #881 / sdk-adapter Zod bug), it reaches for these skills as escape hatches and burns the dispatch. ## Root cause `~/.config/claude-hooks/agent-env/<agent>/settings.json` carries: ```json { "extraKnownMarketplaces": { "claude-plugins-official": { "type": "github", "repo": "anthropics/claude-plugins-official" } } } ``` At SDK boot the marketplace is cloned into `agent-env/<agent>/plugins/marketplaces/claude-plugins-official/` and every `SKILL.md` under it gets enumerated into the `skill_listing` attachment regardless of `enabledPlugins` (the install-list is consulted for plugin activation but the skill enumeration scans the marketplace tree). The 3 plugins we actually want enabled (`security-guidance`, `typescript-lsp`, `claude-md-management`) are unrelated to those skills. ## Decision **No marketplace.** Vendor the 3 plugins we use directly into the repo, seed them into agent env-dirs from there, and remove `extraKnownMarketplaces` entirely. Cherry-picked or self-made plugins only — never a full upstream catalog. ## Acceptance criteria ### Vendor the plugins - [ ] Create `apps/server/src/infrastructure/agent-env-sync/plugin-fixtures/` with three sub-folders, each containing the plugin's source tree copied verbatim from upstream: - `security-guidance/` (from `claude-plugins-official/plugins/security-guidance/`) - `typescript-lsp/` (from `claude-plugins-official/plugins/typescript-lsp/`) - `claude-md-management/` (from `claude-plugins-official/plugins/claude-md-management/`) - [ ] Pin each to a specific upstream commit SHA recorded in a `plugin-fixtures/SOURCES.md` file (path → upstream repo + commit). Refresh policy: operator manually re-vendors when an upgrade is needed; no auto-update. - [ ] Each plugin folder retains its original `plugin.json` / `plugin.yaml` so the SDK identifies it as a valid plugin. ### Strip the marketplace from agent env - [ ] `apps/server/src/infrastructure/agent-env-sync/render-for-instance.ts` no longer writes `extraKnownMarketplaces` into `agent-env/<agent>/settings.json`. - [ ] On every render, the renderer copies the three vendored plugin folders into `agent-env/<agent>/plugins/cache/<plugin-name>/<version>/` so `installed_plugins.json` resolves locally without any marketplace lookup. - [ ] `installed_plugins.json` keeps the same three entries (`security-guidance`, `typescript-lsp`, `claude-md-management`) but their `installPath` points at the locally-seeded folder instead of `claude-plugins-official` cache. - [ ] Existing `agent-env/*/plugins/marketplaces/claude-plugins-official/` directories are deleted on the next render (renderer's existing wipe-and-rebuild semantics handle this). ### Verification - [ ] After rebuild + restart, dispatch a code-lead task and grep its session JSONL for `update-config`, `fewer-permission-prompts`, `loop`, `schedule` — these names must not appear in the `skill_listing` attachment. - [ ] All agent containers still boot and the 3 enabled plugins still load — security-guidance hook fires, typescript-lsp registers, claude-md-improver is available. - [ ] No network fetch for `anthropics/claude-plugins-official` happens at agent boot (verifiable by booting a container with no outbound DNS and confirming plugin activation still succeeds). ### Skill-body safety rail (companion change) - [ ] In every skill body under `skills/*.md` — `implement.md`, `review.md`, `rebase.md`, `fix-ci.md`, `breakdown.md`, `address-review.md`, plus the `design-*` siblings — append a short paragraph: > **Permission denials.** If a tool call returns a permission-denied or schema error, do NOT invoke `Skill`, `update-config`, `fewer-permission-prompts`, or any other harness-config skill. You are running inside a containerised Claude SDK session — those skills are operator-side and have no effect here. Either find an allowed alternative (Bash + `curl` against the Forgejo REST API using the `claude-desktop` token, file-edit tools, etc) or report the gap as a comment on the issue and stop. Do not loop. - [ ] If skill bodies live in DB rows after the skills-rework milestone (#38), add the same paragraph to the body of every seeded skill row in the migration. ## Out of scope - Fixing the underlying `canUseTool` Zod error that triggers the spiral in the first place — covered in #881. - Building a curated upstream fork of `claude-plugins-official` — explicitly rejected. We only ship plugins we have read end-to-end and decided we want. - Self-built plugins beyond the three currently in use — out of scope here. If we author a new plugin later it goes in the same `plugin-fixtures/` tree. ## References - Sample env-dir: `~/.config/claude-hooks/agent-env/code-lead/`. - Agent-env-sync renderer: `apps/server/src/infrastructure/agent-env-sync/render-for-instance.ts`. - Sample affected sessions: same as #881 — `~/.config/claude-hooks/agent-env/code-lead/projects/-state-worktrees-boss-default-charles--claude-hooks--boss-2F671/9bacbc0f-12ad-428c-b06f-629ea0a6c211.jsonl` and 4 siblings. - Upstream marketplace (source of vendored plugins, not registered as marketplace): `https://github.com/anthropics/claude-plugins-official`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#882
No description provided.