Summary
Selecting multiple targets whose names have the same length (claude, cursor, plugin are all 6 chars) makes artifact MCP validation attribute each target's files to the other targets, producing spurious AB6017 reference-count diagnostics. A real consumer (cargo-conductor) had to drop from targets: ['claude','codex','cursor','plugin'] to ['plugin','portable'] to get a green build.
Root cause
packages/agent-bundle/src/build/artifact-layout.ts:
export const pathInTargetOutputLayout = (
targetPath: string,
target: string,
layout: TargetArtifactOutputLayout | undefined,
): boolean => isDirectOutputLayoutPath(targetPath.slice(target.length + 1), layout);
The target prefix is stripped by length only — the function never checks that targetPath actually starts with ${target}/. So checking cursor/mcp/mcp-x.mjs against target claude slices 7 chars and yields mcp/mcp-x.mjs, which is inside the MCP layout ⇒ match.
validate-artifact-mcp.ts then filters the whole artifact file list per target:
const mcpEntries = options.files.filter((file) => pathInTargetOutputLayout(file.path, target.name, mcpLayout));
so with equal-length sibling targets, every target's mcp/ bundles are counted as entries of every other same-length target, and the per-target reference accounting fails (AB6017: unreferenced/multiply-referenced MCP entries).
The hooks validator is only accidentally immune: validate-artifact-hooks.ts checks hook.path.startsWith(expectedPrefix) separately before calling pathInTargetOutputLayout.
Repro
agent-bundle.config.ts with any MCP server and targets: ['claude', 'cursor'] (or any two same-length target names) → agent-bundle build → AB6017.
Suggested fix
Guard the prefix before slicing (the counterpart targetArtifactPath already produces ${target}/${path}):
export const pathInTargetOutputLayout = (targetPath, target, layout) =>
targetPath.startsWith(`${target}/`) &&
isDirectOutputLayoutPath(targetPath.slice(target.length + 1), layout);
Observed on the 560124af preview; the slice is unchanged at current main (3f8f08fc).
Summary
Selecting multiple targets whose names have the same length (
claude,cursor,pluginare all 6 chars) makes artifact MCP validation attribute each target's files to the other targets, producing spuriousAB6017reference-count diagnostics. A real consumer (cargo-conductor) had to drop fromtargets: ['claude','codex','cursor','plugin']to['plugin','portable']to get a green build.Root cause
packages/agent-bundle/src/build/artifact-layout.ts:The target prefix is stripped by length only — the function never checks that
targetPathactually starts with${target}/. So checkingcursor/mcp/mcp-x.mjsagainst targetclaudeslices 7 chars and yieldsmcp/mcp-x.mjs, which is inside the MCP layout ⇒ match.validate-artifact-mcp.tsthen filters the whole artifact file list per target:so with equal-length sibling targets, every target's
mcp/bundles are counted as entries of every other same-length target, and the per-target reference accounting fails (AB6017: unreferenced/multiply-referenced MCP entries).The hooks validator is only accidentally immune:
validate-artifact-hooks.tscheckshook.path.startsWith(expectedPrefix)separately before callingpathInTargetOutputLayout.Repro
agent-bundle.config.tswith any MCP server andtargets: ['claude', 'cursor'](or any two same-length target names) →agent-bundle build→ AB6017.Suggested fix
Guard the prefix before slicing (the counterpart
targetArtifactPathalready produces${target}/${path}):Observed on the
560124afpreview; the slice is unchanged at currentmain(3f8f08fc).