The quick.md fix in this PR changes the file content, so its per-runtime
content hash in the golden-install-parity snapshot (tests/golden-install-parity.test.cjs,
Linux/macOS-only) is stale. Regenerated with UPDATE_GOLDEN=1 on Linux
(WSL); only the gsd-core/workflows/quick.md hash line changed in each of
the 16 runtime fixtures — no other drift.
Claude Code's isolation="worktree" forks new worktrees from origin/HEAD, not
the live local HEAD. When prior local commits (e.g. an earlier quick task in
the same session, or this task's own Step 5.6 pre-dispatch plan commit)
advance local HEAD without an intervening push, origin/HEAD stays pinned to a
stale ancestor and the executor's worktree_branch_check guard halts with a
base-mismatch fatal that can be many commits behind, not just one.
Port the worktree.base-check auto-degrade pattern already used by
execute-phase (#683/#1369) into quick.md's single-dispatch path, run
immediately before EXPECTED_BASE is captured in Step 6.
AGENTS.md is legitimately created by gsd install copilot (issue #786) when
the installer runs inside a repo checkout. The file may exist on disk — that
is expected. The test must verify it is not git-tracked, not that it is absent
from the working tree. Uses git ls-files --error-unmatch instead of fs.existsSync.
- Teach applySurface to build agentCtx (pathPrefix + attribution) and pass it
to kind.stage() for agents kind, mirroring createRuntimeArtifactInstallPlan
(ADR-1235 §1). Surface-path agents now receive path-rewrite + attribution +
converter + normalize, matching install output byte-for-byte.
- Pass skills:'*' sentinel for agents staging when no surface state modifications
exist, so ALL agents are staged (not just those referenced by _calls_agents_).
- Declare converted agents kind in copilot and antigravity capability.json;
add to _DESCRIPTOR_AGENTS_RUNTIMES in bin/install.js.
- Handle copilot .agent.md filename rename in both _copyStaged (install path)
and _syncGsdDir (surface path).
- Ship golden-parity harness (ADR-1235 §0): tests/issue-1575-agent-descriptor-
parity.test.cjs asserts applySurface output is byte-identical to
installRuntimeArtifacts for all 7 descriptor-driven runtimes, plus stale-
cleanup convergence and prune data-loss coverage.
- Update ADR-1235 with cutover progress.
Cline remains deferred (rules-only local branch + local/global complication).
The descriptor-driven gate must treat a runtime as layout-driven when its scoped
artifactLayout is non-empty (any kind), not only when it has a skills kind —
windsurf's global layout is agents-only and is a legitimate layout-driven runtime.
Preserve the three legacy special-cased paths (opencode/kilo combined path,
claude-local copyWithPathReplacement).
zcode falls into the default package.json+hooks path (not in the CommonJS-mode
exclusion roster), so its install contract is packageJson: true — matching
observed install output. No runtime === 'zcode' branch is added (AC#3): zcode
gets the default by not being excluded.
The legacy _isSkillsRuntime gate was a hardcoded isCodex || isCopilot || ...
roster; zcode was absent so the layout-driven skills-install path was skipped
(commands + agents installed, but no skills). Adding || isZcode would violate
AC#3 (no runtime === 'zcode' branches), so the gate is now derived from the
descriptor: a runtime takes the skills path iff its scoped artifactLayout
declares a skills kind. Behavior-preserving for all 15 existing runtimes
(verified by their install contracts); includes zcode (and any future skills
runtime) with zero per-runtime branches. opencode/kilo keep their specialized
combined path.
Regenerate the golden-install-parity fixtures: the non-zcode runtimes shift only
the shared model-catalog.json hash (zcode was added to the catalog); zcode now
includes its skills tree.
Resolve the zcode test cascade exposed by gsd-test:
- model-catalog.json: add zcode runtimeTierDefaults (null tiers, matching the
other profile-marker-only runtimes) so KNOWN_RUNTIMES stays parity with allRuntimes.
- runtime-name-policy: add zcode to GLOBAL_CONFIG_HOME_FRAGMENTS (~/.zcode) so
getGlobalConfigHomeFragment stops falling through to the .claude default.
- installer-migration-install: add the zcode fresh-install contract (flat-skills,
no settings, no package.json — same shape as trae).
- golden-install-parity: capture the zcode fixture via a standalone generator
script (not node --test — the gate stays gsd-test).
- capability-matrix.md: regenerate so zcode appears as a first-party row.
Fluidify the remaining count-pinned guards so adding a runtime no longer trips a
hand-pinned snapshot: gemini-runtime-removed (flag count derives from the
registry), non-claude-runtimes-registry-derivation (golden list derived from the
registry).
Add ZCode as a first-party runtime via a declarative capability descriptor
(capabilities/zcode/capability.json) with zero hardcoded runtime === 'zcode'
branches — exercising the de-hardcoded, data-driven runtime path that 1.7.0
(ADR-1016 / ADR-1239) enables.
Descriptor (all axes sourced verbatim from zcode.z.ai docs):
- configHome ~/.zcode; nested skills + flat commands/agents; profile-marker install
- Claude-shaped skill format reuses convertClaudeCommandToClaudeSkill (no new converter)
- hostIntegration: declarative / slash-file / mcp / electron; dispatch background=false
(foreground-only per docs); nested+maxDepth undocumented; passive model mode
Installer registration (data, not branches): --zcode flag, allRuntimes, runtimeMap,
interactive menu, --all list. getGlobalConfigDir + resolveRuntimeArtifactLayout +
ALLOWED_CONFIG_RUNTIMES + resolveInstallPlan all derive from the descriptor.
Revamped the brittle per-runtime golden-master tests to be count-agnostic,
descriptor-derived property tests (1.7.0 makes runtimes pluggable data, so pinning
frozen '15 runtime' snapshots is the wrong invariant): getdirname, label-policy,
config-adapter-registry (intent + install-plan golden master), capability-registry,
host-integration-descriptors (counts derive from curated maps). Adding a runtime
descriptor now extends coverage with zero edits to those suites.
Welcome banner, --zcode help, supported-runtimes how-to, and the host-integration
capability matrix (every axis cited) updated.
The compiled gsd-core/bin/lib/*.cjs modules are gitignored build artifacts
(ADR-457) shipped prebuilt in the npm tarball. A Claude Code plugin-marketplace
/ git-clone install materializes the repo tree directly and never runs
`npm run build:lib`, so those ~148 files are absent and every CLI command dies
at load with `Cannot find module './lib/cli-exit.cjs'`.
gsd-tools.cjs now calls ensureRuntimeBuild() before its ./lib requires: a new
committed bin helper that compiles the tree once on demand (lock-guarded,
incremental-cache-cleared, portable `node <tsc>` invocation) when the sentinel
cli-exit.cjs is missing, and surfaces an actionable error when TypeScript is
unavailable. The already-built npm path is a single fs.existsSync no-op.
ADR-457 (gitignored, build-at-publish) is preserved; no release-pipeline change.
Verified against a simulated marketplace checkout (148 build outputs removed):
the CLI self-heals and the result is byte-identical to `npm run build:lib`.
Closes#2002
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The #2018 regression test used raw fs.rmSync() in afterEach, tripping the
local/no-raw-rmsync-in-tests eslint rule (Windows-EBUSY retry budget). CI's
lint-tests job (no eslint cache) caught it on next; local --cache runs had
false-greened it. Switch to helpers.cleanup(). Also drop two unused
destructured imports (evaluateCommandExitZero, interpolate) in the
gate-predicate-evaluator test that emitted no-unused-vars warnings.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#2012): scope Progress-row regex to ## Progress section (was binding to earlier table)
The Progress-row writer used a non-global regex that matched ANY table row
starting with the phase number. When an earlier table (e.g. Requirements
coverage | Phase | Requirements | Count |) preceded ## Progress, the regex
bound to the wrong row (3-column), no-op'd, and never reached the real
Progress row. roadmap_updated stayed true (it's existsSync), masking the
failure.
- src/phase.cts: scope the tableRowPattern regex to the ## Progress section
(indexOf + slice) so it only matches Progress-table rows.
- tests/phase.test.cjs: regression test — ROADMAP with a phase-numbered
Requirements table before ## Progress → Progress row updated, Requirements
row untouched.
Closes#2012
* docs(#2012): backfill changeset pr 2032
* fix(#2018): applySurface empty manifest no longer deletes gsd-* agents
The agent-prune loop in _syncGsdDir deleted any gsd-*.md not in the staged
set. When the manifest resolved empty (null/non-object, no entries, no files
key, or unresolvable install source root), the staged set was empty → every
gsd-* agent was deleted. Skills were guarded by pruneSkillDirs' manifest-
membership check; agents had no equivalent.
- src/surface.cts: skip the agent-prune loop when the manifest is empty/absent
(copy still runs — genuinely new agents are added).
- tests/surface-empty-manifest-agents.test.cjs: boundary matrix — empty
manifest preserves agents (Map() + undefined); non-empty manifest + empty
staged still prunes (boundary); empty manifest + new staged copies new +
preserves existing.
Closes#2018
* docs(#2018): backfill changeset pr 2031
* fix(#2017: grant mcp__plugin_context7_context7__* for plugin-marketplace context7
The 8 context7-using agents granted only mcp__context7__* (standalone server
form). Claude Code's plugin-marketplace context7 install names tools
mcp__plugin_context7_context7__*, so the grant never matched and every
researcher/planner/executor silently lost doc lookup (fell back to WebSearch).
- 8 agents: add mcp__plugin_context7_context7__* alongside mcp__context7__*.
- scripts/research-profiles.cjs: update the researcher profile tools to match.
- tests/context7-plugin-grant-parity.test.cjs: regression guard — no agent
grants the standalone form without the plugin form.
Closes#2017
* docs(#2017): backfill changeset pr 2029
* fix(#2022): gate roadmap update-plan-progress checkbox on verification passed
cmdRoadmapUpdatePlanProgress stamped the phase checkbox + completion date
the moment all summaries landed, with NO verification gate — unlike
cmdPhaseComplete (phase.cts:1436) which checks readVerificationStatus. Since
update-plan-progress is called after every wave and every plan, the checkbox
fired before gsd-verifier confirmed the phase.
- src/roadmap.cts: isComplete now requires summaryCount >= planCount AND
readVerificationStatus(phaseDir).status === 'passed'.
- tests/roadmap.test.cjs: 2 regression tests (no VERIFICATION.md → not
complete; gaps_found → not complete) + updated 3 existing complete tests
to include a passed VERIFICATION.md.
Closes#2022
* docs(#2022): backfill changeset pr 2030
* fix(#2020): remove dead SDK file refs that triggered infinite find.exe on Windows
gsd-executor.md referenced sdk/src/query/QUERY-HANDLERS.md and reapply-patches.md
referenced sdk/dist/cli.js — both retired with the SDK (ADR-0174). AI runtimes
that locate doc refs via filesystem search ran find /, which on Git Bash for
Windows traverses the whole drive (14h+, orphaned find.exe, 4M+ handles).
- agents/gsd-executor.md: drop dead QUERY-HANDLERS.md ref.
- workflows/reapply-patches.md: drop dead sdk/dist/cli.js clause.
- tests/no-dead-sdk-refs.test.cjs: regression guard — no sdk/src|dist|handlers
file refs in agents/workflows/references markdown.
Closes#2020
* docs(#2020): backfill changeset pr 2027
* fix(#2019): planning-config.md global-learnings path ~/.gsd/learnings → ~/.gsd/knowledge
The features.global_learnings row pointed at ~/.gsd/learnings/ but the
implementation (src/learnings.cts, execute-phase.md) uses ~/.gsd/knowledge/.
Docs now match the code.
Closes#2019
* docs(#2019): backfill changeset pr 2026
* fix(#1921): preserve verify-work state across gap-closure + defer follow-ups
Resuming /gsd:verify-work after /gsd:execute-phase --gaps-only lost the
verification state: UAT ## Gaps still read status: failed even after their
fix plans executed, so verify-work re-diagnosed them as fresh blockers,
spawned a new gap plan, and reported only the new plan verified. A new
state contract links each gap to its fix plan so fixed gaps are recognized.
- verify-work.md gap YAML: stable gap_id (G-{phase}-{N}) per gap.
- plan_gap_closure planner prompt: each *-PLAN.md tags the gap_ids it
addresses in its frontmatter (gap_closure: true, gap_ids: [...]).
- new reconcile_gaps step (run at resume_from_file entry): marks a gap
status: resolved when its plan has a matching *-SUMMARY.md — fixed gaps
are not re-diagnosed and do not spawn new gap plans; a re-reported break
is treated as a fresh regression with a new gap_id.
- deferred-follow-up branch: a future-work idea (signals: 'later',
'next version', 'out of scope', ...) is captured to UAT ## Deferred
Follow-Ups instead of becoming a blocking gap/plan.
- workflow-size baseline recaptured (verify-work LARGE tier, 38221/61440).
Closes#1921
* docs(#1921): backfill changeset pr 2025
* fix(#1921): balance <step> tags in verify-work.md via 4-backtick outer fence
The plan_gap_closure step nested a ```yaml example inside a ``` block;
same-length fences made stripFencedCode close the outer block early,
swallowing the step's </step> (14 opens / 13 closes). Widen the outer
fence to 4 backticks so the nested yaml is contained. Regenerate golden
fixtures + size baselines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Locks in the 6a51bd11 fix. The fast-map gate (fastMode && mapReadiness==='fast') previously ran before the partial-planning check and lacked a !projectExists guard, so rerunning /gsd-onboard --fast after project setup, or with incomplete planning, misrouted to complete-map-before-new-project instead of write-summary/partial-planning. Both cases now covered; each fails on the pre-fix ordering.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
- runtime-launcher-parity: recognize workflows that delegate gsd_run to references/gsd-run-resolver.md (onboard.md) and exempt them from the inline-preamble checks; add a compensating byte-equality guard (B2) asserting the reference bash block matches _runtime-launcher.snippet.sh. - onboard.md: document the TEXT_MODE plain-text/numbered-list fallback for AskUserQuestion on non-Claude runtimes (fixes ask-user-questions-fallback, #2012). - Regenerate golden-install-parity fixtures, docs/INVENTORY-MANIFEST.json (add gsd-run-resolver.md + onboard-projection.cjs), and tests/workflow-size-baseline.json.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Integrate the brownfield /gsd:onboard skill into the skill subsystems so the
full CI suite passes:
- Route onboard under commands/gsd/ns-project.md (requires + routing row) so it
nests as gsd-ns-project/skills/onboard on nested-layout runtimes instead of
leaking as a 7th top-level skill dir (fixes install-nested-layout + issue-69).
- Remove onboard from PROFILES.core (src/install-profiles.cts) so the frozen
main-loop core stays at 8 skills; onboard remains in standard/full.
- Add the TEXT_MODE plain-text fallback note to gsd-core/workflows/onboard.md
for non-Claude runtimes (#2012).
- Allowlist onboard.md as a user-invocable skill (enh-2790 ratchet).
- Regenerate docs/INVENTORY-MANIFEST.json, golden-install-parity fixtures, and
the workflow size baseline to match.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Fixes the two Tests-workflow failures on the onboard PR.
lint-tests (lint-allow-test-rule-refs): the new
tests/onboard-command.test.cjs carried a `source-text-is-the-product`
allow-test-rule exemption with no tracking ref, tripping the novel-offender
gate. Add `(see #1990)` per ADR-456 so the exemption is traceable.
golden-install-parity: the /gsd:onboard feature adds
gsd-core/workflows/onboard.md and skills/gsd-onboard, and updates
templates/project.md, workflows/do.md, the help modes, and map-codebase.
Recapture the golden fixtures (UPDATE_GOLDEN=1) for all 16 runtimes so the
installed-output manifest matches the intended source changes.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
init new-project now uses the same seven-file codebase map completeness
check as init onboard, so partial .planning/codebase/ directories no longer
skip the brownfield mapping offer after onboarding warns about an incomplete
map.
The onboard workflow now branches on onboarding_summary_exists and asks for
confirmation before regenerating SUMMARY.md on repeat runs.
Wire the two forward-declared mempalace.memory_mode modes so they actually
route recall/capture instead of silently behaving as `augment`:
- kg_backend: the palace temporal KG is the primary knowledge-graph source;
native .planning/graphs/ is the fallback. Non-KG drawer recall stays additive.
- replace: recall resolves through the palace as the source of truth; native
artifacts are the fallback.
Every mode stays onError:skip and default-resilient — an unreachable palace
degrades to native memory and GSD keeps writing .planning/graphs/, so no memory
is lost. Cross-mode .planning/graphs/ migration remains a documented open
question (PRD/ADR §17), out of scope here.
Surfaces updated (instruction-only contract): recall/capture commands (+ generated
skills), discuss/wave fragments, curator agent, capability.json schema. Docs:
how-to Step 3, CONFIGURATION, FEATURES, CONTEXT glossary. Regenerated
capability-registry, golden install-parity fixtures (mempalace hashes only),
agent-size-baseline. Added a routing-contract + cross-surface parity test.
Incidental (folded per no-defer rule): removed pre-existing unused imports
(spawnSync in capability-registry.test.cjs; fs in issue-498-package-identity.test.cjs)
that eslint flagged in/alongside the touched files.
Closes#2007
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#1825): configurable graphify graph location (graphify.graph_path)
Add a graphify.graph_path config key (.planning/config.json) that overrides
where /gsd-graphify query|status|diff read the knowledge graph, so one curated
umbrella-level cross-repo graph can serve multiple sibling projects without N
drifting ~5 MB mirror copies. Previously the graph location was hardcoded to
<cwd>/.planning/graphs/.
- src/graphify.cts: resolveGraphLocation(cwd, planningDir) honors the key
(resolved relative to project root; absolute paths honored via path.resolve);
falls back to the historical .planning/graphs/graph.json when unset/blank/
non-string (byte-identical). Wired into graphifyQuery, graphifyStatus,
graphifyDiff (snapshot travels with the configured graph via dirname), and
writeSnapshot. Configured-but-missing -> actionable error naming the path.
Build stays project-scoped (skill hardcodes the cp dest); umbrella graph is
built in the umbrella project, sub-projects only READ it.
- config-schema.manifest.json: register graphify.graph_path in validKeys.
- tests/graphify-graph-path.test.cjs: boundary matrix (unset byte-identical,
set+present reads configured graph not default, set+missing actionable error,
relative resolved vs project root, blank treated as unset, snapshot alongside
configured graph, diff from configured dir, build project-scoped) +
VALID_CONFIG_KEYS registration.
- docs: CONFIGURATION.md row, FEATURES.md REQ-GRAPH-06, CONTEXT.md module note,
.changeset (Added).
Closes#1825
* docs(#1825): backfill changeset pr number 2013
* fix(#1864): balance <step> tags in settings-advanced.md §8 Model Policy
§8 Model Policy ended with </step> but had no matching opening tag (5 opens /
6 closes), leaving it as loose inter-step content. Add the missing
<step name="model_policy"> opener so the section is a proper step.
- gsd-core/workflows/settings-advanced.md: add <step name="model_policy">
- tests/workflow-step-tag-balance.test.cjs: regression guard — every top-level
workflow must have balanced <step>/</step> (fenced code stripped), plus a
focused assertion that §8 is wrapped in model_policy.
- goldens + workflow-size baseline recaptured.
Closes#1864
* docs(#1864): backfill changeset pr 2014
* fix(#1865): runtime launcher honors CLAUDE_CONFIG_DIR
The gsd_run preamble resolved the Claude global install only at
$HOME/.claude/gsd-core/bin/, but the installer honors CLAUDE_CONFIG_DIR —
so a global install redirected via CLAUDE_CONFIG_DIR was invisible to every
gsd_run call (every command failed with 'gsd-tools.cjs not found').
The Claude resolver arm now uses ${CLAUDE_CONFIG_DIR:-$HOME/.claude},
matching the installer + the other runtimes' ${VAR:-default} pattern.
Default $HOME/.claude behavior is unchanged.
- _runtime-launcher.snippet.sh: Claude arm honors CLAUDE_CONFIG_DIR.
- sync-runtime-launcher.cjs re-run: 95 workflows/agents re-synced.
- review.md / discuss-phase.md: trimmed to stay under their byte budgets.
- runtime-launcher-parity.test.cjs: (A) substring updated for the new form
+ explicit #1865 assertion that the snippet honors CLAUDE_CONFIG_DIR.
- goldens + size baselines recaptured.
Closes#1865
* docs(#1865): backfill changeset pr 2024