fix(#4782): claude's agents kind skips compact variants — consumed only by the non-claude persona gate (#4878)

* test(#4782): failing-first — claude install must not stage compact agent variants (and must prune stale ones)

* fix(#4782): claude's agents kind skips compact variants — they are consumed only by the non-claude persona-fallback gate

Emitted-Drift-Ack-Hash: agents/gsd-advisor-researcher.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-ai-researcher.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-assumptions-analyzer.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-code-fixer.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-code-reviewer.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-codebase-mapper.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-debug-session-manager.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-doc-classifier.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-doc-synthesizer.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-doc-verifier.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-doc-writer.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-dom-verifier.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-domain-researcher.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-eval-auditor.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-eval-planner.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-framework-selector.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-integration-checker.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-intel-updater.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-mempalace-curator.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-nyquist-auditor.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-pattern-mapper.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-project-researcher.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-research-synthesizer.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-roadmapper.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-security-auditor.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-ui-auditor.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-ui-checker.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-ui-researcher.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies
Emitted-Drift-Ack-Hash: agents/gsd-user-profiler.compact.md — #4782: claude never selects compact (the init agent-skills persona gate is non-claude-only), so the claude agents kind no longer stages name-shadowing copies

* chore(#4782): changeset fragment

* chore(#4782): backfill changeset PR number (4878)

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-09-19 17:25:21 -04:00
committed by GitHub
parent 4fe2837ab5
commit d435723c95
5 changed files with 116 additions and 58 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 4878
---
**Claude installs no longer stage the 29 compact agent variants** — agents/*.compact.md shipped beside their canonical siblings with identical name frontmatter, leaving the harness's pick unstated; Claude never selects compact (it is a non-Claude-runtime payload), so the Claude agents directory now holds only the canonical agents, and upgrading removes the stale copies. (#4782)

View File

@@ -792,6 +792,15 @@ function _copyStaged(stagedDir: string, destDir: string, kind: any, configDir: s
for (const entry of entries) {
if (!entry.isFile()) continue;
if (!entry.name.endsWith('.md')) continue;
// #4782: compact agent variants are consumed ONLY through the non-claude
// gate in init.cts's agent-skills persona fallback (`runtime !== 'claude'`
// — claude's contract is a skills-injection path, never a persona
// fallback). Staging them into Claude's agents directory shipped 29 dead
// files whose `name:` frontmatter is identical to their canonical
// sibling's, leaving the harness resolution unstated. Claude never
// selects compact, so claude is the one runtime whose agents kind skips
// them; every other runtime's emission is byte-identical.
if (kind.kind === 'agents' && runtime === 'claude' && entry.name.endsWith('.compact.md')) continue;
const stem = entry.name.slice(0, -3); // strip .md
let destName: string;

View File

@@ -0,0 +1,102 @@
'use strict';
/**
* #4782 — compact agent variants must not stage into Claude's agents directory.
*
* The compact/canonical choice is made in code at init.cts's agent-skills
* persona fallback, gated `runtime !== 'claude'` (claude's contract is a
* skills-injection path, not a persona fallback). Staging the 29
* `agents/*.compact.md` files into `.claude/agents/` therefore shipped dead
* files whose `name:` frontmatter is byte-identical to their canonical
* sibling's — the harness has to pick one and nothing states which (#4553
* family). Claude is the one runtime whose agents kind skips them; every
* other runtime's emission is byte-identical, and the existing
* `_removeGsdEntries` prune (every `gsd-*` entry) cleans previously staged
* compact copies on upgrade.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { createTempDir, cleanup } = require('./helpers.cjs');
const { runMinimalInstall } = require('./helpers/install-shared.cjs');
const SOURCE_AGENTS_DIR = path.join(__dirname, '..', 'agents');
const listAgents = (agentsDir) => (fs.existsSync(agentsDir) ? fs.readdirSync(agentsDir) : []);
const isCompact = (f) => f.endsWith('.compact.md');
describe('#4782 compact agent staging', () => {
test('claude install stages no *.compact.md and leaves canonical agents intact', (t) => {
const root = createTempDir('gsd-4782-clean-');
t.after(() => cleanup(root));
const { configDir } = runMinimalInstall({ runtime: 'claude', scope: 'global', root });
assert.strictEqual(configDir, path.join(root, '.claude'));
const staged = listAgents(path.join(configDir, 'agents'));
const compact = staged.filter(isCompact);
const canonical = staged.filter((f) => f.startsWith('gsd-') && f.endsWith('.md') && !isCompact(f));
assert.deepStrictEqual(
compact,
[],
`claude agents dir must not contain compact variants; got: ${compact.join(', ')}`,
);
const sourceCanonical = fs.readdirSync(SOURCE_AGENTS_DIR)
.filter((f) => f.startsWith('gsd-') && f.endsWith('.md') && !isCompact(f));
assert.strictEqual(
canonical.length,
sourceCanonical.length,
`canonical agent count must be unchanged (source: ${sourceCanonical.length})`,
);
});
test('claude install removes previously staged compact variants (upgrade over 1.14.0)', (t) => {
const root = createTempDir('gsd-4782-upgrade-');
t.after(() => cleanup(root));
// Plant what a 1.14.0 install left behind: GSD-owned compact copies (one
// real, one synthetic) next to the canonical agent.
const agentsDir = path.join(root, '.claude', 'agents');
fs.mkdirSync(agentsDir, { recursive: true });
fs.writeFileSync(path.join(agentsDir, 'gsd-doc-writer.compact.md'), '---\nname: gsd-doc-writer\n---\ncompact');
fs.writeFileSync(path.join(agentsDir, 'gsd-zombie.compact.md'), '---\nname: gsd-zombie\n---\ncompact');
const { configDir } = runMinimalInstall({
runtime: 'claude',
scope: 'global',
root,
// The planted copies are not manifest-managed, so the first-time
// baseline-scan migration demands an explicit operator choice for them
// (fail-closed by design). Resolving "remove" IS the upgrade semantics
// under test: the operator retires the stale compact copies.
extraEnv: { GSD_INSTALLER_MIGRATION_RESOLVE: 'remove' },
});
const staged = listAgents(path.join(configDir, 'agents'));
assert.deepStrictEqual(
staged.filter(isCompact),
[],
'the stale-agent prune must remove previously staged compact variants',
);
assert.ok(
staged.includes('gsd-doc-writer.md'),
'the canonical agent must still be staged',
);
});
test('non-claude agents kinds still stage compact variants (opencode parity)', (t) => {
const root = createTempDir('gsd-4782-opencode-');
t.after(() => cleanup(root));
const { configDir } = runMinimalInstall({ runtime: 'opencode', scope: 'global', root });
const compact = listAgents(path.join(configDir, 'agents')).filter(isCompact);
const sourceCompact = fs.readdirSync(SOURCE_AGENTS_DIR).filter(isCompact);
assert.strictEqual(
compact.length,
sourceCompact.length,
`opencode must still stage every compact variant (source: ${sourceCompact.length})`,
);
});
});

View File

@@ -1,67 +1,38 @@
[
".gsd-profile",
"agents/gsd-advisor-researcher.compact.md",
"agents/gsd-advisor-researcher.md",
"agents/gsd-ai-researcher.compact.md",
"agents/gsd-ai-researcher.md",
"agents/gsd-assumptions-analyzer.compact.md",
"agents/gsd-assumptions-analyzer.md",
"agents/gsd-code-fixer.compact.md",
"agents/gsd-code-fixer.md",
"agents/gsd-code-reviewer.compact.md",
"agents/gsd-code-reviewer.md",
"agents/gsd-codebase-mapper.compact.md",
"agents/gsd-codebase-mapper.md",
"agents/gsd-debug-session-manager.compact.md",
"agents/gsd-debug-session-manager.md",
"agents/gsd-debugger.md",
"agents/gsd-doc-classifier.compact.md",
"agents/gsd-doc-classifier.md",
"agents/gsd-doc-synthesizer.compact.md",
"agents/gsd-doc-synthesizer.md",
"agents/gsd-doc-verifier.compact.md",
"agents/gsd-doc-verifier.md",
"agents/gsd-doc-writer.compact.md",
"agents/gsd-doc-writer.md",
"agents/gsd-dom-verifier.compact.md",
"agents/gsd-dom-verifier.md",
"agents/gsd-domain-researcher.compact.md",
"agents/gsd-domain-researcher.md",
"agents/gsd-eval-auditor.compact.md",
"agents/gsd-eval-auditor.md",
"agents/gsd-eval-planner.compact.md",
"agents/gsd-eval-planner.md",
"agents/gsd-executor.md",
"agents/gsd-framework-selector.compact.md",
"agents/gsd-framework-selector.md",
"agents/gsd-integration-checker.compact.md",
"agents/gsd-integration-checker.md",
"agents/gsd-intel-updater.compact.md",
"agents/gsd-intel-updater.md",
"agents/gsd-mempalace-curator.compact.md",
"agents/gsd-mempalace-curator.md",
"agents/gsd-nyquist-auditor.compact.md",
"agents/gsd-nyquist-auditor.md",
"agents/gsd-pattern-mapper.compact.md",
"agents/gsd-pattern-mapper.md",
"agents/gsd-phase-researcher.md",
"agents/gsd-plan-checker.md",
"agents/gsd-planner.md",
"agents/gsd-project-researcher.compact.md",
"agents/gsd-project-researcher.md",
"agents/gsd-research-synthesizer.compact.md",
"agents/gsd-research-synthesizer.md",
"agents/gsd-roadmapper.compact.md",
"agents/gsd-roadmapper.md",
"agents/gsd-security-auditor.compact.md",
"agents/gsd-security-auditor.md",
"agents/gsd-ui-auditor.compact.md",
"agents/gsd-ui-auditor.md",
"agents/gsd-ui-checker.compact.md",
"agents/gsd-ui-checker.md",
"agents/gsd-ui-researcher.compact.md",
"agents/gsd-ui-researcher.md",
"agents/gsd-user-profiler.compact.md",
"agents/gsd-user-profiler.md",
"agents/gsd-verifier.md",
"commands/gsd-add-tests.md",

View File

@@ -1,67 +1,38 @@
[
".gsd-profile",
"agents/gsd-advisor-researcher.compact.md",
"agents/gsd-advisor-researcher.md",
"agents/gsd-ai-researcher.compact.md",
"agents/gsd-ai-researcher.md",
"agents/gsd-assumptions-analyzer.compact.md",
"agents/gsd-assumptions-analyzer.md",
"agents/gsd-code-fixer.compact.md",
"agents/gsd-code-fixer.md",
"agents/gsd-code-reviewer.compact.md",
"agents/gsd-code-reviewer.md",
"agents/gsd-codebase-mapper.compact.md",
"agents/gsd-codebase-mapper.md",
"agents/gsd-debug-session-manager.compact.md",
"agents/gsd-debug-session-manager.md",
"agents/gsd-debugger.md",
"agents/gsd-doc-classifier.compact.md",
"agents/gsd-doc-classifier.md",
"agents/gsd-doc-synthesizer.compact.md",
"agents/gsd-doc-synthesizer.md",
"agents/gsd-doc-verifier.compact.md",
"agents/gsd-doc-verifier.md",
"agents/gsd-doc-writer.compact.md",
"agents/gsd-doc-writer.md",
"agents/gsd-dom-verifier.compact.md",
"agents/gsd-dom-verifier.md",
"agents/gsd-domain-researcher.compact.md",
"agents/gsd-domain-researcher.md",
"agents/gsd-eval-auditor.compact.md",
"agents/gsd-eval-auditor.md",
"agents/gsd-eval-planner.compact.md",
"agents/gsd-eval-planner.md",
"agents/gsd-executor.md",
"agents/gsd-framework-selector.compact.md",
"agents/gsd-framework-selector.md",
"agents/gsd-integration-checker.compact.md",
"agents/gsd-integration-checker.md",
"agents/gsd-intel-updater.compact.md",
"agents/gsd-intel-updater.md",
"agents/gsd-mempalace-curator.compact.md",
"agents/gsd-mempalace-curator.md",
"agents/gsd-nyquist-auditor.compact.md",
"agents/gsd-nyquist-auditor.md",
"agents/gsd-pattern-mapper.compact.md",
"agents/gsd-pattern-mapper.md",
"agents/gsd-phase-researcher.md",
"agents/gsd-plan-checker.md",
"agents/gsd-planner.md",
"agents/gsd-project-researcher.compact.md",
"agents/gsd-project-researcher.md",
"agents/gsd-research-synthesizer.compact.md",
"agents/gsd-research-synthesizer.md",
"agents/gsd-roadmapper.compact.md",
"agents/gsd-roadmapper.md",
"agents/gsd-security-auditor.compact.md",
"agents/gsd-security-auditor.md",
"agents/gsd-ui-auditor.compact.md",
"agents/gsd-ui-auditor.md",
"agents/gsd-ui-checker.compact.md",
"agents/gsd-ui-checker.md",
"agents/gsd-ui-researcher.compact.md",
"agents/gsd-ui-researcher.md",
"agents/gsd-user-profiler.compact.md",
"agents/gsd-user-profiler.md",
"agents/gsd-verifier.md",
"gsd-core/.gsd-runtime",