Skip to content

Skill activation: /workspace/.skills/<name> missing scripts/data subdirs on docker backend (nested bind mount) #2271

Description

@huanghuoguoguo

Issue: Activated skill's subdirectories (scripts/, data/) missing under /workspace/.skills/<name> (docker backend)

Status: Open — NOT caused by the skill all-tool-model branch (feat/agent-runner-plugin). Confirmed via git: the entire mount/register/package chain is byte-identical to origin/master.

Symptom

After an agent creates → registers → activates a multi-file skill, running its script from the activated mount path fails:

exec workdir=/workspace/.skills/lb-e2e-skill  cmd="python3 scripts/use.py"
-> exit_code=2  stderr="python3: can't open file '/workspace/.skills/lb-e2e-skill/scripts/use.py': No such file or directory"

Inside the running sandbox container:

  • /workspace/lb-e2e-skill (where the agent built it) → has SKILL.md + scripts/use.py
  • /workspace/.skills/lb-e2e-skill (the activate mount point) → empty

So activate succeeds (skill registered, manager.py: Loaded 1 skills, mount point created), but the activated path does not expose the skill's scripts//data/ subdirectories — only the top-level SKILL.md is recognized.

Repro

  1. local docker backend, host shares filesystem with box (shares_filesystem_with_box=True).
  2. Agent: exec to create /workspace/<skill> with SKILL.md + scripts/use.py.
  3. register_skill path=/workspace/<skill>box_service.create_skill → Box runtime skill store.
  4. activate skill_name=<skill>.
  5. exec workdir=/workspace/.skills/<skill> cmd="python3 scripts/use.py"No such file.

Diagnosis (where it breaks)

  • register_skill (skill_authoring.py) resolves the sandbox dir to a host path and calls skill_service.create_skill({package_root: host_path})box_service.create_skillself.client.create_skill (Box runtime, SDK).
  • package_root consumed at activation comes from Box runtime list_skills() (manager.py: "Loaded N skills from Box runtime"), not the agent's raw workspace dir.
  • build_skill_extra_mounts (box/service.py:261) bind-mounts that package_root to /workspace/.skills/<name> — while /workspace itself is already bind-mounted to data/box/default. This nested bind mount (mounting a workspace subdir back into another path inside the same workspace), under docker + shared filesystem, ends up exposing an empty dir.

Two non-exclusive root causes to confirm:

  1. Box runtime skill store packaging may only persist SKILL.md/metadata, not the scripts//data/ subtree, so package_root points at an incomplete copy.
  2. Docker nested bind mount of a path inside an already-bind-mounted /workspace is masked/empty.

Code ownership (git evidence)

The whole chain is unchanged by feat/agent-runner-plugin:

layer file feat vs master
mount helpers skill.py (get_visible_skills / package_root / parse_skill_mount / resolve_virtual) 0 changes
host skill service box/service.py (build_skill_extra_mounts, create_skill) unchanged (only box-session-id-template lookup source changed elsewhere)
SDK mount exec langbot-plugin-sdk/box/backend.py (docker run + -v extra_mounts) 0 changes
SDK skill store SDK box/ (Box runtime create_skill / list_skills) 0 changes

build_skill_extra_mounts (the nested-mount logic) was introduced in 96b04184 "Feat/sandbox (#2072)" (2026-06-03); later hardened by 8e558ad3 (only added a package_root existence guard). The all-tool-model branch only exposed this path end-to-end for the first time (agent can now reach activate→exec-on-mount), it did not change the mounting code.

Unconfirmed

  • Whether skill activation e2e was ever actually run-and-passed on the docker backend before (QA case sandbox-skill-authoring-e2e.yaml exists, but no pass record found in master's STATUS). The flow may have only been exercised on nsjail/E2B, or this docker+shared-fs nested-mount case may have always failed.

Suggested fix directions

  • When host shares filesystem with box, avoid nested bind-mounting package_root into /workspace/.skills/<name>; instead create a symlink inside the workspace (/workspace/.skills/<name> → the real skill dir), sidestepping docker's masking of nested mounts.
  • And/or verify Box runtime's skill store persists the full package (incl. scripts/, data/), not just SKILL.md.

Reproduction env

  • backend=docker, image rockchin/langbot-sandbox:latest, host_path=data/box/default, mount_path=/workspace, read_only_rootfs=true.
  • Pipeline 3e645b04-... (Agent QA Local Agent Debug Chat), model claude-opus-4-8.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug?Bug或Bug修复相关 / maybe a bugpd: Need reproducingpending: 需要测试以复现的issue,若您遇到相同问题,请提供更多的有价值的信息 / Please add more info as you can for us to reproduce

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions