* test(#3547): failing-first regression for collapsed global install shape * fix(#3547): exercise the real global config-home shape in the install harness * test(#3547): align ripple suites with the real global install shape * test(#3547): update stale collapsed-shape pins in provenance and migration suites * fix(#3547): bump emitted-baseline schema version for the real install shape --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -323,9 +323,10 @@ test('spot-check: cline rules are code-derived (attributable), not exempt', () =
|
|||||||
assert.equal(got.kind, 'code-derived', `${rel} must stay attributable`);
|
assert.equal(got.kind, 'code-derived', `${rel} must stay attributable`);
|
||||||
assert.deepEqual(got.sources, [CLINE_BODY_SRC]);
|
assert.deepEqual(got.sources, [CLINE_BODY_SRC]);
|
||||||
}
|
}
|
||||||
const agentsMd = attributeEmittedPath('.agents/AGENTS.md', 'cline');
|
// #3547 dropped the `.agents/AGENTS.md` spot-check: that path only entered a
|
||||||
assert.equal(agentsMd.kind, 'code-derived');
|
// manifest while the harness's collapsed --config-dir walked cline's
|
||||||
assert.deepEqual(agentsMd.sources, [CLINE_BODY_SRC]);
|
// ~/.agents sibling; with the real `.cline` config home it can never occur,
|
||||||
|
// and the rule was removed by the dead-rule arm.
|
||||||
});
|
});
|
||||||
|
|
||||||
test('spot-check: install-time state is exempt with an empty source list', () => {
|
test('spot-check: install-time state is exempt with an empty source list', () => {
|
||||||
@@ -334,7 +335,6 @@ test('spot-check: install-time state is exempt with an empty source list', () =>
|
|||||||
['gsd-core/VERSION', 'claude'],
|
['gsd-core/VERSION', 'claude'],
|
||||||
['gsd-core/.gsd-runtime', 'claude'],
|
['gsd-core/.gsd-runtime', 'claude'],
|
||||||
['package.json', 'opencode'],
|
['package.json', 'opencode'],
|
||||||
['.gsd/defaults.json', 'opencode'],
|
|
||||||
['opencode.json', 'opencode'],
|
['opencode.json', 'opencode'],
|
||||||
]) {
|
]) {
|
||||||
const got = attributeEmittedPath(rel, rt);
|
const got = attributeEmittedPath(rel, rt);
|
||||||
@@ -620,7 +620,9 @@ test('emitted paths that could traverse out of the repo are rejected', () => {
|
|||||||
assert.throws(() => attributeEmittedPath('', 'claude'), /non-empty string/);
|
assert.throws(() => attributeEmittedPath('', 'claude'), /non-empty string/);
|
||||||
|
|
||||||
// A dot-prefixed segment is NOT traversal — this must still resolve normally.
|
// A dot-prefixed segment is NOT traversal — this must still resolve normally.
|
||||||
assert.doesNotThrow(() => attributeEmittedPath('.gsd/defaults.json', 'opencode'));
|
// #3547: '.gsd/defaults.json' left the manifests with the collapsed shape; a
|
||||||
|
// dot-prefixed STILL-EMITTED path serves the same not-traversal example.
|
||||||
|
assert.doesNotThrow(() => attributeEmittedPath('.gsd-profile', 'opencode'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sampleLimit truncation is exact at limit-1 / limit / limit+1', () => {
|
test('sampleLimit truncation is exact at limit-1 / limit / limit+1', () => {
|
||||||
|
|||||||
1
tests/fixtures/install-tree/antigravity.json
vendored
1
tests/fixtures/install-tree/antigravity.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/augment.json
vendored
1
tests/fixtures/install-tree/augment.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
2
tests/fixtures/install-tree/cline.json
vendored
2
tests/fixtures/install-tree/cline.json
vendored
@@ -1,9 +1,7 @@
|
|||||||
[
|
[
|
||||||
".agents/AGENTS.md",
|
|
||||||
".clinerules/gsd.md",
|
".clinerules/gsd.md",
|
||||||
".clinerules/hooks/PreToolUse",
|
".clinerules/hooks/PreToolUse",
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/codebuddy.json
vendored
1
tests/fixtures/install-tree/codebuddy.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
72
tests/fixtures/install-tree/codex.json
vendored
72
tests/fixtures/install-tree/codex.json
vendored
@@ -1,77 +1,5 @@
|
|||||||
[
|
[
|
||||||
".agents/skills/gsd-add-tests/SKILL.md",
|
|
||||||
".agents/skills/gsd-ai-integration-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-audit-fix/SKILL.md",
|
|
||||||
".agents/skills/gsd-audit-milestone/SKILL.md",
|
|
||||||
".agents/skills/gsd-audit-uat/SKILL.md",
|
|
||||||
".agents/skills/gsd-autonomous/SKILL.md",
|
|
||||||
".agents/skills/gsd-capture/SKILL.md",
|
|
||||||
".agents/skills/gsd-cleanup/SKILL.md",
|
|
||||||
".agents/skills/gsd-code-review/SKILL.md",
|
|
||||||
".agents/skills/gsd-complete-milestone/SKILL.md",
|
|
||||||
".agents/skills/gsd-config/SKILL.md",
|
|
||||||
".agents/skills/gsd-debug/SKILL.md",
|
|
||||||
".agents/skills/gsd-discuss-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-docs-update/SKILL.md",
|
|
||||||
".agents/skills/gsd-eval-review/SKILL.md",
|
|
||||||
".agents/skills/gsd-execute-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-explore/SKILL.md",
|
|
||||||
".agents/skills/gsd-extract-learnings/SKILL.md",
|
|
||||||
".agents/skills/gsd-fast/SKILL.md",
|
|
||||||
".agents/skills/gsd-forensics/SKILL.md",
|
|
||||||
".agents/skills/gsd-graphify/SKILL.md",
|
|
||||||
".agents/skills/gsd-health/SKILL.md",
|
|
||||||
".agents/skills/gsd-help/SKILL.md",
|
|
||||||
".agents/skills/gsd-import/SKILL.md",
|
|
||||||
".agents/skills/gsd-inbox/SKILL.md",
|
|
||||||
".agents/skills/gsd-ingest-docs/SKILL.md",
|
|
||||||
".agents/skills/gsd-manager/SKILL.md",
|
|
||||||
".agents/skills/gsd-map-codebase/SKILL.md",
|
|
||||||
".agents/skills/gsd-mempalace-capture/SKILL.md",
|
|
||||||
".agents/skills/gsd-mempalace-recall/SKILL.md",
|
|
||||||
".agents/skills/gsd-milestone-summary/SKILL.md",
|
|
||||||
".agents/skills/gsd-mvp-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-new-milestone/SKILL.md",
|
|
||||||
".agents/skills/gsd-new-project/SKILL.md",
|
|
||||||
".agents/skills/gsd-next/SKILL.md",
|
|
||||||
".agents/skills/gsd-ns-context/SKILL.md",
|
|
||||||
".agents/skills/gsd-ns-ideate/SKILL.md",
|
|
||||||
".agents/skills/gsd-ns-manage/SKILL.md",
|
|
||||||
".agents/skills/gsd-ns-project/SKILL.md",
|
|
||||||
".agents/skills/gsd-ns-review/SKILL.md",
|
|
||||||
".agents/skills/gsd-ns-workflow/SKILL.md",
|
|
||||||
".agents/skills/gsd-onboard/SKILL.md",
|
|
||||||
".agents/skills/gsd-pause-work/SKILL.md",
|
|
||||||
".agents/skills/gsd-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-plan-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-plan-review-convergence/SKILL.md",
|
|
||||||
".agents/skills/gsd-pr-branch/SKILL.md",
|
|
||||||
".agents/skills/gsd-profile-user/SKILL.md",
|
|
||||||
".agents/skills/gsd-progress/SKILL.md",
|
|
||||||
".agents/skills/gsd-quick/SKILL.md",
|
|
||||||
".agents/skills/gsd-resume-work/SKILL.md",
|
|
||||||
".agents/skills/gsd-review-backlog/SKILL.md",
|
|
||||||
".agents/skills/gsd-review/SKILL.md",
|
|
||||||
".agents/skills/gsd-secure-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-settings/SKILL.md",
|
|
||||||
".agents/skills/gsd-ship/SKILL.md",
|
|
||||||
".agents/skills/gsd-sketch/SKILL.md",
|
|
||||||
".agents/skills/gsd-spec-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-spike/SKILL.md",
|
|
||||||
".agents/skills/gsd-stats/SKILL.md",
|
|
||||||
".agents/skills/gsd-surface/SKILL.md",
|
|
||||||
".agents/skills/gsd-thread/SKILL.md",
|
|
||||||
".agents/skills/gsd-ui-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-ui-review/SKILL.md",
|
|
||||||
".agents/skills/gsd-ultraplan-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-undo/SKILL.md",
|
|
||||||
".agents/skills/gsd-update/SKILL.md",
|
|
||||||
".agents/skills/gsd-validate-phase/SKILL.md",
|
|
||||||
".agents/skills/gsd-verify-work/SKILL.md",
|
|
||||||
".agents/skills/gsd-workspace/SKILL.md",
|
|
||||||
".agents/skills/gsd-workstreams/SKILL.md",
|
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-advisor-researcher.toml",
|
"agents/gsd-advisor-researcher.toml",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/copilot.json
vendored
1
tests/fixtures/install-tree/copilot.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.agent.md",
|
"agents/gsd-advisor-researcher.agent.md",
|
||||||
"agents/gsd-ai-researcher.agent.md",
|
"agents/gsd-ai-researcher.agent.md",
|
||||||
"agents/gsd-assumptions-analyzer.agent.md",
|
"agents/gsd-assumptions-analyzer.agent.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/cursor.json
vendored
1
tests/fixtures/install-tree/cursor.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/hermes.json
vendored
1
tests/fixtures/install-tree/hermes.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/kilo.json
vendored
1
tests/fixtures/install-tree/kilo.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
68
tests/fixtures/install-tree/kimi-code.json
vendored
68
tests/fixtures/install-tree/kimi-code.json
vendored
@@ -1,39 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
".kimi-code/hooks/gsd-agent-isolation-guard.js",
|
|
||||||
".kimi-code/hooks/gsd-check-update-worker.js",
|
|
||||||
".kimi-code/hooks/gsd-check-update.js",
|
|
||||||
".kimi-code/hooks/gsd-config-reload.js",
|
|
||||||
".kimi-code/hooks/gsd-context-monitor.js",
|
|
||||||
".kimi-code/hooks/gsd-cursor-post-tool.js",
|
|
||||||
".kimi-code/hooks/gsd-cursor-pre-tool.js",
|
|
||||||
".kimi-code/hooks/gsd-cursor-session-start.js",
|
|
||||||
".kimi-code/hooks/gsd-cursor-stop.js",
|
|
||||||
".kimi-code/hooks/gsd-cursor-subagent-start.js",
|
|
||||||
".kimi-code/hooks/gsd-cursor-subagent-stop.js",
|
|
||||||
".kimi-code/hooks/gsd-ensure-canonical-path.js",
|
|
||||||
".kimi-code/hooks/gsd-graphify-update.sh",
|
|
||||||
".kimi-code/hooks/gsd-phase-boundary.sh",
|
|
||||||
".kimi-code/hooks/gsd-prompt-guard.js",
|
|
||||||
".kimi-code/hooks/gsd-read-guard.js",
|
|
||||||
".kimi-code/hooks/gsd-read-injection-scanner.js",
|
|
||||||
".kimi-code/hooks/gsd-session-state.sh",
|
|
||||||
".kimi-code/hooks/gsd-statusline.js",
|
|
||||||
".kimi-code/hooks/gsd-update-banner.js",
|
|
||||||
".kimi-code/hooks/gsd-validate-commit.sh",
|
|
||||||
".kimi-code/hooks/gsd-windsurf-pre-command.js",
|
|
||||||
".kimi-code/hooks/gsd-windsurf-pre-write.js",
|
|
||||||
".kimi-code/hooks/gsd-workflow-guard.js",
|
|
||||||
".kimi-code/hooks/gsd-worktree-path-guard.js",
|
|
||||||
".kimi-code/hooks/gsd-write-guard.js",
|
|
||||||
".kimi-code/hooks/lib/cursor-workspace.js",
|
|
||||||
".kimi-code/hooks/lib/git-cmd.js",
|
|
||||||
".kimi-code/hooks/lib/gsd-graphify-rebuild.sh",
|
|
||||||
".kimi-code/hooks/lib/injection-patterns.js",
|
|
||||||
".kimi-code/hooks/lib/isolation-sentinel.js",
|
|
||||||
".kimi-code/hooks/managed-hooks-registry.cjs",
|
|
||||||
".kimi-code/hooks/package.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
@@ -68,6 +34,7 @@
|
|||||||
"agents/gsd-ui-researcher.md",
|
"agents/gsd-ui-researcher.md",
|
||||||
"agents/gsd-user-profiler.md",
|
"agents/gsd-user-profiler.md",
|
||||||
"agents/gsd-verifier.md",
|
"agents/gsd-verifier.md",
|
||||||
|
"config.toml",
|
||||||
"gsd-core/.gsd-runtime",
|
"gsd-core/.gsd-runtime",
|
||||||
"gsd-core/VERSION",
|
"gsd-core/VERSION",
|
||||||
"gsd-core/bin/check-latest-version.cjs",
|
"gsd-core/bin/check-latest-version.cjs",
|
||||||
@@ -402,6 +369,39 @@
|
|||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
|
"hooks/gsd-agent-isolation-guard.js",
|
||||||
|
"hooks/gsd-check-update-worker.js",
|
||||||
|
"hooks/gsd-check-update.js",
|
||||||
|
"hooks/gsd-config-reload.js",
|
||||||
|
"hooks/gsd-context-monitor.js",
|
||||||
|
"hooks/gsd-cursor-post-tool.js",
|
||||||
|
"hooks/gsd-cursor-pre-tool.js",
|
||||||
|
"hooks/gsd-cursor-session-start.js",
|
||||||
|
"hooks/gsd-cursor-stop.js",
|
||||||
|
"hooks/gsd-cursor-subagent-start.js",
|
||||||
|
"hooks/gsd-cursor-subagent-stop.js",
|
||||||
|
"hooks/gsd-ensure-canonical-path.js",
|
||||||
|
"hooks/gsd-graphify-update.sh",
|
||||||
|
"hooks/gsd-phase-boundary.sh",
|
||||||
|
"hooks/gsd-prompt-guard.js",
|
||||||
|
"hooks/gsd-read-guard.js",
|
||||||
|
"hooks/gsd-read-injection-scanner.js",
|
||||||
|
"hooks/gsd-session-state.sh",
|
||||||
|
"hooks/gsd-statusline.js",
|
||||||
|
"hooks/gsd-update-banner.js",
|
||||||
|
"hooks/gsd-validate-commit.sh",
|
||||||
|
"hooks/gsd-windsurf-pre-command.js",
|
||||||
|
"hooks/gsd-windsurf-pre-write.js",
|
||||||
|
"hooks/gsd-workflow-guard.js",
|
||||||
|
"hooks/gsd-worktree-path-guard.js",
|
||||||
|
"hooks/gsd-write-guard.js",
|
||||||
|
"hooks/lib/cursor-workspace.js",
|
||||||
|
"hooks/lib/git-cmd.js",
|
||||||
|
"hooks/lib/gsd-graphify-rebuild.sh",
|
||||||
|
"hooks/lib/injection-patterns.js",
|
||||||
|
"hooks/lib/isolation-sentinel.js",
|
||||||
|
"hooks/managed-hooks-registry.cjs",
|
||||||
|
"hooks/package.json",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
"scripts/changeset/github-release-notes.cjs",
|
"scripts/changeset/github-release-notes.cjs",
|
||||||
|
|||||||
34
tests/fixtures/install-tree/kimi.json
vendored
34
tests/fixtures/install-tree/kimi.json
vendored
@@ -1,39 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
".kimi/hooks/gsd-agent-isolation-guard.js",
|
|
||||||
".kimi/hooks/gsd-check-update-worker.js",
|
|
||||||
".kimi/hooks/gsd-check-update.js",
|
|
||||||
".kimi/hooks/gsd-config-reload.js",
|
|
||||||
".kimi/hooks/gsd-context-monitor.js",
|
|
||||||
".kimi/hooks/gsd-cursor-post-tool.js",
|
|
||||||
".kimi/hooks/gsd-cursor-pre-tool.js",
|
|
||||||
".kimi/hooks/gsd-cursor-session-start.js",
|
|
||||||
".kimi/hooks/gsd-cursor-stop.js",
|
|
||||||
".kimi/hooks/gsd-cursor-subagent-start.js",
|
|
||||||
".kimi/hooks/gsd-cursor-subagent-stop.js",
|
|
||||||
".kimi/hooks/gsd-ensure-canonical-path.js",
|
|
||||||
".kimi/hooks/gsd-graphify-update.sh",
|
|
||||||
".kimi/hooks/gsd-phase-boundary.sh",
|
|
||||||
".kimi/hooks/gsd-prompt-guard.js",
|
|
||||||
".kimi/hooks/gsd-read-guard.js",
|
|
||||||
".kimi/hooks/gsd-read-injection-scanner.js",
|
|
||||||
".kimi/hooks/gsd-session-state.sh",
|
|
||||||
".kimi/hooks/gsd-statusline.js",
|
|
||||||
".kimi/hooks/gsd-update-banner.js",
|
|
||||||
".kimi/hooks/gsd-validate-commit.sh",
|
|
||||||
".kimi/hooks/gsd-windsurf-pre-command.js",
|
|
||||||
".kimi/hooks/gsd-windsurf-pre-write.js",
|
|
||||||
".kimi/hooks/gsd-workflow-guard.js",
|
|
||||||
".kimi/hooks/gsd-worktree-path-guard.js",
|
|
||||||
".kimi/hooks/gsd-write-guard.js",
|
|
||||||
".kimi/hooks/lib/cursor-workspace.js",
|
|
||||||
".kimi/hooks/lib/git-cmd.js",
|
|
||||||
".kimi/hooks/lib/gsd-graphify-rebuild.sh",
|
|
||||||
".kimi/hooks/lib/injection-patterns.js",
|
|
||||||
".kimi/hooks/lib/isolation-sentinel.js",
|
|
||||||
".kimi/hooks/managed-hooks-registry.cjs",
|
|
||||||
".kimi/hooks/package.json",
|
|
||||||
"agents/gsd.md",
|
"agents/gsd.md",
|
||||||
"agents/gsd.yaml",
|
"agents/gsd.yaml",
|
||||||
"agents/subagents/gsd-advisor-researcher.md",
|
"agents/subagents/gsd-advisor-researcher.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/opencode.json
vendored
1
tests/fixtures/install-tree/opencode.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/pi.json
vendored
1
tests/fixtures/install-tree/pi.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"extensions/gsd.js",
|
"extensions/gsd.js",
|
||||||
"extensions/package.json",
|
"extensions/package.json",
|
||||||
"gsd-core/.gsd-runtime",
|
"gsd-core/.gsd-runtime",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/qwen.json
vendored
1
tests/fixtures/install-tree/qwen.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/trae.json
vendored
1
tests/fixtures/install-tree/trae.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/windsurf.json
vendored
1
tests/fixtures/install-tree/windsurf.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
1
tests/fixtures/install-tree/zcode.json
vendored
1
tests/fixtures/install-tree/zcode.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
[
|
[
|
||||||
".gsd-profile",
|
".gsd-profile",
|
||||||
".gsd/defaults.json",
|
|
||||||
"agents/gsd-advisor-researcher.md",
|
"agents/gsd-advisor-researcher.md",
|
||||||
"agents/gsd-ai-researcher.md",
|
"agents/gsd-ai-researcher.md",
|
||||||
"agents/gsd-assumptions-analyzer.md",
|
"agents/gsd-assumptions-analyzer.md",
|
||||||
|
|||||||
@@ -151,3 +151,65 @@ test('install tree — claude (local legacy layout)', async (t) => {
|
|||||||
assert.deepEqual(actual, fixture, lines.join('\n'));
|
assert.deepEqual(actual, fixture, lines.join('\n'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── #3547: the harness's global install must exercise the REAL config-home shape ───
|
||||||
|
//
|
||||||
|
// runMinimalInstall's global scope used to pass `--config-dir <root>` where the
|
||||||
|
// same <root> was also the sandbox HOME — collapsing configDir onto HOME. A real
|
||||||
|
// global install resolves its config home to a STRICT SUBDIRECTORY of $HOME
|
||||||
|
// (<HOME>/.claude, <HOME>/.codex, <HOME>/.config/opencode, …), and that is what
|
||||||
|
// computePathPrefix's `isGlobal && posixTarget.startsWith(posixHome)` branch
|
||||||
|
// needs to emit `$HOME/<suffix>/`-shaped prefixes. Under the collapsed shape the
|
||||||
|
// prefix was bare `$HOME/`, so every emitted global artifact referenced
|
||||||
|
// `$HOME/gsd-core/…` — a path that exists on no real install — and the entire
|
||||||
|
// emitted-artifact apparatus (ADR-2719 differential, install-tree fixtures,
|
||||||
|
// 19-family baseline) was structurally blind to drift confined to the real
|
||||||
|
// global shape (#3544's fix rewrote 54 includes on live installs with ZERO
|
||||||
|
// manifest/fixture diffs).
|
||||||
|
|
||||||
|
test('#3547 global harness install uses the real config-home shape (claude)', async (t) => {
|
||||||
|
const { configDir, root } = runMinimalInstall({ runtime: 'claude', scope: 'global' });
|
||||||
|
t.after(() => cleanup(root));
|
||||||
|
|
||||||
|
// Strict-subdirectory shape: <root>/.claude, never the collapsed <root>.
|
||||||
|
assert.equal(configDir, path.join(root, RUNTIME_META.claude.globalSuffix),
|
||||||
|
'global configDir must be the runtime\'s real global dir under the sandbox HOME');
|
||||||
|
assert.ok(path.dirname(configDir) === root, 'configDir must be a direct subdirectory of the sandbox HOME');
|
||||||
|
|
||||||
|
// The deployed contract: emitted agent text references $HOME/.claude/gsd-core/,
|
||||||
|
// never the unsuffixed $HOME/gsd-core/ form (posixNormalized on every platform).
|
||||||
|
const planner = fs.readFileSync(path.join(configDir, 'agents', 'gsd-planner.md'), 'utf-8');
|
||||||
|
assert.match(planner, /\$HOME\/\.claude\/gsd-core\//,
|
||||||
|
'emitted agents must carry the real global prefix shape');
|
||||||
|
assert.doesNotMatch(planner, /\$HOME\/gsd-core\//,
|
||||||
|
'the collapsed bare-$HOME prefix must not appear in real-shape installs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#3547 codex global harness install resolves the .codex subdirectory', async (t) => {
|
||||||
|
const { configDir, root } = runMinimalInstall({ runtime: 'codex', scope: 'global' });
|
||||||
|
t.after(() => cleanup(root));
|
||||||
|
assert.equal(configDir, path.join(root, RUNTIME_META.codex.globalSuffix));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#3547 opencode global harness install resolves the XDG subdirectory', async (t) => {
|
||||||
|
const { configDir, root } = runMinimalInstall({ runtime: 'opencode', scope: 'global' });
|
||||||
|
t.after(() => cleanup(root));
|
||||||
|
assert.equal(configDir, path.join(root, '.config', 'opencode'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#3547 local scope configDir unchanged', async (t) => {
|
||||||
|
const { configDir, root } = runMinimalInstall({ runtime: 'claude', scope: 'local' });
|
||||||
|
t.after(() => cleanup(root));
|
||||||
|
assert.equal(configDir, path.join(root, '.claude'),
|
||||||
|
'local installs keep the root/<localDir> shape — untouched by #3547');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#3547 unknown runtime global scope fails loudly', () => {
|
||||||
|
// grok is in the capability registry but not RUNTIME_META: a silent
|
||||||
|
// `path.join(root, undefined)` is the #3023 failure mode this guards.
|
||||||
|
assert.throws(
|
||||||
|
() => runMinimalInstall({ runtime: 'grok', scope: 'global' }),
|
||||||
|
/grok|RUNTIME_META/,
|
||||||
|
'a runtime without a known global dir must fail loudly before any install spawns',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -47,8 +47,14 @@ const BASELINE_ENV = 'GSD_EMITTED_BASELINE';
|
|||||||
/** Default on-disk cache location, relative to the repo root. */
|
/** Default on-disk cache location, relative to the repo root. */
|
||||||
const DEFAULT_CACHE_PATH = '.gsd-cache/emitted-baseline.json';
|
const DEFAULT_CACHE_PATH = '.gsd-cache/emitted-baseline.json';
|
||||||
|
|
||||||
/** Baseline artifact schema version — pinned so a format change fails loudly. */
|
/** Baseline artifact schema version — pinned so a format change fails loudly.
|
||||||
const BASELINE_VERSION = 1;
|
* v2 (#3547): the harness now measures every global family in the REAL
|
||||||
|
* config-home shape (<root>/<globalSuffix> instead of the collapsed
|
||||||
|
* <root>), so manifests from a v1 baseline — built with the collapsed
|
||||||
|
* harness — are not comparable (HOME-level paths present, prefix bytes
|
||||||
|
* absent). Bumping refuses v1 caches and forces the same-schema fallback
|
||||||
|
* build; the push-to-next job republishes at v2 when this merges. */
|
||||||
|
const BASELINE_VERSION = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a baseline artifact's shape and freshness.
|
* Validate a baseline artifact's shape and freshness.
|
||||||
|
|||||||
@@ -530,13 +530,17 @@ const PROVENANCE_RULES = [
|
|||||||
pattern: /^(gsd\.md|hooks\/PreToolUse)$/,
|
pattern: /^(gsd\.md|hooks\/PreToolUse)$/,
|
||||||
sources: () => [CLINE_BODY_SRC],
|
sources: () => [CLINE_BODY_SRC],
|
||||||
},
|
},
|
||||||
{
|
// #3547 removed `agents-md-code-derived` (roots: ['.agents'],
|
||||||
id: 'agents-md-code-derived',
|
// ^AGENTS\.md$) and `synthesized-gsd-defaults` (^\.gsd/defaults\.json$):
|
||||||
kind: 'code-derived',
|
// both paths only appeared in a manifest while the harness collapsed
|
||||||
roots: ['.agents'],
|
// configDir onto the sandbox HOME, walking HOME-level siblings
|
||||||
pattern: /^AGENTS\.md$/,
|
// (cline's ~/.agents/AGENTS.md, every non-Claude runtime's
|
||||||
sources: () => [CLINE_BODY_SRC],
|
// ~/.gsd/defaults.json). With the harness installing into each runtime's
|
||||||
},
|
// real global subdirectory those files sit outside the walked configDir,
|
||||||
|
// the rules matched nothing, and the totality guard's dead-rule arm fired
|
||||||
|
// — exactly as designed. The files are still written, still covered by the
|
||||||
|
// existence/config suites (kimi-upgrades, codex-config, install suites);
|
||||||
|
// they are simply no longer manifest members to attribute.
|
||||||
{
|
{
|
||||||
id: 'hermes-category-description',
|
id: 'hermes-category-description',
|
||||||
kind: 'code-derived',
|
kind: 'code-derived',
|
||||||
@@ -568,13 +572,6 @@ const PROVENANCE_RULES = [
|
|||||||
pattern: /^(\.gsd-profile|package\.json|\.kimi(-code)?\/package\.json|gsd-core\/VERSION|gsd-core\/\.gsd-runtime)$/,
|
pattern: /^(\.gsd-profile|package\.json|\.kimi(-code)?\/package\.json|gsd-core\/VERSION|gsd-core\/\.gsd-runtime)$/,
|
||||||
sources: () => [],
|
sources: () => [],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'synthesized-gsd-defaults',
|
|
||||||
kind: 'synthesized',
|
|
||||||
roots: null,
|
|
||||||
pattern: /^\.gsd\/defaults\.json$/,
|
|
||||||
sources: () => [],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'synthesized-host-config',
|
id: 'synthesized-host-config',
|
||||||
kind: 'synthesized',
|
kind: 'synthesized',
|
||||||
|
|||||||
@@ -191,18 +191,22 @@ const HOOK_CONFIG_FILES = new Set(['settings.json', 'settings.local.json', 'hook
|
|||||||
// platform-stable `[features] hooks = true` flag — the real hook commands
|
// platform-stable `[features] hooks = true` flag — the real hook commands
|
||||||
// live in Codex's separate hooks.json, already excluded above). Blanket-
|
// live in Codex's separate hooks.json, already excluded above). Blanket-
|
||||||
// excluding the 'config.toml' basename would silently blind Codex's fixture
|
// excluding the 'config.toml' basename would silently blind Codex's fixture
|
||||||
// to any future regression there. Kimi's config.toml instead lives OUTSIDE
|
// to any future regression there — and it would blind kimi-code's too: since
|
||||||
// its GSD configDir at runtime (resolveKimiHooksTomlDir resolves ~/.kimi, a
|
// #3547 the harness installs into each runtime's REAL global subdirectory, so
|
||||||
// sibling of the configDir ~/.config/agents) — it only appears inside this
|
// kimi-code's hooks config.toml sits at its configDir root (rel `config.toml`)
|
||||||
// harness's walked tree at all because runMinimalInstall sets HOME to the
|
// and is legitimately manifest-visible. Tracking it is safe now: the
|
||||||
// same temp root used as --config-dir, collapsing the two into one directory
|
// install-tree fixture carries paths only, and the ADR-2719 differential
|
||||||
// for the isolated test run. So it is excluded by its exact relative path
|
// compares base-vs-current on the same machine, so the platform-varying
|
||||||
// under that collapsed root, not by basename.
|
// node-runner command embedded in the TOML never crosses platforms inside a
|
||||||
// Both Kimi products' native config.toml embeds a platform-varying node-runner
|
// gate (that was a golden-content-era hazard, and the goldens are gone).
|
||||||
// command, so neither belongs in the golden-tracked emitted manifest. kimi-code
|
// Kimi CLI's config.toml (KIMI_SHARE_DIR root ~/.kimi) lives OUTSIDE its GSD
|
||||||
// resolves its own root since #2755 — listing only `.kimi/config.toml` here made
|
// configDir (~/.config/agents) and never enters the walk. The pre-#3547
|
||||||
// kimi-code's config.toml newly manifest-visible and unattributable.
|
// relative-path exclusions ('.kimi/config.toml', '.kimi-code/config.toml')
|
||||||
const HOOK_CONFIG_RELATIVE_PATHS = new Set(['.kimi/config.toml', '.kimi-code/config.toml']);
|
// existed only for the collapsed shape — where the walked root was the HOME
|
||||||
|
// itself and those HOME-level siblings were inside it; with no walker rooting
|
||||||
|
// at HOME anymore they matched nothing and were removed (#3547). kimi-code
|
||||||
|
// resolves its own root since #2755.
|
||||||
|
const HOOK_CONFIG_RELATIVE_PATHS = new Set();
|
||||||
|
|
||||||
// Path prefixes excluded from the parity manifest. `gsd-core/bin/lib/` holds the
|
// Path prefixes excluded from the parity manifest. `gsd-core/bin/lib/` holds the
|
||||||
// tsc-built runtime artifacts (compiled from src/*.cts) that the install COPIES
|
// tsc-built runtime artifacts (compiled from src/*.cts) that the install COPIES
|
||||||
@@ -570,8 +574,31 @@ function runMinimalInstall({ runtime, scope, extraArgs = [], installScript = INS
|
|||||||
let cwd = process.cwd();
|
let cwd = process.cwd();
|
||||||
const args = [installScript, `--${runtime}`];
|
const args = [installScript, `--${runtime}`];
|
||||||
if (scope === 'global') {
|
if (scope === 'global') {
|
||||||
args.push('--global', '--config-dir', root);
|
// #3547 — install into the runtime's REAL global config home: the strict
|
||||||
configDir = root;
|
// subdirectory of the sandbox HOME a genuine global install resolves
|
||||||
|
// (RUNTIME_META.globalSuffix mirrors the registry's getGlobalConfigDir
|
||||||
|
// for every runtime). The previous `--config-dir <root>` collapsed
|
||||||
|
// configDir onto HOME, so computePathPrefix emitted bare `$HOME/`
|
||||||
|
// prefixes and the emitted bytes referenced `$HOME/gsd-core/…` — a path
|
||||||
|
// no real install produces — leaving every emitted-artifact gate
|
||||||
|
// (ADR-2719 differential, install-tree fixtures, the 19-family baseline)
|
||||||
|
// blind to drift confined to the real global shape (#3544 evidence: 54
|
||||||
|
// includes rewritten on live installs, zero manifest/fixture diffs). The
|
||||||
|
// explicit flag stays: hermeticity-by-override is immune to ambient
|
||||||
|
// redirect envs (CI runners export XDG_CONFIG_HOME, which redefines the
|
||||||
|
// opencode/kilo XDG descriptors' resolution when no explicit dir wins).
|
||||||
|
const globalMeta = RUNTIME_META[runtime];
|
||||||
|
if (!globalMeta || !globalMeta.globalSuffix) {
|
||||||
|
// #3023 lesson: a silent `path.join(root, undefined)` here throws a
|
||||||
|
// bare TypeError naming neither the runtime nor the map at fault; a
|
||||||
|
// runtime without a known global home must fail loudly before any
|
||||||
|
// install spawns.
|
||||||
|
throw new Error(
|
||||||
|
`runMinimalInstall: no RUNTIME_META.globalSuffix for runtime "${runtime}" — refusing to guess a global config dir (#3547)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
configDir = path.join(root, globalMeta.globalSuffix);
|
||||||
|
args.push('--global', '--config-dir', configDir);
|
||||||
} else {
|
} else {
|
||||||
args.push('--local');
|
args.push('--local');
|
||||||
cwd = root;
|
cwd = root;
|
||||||
|
|||||||
@@ -285,11 +285,13 @@ test('UPGRADE 2: a corrupted/undeclared dispatch still fails closed to inline (s
|
|||||||
// docs/reference/host-integration-capability-matrix.md's kimi EoS-status
|
// docs/reference/host-integration-capability-matrix.md's kimi EoS-status
|
||||||
// paragraph) — the installer's deliverable stops at the Agent-tool grant plus
|
// paragraph) — the installer's deliverable stops at the Agent-tool grant plus
|
||||||
// the negotiated backgroundDispatch axis asserted above.
|
// the negotiated backgroundDispatch axis asserted above.
|
||||||
test('UPGRADE 2 (installer-testable proxy): kimi --global install with subagents present grants kimi_cli.tools.agent:Agent on the root agent', (t) => {
|
test('UPGRADE 2 (installer-testable proxy): kimi --global install with subagents present grants kimi_cli.tools.agent:Agent on the root agent', (t) => {
|
||||||
const { root } = runMinimalInstall({ runtime: 'kimi', scope: 'global' });
|
const { configDir, root } = runMinimalInstall({ runtime: 'kimi', scope: 'global' });
|
||||||
t.after(() => cleanup(root));
|
t.after(() => cleanup(root));
|
||||||
|
|
||||||
const rootYamlPath = path.join(root, 'agents', 'gsd.yaml');
|
// #3547 — the root agent lives under the runtime's real global config
|
||||||
|
// home (<root>/.config/agents), not the sandbox HOME itself.
|
||||||
|
const rootYamlPath = path.join(configDir, 'agents', 'gsd.yaml');
|
||||||
assert.ok(fs.existsSync(rootYamlPath), 'kimi: agents/gsd.yaml must exist');
|
assert.ok(fs.existsSync(rootYamlPath), 'kimi: agents/gsd.yaml must exist');
|
||||||
const rootYaml = fs.readFileSync(rootYamlPath, 'utf8');
|
const rootYaml = fs.readFileSync(rootYamlPath, 'utf8');
|
||||||
|
|
||||||
@@ -420,8 +422,11 @@ describe('kimi vs kimi-code hooks-TOML root (#2755)', () => {
|
|||||||
|
|
||||||
assert.ok(hasGsdHooksBlock(path.join(altHome, 'config.toml')),
|
assert.ok(hasGsdHooksBlock(path.join(altHome, 'config.toml')),
|
||||||
'KIMI_CODE_HOME must redirect the hooks block');
|
'KIMI_CODE_HOME must redirect the hooks block');
|
||||||
assert.ok(!fs.existsSync(path.join(root, '.kimi-code')),
|
// #3547 — <root>/.kimi-code legitimately exists as kimi-code's GSD config
|
||||||
'the default kimi-code root must not be used when the env var is set');
|
// home in the harness's real global shape; what must NOT happen is the
|
||||||
|
// DEFAULT hooks root receiving the GSD block while the override is set.
|
||||||
|
assert.ok(!hasGsdHooksBlock(path.join(root, '.kimi-code', 'config.toml')),
|
||||||
|
'the default kimi-code hooks root must not receive the GSD hooks block when the env var is set');
|
||||||
assert.ok(!fs.existsSync(path.join(root, '.kimi')),
|
assert.ok(!fs.existsSync(path.join(root, '.kimi')),
|
||||||
"Kimi CLI's root must not be touched either");
|
"Kimi CLI's root must not be touched either");
|
||||||
});
|
});
|
||||||
@@ -488,8 +493,10 @@ describe('kimi vs kimi-code hooks-TOML root (#2755)', () => {
|
|||||||
assert.ok(hasGsdHooksBlock(path.join(codeAlt, 'config.toml')),
|
assert.ok(hasGsdHooksBlock(path.join(codeAlt, 'config.toml')),
|
||||||
'kimi-code must honor KIMI_CODE_HOME while KIMI_SHARE_DIR is also set');
|
'kimi-code must honor KIMI_CODE_HOME while KIMI_SHARE_DIR is also set');
|
||||||
assert.ok(!fs.existsSync(path.join(root, '.kimi')),
|
assert.ok(!fs.existsSync(path.join(root, '.kimi')),
|
||||||
'neither default root may be used when both overrides are set');
|
'neither default hooks root may be used when both overrides are set');
|
||||||
assert.ok(!fs.existsSync(path.join(root, '.kimi-code')),
|
// #3547 — <root>/.kimi-code is kimi-code's GSD config home now; assert the
|
||||||
'neither default root may be used when both overrides are set');
|
// hooks BLOCK stayed off the default root instead of directory absence.
|
||||||
|
assert.ok(!hasGsdHooksBlock(path.join(root, '.kimi-code', 'config.toml')),
|
||||||
|
'neither default hooks root may receive the GSD block when both overrides are set');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,12 +53,16 @@ const EXPECTED_COMMAND_DIR = 'commands';
|
|||||||
/** Re-run the installer against an EXISTING configDir/root to simulate an
|
/** Re-run the installer against an EXISTING configDir/root to simulate an
|
||||||
* upgrade/reapply pass (the installer runs its migration planner on every
|
* upgrade/reapply pass (the installer runs its migration planner on every
|
||||||
* invocation — see bin/install.js installAllRuntimes -> install() ->
|
* invocation — see bin/install.js installAllRuntimes -> install() ->
|
||||||
* runInstallerMigrations, unconditional, not gated on first-install). */
|
* runInstallerMigrations, unconditional, not gated on first-install).
|
||||||
function reinstallOpencode(root, scope = 'global') {
|
* #3547 — `configDir` is the runtime's REAL global home (<root>/<globalSuffix>),
|
||||||
|
* the same shape runMinimalInstall now installs into; reinstalling into the
|
||||||
|
* bare <root> would target a different config home and never see the
|
||||||
|
* fabricated legacy state. */
|
||||||
|
function reinstallOpencode(root, configDir = null, scope = 'global') {
|
||||||
const args = [INSTALL_SCRIPT, '--opencode'];
|
const args = [INSTALL_SCRIPT, '--opencode'];
|
||||||
let cwd = process.cwd();
|
let cwd = process.cwd();
|
||||||
if (scope === 'global') {
|
if (scope === 'global') {
|
||||||
args.push('--global', '--config-dir', root);
|
args.push('--global', '--config-dir', configDir || root);
|
||||||
} else {
|
} else {
|
||||||
args.push('--local');
|
args.push('--local');
|
||||||
cwd = root;
|
cwd = root;
|
||||||
@@ -305,7 +309,7 @@ describe('#2329: upgrading an install with an orphaned command/ dir migrates it
|
|||||||
assert.ok(gsdMdFiles(legacyDir).length >= 60, 'sanity: legacyDir must be populated before reinstall');
|
assert.ok(gsdMdFiles(legacyDir).length >= 60, 'sanity: legacyDir must be populated before reinstall');
|
||||||
assert.ok(!fs.existsSync(pluralDir), 'sanity: pluralDir must not exist before reinstall (fabricated pre-fix state)');
|
assert.ok(!fs.existsSync(pluralDir), 'sanity: pluralDir must not exist before reinstall (fabricated pre-fix state)');
|
||||||
|
|
||||||
const result = reinstallOpencode(root, 'global');
|
const result = reinstallOpencode(root, configDir, 'global');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
result.status, 0,
|
result.status, 0,
|
||||||
`reinstall (upgrade) must exit 0\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
|
`reinstall (upgrade) must exit 0\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
|
||||||
@@ -341,7 +345,7 @@ describe('#2329: upgrading an install with an orphaned command/ dir migrates it
|
|||||||
const userFileLegacy = path.join(legacyDir, 'my-notes.md');
|
const userFileLegacy = path.join(legacyDir, 'my-notes.md');
|
||||||
fs.writeFileSync(userFileLegacy, userContent, 'utf8');
|
fs.writeFileSync(userFileLegacy, userContent, 'utf8');
|
||||||
|
|
||||||
const result = reinstallOpencode(root, 'global');
|
const result = reinstallOpencode(root, configDir, 'global');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
result.status, 0,
|
result.status, 0,
|
||||||
`reinstall (upgrade) must exit 0\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
|
`reinstall (upgrade) must exit 0\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
|
||||||
@@ -406,7 +410,7 @@ describe('#2329: upgrading an install with an orphaned command/ dir migrates it
|
|||||||
`sanity: manifest must record >=60 "command/" keys before reinstall, got ${legacyManifestKeys.length}`
|
`sanity: manifest must record >=60 "command/" keys before reinstall, got ${legacyManifestKeys.length}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = reinstallOpencode(root, 'global');
|
const result = reinstallOpencode(root, configDir, 'global');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
result.status, 0,
|
result.status, 0,
|
||||||
`reinstall (upgrade) must exit 0\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
|
`reinstall (upgrade) must exit 0\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ const RUNTIMES = Object.keys(RUNTIME_META);
|
|||||||
* assert success — callers decide (row 36 expects failure). */
|
* assert success — callers decide (row 36 expects failure). */
|
||||||
function spawnGlobalInstall(installScript, runtime, extraArgs = []) {
|
function spawnGlobalInstall(installScript, runtime, extraArgs = []) {
|
||||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-2930-dest-${runtime}-`));
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-2930-dest-${runtime}-`));
|
||||||
|
// #3547 — same real config-home shape as runMinimalInstall: the strict
|
||||||
|
// <root>/<globalSuffix> subdirectory, not the collapsed <root>. The size
|
||||||
|
// comparisons below normalize each side's own root out of the text; a
|
||||||
|
// collapsed stub against a real-shape install bakes in a `.claude`-style
|
||||||
|
// suffix delta that has nothing to do with the compose wiring under test.
|
||||||
|
const configDir = path.join(root, RUNTIME_META[runtime].globalSuffix);
|
||||||
const args = [
|
const args = [
|
||||||
'--preserve-symlinks',
|
'--preserve-symlinks',
|
||||||
'--preserve-symlinks-main',
|
'--preserve-symlinks-main',
|
||||||
@@ -87,7 +93,7 @@ function spawnGlobalInstall(installScript, runtime, extraArgs = []) {
|
|||||||
`--${runtime}`,
|
`--${runtime}`,
|
||||||
'--global',
|
'--global',
|
||||||
'--config-dir',
|
'--config-dir',
|
||||||
root,
|
configDir,
|
||||||
...extraArgs,
|
...extraArgs,
|
||||||
];
|
];
|
||||||
const seamResult = runNode(args, {
|
const seamResult = runNode(args, {
|
||||||
@@ -96,7 +102,7 @@ function spawnGlobalInstall(installScript, runtime, extraArgs = []) {
|
|||||||
timeoutMs: INSTALL_TIMEOUT_MS,
|
timeoutMs: INSTALL_TIMEOUT_MS,
|
||||||
});
|
});
|
||||||
const result = { status: seamResult.exitCode, stdout: seamResult.stdout, stderr: seamResult.stderr };
|
const result = { status: seamResult.exitCode, stdout: seamResult.stdout, stderr: seamResult.stderr };
|
||||||
return { result, configDir: root, root };
|
return { result, configDir, root };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert native path separators to POSIX forward slashes, unconditionally
|
/** Convert native path separators to POSIX forward slashes, unconditionally
|
||||||
|
|||||||
Reference in New Issue
Block a user