Skip to content

provision_worktree: seed_files containment failures are silent (the remaining leg) #415

Description

@pbean

What

provision_worktree's seed_files loop skips a worktree_seed entry silently when the
source resolves outside repo_root:

src = (repo_root / rel).resolve()
dst = (worktree / rel).resolve()
if not src.is_relative_to(repo_root) or not dst.is_relative_to(worktree):
    continue                       # <- silent
if not src.exists():
    continue                       # <- silent
if dst.exists():
    skipped.append(str(rel))       # <- the ONLY reported outcome

So from the caller's side, a seed entry whose source is a symlink pointing outside the repo is
indistinguishable from one that applied. The engine journals worktree-seed-skipped from the
returned list, so nothing is journalled either.

Why it matters

Same shape as the two cases already fixed on #406:

  • _bmad/scripts + _bmad/config.toml — covered by _bmad_scripts_seed_incomplete /
    _central_config_seed_incomplete, which re-ask the question of the result.
  • the BASE_SKILLS copy — covered by base_skills_seed_incomplete (897988f).

seed_files is the remaining leg. A shared/symlinked config (an MCP config, a CLI settings file
outside the repo) is exactly the wiring that triggers it, and the seeded configs are the things
an isolated session needs to reach its MCP server.

Lower severity than the two fixed cases: a missing seeded config degrades a session rather than
guaranteeing a result-less Stop, and worktree_seed is opt-in. That is why it was left out of
the 0.9.1 hotfix rather than fixed alongside.

Also worth a look

The seed_globs loop has the same containment continue. Its silence is documented — a
plugin's glob is expected to hit paths the checkout already carries — but that justification is
written for the dst.exists() case and the containment failure rides along in the same silence
with no separate rationale.

Suggested shape

Report the containment leg the way the other three now do: re-probe the result rather than
accumulating inside the loop, so the report survives any future path that drops a file. Whether
it should escalate (as the skills/renderer legs do) or stay a journal line is the open design
question — it depends on whether a seed entry is treated as required.

Provenance

Found while triaging the Codex review on #406 (PR #406 comment thread). Not introduced by #405
the guard predates it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions