* 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>
This commit is contained in:
@@ -739,6 +739,65 @@ function extensionEventSurfaceFor(extensionEvents: unknown): readonly string[] |
|
||||
return EXTENSION_EVENT_SURFACES[extensionEvents] || null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// resolveOrchestratorExec — ADR-1239 Codex-binding amendment (#2584), Phase 2
|
||||
//
|
||||
// The `orchestratorExec` descriptor field (sibling of `runtime.hostBehaviors`
|
||||
// in capability.json) tells GSD how to process-spawn a host's own CLI as the
|
||||
// executor inside a worktree GSD itself created (`isolation:
|
||||
// 'orchestrator-worktree'`). Pure, no I/O — this only shapes the argv/cwd a
|
||||
// caller would pass to a process-spawn primitive; it does not spawn anything
|
||||
// itself. UNCONSUMED in Phase 2 — no scheduler calls this yet (Phase 3 wires
|
||||
// it to the actual spawn).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface OrchestratorExec {
|
||||
command: string;
|
||||
args?: string[];
|
||||
cwdFlag?: string | null;
|
||||
}
|
||||
|
||||
type OrchestratorExecResolution =
|
||||
| { ok: true; command: string; args: string[]; cwd: string }
|
||||
| { ok: false; reason: string };
|
||||
|
||||
/**
|
||||
* Resolve an `orchestratorExec` descriptor + target cwd into a concrete
|
||||
* argv/cwd shape for a process-spawn primitive.
|
||||
*
|
||||
* Fail-closed: never throws, always returns a discriminated result. When
|
||||
* `cwdFlag` is a non-empty string, `[cwdFlag, cwd]` is appended to `args`
|
||||
* exactly once (e.g. codex: `exec --cd <cwd>`); when `cwdFlag` is `null` or
|
||||
* absent (e.g. kimi-code, which binds via the spawned process's own cwd —
|
||||
* "process-cwd" case), no flag is appended and `cwd` is returned for the
|
||||
* caller to bind via the subprocess's own working-directory option.
|
||||
*/
|
||||
function resolveOrchestratorExec(orchestratorExec: OrchestratorExec | undefined, cwd: string): OrchestratorExecResolution {
|
||||
if (!orchestratorExec || typeof orchestratorExec !== 'object' || Array.isArray(orchestratorExec)) {
|
||||
return { ok: false, reason: 'missing_command' };
|
||||
}
|
||||
const oe = orchestratorExec as unknown as Record<string, unknown>;
|
||||
if (typeof oe.command !== 'string' || oe.command.length === 0) {
|
||||
return { ok: false, reason: 'missing_command' };
|
||||
}
|
||||
if (typeof cwd !== 'string' || cwd.length === 0) {
|
||||
return { ok: false, reason: 'invalid_cwd' };
|
||||
}
|
||||
if (oe.args !== undefined && (!Array.isArray(oe.args) || !oe.args.every((a) => typeof a === 'string'))) {
|
||||
return { ok: false, reason: 'invalid_args' };
|
||||
}
|
||||
if (oe.cwdFlag !== undefined && oe.cwdFlag !== null && typeof oe.cwdFlag !== 'string') {
|
||||
return { ok: false, reason: 'invalid_cwd_flag' };
|
||||
}
|
||||
|
||||
const baseArgs = Array.isArray(oe.args) ? [...oe.args] : [];
|
||||
const args = typeof oe.cwdFlag === 'string' && oe.cwdFlag.length > 0
|
||||
? [...baseArgs, oe.cwdFlag, cwd]
|
||||
: baseArgs;
|
||||
|
||||
return { ok: true, command: oe.command, args, cwd };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module export (CommonJS — matches existing src/*.cts pattern)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -759,4 +818,5 @@ export = {
|
||||
resolveDispatchType,
|
||||
hookEventSurfaceFor,
|
||||
extensionEventSurfaceFor,
|
||||
resolveOrchestratorExec,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user