fleetd #362: memberSkills seeds bridge skills into worktrees
+46
@@ -4529,3 +4529,49 @@ closes the channel it ran on — reusing the publish channel would let one miss
|
||||
publish on that instance. And `FleetMcp.probe` cancels a timed-out probe rather than abandoning it;
|
||||
without that, a hung (not down) broker would orphan one channel per `fleet_list` call until the
|
||||
connection's channel-max ran out, breaking publish by a different route.
|
||||
|
||||
## Bridge skills are seeded into every provisioned worktree
|
||||
|
||||
**What it does.** fleetd copies a directory of skill folders into each worker worktree it creates,
|
||||
at `<worktree>/.claude/skills/`. A skill folder whose name the target repo already ships is never
|
||||
touched — the repo's own copy wins, byte for byte.
|
||||
|
||||
**On.** Point `memberSkills:` at the directory:
|
||||
|
||||
```yaml
|
||||
memberSkills: /Users/dai.ha/LTMS/claude-bridge/.claude/skills
|
||||
```
|
||||
|
||||
Leave it out and nothing is seeded, which is the old behaviour. This is a deferred key — the
|
||||
`GitWorktrees` that reads it is built once at startup, so a change needs a daemon restart.
|
||||
|
||||
**Why it exists.** Every brief starts with `Load the <name> skill.`, and outside this repo that
|
||||
line was silently a no-op. A member spawned against any other repo — kb on fleet01, for example —
|
||||
had no `implementer`, `reviewer` or `hunter` to load, and nothing said so. The skills could not
|
||||
travel in the plugin either: `ClaudeCodeLauncher` exports `CLAUDE_CONFIG_DIR`, so a member never
|
||||
reads the operator's plugin store. The worktree is the only channel that reaches a member.
|
||||
fleetd #362 item 3.
|
||||
|
||||
**One thing to know for maintenance.** `core.excludesFile` is **single-valued**. The seeded paths
|
||||
are hidden from `git status` by pointing that key at a fleetd-written file, scoped `--worktree` —
|
||||
and a worktree-scoped value *replaces* the operator's global one rather than adding to it. The
|
||||
first cut of this feature did exactly that, and the consequence was severe: this repo's own
|
||||
`.gitignore` does not ignore `target/`, only an operator's global excludesFile does, so every
|
||||
worker that ran `mvn clean install` made `target/` untracked. `GitWorktrees.hasUncommitted` counts
|
||||
untracked files on purpose (CB-576), so `SessionManager` would have preserved every worktree that
|
||||
built, forever, with no error to notice.
|
||||
|
||||
So the file is **composed, not replaced**: whatever `core.excludesFile` resolved to beforehand is
|
||||
copied in ahead of the seeded patterns, including git's own default (`$XDG_CONFIG_HOME/git/ignore`,
|
||||
else `$HOME/.config/git/ignore`) when the key was unset. Measured on merge: removing the
|
||||
composition fails 2 tests, and removing just the default-file fallback fails 1.
|
||||
|
||||
Two smaller things. The composed content is a **snapshot** taken at seed time, so an operator
|
||||
editing their own excludesFile later does not change an already-seeded worktree. And the exclude
|
||||
file itself lives under the worktree's private git dir (`git rev-parse --absolute-git-dir`), not in
|
||||
the working tree, so it cannot be committed and `git worktree remove --force` deletes it along with
|
||||
everything else.
|
||||
|
||||
`.git/info/exclude` was rejected as the mechanism, and this is worth knowing before someone tries
|
||||
it again: from a linked worktree it resolves to the **common** git dir, so it would have hidden the
|
||||
seeded paths in the primary checkout and every sibling worktree too.
|
||||
|
||||
Reference in New Issue
Block a user