Repository navigation
fix(plugin): clean candidate tree, skill script paths and k-architect delegation - #57
Merged
Merged
Conversation
scripts/build-plugin-candidate-commit.sh used to run git rm -rq . + cp -a "$ASSEMBLE_DIR"/. . + git add -A inside the same checkout that an earlier workflow step had already run `make -C cli build` in. git rm -rq . also deleted the checked-out .gitignore, so git add -A then picked up whatever untracked build output was left in that checkout (cli/konductor-rs/target/, build/cli/konductor, dist/, generated/claude-plugin/), with no .gitignore left to exclude it. The script now builds the candidate's tree directly from --assemble-dir's own contents, using a scratch git index that is never associated with the checkout's real index or working directory. The checkout's working directory is never read or written, so untracked build output there can never reach the candidate commit regardless of what caused it. A final safety check confirms the candidate commit's file listing matches --assemble-dir's file listing on disk exactly before the branch is created. Adds a regression test that reproduces the defect: untracked build junk in the checkout, with the base branch tracking a .gitignore that would have excluded it. The test fails against the prior script and passes against this one.
…in installs
dispatch.sh and cmux-dispatch.sh were documented and invoked by a path
relative to the current working directory (skills/mux-dispatch/dispatch.sh,
skills/cmux-dispatch/cmux-dispatch.sh). That path only resolves from a
source checkout of this package; a plugin or Kiro CLI install puts the
user's own project at the cwd, with no skills/ directory under it, so
delegation could not run at all.
Both SKILL.md files now resolve the skill's own directory first via
Claude Code's ${CLAUDE_SKILL_DIR} substitution, falling back to a source
checkout, a global Kiro CLI install, or a project-level Kiro CLI install.
The konductor-mux-orchestrator and konductor-cmux-orchestrator agent
prompts point at the same skill-driven resolution instead of duplicating
their own path logic (which also named an internal package directory as
an example).
cmux-dispatch.sh now resolves its own real directory via BASH_SOURCE[0]
(symlink-safe, matching dispatch.sh's existing approach) and warns if its
sibling cmux-completion-hook.sh is missing from a broken install, instead
of silently depending on an unverified relative path.
persistent-memory's memory-validator.sh and legacy-to-agentic-estimate's
convert_estimates.py were located via a source-checkout path
(git rev-parse --show-toplevel) or a global Kiro CLI install
($HOME/.konductor/skills), with no path that resolves in a Claude Code
plugin install.
Both now try ${CLAUDE_SKILL_DIR} first (Claude Code substitutes this to
the skill's own directory for both a plugin and a standalone skill
install), then fall back to the source-checkout and global-install paths,
and now also to a project-level Kiro CLI install (.konductor/skills/ at
the project root).
legacy-to-agentic-estimate also fails closed with a clear error instead
of running python3 against an empty path when none of the known
locations resolves, matching persistent-memory's existing behavior.
k-architect's Claude Code tool list granted Agent(...) access using a stale package-prefixed sub-agent name. That name cannot resolve to the shipped k-developer/k-quality-assurance agents in a plugin install, so the delegation silently failed. Changed to Agent(k-developer, k-quality-assurance), the unprefixed form every other agent spec already uses.
Adds:
- test_agent_tool_subagent_references.py: every Agent(...) tool grant
across agents/*.agent-spec.json must name an agent actually shipped in
this package, and must never carry an internal package-install prefix.
- test_skill_script_path_resolution.py: every SKILL.md that resolves a
bundled script's path must try ${CLAUDE_SKILL_DIR} before falling back
to a source checkout or a global/project .konductor install.
- tests/skills/mux-dispatch/test-dispatch-sh-cwd-independence.sh:
dispatch.sh reaches the correct sibling backend script when invoked by
absolute path (direct or through a symlink) from an unrelated cwd.
- tests/skills/cmux-dispatch/test-self-locating.sh: cmux-dispatch.sh
resolves its own real directory before anything else, so it runs
correctly from any caller cwd or through a symlink, and warns instead
of failing silently when its completion-hook sibling is missing.
17 of 25 tasks
knihit
marked this pull request as ready for review
October 16, 2026 16:57
knihit
requested review from
georgebearden,
ihmaws,
shsenior and
simonkrol
as code owners
October 16, 2026 16:57
The candidate commit and make claude-plugin-check now reject any top-level entry outside the files the marketplace plugin ships, so a later change to the assembly script can't publish build output such as cli/ or dist/.
Claude Code names plugin agents <plugin>:<agent>, so the bare Agent(k-developer, ...) grants in a plugin-only install matched no agent and delegation failed with "Agent type 'k-developer' not found". Assembly now rewrites every Agent() grant name to <plugin>:<agent>, using the name and agent list from the rendered plugin.json, and fails if a grant names an agent the plugin does not ship. Source specs keep bare names, which is what .claude/agents/ installs need.
The dispatch scripts only looked for agents under ~/.claude/agents and rejected ':' in --agent, so a plugin-only install could not dispatch. When ~/.claude/agents has no match and the skill sits inside a Claude Code plugin (<root>/.claude-plugin/plugin.json two levels up), launch claude --agent <plugin>:<agent> if the plugin ships that agent. An already-qualified <plugin>:<agent> is launched as-is. The existing lookup, ambiguity error and not-installed error are unchanged.
Describe the <plugin>:<agent> fallback in the mux-dispatch and cmux-dispatch skills, replace stale agent-name examples in delegation-protocol and claude-teams-behavior with the names a plugin or .claude/agents/ install actually uses, and add changelog entries.
…lback order
Claude Code substitutes only the exact text ${CLAUDE_SKILL_DIR}, so the
${CLAUDE_SKILL_DIR:-} form always expanded to nothing. The snippets now use
the exact form, then fall back to the global install, a project install with
konductor's .konductor/manifest, and a konductor source checkout. Any other
repo's skills/ directory is never used, and every snippet fails closed.
The test now runs each snippet against plugin, Kiro global, project,
source-checkout and hostile-repo layouts.
… injection A qualified --agent <plugin>:<agent> now launches only when <plugin> is the plugin the script runs from and that plugin ships the agent. A plugin-only install launches the bare name, which Claude resolves itself. cmux-dispatch pasted a prefix taken from a ~/.claude/agents filename into the pane command, so a crafted filename could run a command. The prefix must now match [A-Za-z0-9._-]*. The resolution tests are executable and listed in tests/skills/README.md.
…e no-op path The scratch index now uses git add -A -f, so an ignore rule inside the assembled tree cannot drop a file and turn a real change into a silent no-op. The tree is compared with the assembled directory before the no-op check, by path, mode and blob ID from git hash-object --no-filters, so content rewritten by git attributes fails the build. Symlinks and the executable bit are compared too. The branch ref is created with an empty old value so it cannot overwrite a branch created meanwhile.
check-claude-plugin-tree.sh skipped any entry that failed [ -e ], which includes a symlink to a missing target, so a dangling cli or dist link passed the allowlist check.
The assembly step's exit status was ignored, so a rejected Agent() grant still ended with "validated successfully". A failed assembly now fails the target and skips the tree check and validation.
11 of 13 tasks
12 of 14 tasks
…ing grants Both grant rewrites split an agent file on newlines and wrote the two halves back with no newline between them. The blank line after the closing --- was lost, and an agent whose body started right after the fence would have had its fence merged into the body.
ihmaws
reviewed
Oct 20, 2026
ihmaws
left a comment
Member
There was a problem hiding this comment.
Recommendation for future changes, this would have been easier to review as four PRs:
- the candidate-tree fix and tree check
- the Agent() grant rewrite and the k-architect grant
- dispatch plugin resolution and the cmux prefix fix, and
- skill script-path resolution. They don't depend on each other, and (1) is the release-blocking one.
Each Bash call starts a new shell, so MUX_DISPATCH_DIR and CMUX_DISPATCH_DIR were empty by the time a later dispatch command ran, which then executed /dispatch.sh. The resolver now prints the path, and the skills and orchestrator specs use it through a placeholder. Also trims the section to the resolver and one line on using its output.
…tter The agent grant rewrite skipped any agents/*.md without a frontmatter block, which also skipped the check that every Agent(...) grant names an agent the plugin ships. It now fails before writing anything. Both rewrite scripts also end frontmatter only at a column 0 fence, so an indented --- inside a block scalar no longer cuts the block short.
The old wording said a skills/ directory in any other repo is never used. A repo with .konductor/manifest, or a marketplace.json naming konductor, is searched, so state those markers instead.
ihmaws
previously approved these changes
Oct 22, 2026
simonkrol
previously approved these changes
Oct 22, 2026
knihit
added a commit
that referenced
this pull request
Oct 22, 2026
Resolve conflicts with #57: keep both assembly steps, the Agent() grant rewrite before the telemetry hook, and keep both [Unreleased] sections.
7 of 9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes found while preparing the first Claude Code marketplace release. Each one broke a plugin install or the plugin release itself.
Release: the candidate commit shipped build output
scripts/build-plugin-candidate-commit.shused to commit from the job's own checkout.git rm -rq .also removed the checked-out.gitignore, so untracked build output (cli/konductor-rs/target/,build/,dist/,generated/) was committed. That is the ~1,383 extra files in Publish Claude Code plugin v1.1.0 #56.--assemble-diralone, through a scratch index (git add -A -f,write-tree,commit-tree). It never reads the checkout.--assemble-dir: path, mode and blob ID (git hash-object --no-filters). An ignore rule or line-ending filter can't drop or change a file silently.update-refpasses an empty old value, so it never overwrites an existing branch.scripts/check-claude-plugin-tree.shfails on any top-level entry outside.claude-plugin,.mcp.json,LICENSE.txt,README.md,agents,docs,hooks,skills, including dangling symlinks.docsandhooksare for feat: add a telemetry hook to the Claude Code plugin #60. It runs in the release script and inmake claude-plugin-check.make claude-plugin-checknow fails when plugin assembly fails. Before, a failed assembly still ended "validated successfully".release.ymlis unchanged.Plugin agents: delegation was blocked
konductor:<agent>.Agent(...)grants andsubagent_typedon't resolve bare names, soAgent(k-developer, ...)matched nothing.scripts/rewrite-claude-plugin-agent-grants.pyruns during plugin assembly and qualifies each grant with the plugin name (12 names acrosskonductor,k-architect,k-quality-assurance). It fails if a grant names an agent the plugin doesn't ship. Source specs keep bare names, so CLI installs are unchanged.---, so an indented---in a block scalar no longer cuts it short.k-architect's grant also carried a stale package prefix. It is nowAgent(k-developer, k-quality-assurance)in the source.rewrite-claude-plugin-agent-grants.py, and the existingrewrite-claude-plugin-mcp-tool-names.py) dropped the newline after the closing---. The blank line after the fence was lost, and a body starting right after the fence would have merged into it. They now write the file back unchanged apart from the grants.delegation-protocolandclaude-teams-behaviorexamples now showkonductor:k-*for plugin installs andk-*for.claude/agents/installs.Dispatch: mux and cmux refused to dispatch in plugin installs
tmux-dispatch.sh,zellij-dispatch.shandcmux-dispatch.shonly found agents under~/.claude/agents/. A plugin install has none there.local-<pkg>-<agent>, then a registry<pkg>-<agent>, then the bare file.claude --agent k-developerresolves tokonductor:k-developerby itself.--agent konductor:<agent>is accepted only when it names this plugin and an agent it ships. Anything else gets the not-installed error.local-x$(cmd)-k-developer.mdrancmd.Skills: bundled scripts weren't found, and a repo could plant one
mux-dispatch,cmux-dispatch,persistent-memoryandlegacy-to-agentic-estimatelooked for their scripts relative to the working directory or the git root.${CLAUDE_SKILL_DIR}, which Claude Code substitutes with the skill's own directory${SKILLS_HOME:-$HOME/.konductor/skills}/<skill><git root>/.konductor/skills/<skill>, only if.konductor/manifestexists<git root>/skills/<skill>, only if the repo's.claude-plugin/marketplace.jsonnameskonductordispatch.shor the memory validator.cmux-dispatch.shfinds its sibling files from its own location, likedispatch.sh.Fixes #56's root causes. #56 was closed without merging.
Type of Change
Testing
python3 -m pytest tests/ -q: 221 passedtests/skills/mux-dispatch/*.shandtests/skills/cmux-dispatch/*.sh: all pass.test-plugin-agent-resolution.shhas 24 cases for tmux and zellij and 14 for cmux, covering CLI,local-and registry installs, plugin-only, qualified names, unknown names and the cmux prefix injection.make claude-plugin-checkpasses. With a bogus grant it exits non-zero.local-*lookup on purpose fails 3 dispatch cases.~/.claude/agents):konductor:konductordelegates and the subagent replies.k-developerworks, andkonductor:k-missingis refused.persistent-memoryandlegacy-to-agentic-estimaterun their scripts from the substituted plugin path.konductor install --harness claude, global and--target): skills resolve under.claude/skills/, bare delegation works, and tmux dispatch finds the~/.claude/agentsfile.CLAUDE_SKILL_DIRunset) in a repo containing its ownskills/mux-dispatch/dispatch.sh:cd cli && make test: nocli/changesChecklist
Agents Affected
Agent(...)grantsmux-dispatchcmux-dispatchAgent(...)grantsNotes for Reviewers
scripts/assemble-claude-plugin-branch.shconflicts with feat: add a telemetry hook to the Claude Code plugin #60. Keep both steps, the grant rewrite first. The CHANGELOG[Unreleased]blocks also need a manual merge.--plugin-dirsessions: a pane launched from aclaude --plugin-dirsession doesn't inherit the flag, so the child can't see the plugin. Marketplace installs are fine.