The Gemini, Claude, and Codex reviewer blocks invoked the CLIs with no explicit
timeout, so each inherited the host default (~2 min on Claude Code). A source-
grounded review of a large plan set takes ~570s (Codex xhigh) / ~525s (headless
Claude) — both exceed that window, so the lane is killed mid-review, its output
is empty, and the cross-AI review silently proceeds with fewer lanes. CodeRabbit
and OpenCode already documented a timeout; the four main lanes did not.
Add a shared timeout-guidance note directing a high Bash timeout (>= 900000;
1200000 for Codex xhigh / headless Claude), referencing BASH_MAX_TIMEOUT_MS for
the Claude Code host cap, and framing a slow-lane empty output as a timeout kill
(not the 0xc0000142 crash it gets misdiagnosed as) so operators re-run with more
time instead of diagnosing a CLI failure.
Closes#2194
Recaptures the 18 golden-install-parity fixtures + workflow-size baseline (only
the review.md entry changed in each; review is LARGE-tier, 47168 < 61440).
Source-text contract guard: review.md IS the product the runtime loads. Assert
its reviewer-invocation section carries Bash timeout guidance (>= 900000ms) for
the Gemini/Claude/Codex lanes and frames a slow-lane empty output as a timeout
rather than a crash.
CI lint (local/no-crlf-fragile-split + no-regex-spaces) flagged the regex
assertions: a bare \n on readFileSync content is CRLF-fragile on Windows
git-autocrlf, and literal double-spaces are hard to count. Switch to .includes()
strings — same validation intent, lint-clean on all platforms.
Adds the frontmatter-bearing STATE.md fixture the suite lacked. Asserts the
body Progress: line (not the YAML progress: key) is the update target, the
descriptive suffix after [bar] NN% is preserved, the frontmatter block is not
mangled, and a body with no Progress: line reports updated:false even when the
frontmatter has a progress: key (no false success).
Adds the Linuxbrew Cellar layout (/home/linuxbrew/.linuxbrew/Cellar/...), a
post-version-bump path, a node@version formula, and a custom HOMEBREW_PREFIX —
all mapping to the stable <prefix>/bin/node symlink. Mirrored in both
consolidated describe blocks.
The subtest asserted project-only model_overrides but called
readGsdEffectiveModelOverrides without isolating HOME, so the real
~/.gsd/defaults.json global overrides bled into the deepEqual on any dev box or
non-hermetic runner that has one. Pass a sandboxed homedir (mirroring the sibling
warnIfStaleBake subtests that already inject homedir).
Review found the hint value (yes|no) matched prefixes ('nope'/'not' read as
'no') and the hint regex was unanchored, so a mid-line prose mention like 'see
**UI hint**: no above' was treated as the authoritative metadata line. Line-
anchor (^ + m flag) so only a real hint line counts; word-boundary on the value
so nope/not do not mean no. Adds coverage for hint:yes over a pure-backend body
and the nope fall-through.
Adds the divergent input the green suite lacked: the documented `**UI hint**:
no` metadata line. Asserts hint:no is authoritative non-frontend (no false
positive), hint:yes is authoritative frontend, a malformed hint is stripped so
its bare UI token does not fire, and hint:no overrides genuine UI language.
Adds the fixture the green suite lacked: an ID with a checkbox and a traceability
table that does NOT mention it. Asserts (1) a checkbox-only reconcile surfaces
table_unmatched instead of a silent full-success payload, (2) re-run on the
half-written file does NOT mask the drift as already_complete, and (3) a
REQUIREMENTS.md with no traceability table stays a clean success.
Review (MEDIUM): `git push ... 2>&1` did not check exit code, so a silent push
failure (auth-token expiry, network blip, non-fast-forward) would proceed to the
report step and declare success — silently reproducing the exact #2138 defect.
Add a fallback warning naming the rerun command so a failed push is visible
(best-effort: the PR already exists, so we still report it rather than abort).
Recaptures goldens + size baseline.
track_shipping committed the STATE ship-note ('Phase N shipped — PR #N') AFTER
create_pr and never pushed it, so the commit stayed local-only. When the GitHub
PR merged (especially fast/auto-merge) the ship-note was not in the source branch
and never reached the default branch — STATE's ship-status was silently lost,
recoverable only by STATE self-heal on the next /gsd-start.
Push the ship-note commit onto the PR branch with a [ci skip] trailer. GitHub
honors [ci skip]/[skip ci], so this lands the note on merge without triggering a
redundant pipeline, and preserves the PR number in STATE.
Recaptures the 18 golden-install-parity fixtures + the workflow-size baseline
(only the ship.md entry changed in each).
Closes#2138
ship.md IS the product the runtime loads. Assert its track_shipping step pushes
the committed ship-note onto the PR branch and carries a [ci skip] trailer, so a
regression to the local-only commit (the #2138 bug) is caught.
Adds a dedicated regression (tests/fix-2136-clock-local-today.test.cjs):
- realClock.localToday() returns the LOCAL calendar day under a pinned instant
+ TZ (America/Chicago → 2020-06-14, the issue's repro instant).
- realClock.today() is unchanged (UTC) — internal/cosmetic stamps stay UTC.
- localToday === today on a UTC host (no spurious divergence).
- makeFakeClock mirrors localToday (drop-in Clock substitute).
- field-level: a 'sync' transition with a split clock (today≠localToday)
writes the LOCAL day into Last Activity, proving the wiring uses localToday.
Mirrors localToday() in the fake clock helper and the inline fixedClock stubs
in state-transition.test.cjs / state-rebuild.test.cjs (the Clock interface now
requires it).
Adds the issue's verification matrix (cases A-E): a 🚧 bullet quoting a
nameless ## heading in backticks (the corruption), a nameless heading plus a
🚧 sub-heading carrying the name, canonical colon/em-dash shapes (no
regression), a 🚧 bullet only, and an anchored-regex guard proving a ##
heading quoted mid-line in backticks never matches.
The fast.md log_to_state fix changes an installed workflow file, so the per-
runtime golden-install-parity fixtures (18 runtimes) and the workflow-size
baseline are recaptured. Diff is exactly one entry per fixture (the fast.md
hash) and one baseline byte count — no spurious drift.
The first draft banned the literal /NF-1/ anywhere in the block, but the
explanatory comment legitimately references 'NF-1' to document the off-by-one
root cause. Match the COL_COUNT awk assignment specifically so the structural
guard targets the executable formula, not the prose.
Replaces the removed prose-regex test (bug-3805-*) that let the off-by-one
ship green. Extracts the actual bash block deployed in fast.md's log_to_state
step and EXECUTES it against real STATE.md fixtures, asserting on the
filesystem result: a row is appended with a cell count matching the header for
both the 5-column (non-validate) and 6-column (validate) schemas, and an
unrecognized schema still skips with a warning.
Four require() examples in commands/gsd/surface.md used bare
'gsd-core/...' specifiers that Node cannot resolve (wrong package
name + runtime-mirror layout off module path). Now derives the path
from runtimeConfigDir. Also fixes reinstall hint from
'npm i -g gsd-core' to 'npm i -g @opengsd/gsd-core'.
scanEntropyAnomalies + shannonEntropy were dead exports with zero
production callers — the live hooks (gsd-prompt-guard.js,
gsd-read-injection-scanner.js) inline their own pattern subsets for
hook independence and never called these functions.
Changes:
- Remove scanEntropyAnomalies + shannonEntropy from src/security.cts
- Remove scanEntropyAnomalies test block from tests/security.test.cjs
- Correct REQ-SCAN-INJ-02/-03 in FEATURES.md (EN/zh-CN/ja-JP) to
describe what actually runs live (injection patterns, invisible
Unicode) vs CI-only (base64-decode, codebase scan)
- Correct docs/security/baseline.md §2.4 to clarify live hooks inline
patterns, not import from security.cts
- Add regression test asserting the corrected contract
- scanForInjection retained: it serves as the CI codebase-scanner engine
VS Code is a net-new EoS runtime that — unlike every prior migration — is NOT
CLI-installed (Marketplace/VSIX extension). It has zero runtime==='vscode'
branches in bin/install.js and stays that way (regression-guarded); it is driven
entirely through the negotiated imperative Host-Integration adapter.
Registry + validator (the hard part):
- capabilities/vscode/capability.json (role:runtime): full hostIntegration block
(imperative / palette / active vscode.lm model / engine hook bus /
sandboxed-storage / mcp transport / sandboxed-web runtime; dispatch nested,
maxDepth 5 per VS Code's documented subagent depth).
- capability-validator.cjs extended so a role:runtime capability can legitimately
declare "extension-distributed, no config directory": new configHome.kind:'none'
+ installSurface:'none' (+ GATE-A pairing + the parity maps), with localConfigDir
and configHome.name made conditional on kind!=='none'. All 18 runtimes still
validate; getDirName returns a distinct sentinel (not '.claude') for a no-config
runtime.
- The add-a-registry-runtime tax: NON_INSTALLABLE_RUNTIMES exemption in the
runtime-flags drift guard, vscode added to global-config-home SPECIAL_CASED,
EXPECTED_PROFILES.vscode='ide', and the config-adapter/derivation/pin-count
guards updated. No golden-install fixture, model-catalog, or CONFIGURATION rows
(vscode never enters allRuntimes).
Dispatch + extension surface:
- Fixed vscode/extension.js's createHub()-no-args bug (every dispatch was
UnknownCommand, masked by a vacuous reachability test) — now reuses the shared
dispatchGsdCommand subprocess-shim (Node/desktop); the reachability test is
tightened to assert real dispatch.
- Promoted the #1933 host binding to a shipped vscode/host-binding.js; activate()
now composes the model/hookBus/stateIO seams through it. Corrected the model
seam to VS Code's real API (vscode.lm.selectChatModels() -> model.sendRequest();
vscode.lm.sendRequest does not exist) so the binding actually composes on real
desktop VS Code instead of throwing.
- New vscode/browser.js Web Extension entry with ZERO Node APIs (the engine's
config/capability loading is Node-bound, so the web entry registers the surface
and directs full dispatch to the native MCP server — honestly documented).
- UPGRADE 1: GSD skills as native Language Model Tools (contributes.languageModelTools
+ vscode.lm.registerTool), invoke() dispatching through the hub.
- UPGRADE 2: native subagent dispatch wired onto #runSubagent /
chat.subagents.allowInvocationsFromSubagents (fail-soft on API availability,
maxDepth 5 enforced).
- vscode/package.json: browser entry, engines.vscode ^1.105, chatParticipants +
languageModelTools contributions; fixed a stale activationPoints->activationEvents
manifest key. Added "vscode" to the package files array.
Docs (## vscode matrix section) + changeset (Added).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
audit-milestone's Nyquist scan classified a phase from `nyquist_compliant`
alone, so a phase seeded by plan-phase but never run through validate-phase
read PARTIAL — identical to a phase that validated and genuinely failed. The
template's `status` field could discriminate the two, but no workflow ever
promoted it off `draft`, so it was dead.
Make `status` live and read it:
- validate-phase.md §6: set `status: validated` in both the create (State B)
and update (State A) VALIDATION.md paths.
- audit-milestone.md §5.5: parse `status`; add a distinct NOT-VALIDATED bucket
keyed on `status: draft`, gate COMPLIANT/PARTIAL on `status: validated`, and
report `not_validated_phases` in the audit YAML.
- VALIDATION.md template: document the draft → validated lifecycle.
Tests & generated artifacts:
- Regression test folded into policy-138 (owning workflow-contract file);
fail-first verified vs origin/next (0 matches pre-fix).
- Regenerate golden-install-parity fixtures cleanly: adds the previously-missed
qwen.json and removes a contaminated `settings.local.json` entry that had
leaked into claude-local.json (the harness excludes hook-config files).
- Correct a stale validate-phase.md workflow-size-baseline entry.
Closes#2117
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Net-new EoS/pi installable runtime — purely additive (no prior runtime==='pi'
branches). pi is a bun-runtime programmatic-CLI whose /gsd command is registered
by a native ExtensionAPI extension and dispatches through the embedded engine.
Stage 1 (install plumbing):
- capabilities/pi/capability.json: full hostIntegration descriptor (imperative /
slash-programmatic / active-model / native-extension / bun) + hostBehaviors
{nativePlugin, pluginOnlyInstall}.
- --pi flag + interactive-menu renumber (All 17->18); pi added to RUNTIME_FLAG_IDS,
RUNTIME_LABELS, RUNTIME_META, allRuntimes/runtimeMap, model-catalog defaults.
- Install mirrors OpenCode: pi installs the gsd.cjs extension + the shared engine
payload (gsd-core + scripts + config markers) + the shared hooks bundle (spawned
by the extension at lifecycle events, like OpenCode's plugin). pluginOnlyInstall
EXCLUDES declarative command/agent/skill markdown, which pi has no host-read
surface for (its /gsd is programmatic). _installNativePluginIfDeclared (extracted
from the opencode-family path) copies pi/gsd.cjs -> ~/.pi/agent/extensions/gsd.cjs
(global) / .pi/extensions/ (local). pi added to package.json files.
- Golden: new pi.json (320 files: extension + engine + 27-file hooks bundle, no
markdown); the 16 other fixtures + claude-local change only by the shared
model-catalog hash line.
Stage 2 (real dispatch + upgrades):
- Shared dispatchGsdCommand() (shell-command-projection): bounded, no-throw
subprocess-shim to gsd-tools.cjs (the only full-surface dispatch path; no
in-process full-hub factory exists). Fixes pi/gsd.cjs's createHub()-no-args bug
(every dispatch was UnknownCommand) AND the identical bug in mcp-server.cts's
gsd_invoke_command, which a vacuous unknown-family-only test had masked (now has
a real dispatch regression test).
- pi/gsd.cjs: /gsd handler now (args, ctx) - tokenizes (quote-aware, via the
shipped hooks/lib/git-cmd.js) + dispatches real family/subcommand (not hardcoded
query/help); gsd_invoke gets a TypeBox (JSON-schema-fallback) parameters schema +
consumes params; getArgumentCompletions; before_provider_request active-model
steering (fail-open on null resolution); functional session_start /
before_agent_start / session_before_compact hook bridges (spawn the shipped GSD
hook scripts).
- EXTENSION_EVENT_SURFACES.pi expanded from ['tool_call'] to the full 30-event
vocabulary.
Docs (host-integration matrix + how-to) + changeset (Added).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The issue's "0 conditional branches" premise missed a live one: the `!isZcode`
shared-hooks exclusion (bin/install.js). Fold it onto descriptor-driven
hostBehaviors.skipSharedHooksInstall:true (zcode's golden has zero hook files —
byte-parity verified) and drop the now-unused isZcode destructure. Zero live
runtime==='zcode'/isZcode branches remain (AC2 source-grep guard over
bin/install.js + install-engine.cts + surface.cts + runtime-artifact-conversion.cts).
The 6 CLI-bookkeeping zcode mentions (--zcode flag, menu, roster, help) stay.
Reference test (declarative-reference-zcode.test.cjs): profileOf → declarative-cli;
createDeclarativeAdapter({runtime:'zcode'}).kind → declarative; a real install emits
the invocable nested-skills/commands/agents surface (no hooks); negotiateHostCapabilities
fail-closes (empty/corrupt descriptor; the nested/maxDepth undocumented sub-axes degrade
to most-restrictive); validateCapability clean.
UPGRADES documented as BLOCKED (verified doc gaps — NOT guessed, to avoid a
non-functional false-green): both of ZCode's documented capabilities lack a published
on-disk config format.
- Hook automation: zcode.z.ai/en/docs/plugin documents the Hook component only as
"automation hooks triggered on specific events" (capability detected from directory
layout) — no config file format/location/event schema. Cannot faithfully wire.
- MCP registration: zcode.z.ai/en/docs/mcp-services says servers are "stored in the
.zcode configuration file" (UI-only) with no documented on-disk filename/path/schema —
exactly the settings-filename gap the issue AC anticipated.
Both are documented (with the search trail) in the capability matrix + how-to, per AC4's
block-documentation clause; hookBus/transport stay declared for when ZCode publishes the
formats. No hook scripts or MCP artifacts added → no golden change, no other-runtime impact.
Golden: byte-identical for all 16 runtimes (the fold is byte-parity; no upgrade artifacts).
Matrix ## zcode EoS note + how-to; changeset (Changed). capability-registry regenerated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold all 10 residual isWindsurf branches in bin/install.js onto descriptor-driven
hostBehaviors (byte-parity — no fold changes any install output):
- 2 dead destructures dropped (uninstall, finishInstall); the dead
`else if (isWindsurf)` legacy agent-loop arm removed (windsurf ∈
_DESCRIPTOR_AGENTS_RUNTIMES → unreachable).
- skipSharedHooksInstall:true folds the two `!isWindsurf` shared-hooks exclusions.
- legacyDevinSkillsCleanup:true folds the `.devin`→`.windsurf` one-time cleanup gate.
- installsCommandBodiesForWorkflowDelegation:true folds the #1629 command-body copy
(workflow-delegation target — load-bearing; local-install verified intact).
- verificationStyle:"windsurf-workflows" folds the workflow-count report.
- Corrected stale _LEGACY_SCAN_SUBDIR_NAMES + hooks-json manifest comments (cursor + windsurf).
Zero live runtime==='windsurf'/isWindsurf branches remain across bin/install.js,
install-engine.cts, surface.cts, runtime-artifact-conversion.cts (AC2 guard scans all four).
UPGRADE (Cascade hook bus): wire GSD's write/command safety guards into Windsurf's
native hook bus. New hooksSurface 'windsurf-hooks-json' (VALID_HOOKS_SURFACES 7→8, GATE A
profile-marker-only allowlist, the HooksSurface union) + writeWindsurfHooksJson
(Cursor-templated, Cascade's flat {hooks:{<event>:[{command}]}} shape) writing
.windsurf/hooks.json with two BLOCKING pre-hooks:
- pre_write_code → gsd-windsurf-pre-write.js: blocks writes to a file outside the
active git worktree / into .git internals.
- pre_run_command → gsd-windsurf-pre-command.js: conservative destructive-command
deny-list (rm -rf of root/home incl. sudo/env/path-prefixed forms; fork bombs;
force-push refspec forms — HEAD:main, +main, --force/-f — to main/master/next).
Both use Cascade's protocol (stdin JSON, exit 2 + stderr to block, exit 0 to allow,
fail-open on error/timeout). Tokenize-based classifier (no catastrophic-backtracking regex;
4096-char cap) with the fail-closed false-positives fixed post-review.
The 4 advisory GSD guards + pre_mcp_tool_use + 5 post_* logging events are deliberately
NOT wired: Cascade has no context-injection channel for advisory hooks and GSD has no MCP
guard — porting them would be non-functional padding (documented; codebuddy #2098 / copilot
#2099 faithful-subset precedent). extendedHookEvents stays [].
Golden: the 2 guard scripts ship in the shared hook bundle (HOOKS_TO_COPY + the shared
managed-hooks-registry), exactly like cursor's 6 gsd-cursor-*.js scripts — so the 8
shared-bundle runtimes' fixtures gain the 2 inert windsurf scripts + the registry hash
(functionally inert for non-windsurf; the established cursor pattern). No install-output
change beyond that (the folds are byte-parity; skip-bundle runtimes untouched). New scripts
registered in managed-hooks-registry + build-hooks + INVENTORY. Tests: declarative-reference-
windsurf (adapter/axes/fail-closed + AC2 guard) + windsurf-hooks-bridge (live exit-2 blocking
+ allow/fail-open + ReDoS-bound + writer/reconcile/remove idempotency); VALID_HOOKS_SURFACES
pin updated to 8. Matrix hookBus delta + changeset (Changed). capability-registry regenerated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses findings from the orthogonal /code-review + /security-review passes:
- Markdown-injection (CRITICAL/HIGH): escape untrusted free-text (name, description,
license, author, eos axes) with mdInline() and size install/uninstall code fences
dynamically so a crafted entry cannot inject phishing links, break the summary
table, or escape the code fence in the committed, GitHub-rendered catalog.
- validateEntries no longer throws on a null/non-object array element (kept the
--json contract); rejects control characters in free-text fields; caps field
lengths and entry count; tightens the discussion and license regexes so neither
admits Markdown metacharacters / newlines.
- gen-registry treats a missing capabilities.json as an error (only eos.json is
optional pre-PR2); disambiguated from gen-capability-registry.cjs.
- Renders the required 'author' field (was captured but never shown).
- Adds tests for every fix: escaping/link-hijack/fence, null guard, control chars,
length + entry caps, tightened regexes, eos render path, interactions guards.
Refs #2182
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the three pure functions in registry-schema.cjs (isValidGsdRange,
validateEntries, renderMarkdown) that were stubbed in the previous commit, turning
the red suite green: strict per-type schema validation (capability + eos), a
self-contained engines.gsd range validator (no semver dep), and deterministic
Markdown generation with shields.io release badges + per-entry Discussion links.
Regenerates docs/registries/capability-registry.md and adds the Added changeset.
Also hardens the isValidGsdRange fast-check property (letters-only major) so it
cannot intermittently generate a valid prerelease range and flake.
Closes#2182
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the discoverability-registry surface for issue #2182: JSON-sourced
capability/eos catalogs, a pure schema/vocab module (registry-schema.cjs)
with the ADR-857 loop points + ADR-1239 axes, thin validate/gen CLIs,
the registry-entry PR template, README spec, and CONTEXT.md glossary terms.
The three pure functions (isValidGsdRange/validateEntries/renderMarkdown)
are stubbed here so the comprehensive test suite fails first (red), per the
feature-implementation red-first directive; the next commit implements them.
Refs #2182
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold Copilot's residual runtime-literal branches onto descriptor-driven
hostBehaviors. Several issue premises were inaccurate (verified via research)
and deliberately NOT followed: writesSharedSettings/"legacy exclusion list"
(per-runtime descriptor data, copilot's false is correct); RUNTIME_CONTENT_DISPATCH.copilot
+ installSurface==='copilot-instructions' (already descriptor-driven);
extendedHookEvents (closed Claude/Gemini enum — hooks extended in code instead);
reapply ternary (already folded, kimi #2095).
Real folds (all byte-parity — golden byte-identical for every runtime):
- src/install-engine.cts + src/surface.cts: the two `.agent.md` filename cutovers
(_copyStaged + _syncGsdDir) unified onto hostBehaviors.agentFileExtension via a
new exported agentFileExtensionFor() accessor (kills the two-mechanism divergence).
- src/runtime-artifact-conversion.cts: applyAgentPathRewrites' copilot skip →
hostBehaviors.noPathRewrite:true (antigravity #2096 precedent).
- bin/install.js uninstall: the two isCopilot cleanup branches → installSurface===
'copilot-instructions' gate (symmetric with install-time).
- bin/install.js: `!isCopilot` in the two skipSharedHooksInstall checks →
hostBehaviors.skipSharedHooksInstall:true (copilot has no shared gsd-*.js hooks).
- bin/install.js: three dead legacy inline-agent-loop isCopilot refs removed
(copilot ∈ _DESCRIPTOR_AGENTS_RUNTIMES → unreachable; byte-parity proven by clean
golden + real reachable-runtime install diffs). isCopilot dropped from 4 destructures.
Zero live `runtime==='copilot'`/`isCopilot` branches remain in bin/install.js,
install-engine.cts, surface.cts, or runtime-artifact-conversion.cts (AC2 guard scans all four).
UPGRADE 1 (multi-event hook bus): buildCopilotHookConfig() now emits preToolUse/
postToolUse/userPromptSubmitted/sessionEnd advisory handlers alongside sessionStart
(static inline bash/powershell — deterministic, golden-trackable). Only copilot.json's
gsd-session.json hash changes.
UPGRADE 2 (background dispatch): surfaced via the negotiated contract only —
dispatch.background:true exceeds the declarative-cli baseline and survives negotiation
with no downgrade warning. NO .agent.md frontmatter field (copilot has none). MCP
companion out of scope (AC4 names only 2 upgrades).
Tests: declarative-reference-copilot (adapter/axes/fail-closed + AC2 4-file source-grep
guard) + copilot-upgrades (live 5-event hook wiring; dispatch.background negotiation).
Matrix EoS note + how-to; changeset (Changed). capability-registry regenerated.
Incidental flaky-test RE-ARCHITECTURE (no-defer, maintainer-directed):
tests/opencode-review-reconstruction.property.test.cjs spawned ~600 synchronous
execFileSync('jq') subprocesses (numRuns:200 × 3 fast-check properties, one jq per
generated stream); a single jq freezing on a contended macos-22 CI runner hung the whole
unit-test chunk to its 600s kill (this PR's CI). --test-force-exit can't interrupt a
synchronous execFileSync, so the cure is to stop spawning per case, not just time-bound
it. Re-architected to run the SHIPPED jq program over the whole fast-check corpus in ONE
jq process: each generated stream is one compact-JSON array per line in a temp file,
`jq -c <PROGRAM>` (no -s) applies PROGRAM to each array (`.` == the array, exactly what
production's `jq -rs <file>` sees after slurping) and emits one result per line —
empirically byte-identical to the per-stream form across embedded-newline/empty/quote/
unicode/null-drop cases, and file-input (like production) so there's no stdin pipe to
deadlock on large I/O. ~600 spawns → 6; coverage unchanged (200-case corpus per property,
deterministic seeds) plus explicit boundary/diagnostic example batches. Still property-
tests the real shipped jq (no JS reimplementation). Per-call jq timeout retained as a
belt-and-suspenders bound.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold CodeBuddy's residual runtime-literal branches onto descriptor-driven
hostBehaviors (the issue's "zero branches remain" premise was inaccurate — 2
lived):
- Folded the `if (isCodebuddy)` commands/ report duplicate (byte-identical to
the generic reportCommandsDir block above it) onto
hostBehaviors.reportCommandsDir:true, matching cursor's #2089 precedent.
- Deleted the dead `else if (isCodebuddy)` agent-conversion arm (codebuddy ∈
_DESCRIPTOR_AGENTS_RUNTIMES → gated out before the legacy chain, same dead-arm
pattern removed for augment/trae in #2097).
- Removed isCodebuddy from all 4 destructure sites (now comments only, mirroring
the #2096 isAntigravity fold). Zero live isCodebuddy reads remain.
UPGRADE 1 (extended hook bus): populate extendedHookEvents with the full
extended set SubagentStop/Stop/PreCompact/SubagentStart (qwen #2092 / kimi
precedent — codebuddy previously had extendedHookEvents:[] so it got NONE of
these). The generic applySettingsJsonHooks loop wires them, so CodeBuddy's
settings.json now gains all four subagent-lifecycle + stop/compact hooks it
previously lacked, matching Qwen/Kimi coverage. No source change (the
HOOK_EVENT_SURFACES SDK catalog is a locked dict; all 4 events are already in
the validator enum; hookEvents already 'claude'). settings.json is golden-
excluded → no golden change.
UPGRADE 2 (background dispatch): surfaced via the negotiated capability contract
only. CodeBuddy's dispatch.background:true legitimately exceeds the declarative-
cli baseline (false) and survives negotiation with no warning. NO agent-file
frontmatter field is emitted: the CodeBuddy CLI (GSD's install target,
~/.codebuddy/agents/) has NO background-dispatch frontmatter field — background
is a caller-side run_in_background invocation param (verified against
codebuddy.ai/docs/cli/sub-agents); the issue's agentMode/enabledAutoRun are
IDE-only (codebuddy.cn, a different product). Emitting them would be a non-
functional false-green, so it is deliberately not done.
Golden: byte-identical for all 16 runtimes (folds are console/failures-report
only; UPGRADE 1 → golden-excluded settings.json; UPGRADE 2 → no artifact
change). Tests: declarative-reference-codebuddy (adapter/axes/fail-closed
negotiation + AC2 source-grep guard) + codebuddy-upgrades (live install asserts
all 4 extended hooks wired; dispatch.background survives negotiation above
baseline). Matrix + install-on-your-runtime updated; changeset (Changed).
capability-registry regenerated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold augment's runtime-literal conversion branches onto descriptor-driven
hostBehaviors and delete dead code:
- Site A (_applyRuntimeRewrites case 'augment'): the 4 ~/.augment dot-dir
regexes now derive from getDirName('augment') via escapeRegExp (byte-
identical; getDirName('augment')==='.augment') — no runtime literal.
- Site B (applyRuntimeContentRewritesForCommandsInPlace): the
`if (runtime==='augment')` markdown-converter branch now reads
runtime.hostBehaviors.commandBodyConverter and dispatches through a local
COMMAND_BODY_CONVERTERS map (degrade-closed on unknown/absent name).
- Deleted dead `claudeToAugmentTools` map (zero refs; orphaned by ADR-1508
single-sourcing) and the unreachable `else if (isAugment)` agent-conversion
branch (augment ∈ _DESCRIPTOR_AGENTS_RUNTIMES → gated out upstream).
- Incidental orphan cleanup (no-defer): removed the equally-unreachable
`else if (isTrae)` agent-conversion arm left behind by trae's already-merged
migration #2094 (trae ∈ _DESCRIPTOR_AGENTS_RUNTIMES, same upstream gate).
copilot/windsurf/codebuddy arms are removed by their own pending migrations.
UPGRADE 3 (transport:mcp): register the GSD companion MCP server in Augment's
settings.json under mcpServers.gsd (Augment hosts MCP in settings.json, not a
standalone file). mergeGsdMcpServerIntoSettings mutates the in-memory settings
object finishInstall already writes (gated on hostBehaviors.mcpCompanion===
'settings-json'); non-destructive + idempotent; symmetric uninstall removal.
settings.json is golden-excluded, so no golden change. UPGRADE 1 (named/
background dispatch) + UPGRADE 2 (settings-json hook bus, Claude dialect)
were already live in production — this adds tests exercising both.
Golden: byte-identical for all 16 runtimes (folds preserve regex behavior;
MCP lives in golden-excluded settings.json) — verified by a real double-install
tree diff. Tests: declarative-reference-augment (adapter/axes/fail-closed/
undocumented-sub-axes + source-grep guard scoped to conversion-logic branches)
+ augment-upgrades (dispatch negotiation, hook-bus live install, MCP add/
idempotent/preserve/uninstall). Matrix + connect-gsd-mcp-server + a stale
install-on-your-runtime hook-ownership claim corrected; changeset (Changed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
deriveProgressFromRoadmap read the `## Progress` table with two
4-column-only regexes, so every project past its v1.0 milestone — which
uses the 5-column milestone-grouped shape the same template ships
(| Phase | Milestone | Plans Complete | Status | Completed |) — parsed to
{completedPhases: null, totalPhases: null, totalPlans: null}. Because the
body is wrapped in a bare try/catch the failure was silent, and
phase.complete flipped the ROADMAP row to Complete but never advanced
STATE's progress percent / completed-phase count.
Parse the table by header instead: locate the `## Progress` section
(line-anchored h2, mirroring the writer's #2012 scoping so an earlier
Phase/Status/Completed-shaped table can't hijack it), read the header row
to find the Status, Completed, and plans columns by name, then index each
data row positionally. One code path now handles both the 4-column
greenfield and 5-column milestone-grouped shapes — the same understanding
the writer (cmdPhaseComplete, with its `cells.length === 5` branch)
already had. Preserves the 999.x backlog exclusion, M/N plan summing, and
the prior contract that a count of 0 is reported as null.
Regression tests fold into tests/state.test.cjs: the template's own
5-column Progress block parses non-null, both shapes derive identically,
999.x stays excluded in the 5-column shape, and decoy tables (an earlier
## Retrospective table and an ### Progress h3 subheading) do not hijack
the scope.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold all runtime==='kimi'/isKimi logic branches into descriptor-driven
hostBehaviors (localInstallDeferred, verificationStyle, agentManifestStyle,
reapplyCommand, doneBannerStyle) + add 'kimi' to _DESCRIPTOR_AGENTS_RUNTIMES.
Kimi's skills/kimi-agents dispatch was already descriptor-driven (converter-by-
name + kimi-agents kind). Zero isKimi/runtime==='kimi' branches remain.
UPGRADE 1 (native hook bus): new hooksSurface 'kimi-hooks-toml' + a marker-
delimited config.toml [[hooks]] emitter (buildKimiHooksTomlBlock/writeKimiHooksToml
in runtime-hooks-surface.cts; resolveKimiHooksTomlDir in runtime-homes.cts).
GSD's lifecycle hooks now wire into Kimi's native ~/.kimi/config.toml (Context7-
confirmed path) at SessionStart/PreToolUse/Stop/PreCompact/SubagentStart/
SubagentStop — kimi becomes a hooks/ consumer (the 3 && !isKimi exclusion guards
removed). config.toml holds absolute install paths so it's golden-excluded via
an exact relative-path (.kimi/config.toml), not a basename (which would blind
Codex's config.toml). New hooksSurface value added to the closed enum in
capability-validator + runtime-config-adapter-registry.
UPGRADE 2 (background dispatch): flip dispatch.backgroundDispatch true (Kimi's
Agent tool takes run_in_background; root agent already gets the Agent tool), so
negotiation no longer flattens dispatch. subagentToolkit stays 'undocumented'
per AC (coder/explore/plan have distinct tool policies).
MCP transport explicitly deferred (no installer-driven MCP for any runtime).
Golden: only kimi.json changes (hooks/ scripts now installed); all 15 others +
claude-local byte-identical (kilo/zcode keep their own exclusions). Tests:
kimi-imperative-reference (adapter/axes/fail-closed/hostBehaviors + source-grep
guard) + kimi-upgrades (config.toml [[hooks]] SessionStart + marker idempotency
+ backgroundDispatch negotiation). CONTEXT.md glossary + matrix + how-to updated;
changeset (Added).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold trae logic branches into descriptor-driven reads: skipSharedHooksInstall
gates (dropped && !isTrae), and the case 'trae' path-rewrite arm now computes
the self-alias from the descriptor-driven dirName (.trae). Dead isTrae bindings
removed from uninstall/writeManifest/finishInstall. trae's skills dispatch was
already descriptor-driven (converter-by-name). RUNTIME_CONTENT_DISPATCH.trae is
left as a runtime-keyed table registration (its regex/callback rewrites can't be
a byte-identical descriptor map — matches cursor/windsurf/cline). trae stays in
RUNTIME_FLAG_IDS: isTrae still gates the agents-converter selection (agents out
of scope; removal gated on the cross-runtime agents-dispatch migration).
Byte-identical golden parity for all 16 runtimes.
UPGRADE: SOLO stage/trigger metadata — emitted Trae SKILL.md now carries
stage: workflow (descriptor-gated via hostBehaviors.soloStageMetadata) so
Trae's SOLO Agent can auto-invoke GSD skills at the corresponding stage. Field
shape is best-effort/inferred (Trae publishes no formal schema). trae.json
golden regenerated.
Tests: trae-imperative-reference (adapter/axes/fail-closed shouldFlattenDispatch
+ no runtime==='trae' source-grep, isTrae exempted for agents) + trae-upgrades
(stage: workflow on installed SKILL.md, descriptor-gated). Matrix note +
changeset added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold remaining isKilo logic branches into descriptor-driven reads:
finishPermissionWriter (uninstall cleanup), skipSharedHooksInstall (hooks
copy), and a skills converter-name registry (the artifactLayout.converter
field is now load-bearing, not decorative). frontmatterDialect stays the
documented dispatch key for frontmatter (no descriptor field for it). Dead
isKilo destructure bindings removed. Byte-identical golden parity for all 16
runtimes (opencode, which shares kilo's combined-family path, verified clean).
UPGRADE 1 (hook bus): install .kilo/plugins/gsd-core.js native plugin +
extensionEvents:"kilo" + EXTENSION_EVENT_SURFACES.kilo (OpenCode-fork bus).
UPGRADE 2 (active model): populate runtimeTierDefaults.kilo + thread
modelOverride through convertClaudeToKiloFrontmatter — model no longer stripped
from agents. UPGRADE 3 (MCP): document the gsd-core MCP companion under kilo's
mcp config key. UPGRADE 4 (named dispatch): agents/*.md mode:subagent roster is
the Task-tool dispatch surface (tested); subagentToolkit stays 'undocumented'
per AC so dispatch degrades to 'degraded' by design.
Model-catalog single-source edit ripples the shared model-catalog.json hash
into all 16 golden fixtures (expected). Inline defect fixes (no-defer): stale-
bake-guard resolveAgentDir 'agent'->'agents' (was a silent no-op for opencode/
codex), hardcoded 'Removed OpenCode plugin' uninstall log -> generic, and the
connect-gsd-mcp-server.md OpenCode mcpServers->mcp doc error.
Tests: kilo-imperative-reference (adapter/axes/fail-closed/degradation/
hostBehaviors + widened isKilo source-grep across 4 modules) + kilo-upgrades
(plugin parity+load, model-override converter, agents dispatch surface, MCP
doc). Matrix + how-to + config docs updated; changeset added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>