Skip to content

fix(daemon): install a placed session's skills into its checkout - #2801

Merged
zfy0701 merged 2 commits into
agentconnect-md:mainfrom
joerideturck:fix/placed-session-skills-in-cwd
Oct 6, 2026
Merged

zfy0701 merged 2 commits into
agentconnect-md:mainfrom
joerideturck:fix/placed-session-skills-in-cwd

Conversation

@joerideturck

Copy link
Copy Markdown
Contributor

Problem

A session placed on another daemon in its group never sees its installed skills. That covers both runtimes we
checked, Codex and Claude. An agent whose instructions name a skill then guesses a path:

sed: can't read …/sessions/session-10ce5e33…/home/.codex/skills/front-email-automation/SKILL.md: No such file or directory

It looks intermittent because it depends on placement. The same agent, runtime and release behave differently:

Session runs on Skills installed at Runtime lists them
its own daemon agents/<agent>/sessions/<leaf>/workspace/.agents/skills (inside the checkout) yes
another daemon (executor: session … runs on daemon …) sessions/<leaf>/.agents/skills (above the checkout) no

Measured on two Linux VM daemons in one group, 2.2.0-rc.43:

  • Codex: of 75 placed sessions with installed skills, Codex listed them in none. Their cwd is
    <leaf>/workspace, a git clone, and Codex scans .agents/skills "in every directory from your current working
    directory up to the repository root", which here is workspace/. In 3 sessions the model found the skills only
    by searching the disk after the guess failed.
  • Claude: in placed Claude sessions, the skill listing holds only global skills. None of the 20+ bundles
    installed in <leaf>/.claude/skills appear.

Cause

runAgentWorkspacePreparation prepares a placed session's checkout (cwd = prepareExecutorWorkspace(…)), then
calls reconcileClusterSkills(agent, placed.subject, plane) without that cwd. ExecutorPlane.skillClientFor sends
bare skills requests, so the executor's shim installs at its own workspace root, which is the session directory.
The microsandbox path already aims the same shim at the runtime's cwd (microsandboxSkillTarget sends
{ cwd, request }), and the daemon-local path installs into the cwd too.

Change

  • ExecutorPlane.skillClientFor(subject, cwd?) wraps requests as { cwd, request } when given a cwd.
    cwdSkillRequester in shim/skill-client.ts is shared with microsandboxSkillTarget, so the two stay one
    shape. The shim side already supports this, including refusing a cwd that escapes its root
    (shim-exec-handler.test.ts).
  • reconcileClusterSkills takes an optional cwd and returns the ledger. The placed path passes the prepared cwd.
  • Ledger key: receipt paths are relative to the directory installed into. Installs into a cwd use
    cwdWorkspaceIncarnation(reported, cwd), so receipts recorded at the session directory are never read against
    the checkout. Missing prior bundles are already skipped, but a receipt whose path the repository tracks would
    fail the safety check. The launch fence still compares the incarnation the executor reported.
  • Excluded from git: the bundles and .agentconnect/cluster-skill-state go into the checkout's info/exclude
    through the executor's plane (excludePlacedSessionSkills), as the local and microsandbox paths do, so a placed
    checkout does not report them in git status or pin the retention GC. withSkills keeps its off-disk guard
    unchanged for pool pods, whose bundles stay outside the checkout.
  • session-executors.md: skills are published into the session's checkout.

Pool pods (K8sRuntimePlane) are unchanged: they pass no cwd.

Tests

  • executor-plane.test.ts › "the skills a placed session installs":
    • requests carry { cwd, request } when a cwd is named and are bare without one
    • the cwd ledger key differs from the reported incarnation and per cwd, and is stable
  • git-exclude.test.ts › "excludes a placed session's bundles, installed into its checkout, through the plane
    that holds it": git questions go to the plane's runner, and the exclude file is written through the plane's
    filesystem, never this disk. The existing off-disk test for withSkills still passes unchanged.
  • Related suites: executor, shim skill handler and protocol, cluster skill coordinator and ledger,
    microsandbox shim, sandbox skill ledger, git-exclude: 230 passed. The 2 failures in daemon-session-hosts
    (microsandbox custom TLS) fail identically on main on macOS.
  • k8s and cluster suites (19 files): 464 passed.
  • Daemon typecheck, eslint and prettier are clean, apart from warnings already on main.

Not in this PR

  • Bundles already installed at a placed session's directory stay there, unused. They are outside the checkout
    and harmless.
  • Pool pods install at their workspace root. Whether their runtimes find those skills is a separate question
    this PR does not change.

🤖 Generated with Claude Code

A session placed on another daemon got its skills from reconcileClusterSkills without a
cwd, so the executor's shim installed them at its own root: the session directory, one
level above the checkout. Runtimes scan for project skills from their cwd up to the
repository root only, so neither Codex nor Claude listed them, and agents told to use a
skill guessed paths that do not exist.

The executor plane's skill client now aims requests at the runtime's cwd, as the
microsandbox path already does; installs there keep their own ledger key, and the
bundles are recorded in the checkout's info/exclude through the executor's plane.

@agentconnect-md-test agentconnect-md-test 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.

Reviewed revision 21bb47c28e8bc8c9b30a88d715765ee4639a92e2. No new blocking issues found. The prepared cwd is carried through the skill requests, receipts use a separate cwd-specific key while retaining the launch fence, and Git exclusions are written through the executor’s filesystem. Pool behavior remains unchanged.

Validation: git diff --check passed. Focused tests were not executed locally: the fresh checkout required a full dependency installation, which I stopped after slow progress. CI was still running at review time.

sent by review-bot (Codex · gpt-6-astra) · open in session

…st path rules

The workspace manager joins every placed path with node:path, so on Windows the exclude
file path uses backslashes; the test now expects what the manager composes.

@agentconnect-md-test agentconnect-md-test 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.

Reviewed revision 49540a49c2b7f562effd22a27978d06ddd6c4fdc. No new blocking issues found. The only change since the previously approved revision uses path.join consistently in the Git-exclusion test fixture, making its expected filesystem paths match the host’s path rules.

Validation: git diff --check and focused Node assertions using both POSIX and Windows path semantics passed. The full test suite was not rerun locally; CI is still running.

sent by review-bot (Codex · gpt-6-astra) · open in session

@zfy0701
zfy0701 merged commit e21ec4b into agentconnect-md:main Oct 6, 2026
13 checks passed
zfy0701 added a commit that referenced this pull request Oct 6, 2026
…ectory (#2804)

Since #2801 a placed session installs its skills into its checkout
under a ledger of their own. Bundles an earlier daemon installed at the
session directory stayed there under the old ledger, which nothing read
again, so a skill revoked or removed later was never deleted from disk.

After installing into the checkout, the placed path now reconciles that
earlier ledger to nothing through the shim's own root. A failure is
logged and retried at the next preparation; it does not fail the launch.

Tests: a daemon-level test of the placed path (installs aimed at the cwd
under the cwd ledger key, the exclude call, the retirement and its
retry), and the exclude test now refuses an agent-wide filesystem.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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