fix(#1521): resolve own runtime + worktrees-off for all non-Claude installs (#1537)

* fix(#1521): resolve own runtime + worktrees-off for all non-Claude installs

Generalizes the Codex-only #1515/#1519 fix to every non-Claude runtime, and
wires it into the real install path (where it was previously dead-on-arrival).

Root causes:
1. The runtime-default stamping lived only in `_applyRuntimeRewrites`, but the
   installer emits `gsd-core/workflows/*.md` via `copyWithPathReplacement`, which
   never calls it — so a real `--codex`/`--cursor`/etc. install emitted
   `--default claude` and worktrees-on. RUNTIME mis-resolved to claude and the
   workflow ran executors unisolated against the main checkout. (#1515/#1519 were
   also dead-on-arrival in real installs; this repairs them.)
2. Only `case 'codex'` was stamped; every other non-Claude runtime kept the
   Claude default.

Fix:
- New `_stampNonClaudeRuntimeDefaults(content, runtime)` (single shared helper)
  stamps `--default <runtime>` + `use_worktrees=false` for every `runtime !=
  claude`; called from both `_applyRuntimeRewrites` and, crucially,
  `copyWithPathReplacement` in bin/install.js (the real workflow emit path).
- Generalize the fail-closed worktree guard `= codex` -> `!= claude` in
  execute-phase/quick/diagnose-issues (worktree isolation is Claude-Code-only).
- Flip manager/autonomous inline-vs-background gating to `codex -> background,
  everything-else -> inline` (research: only Codex can background-nest the
  pipeline's subagents; all others run inline, which they support).

Worktree-capability determination is research-backed (official docs for all 14
non-Claude runtimes: none honor GSD's isolation="worktree" mechanism, only Codex
background-nests). New end-to-end real-install test asserts the EMITTED workflow
is stamped — the regression guard that would have caught the dead-on-arrival bug.

Closes #1521

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vX5eUtWa2wsZEyeMf5i3r

* chore(#1521): backfill changeset PR number (#1537)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vX5eUtWa2wsZEyeMf5i3r

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-21 13:48:47 -04:00
committed by GitHub
parent 2436b76980
commit 2c718bf972
15 changed files with 514 additions and 114 deletions

View File

@@ -66,17 +66,19 @@ test('codex emit defaults workflow.use_worktrees to false', () => {
);
});
test('non-codex runtime (cursor) does NOT rewrite the runtime default — stamping is codex-scoped', () => {
test('claude runtime does NOT rewrite the runtime default — stamping is non-claude-scoped (#1521 inversion)', () => {
// #1521 generalizes stamping to ALL non-Claude runtimes. The negative case
// (no stamping) is now the 'claude' runtime, not other non-Claude runtimes.
const line =
'RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")\n';
const out = conversion._applyRuntimeRewrites(line, 'cursor', '/home/u/.cursor/', true, undefined);
const out = conversion._applyRuntimeRewrites(line, 'claude', '$HOME/.claude/', true, undefined);
assert.ok(
out.includes('--default claude --raw'),
`Expected cursor output to preserve '--default claude --raw'; got:\n${out}`,
`Expected claude output to preserve '--default claude --raw'; got:\n${out}`,
);
assert.ok(
!out.includes('--default codex'),
`Expected cursor output NOT to contain '--default codex'; got:\n${out}`,
`Expected claude output NOT to contain '--default codex'; got:\n${out}`,
);
});
@@ -107,14 +109,17 @@ test('regression: every edited workflow gets codex-stamped (source↔engine pari
// Property tests (RULESET.TESTS.property-based-testing)
// ---------------------------------------------------------------------------
test('property: runtime stamping applies iff runtime is codex (#1515)', () => {
const RUNTIMES = ['claude','codex','cursor','cline','windsurf','augment','trae','qwen','hermes','gemini','opencode','kilo','copilot','antigravity','codebuddy'];
test('property: runtime stamping applies for ALL non-claude runtimes; only claude leaves --default claude unchanged (#1521)', () => {
// #1521: generalised from codex-only to all non-claude runtimes.
// Use the canonical list from the conversion module to avoid hand-rolled array drift.
const { NON_CLAUDE_RUNTIMES } = conversion;
const RUNTIMES = ['claude', ...NON_CLAUDE_RUNTIMES];
const line = 'RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")\n';
fc.assert(fc.property(fc.constantFrom(...RUNTIMES), (rt) => {
const out = conversion._applyRuntimeRewrites(line, rt, `$HOME/.${rt}/`, true, undefined);
return rt === 'codex'
? out.includes('--default codex --raw') && !out.includes('--default claude')
: out.includes('--default claude --raw') && !out.includes('--default codex');
return rt === 'claude'
? out.includes('--default claude --raw') && !out.includes('--default codex')
: out.includes(`--default ${rt} --raw`) && !out.includes('--default claude');
}));
});