Final phase of #2584 (ADR-1239 Codex-binding amendment). execute-phase now negotiates dispatch.isolation and dispatches through the matching adapter, so a wave's independent plans run concurrently on six runtimes instead of one — with no runtime=== branch in the scheduler.
harness-worktree passes the host's declared isolation flag (claude, cursor); orchestrator-worktree creates the worktree via the Phase-2 verb and spawns the executor into it with the resolved argv/cwd (codex, opencode, kimi, kimi-code); none stays sequential. Undeclared/unknown/unresolvable isolation degrades to none — never an unisolated parallel run.
Fixes two shipped Phase-2 descriptors that per-host research found would fail at spawn: kimi lacked its headless flag (would launch the interactive TUI and hang the orchestrator), and kimi-code named a non-existent binary (Kimi Code installs as 'kimi'). Adds the worktree-path root confinement Phase 2 deferred here, and leading-dash guards on the resolver's prompt/cwd matching the existing git-argument guard.
Closes#2627
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(#2584): Phase 2 — worktree create verb + orchestrator-exec resolver
Phase 2 of the negotiated executor-isolation feature (ADR-1239 Codex-binding amendment). Two building blocks for `dispatch.isolation: orchestrator-worktree` hosts, both unconsumed — no scheduler wires them yet (that is Phase 3), so no runtime behavior changes.
worktree create verb (planWorktreeCreate / executeWorktreeCreatePlan / cmdWorktreeCreate in worktree-safety.cts, routed via routeWorktree in gsd-tools.cjs): validates the wave base, creates a bounded branch+worktree, records it in the run manifest reusing record-agent 4-field entry shape, returns the executor working directory. Bounded git (10s timeout, degrade-not-throw); all manifest read/parse/validate/dedupe precedes the single git side effect (no unmanifested-orphan on a bad manifest); timeout-only best-effort partial rollback (a clean collision-exit never removes a live peer worktree); fail-closed on bad base, unsafe leading-dash / .. inputs, and malformed/mis-shaped manifest.
resolveOrchestratorExec (host-integration.cts): pure descriptor->argv resolver reading the new runtime.orchestratorExec descriptor field (codex/opencode/kimi/kimi-code), fail-closed on missing/invalid shape. Validator (capability-validator.cjs) + a parity guard asserting every orchestrator-worktree host declares a resolvable orchestratorExec.
Adding the create route edits the installed gsd-core/bin/gsd-tools.cjs, so the golden-install-parity fixtures for all 19 runtimes are regenerated (npm run gen:golden) — the only changed hash is gsd-tools.cjs. CONTEXT.md glossary updated; capability-registry regenerated. Behavioral tests (worktree-safety + host-integration) incl. a fast-check property test and the parity sweep.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: rebuild tracked state-transition.cjs to match #2400 source
The tracked compiled artifact drifted from src/state-transition.cts: #2400 (commit 2bcfaa2e2) added the progress.total_plans frontmatter sync to source but the tracked bin/lib/state-transition.cjs was never rebuilt, so the fix was not shipping to consumers of the compiled artifact. The mandatory build:lib step for Phase 2 surfaced the drift; recompiling makes the already-merged, already-changelogged #2400 fix effective. Artifact-only resync (no source/test change); drift class tracked by #2591.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#2494): capture stderr and guard empty output on the claude and gemini reviewer legs
The gemini and claude blocks in gsd-core/workflows/review.md were the only
two of the ten prompt-fed reviewer legs with both `2>/dev/null` and no
empty-output guard. Any failure that wrote no stdout — CLI missing,
unauthenticated, rate-limited, crashed — left a zero-byte review file with
the only diagnostic evidence already discarded.
write_reviews substitutes each file's raw content into a `## <Reviewer>
Review` section with no marker separating "empty/failed" from "ran cleanly,
nothing to report", so a failed lane silently degraded the advertised
N-reviewer consensus to N-1 while present_results reported success. Both
/gsd:review and /gsd:plan-review-convergence share the invoke_reviewers
step, so both were affected.
Both legs now redirect stderr to a `.err` sidecar and write a diagnostic
stub with the captured stderr appended when the review file comes back
empty — the same shape the codex and cursor legs already use.
Scope limit, noted in the block comment: the guard only runs if the block
itself completes. A host Bash-tool timeout that kills the whole block skips
it, the same hard bound the OpenCode block already documents. The existing
timeout guidance (#2194) covers that case and is unchanged.
Regression test extracts the two dispatch blocks verbatim from the workflow
and runs them under bash against a failing CLI stub, asserting the review
file is non-empty and carries a diagnosable message plus the captured
stderr. It fails against pre-fix review.md (4 of 5 cases).
Golden install-parity fixtures and the workflow size baseline are
regenerated: one review.md hash per fixture, one size entry.
Fixes#2494
* fix(#2494): stamp the changeset fragment with the filed PR number
The fragment was authored with the documented `pr: 0` placeholder because
the PR number does not exist until `gh pr create` returns. Now that this
PR is #2592, stamp it — scripts/changeset/parse.cjs requires pr > 0, so
the placeholder would fail the Changeset Required check.
* test(#2576): add failing regression for padded resolves_phase compare
* fix(#2576): normalize padded vs unpadded resolves_phase in close_phase_todos
* chore(#2576): backfill changeset pr to 2597
* test(#2576): drop fast-check property test (cross-platform-fragile on Windows CI)
* test(#2474): update dispatch gate test for dual-gate behavior
The #2772 test asserted the gate reads USE_WORKTREES_FOR_PLAN only.
Update to accept the dual-gate (USE_WORKTREES + USE_WORKTREES_FOR_PLAN).
* fix(#2474): gate worktree dispatch on project-level USE_WORKTREES too
The per-plan dispatch condition checked only USE_WORKTREES_FOR_PLAN
(submodule-derived), ignoring the project-level USE_WORKTREES flag.
Add USE_WORKTREES to the gate. Net-negative edit: compress two
nearby prose lines to offset the added shell condition (93353 bytes,
down from 93368).
Closes#2474
* docs(#2474): backfill changeset PR number (2561)
* fix: merge coverage gate into single-process check (#2474)
The test:coverage:unit script chained two c8 invocations with &&:
the first ran tests and wrote coverage data to .nyc_output/, the
second read that data for per-file branch checks. On fast CI runners
(ubuntu/24), the second process started before the filesystem flushed
the first process's writes — a classic TOCTOU race that caused
intermittent coverage gate failures.
Replace the two-process chain with a single c8 invocation that
generates both text and json-summary reports, followed by a Node
script (scripts/check-coverage-gate.cjs) that reads the JSON summary
once and checks both overall and per-file thresholds. No filesystem
race is possible because the JSON report is fully written before the
check script reads it.
* fix(#2279): reword date stamping to overwrite existing dates on Update runs
The map-codebase agent and workflow instructions only said to replace
[YYYY-MM-DD] placeholders, but Update-path files already contain concrete
dates from the prior run. Reword to SET the date stamps unconditionally,
overwriting whatever date is already there.
Closes#2279
* docs(#2279): backfill changeset PR number (2550)
* test(#2269): regression test for query commit --files scoping
Verify secure-phase.md, validate-phase.md, and next.md all pass --files
to their query commit calls.
* fix(#2269): add --files to three unscoped query commit call sites
secure-phase.md, validate-phase.md, and next.md were the only 3 of 65
query commit call sites that omitted --files, causing blanket staging
of .planning/ and committing unrelated files. Add --files with the
specific artifact path to each.
Closes#2269
* docs(#2269): backfill changeset PR number (2549)
* test(#1995): regression test for agent-<id> branch namespace
Add failing-first tests proving that normalizeCleanupManifestEntry and
planWorktreeRecordAgent reject Claude Code's current agent-<id> isolation
branches (only worktree-agent-<id> is accepted). Boundary tests cover both
namespaces plus rejection cases.
* fix(#1995): widen worktree branch regex to accept agent-<id> namespace
Claude Code's isolation="worktree" branch naming changed from
worktree-agent-<id> to agent-<id>. Widen the regex in all 7 locations
from ^worktree-agent-[A-Za-z0-9._/-]+$ to ^(worktree-)?agent-[A-Za-z0-9._/-]+$
so both namespaces are accepted. Introduce a shared WORKTREE_AGENT_BRANCH_RE
constant in src/worktree-safety.cts to prevent future drift.
Closes#1995
* fix(#1995): update workflow guards, test assertions, and baselines
Widen the branch-check regex in execute-phase.md and execute-plan.md.
Update all test assertions that checked for ^worktree-agent- to expect
the widened ^(worktree-)?agent- pattern. Regenerate golden-install-parity
fixtures, agent-size-baseline, and workflow-size-baseline.
Closes#1995
* fix(#1995): update extractCwdGuardBash sanity check for widened regex
The e2e test's sanity check verified the extracted bash block contained
'worktree-agent-'. After widening to '(worktree-)?agent-', update the
check to match the new pattern.
* fix(#1995): widen missed workflow-guard branch check + changeset + lint fixes
- hooks/gsd-workflow-guard.js: widen startsWith('worktree-agent-') to
/^(worktree-)?agent-/ regex — same defect class, was missed in prior commit
- tests/worktree.test.cjs: fix indentation regression from prior edit
- Add .changeset/1995-worktree-agent-branch-namespace.md (pr:0 placeholder)
Found by orthogonal code review (Step 4).
* fix(#1995): regenerate golden + size baselines for workflow-guard change
* docs(#1995): backfill changeset PR number (2548)
* feat(#2454): PR 2 — kimi-code Agent Skills converter + install layout
PR 1 registered the kimi-code EoS descriptor with empty artifactLayout
(SKIP_INSTALL_CONTRACT excluded it from the end-to-end install test).
PR 2 fills in the install surface:
- src/runtime-artifact-conversion.cts: new convertClaudeCommandToKimiCodeSkill
function. Today it delegates to convertClaudeCommandToKimiSkill (Python
kimi-cli) because Kimi Code uses the same Agent Skills format + /skill:
invocation per official docs. The distinct function name lets a future
divergence land cleanly if Kimi Code's skill format evolves independently.
- gsd-core/bin/lib/capability-validator.cjs: add to ALLOWED_SKILLS_CONVERTERS.
- capabilities/kimi-code/capability.json: artifactLayout.global now declares
the skills kind with converter='convertClaudeCommandToKimiCodeSkill' +
home='.kimi-code' (auto-discovered at ~/.kimi-code/skills/ per Kimi Code
docs: merge_all_available_skills = true default).
- tests/installer-migration-install.integration.test.cjs: REMOVE the
SKIP_INSTALL_CONTRACT exclusion — kimi-code now has a full install surface.
- Regenerated capability-registry + capability-matrix + golden install
parity + install tree fixtures for kimi-code.
* fix(#2454): wire kimi-code converter into SKILLS_CONVERTER_REGISTRY + count bump
- src/install-engine.cts: add convertClaudeCommandToKimiCodeSkill to
SKILLS_CONVERTER_REGISTRY so the layout-driven skills install path
can dispatch off the descriptor's converter string.
- tests/capability-registry.test.cjs: bump VALID_CONVERTER_NAMES count
26 → 27 (added convertClaudeCommandToKimiCodeSkill).
* fix(#2454): remove home override from kimi-code skills (inherit configDir)
The home:'.kimi-code' override made the install plan resolve skills dest
to ~/.kimi-code/skills instead of <configDir>/skills, causing the test's
temp configDir to miss the install. Removing it lets skills inherit
configDir like most runtimes.
* fix(#2454): kimi-code install contract surface is flat-skills (no agents)
Kimi Code has NO custom named subagents (per official docs: 3 built-in
coder/explore/plan only). The kimi-skills-agents surface expects agents/
gsd.yaml + subagents/*.yaml which kimi-code does not produce. Changed
to flat-skills which only checks for skills/gsd-* dirs.
* docs(changeset): Phase 2 kimi-code install layout Added (#2509)
* docs(changeset): backfill PR #2520 for Phase 2 (#2509)
* feat(#2454): add kimi-code as an EoS capability (Node Kimi Code CLI)
PR 1 of N for #2454. Establishes the EoS descriptor foundation for splitting
GSD's kimi support into two distinct products per the user's directive:
- kimi (existing): Moonshot's Python kimi-cli (~/.kimi, runtime: python)
- kimi-code (new): Moonshot's Node Kimi Code CLI (~/.kimi-code,
runtime: node, KIMI_CODE_HOME env)
Per ADR-1239 EoS, runtime behavior is driven by capabilities/<id>/capability.json
descriptors, not hardcoded branches in install.js. The new descriptor uses
the existing primitives (dot-home configHome, skills artifactLayout, kimi-hooks-toml
hooksSurface — same TOML [[hooks]] format Kimi Code reads per its docs).
Critical Kimi Code constraint reflected in the descriptor:
hostIntegration.dispatch.namedDispatch: false
hostIntegration.dispatch.builtInSubagents: ['coder', 'explore', 'plan']
hostBehaviors.namedSubagentsSupported: false
Kimi Code's official docs confirm only 3 built-in subagents with NO custom-
subagent registration (the [subagent] table only has timeout_ms). The
kimi-agents YAML layout (used by Python kimi-cli) is therefore NOT in
kimi-code's artifactLayout.
Schema adjustments:
- subagentToolkit set to 'undocumented' (the existing escape hatch); the
schema enum (full/read-only) lacks a 'limited'/'built-in-only' value.
A follow-up PR can extend the schema enum to add 'built-in-only' as a
first-class axis value reflecting Kimi Code's documented model.
Registration:
- capabilities/kimi-code/capability.json (new descriptor, modeled on codex)
- bin/install.js: allRuntimes array + --all list + --kimi-code flag
- gsd-core/bin/shared/runtime-aliases.manifest.json: kimi-code aliases
(kimi-code, kimicode, kimi_code)
- src/runtime-name-policy.cts: FALLBACK_ALIASES map
- gsd-core/bin/lib/capability-registry.cjs: regenerated via
scripts/gen-capability-registry.cjs --write
Tests:
- tests/multi-runtime-select.test.cjs updated for the new runtime count (18)
+ new --kimi-code flag test + 'All' shortcut renumbered 18 → 19.
Out of scope for PR 1 (follow-up PRs in the sequence):
- Install-time decision logic (kimi vs kimi-code detection / prompt)
- agent-install-check semantics for kimi-code (verify Agent Skills presence)
- cmdAgentSkills fallback returning subagent prompt content
- Workflow template mapping (named agents → built-in coder/explore/plan)
- Migration guidance for users currently on 'kimi' who are actually on Kimi Code
- Schema enum extension for subagentToolkit: 'built-in-only'
Refs #2454, #2095 (EoS/kimi migration epic), ADR-1239 (EoS).
* fix(#2454): complete drift-guard registrations for kimi-code runtime
The drift guards caught every surface that pins runtime enumeration. Each
update is mechanical, driven by the guard's named failure mode:
- src/runtime-name-policy.cts RUNTIME_LABELS: 'Kimi Code' label for kimi-code
- src/runtime-name-policy.cts RUNTIME_FLAG_IDS: add kimi-code to the
isKimiCode predicate generator
- bin/install.js runtimeMap: option '11' → 'kimi-code', renumber downstream
entries (11..17 → 12..18), ALL_RUNTIMES_OPTION 18 → 19
- gsd-core/bin/shared/model-catalog.json runtimeTierDefaults: kimi-code entry
(null/null/null — same as kimi, no model tier defaults until configured)
- docs/reference/capability-matrix.md: regenerated via
scripts/gen-capability-matrix.cjs --write (kimi-code row added)
- tests/global-config-home-fragment.test.cjs GOLDEN_FRAGMENT_MAP:
kimi-code → '.kimi-code'
- tests/fixtures/golden-install-parity/*.json: regenerated via npm run gen:golden
(the runtime-aliases.manifest.json hash changed; all 17 runtime fixtures updated)
The capability-registry is already regenerated from the prior commit.
* test(#2454): update drift-guard tests for kimi-code runtime registration
Multiple drift guards pin runtime enumeration counts and option numbering.
Each update is mechanical, driven by the guard's named failure mode:
- tests/runtime-flags.test.cjs: EXPECTED_FLAGS gains isKimiCode (16 → 17);
'all 16 flags' → 'all 17 flags' in test names + messages.
- tests/multi-runtime-select.test.cjs: parseRuntimeInput option renumbering
cascade — kilo moves 11→12, opencode 12→13, pi 13→14, qwen 14→15,
trae 15→16, windsurf 16→17, zcode 17→18, All 18→19. New single-choice
test for kimi-code (option 11). Prompt test updated for new numbering.
- tests/host-integration-descriptors.test.cjs: EXPECTED_PROFILES gains
kimi-code → 'programmatic-cli' (terminal CLI per Kimi Code docs);
EXPECTED_FLATTEN gains kimi-code → false (backgroundDispatch:true per
docs, same as Python kimi/opencode).
- tests/global-config-home-fragment.test.cjs: table-count test renamed
13 → 14 table runtimes (kimi-code added to GOLDEN_FRAGMENT_MAP earlier).
* fix(#2454): empty artifactLayout for kimi-code (PR 1 scope)
The skills kind requires a converter (existing converters are per-runtime
like convertClaudeCommandToKimiSkill). PR 1 of this multi-PR sequence only
registers the descriptor; the actual Agent Skills converter (and a new
'convertClaudeCommandToKimiCodeSkill' function) lands in PR 2 alongside
the install-time decision logic. Empty artifactLayout.global is valid and
means 'nothing to install yet via the layout seam'.
Also: added kimi-code to RUNTIME_META in tests/helpers/install-shared.cjs
(localDir .kimi-code, globalSuffix .kimi-code), and added Kimi Code as
option 11 in install.js's buildRuntimePromptText (renumbered downstream
options 11..17 → 12..18, All 18 → 19).
* fix(#2454): camelCase runtimeFlags for hyphenated ids (kimi-code → isKimiCode)
The runtimeFlags generator previously produced 'isKimi-code' (hyphen preserved)
for the new kimi-code runtime id. Property names with hyphens are awkward for
consumers (flags['isKimi-code'] instead of flags.isKimiCode). The new
runtimeIdToFlagName helper folds -[a-z] boundaries to uppercase, producing
the conventional PascalCase flag name. The 16 prior single-word runtime ids
are unaffected (the regex finds no hyphens).
* fix(#2454): update remaining drift-guard tests + gen kimi-code fixtures
- tests/runtime-flags.test.cjs drift guard: use proper kebab-case
conversion (isKimiCode → kimi-code, not 'kimicode') so the registry
comparison doesn't false-positive on hyphenated runtime ids.
- tests/multi-runtime-select.test.cjs: fix kilo/opencode/pi/qwen/trae
single-choice tests for the renumbered options (kilo 11→12, opencode
12→13, pi 13→14, qwen 14→15, trae 15→16).
- tests/install.test.cjs: Kilo integration option 11→12, prompt test
regex updated.
- tests/fixtures/golden-install-parity/kimi-code.json + install-tree/
kimi-code.json: generated via UPDATE_GOLDEN=1 + UPDATE_INSTALL_TREE=1.
The kimi-code install produces the standard GSD install layout (skills,
contexts, references, etc.) — 436 paths, same shape as other runtimes
that have no custom converter yet.
* fix(#2454): add kimi-code install contract + global config home fragment
- src/runtime-name-policy.cts GLOBAL_CONFIG_HOME_FRAGMENTS: add kimi-code
→ '.kimi-code' so getGlobalConfigHomeFragment returns the correct path
instead of falling through to the default '.claude'.
- tests/installer-migration-install.integration.test.cjs
RUNTIME_INSTALL_CONTRACTS: kimi-code entry (same surface as kimi for
PR 1; PR 2 will specialize once the Agent Skills converter lands).
- tests/multi-runtime-select.test.cjs: fix space-separated-choices test
for the renumbered kilo option (11 → 12).
- tests/fixtures/golden-install-parity/kimi-code.json + install-tree/
kimi-code.json: regenerated after rebasing onto current next (new
planner-reversibility.md from #2471 etc. now included).
* test(#2454): skip kimi-code install contract until PR 2 ships install layout
The end-to-end install test (tests/installer-migration-install.integration
.test.cjs) asserts every allRuntimes entry installs a runtime-specific
artifact surface. PR 1 of #2454 registers kimi-code in allRuntimes + the
capability descriptor + flags + labels, but the install LAYOUT (Agent
Skills converter + global AGENTS.md at $KIMI_CODE_HOME/AGENTS.md) lands
in PR 2. The SKIP_INSTALL_CONTRACT set marks this exclusion explicit and
self-removing — PR 2 removes the entry alongside adding the install
surface, restoring the contract loop to full coverage.
* fix(#2454): restore compact model-catalog.json format (M1 review)
Per code-review M1: my prior 'fix(#2454): complete drift-guard registrations'
commit used python json.dump(indent=2) which inflated the file from 165→607
lines (every nested entry got expanded) and lost the trailing newline. The
semantic change was just a 3-line kimi-code entry. Restored the original
hybrid format (top-level indent=2 + inner entries' one-line style) and
added kimi-code in matching form.
Regenerated golden install parity + install tree fixtures since the
model-catalog.json hash changed.
* fix(#2454): update CONTEXT.md allRuntimes glossary (17 → 18, add kimi-code)
CI lint-tests job failed on the glossary drift guard
(scripts/check-glossary-refs.cjs --check):
✗ CONTEXT.md's allRuntimes enum-count sentence claims 17 values but
bin/install.js's allRuntimes array has 18.
✗ CONTEXT.md's allRuntimes member list has drifted from bin/install.js
(missing from CONTEXT.md's list: kimi-code).
Missed in the prior commits because gsd-test does not run the glossary
check (it's a CI lint-tests-only check). Updating CONTEXT.md's two claims
to 18 values + kimi-code in the member list.
* chore(#2505): regen capability-registry + stamp kimi-code version 1.8.0 (#2511)
* docs(changeset): Phase 1 kimi-code runtime Added (#2511)
* test(#2511): regen kimi-code golden parity fixture after Phase 0 guard normalization lands
* docs(changeset): backfill PR #2519 for Phase 1 (#2511)