github open-gsd/gsd-core v1.21.0

one hour ago

Install

npm i @opengsd/gsd-core@1.21.0
# or
npm i @opengsd/gsd-core@latest

What's Changed

Added

  • The Community Capability Registry now lists Beads Lifecycle — discover the independently maintained davdittrich/gsd-beads capability, which syncs plan tasks into Beads (bd) issues, closes them after each wave, and blocks /gsd-ship while blocking_open or diverged is not 0 or BEADS.md is missing (its ship:pre gate also blocks while bd is missing, unless beads.ship_gate is false), with exact install and uninstall commands. (#5307) (#5384)
  • The Community Capability Registry now lists musk — discover the independently maintained bitcreed/gsd-musk-simplification capability, a subtraction pass that challenges milestones, requirements and implementation before they are built, logs every cut reversibly, and prices the savings from the project's own token usage, with exact install and uninstall commands. (#5869) (#5872)
  • The Community Capability Registry now lists Standing Orders — discover the independently maintained The-Artificer-of-Ciphers-LLC/standing-orders capability, which holds project-wide business rules in one ORDERS.yaml, gives the planner only the rules that apply to the phase, blocks planning on rule conflicts or edits to a rule's check files, requires GSD's fail-first prover to prove each applicable test rule after every execution wave, and records at ship which rules the phase was held to, with exact install and uninstall commands. (#6000) (#6004)
  • verify plan-structure --replay checks that recorded baselines were measured — a plan's [HEAD=<value> EXPECT…] baseline was a claim nobody checked until the executor ran the phase. The opt-in --replay flag runs each criterion's grep or read-only git command as direct argv with no shell, and reports where the printed value differs from the recorded one. It never changes the exit code. (#4310) (#6057)
  • Plans can record a checkable baseline for every count/match gate — write [HEAD=<value> EXPECT<op><value>] beside a criterion and verify plan-structure rejects one that already holds at HEAD (a gate that passes whether or not the work happens), warns on the old free-form # baseline at HEAD: comment, and reports unreadable notations without changing the exit code. (#4309) (#6006)
  • Projects can exempt a committed env template from the secret-read guard — a repo whose placeholder template is named like a secret (.env.production) could not let agents read it without removing the guard for every .env file; list it in hooks.secret_read_exempt and the guard lets it through while it stays committed as a regular file, and an unconfigured project blocks exactly as before. (#4432) (#5834)
  • gsd-tools query project-context prints the resolved project context — one read-only verb now shows which checkout GSD acts in, which directory owns .planning/, the main checkout, and a provenance record (source, reason, degraded, warnings), so a wrong-checkout symptom in a worktree can be diagnosed instead of guessed. (#6043) (#6071)

Changed

  • gsd-tools commit refuses to write outside the checkout it runs in — a commit from a worktree now exits non-zero with committed: false and reason: "outside_worktree" when the working directory, a declared path, or the planning project it would write to lies outside that worktree, including a worktree inside a planning.sub_repos workspace whose .planning/ belongs to the workspace; outside a repository it refuses with not_git_repo. Pass --project-dir <checkout> to commit in the checkout that owns the path. (#6043) (#6071)
  • The project-root walk stops at a symlinked $HOME in either spelling — with $HOME a symlink, a cwd spelled through its target (what process.cwd() reports), or the reverse, walked past $HOME and could take a .planning/ above it as the project; a cwd at $HOME itself with no .planning/ walked above it with any spelling. Both now stop at $HOME, and the isolation hooks, which resolve through the same walk, agree. (#5624) (#5847)
  • gsd-tools commands reject stray words and unknown flags they used to ignore — a misspelled or unsupported flag, a missing required input, or an extra word now stops the command with a usage error naming the problem, instead of being silently ignored; a repeated flag now uses its first value on every command (resolve-execution, update-context, loop render-hooks and record-dispatch-isolation took the last one, or preferred the = form, before); every command's accepted flags are listed in docs/CLI-SURFACE-MANIFEST.json (#6087). (#6100)
  • /gsd-autonomous stops asking about reversible technical choices and asks preference questions without a recommendation — smart discuss now decides cheap-to-undo technical questions (file layout, naming, internal data shapes) itself and lists them in a "Decided autonomously" block, CONTEXT.md and DISCUSSION-LOG.md; hard-to-reverse questions are still asked with a recommendation, and product/UX questions are asked neutrally. (#5069) (#6069)
  • The Claude orchestration Workflow backend now checks whether workflows are actually turned on — detection used to gate on the installed Agent SDK version, which says nothing about a Claude Code session, and missed an organization or user that had disabled workflows. It now honors CLAUDE_CODE_DISABLE_WORKFLOWS and disableWorkflows / enableWorkflows in managed, project and user settings, refuses to emit a script past Claude Code's 4,096-per-call and 1,000-agent caps, and gets each worktree's metadata back as validated structured output. (#4747) (#6070)

Deprecated

  • claude_orchestration.min_agent_sdk_version is deprecated and ignored — the Workflow backend no longer gates on the Agent SDK version, so the key, the --agent-sdk-version flag and GSD_AGENT_SDK_VERSION have no effect; existing configs still load, and the key can be removed. (#4747) (#6070)

Fixed

  • A refused install no longer leaves GSD half-upgraded — when a runtime's install destination is an untrusted symlink, that runtime's install now refuses before writing anything, so its previous install stays intact. In a command that installs several runtimes, runtimes installed before the refusal stay installed. Set GSD_ALLOW_SYMLINKED_DEST=1 to install through the symlink. (#5179) (#5828)
  • Catch-up merges no longer pull the base branch into a phase's evaluation scope — a merge commit listed as a task commit reported every file the base branch changed as the phase's own work, so review, UI safety, schema drift and decision-coverage gates evaluated the wrong files; a merge now contributes only its conflict resolutions and edits, the base branch's files no longer show up as unlisted phase work, and unlisted phase work (including on a sub-branch merged in, or an edit to a file the base also touched) stays named in the outside-the-union list, and a SUMMARY whose every listed commit is a merge degrades to the phase range instead of reporting the merge's edits as the whole phase. (#5751) (#5973)
  • phase add, phase add-batch and phase insert refuse a STATE milestone with no ROADMAP heading, or one already shipped — when STATE.md named a milestone (such as v9.9) that ROADMAP.md has no heading for, or one ROADMAP.md marks shipped, these verbs numbered the new phase and wrote its Depends on line against the wrong milestones (every unshipped one, or the closed one) and reported success; they now stop before writing anything and name the milestone, quoted and length-capped. A version mentioned in a Phase title, a code fence or an HTML comment no longer counts as a milestone heading. A check mark emoji that appears only inside a parenthetical naming another version (such as v2.1 Next (after v2.0 prep) carrying one) no longer marks the milestone shipped; every other closed-status word or check mark still does. A milestone whose name contains a status word, such as "Failed Payment Recovery" or "Closed Beta", is still read as shipped, so the refusal tells you to rename it or add an active marker such as the construction emoji or "(in progress)". (#5726) (#6003)
  • The fork-probe cache stays inside the project and out of git — the cache no longer writes through a symlinked .gsd that points outside the project, and a fresh project no longer shows .gsd/ as untracked or sweeps it into git add -A. (#5757) (#5908)
  • Installs into a nested or unusually named config directory keep correct paths on every runtime — the installer rewrote ~/.claude references in several passes, and the converters that run afterwards (agents, the gsd-core/ spec tree, OpenCode and Kilo commands and skills, command bodies, the Codex agent TOML) scanned the emitted prefix again, so a --config-dir such as ~/.claude/profiles/work, ~/.claude.d or ~/.copilot/work was rewritten inside its own output, and $& in a config dir was expanded into the text around it. A Kilo install wrote paths like .../profiles/work/profiles/work/gsd-core, an OpenCode install wrote paths like .claude/~/.config/opencode/x, and a Codex agent TOML got ~/.codex in place of $&. Each reference is now rewritten once and the emitted prefix is shielded from every later converter, for Kilo, OpenCode, Codex, Cline, CodeBuddy, Hermes, Qwen, Windsurf, Kimi, Trae agents and spec tree, Augment commands, and the Copilot and Antigravity @ includes for a config dir under their own default home. Intentional changes to default-install output: (1) Windsurf global installs turned ~/.devin/skills/x and $HOME/.devin/skills/x into ~/$HOME/.codeium/windsurf/skills/x and $HOME/$HOME/...; they now give $HOME/.codeium/windsurf/skills/x. (2) Claude Code's own files such as ~/.claude.json, $HOME/.claude.json, ~/.claude-plugin and ./.claude-plugin were rewritten into nonexistent names like $HOME/.cline.json; they now stay unchanged. (3) A Trae local install wrote $HOME/.trae/... and ~/.trae paths, including @$HOME/.trae/gsd-core/... includes, into its skills, which point at the global directory; they now point at the project's .trae/. A comparison of real default installs of all 18 installable runtimes, global and local, against the previous build found no other difference in the installed files. (#5715) (#5906)
  • execute-phase no longer marks a finished phase as executing — a resume that dispatched no plan (the verify-only resume, a filter that selects nothing, or a phase stuck on blocked or not-ready plans) still rewrote STATE.md's Current Position, Status and Last activity to "Executing, Plan 1 of N"; the phase start is now recorded only once a plan is about to run (#5843). (#6026)
  • Commit gates no longer halt in planning.sub_repos workspaces — check evaluation-scope returned unresolvable (exit 69) whenever the workspace root was not a git repo, so the safe-resume gate, the TDD gate and the executor commit spot-check stopped /gsd-execute-phase. The resolver now reads each configured sub-repo and merges the commits newest first. (#5867) (#6085)
  • effort sync --apply no longer makes the next install back up synced agents as local patches — the sync rewrote agent files without updating gsd-file-manifest.json, so the next install treated every synced agent as a local modification; the manifest now records the synced files. (#5683) (#5899)
  • config-get --default now prints the JSON value it spells — --default '[]' printed the string "[]" instead of an array, so /gsd-code-review stopped before spawning its reviewer in any project that never set workflow.code_review_depth_overrides; the same applied to numbers, booleans, objects and null. --raw --default null on a secret key now shows (unset) instead of ****, and planner.stall_detection_enabled --default false now returns false. config-set and config-get --default no longer turn a blank value or hex text into a number: ' ', ' 5' and 0x10 are kept as text. (#6029) (#6077)
  • Planners no longer put symbols or endpoints in key-link file fields that fail verify key-links — The shared planner guidance now states that from: and to: must be project-relative file paths, with symbols and endpoints in via:. The planner's Step 5 pointer names that contract and loads this guidance while authoring key links (#4946). (#5149)
  • Ghost REQ-ID warning no longer fires on registered requirements — phase complete warned "not registered anywhere" for a requirement whose checkbox used a mark other than space or x (such as [~]) or whose Traceability table has no Status column, and requirements mark-complete reported the same row as table_unmatched; both now see the row, and a row in a table without a Status column gets its own write-skipped message. (#5844) (#6025)
  • GitHub Release pages list the release's CHANGELOG entries — a hotfix's release page had an empty What's Changed because GitHub built it from merged PRs, which a hotfix's cherry-picks are not; every stable release page now shows that release's CHANGELOG section instead of GitHub's PR list. (#5922) (#5930)
  • A named-pipe SUMMARY file no longer hangs phase commands — a FIFO or endless-source SUMMARY in a phase directory made phase lookup, phase-plan-index and the execute-phase and plan-phase init commands block forever; SUMMARY status is now read through one non-blocking, regular-file-only, size-capped reader. (#5789) (#5979)
  • A named pipe, link or oversized file at the dispatch isolation record can no longer hang or mislead subagent dispatch — such a record could block every subagent dispatch indefinitely or be trusted from outside the project; it is now treated as absent. (#5758) (#5927)
  • Code-review fix now runs whenever code review is enabled — /gsd-code-review-fix and /gsd-code-review --fix no longer exit with "code-review capability inactive" when workflow.code_review_point is execute:wave:post; the gate follows the workflow.code_review toggle. (#5753) (#5929)
  • Inserted phases no longer carry "(INSERTED)" into their name — roadmap get-phase and every init command's phase_name kept the marker phase insert writes into the heading, so execute-phase wrote it into STATE.md's current phase name and the Phase line could not be read back; the name is now bare everywhere, matching roadmap analyze (#5824). (#6023)
  • init --validate now warns on a plan-count mismatch for Plan: N of M and Current Plan: N of M — STATE.md written in the compound or hybrid shape recorded its plan total only in the position line, so init execute-phase and init plan-phase reported state_warnings: [] even when the disk plan count disagreed. (#5786) (#5975)
  • Two orchestrators in one checkout no longer deny each other's sequential dispatches — when parallel workstreams ran execute-phase in the same checkout, each recorded isolation decision overwrote the other's, so a plan resolved to run without a worktree was refused by the isolation guard; each dispatch's decision is now kept for its own phase and plan. (#5682) (#5891)
  • state prune no longer loses entries when a write fails — it now writes STATE-ARCHIVE.md first, atomically and under the STATE.md lock, and puts both files back if the STATE.md write fails; re-running a prune that was interrupted between the two writes finishes it without archiving its entries twice; a symlinked or special-file STATE-ARCHIVE.md is refused instead of written through, a named pipe at STATE.md no longer hangs state commands, and a failed auto-prune in phase complete is reported as a warning (#5831). (#5981)
  • drift-guard phase-status and workstream progress read milestone-prefixed phases correctly — under phase_id_convention: milestone-prefixed every M-NN phase named in ROADMAP Progress rows or STATE.md read as the bare milestone, so phase-status compared STATE against the milestone's first phase and reported a false drift, and workstream progress collapsed the milestone's phases into one. (#5675) (#5887)
  • phase insert names the milestone when the phase belongs to another one — inserting after a phase that lives in a different milestone than the current one reported Phase N not found in ROADMAP.md, the same text as a phase missing from the file; the error now names the milestone that holds it and the current one. (#5679) (#5893)
  • new-project and new-milestone point to the right next step — the closing hand-off said /gsd-plan-phase starts execution; it now recommends /gsd-discuss-phase, with /gsd-plan-phase to plan directly and /gsd-execute-phase to execute. (#5723) (#5905)
  • Gates now accept phase directories under a symlinked out-of-repo .planning — check decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, evaluation-scope and predicate --phase-dir no longer fail with "path escapes its allowed directory", and check verify-command-paths --dir no longer reports its unresolvable "--dir resolves outside the project root" result, for a phase directory under the project's own .planning; all six resolve their path argument through the shared containment helper, and paths outside the project and its own .planning are still refused. check tdd-red-evidence keeps its own single-root check and is not changed (#5474). (#5510)
  • A linked worktree of a planning.sub_repos workspace now resolves the workspace's .planning/ — inside such a worktree GSD fell back to the repository's main checkout and read and wrote the wrong planning store; it now resolves the workspace's .planning/. (#4908) (#6071)
  • A config file that is a named pipe or device no longer hangs gsd-tools — a FIFO at .planning/config.json or a workstream config.json blocked every command, hook, plugin and installer step that reads configuration; each read is now bounded, the command continues without that file (a workstream falls back to the root config), a config.json over 1 MiB is refused, and temporary files are created exclusively without following links. A write that cannot be published atomically now refuses a device, named-pipe, symlinked, multiply-hard-linked or write-only (0200) target instead of writing to it (the atomic publish replaces a link or extra hard link entry and never writes through it, and it refuses to publish if the parent directory was swapped meanwhile) (a user-chosen --output device or pipe such as /dev/stdout is still written, through the descriptor it names rather than by reopening the path, waiting without a time limit for a slow reader), and an existing file keeps its permission bits after a write. (#5759) (#5932)
  • A test file that crashed at load can no longer pass as RED evidence — check tdd-red-evidence returned RED_EVIDENCE_OK when the target was the crashing file's own entry (a vitest bad.test.js, a jest-junit Test suite failed to run, or a node:test file-level failure); it now returns INVALID_RED (fixture_or_load_failure). (#5721) (#5917)
  • phase complete, state sync and init progress read milestone-prefixed phase ids correctly — under phase_id_convention: milestone-prefixed, re-completing a phase such as 3-01 while STATE.md was at 3-02 rewound STATE to the completed phase, a first-time completion advanced STATE to phase 3 named "02: beta", and roadmap checklist rows, phase headings and phase directories such as 03-02-beta were not recognised by init progress, init manager (its checked-phase fallback), find-phase (which reported phase 03 named "02-beta") and the planning snapshot. (#5676) (#5896)
  • Verifier no longer drops pre-verified human checks — an auto task's <human-check> whose <expected> already recorded a verdict could be omitted from the end-of-phase human verification list; every such block is now harvested with its expected text unchanged. (#5741) (#5928)
  • Capability plan:pre steps and gates now run when planning a phase — a plan:pre step that runs a command, a skill step outside auto-chain mode (such as mempalace recall), and any gate other than the built-in UI and drift checks were listed by loop render-hooks plan:pre and then silently skipped, so a capability's blocking gate could not stop planning. Plan-phase now dispatches every active plan:pre step and evaluates every active gate before the planner starts, in manual and auto-chain mode. Planning a phase whose research already existed, was skipped or had just completed also no longer jumps past the validation-strategy step and the security, UI and codebase-drift checks. (#5982) (#5983)
  • Windows hooks no longer block every Read, Grep and Bash call when Git Bash is not found — Claude Code runs hooks in PowerShell when it cannot find Git Bash, where the installed bash node-runner could not parse and the secret-read guard blocked every tool call; on Windows the installer now always writes a PowerShell runner with an explicit shell: "powershell" (Git Bash or not, portable installs included) and rewrites existing bash-runner entries, including the statusline, on reinstall. (#5850) (#6067)
  • The secret-read guard reads past sudo's -a/-c and doas's -a values again — sudo -c ls cat .env, sudo --auth-type echo cat .env and doas -a ls cat .env are denied again, as they were in 1.18.0; the wrapper table now lists those value-taking options from sudo(8) and doas(1). (#5852) (#5874)
  • Commit and branch guards now recognise git --no-advice, --config-env and --attr-source — a git commit or git checkout -b written with one of these global options before the subcommand was classified as not-a-commit and slipped past the commit-message and workflow guards. (#5821) (#6001)
  • Copilot and Codex agents keep their full tool list — agents that declare tools: as a YAML block list (gsd-security-auditor, gsd-nyquist-auditor) were installed with a single mangled entry such as ['- read'] for Copilot or - Read for Codex instead of every declared tool. (#5790) (#5974)
  • The planner now reads MemPalace recall when planning a phase — with the mempalace capability enabled, the MEMORY-RECALL.md file the plan:pre recall step writes into the phase directory was never named in the planner's required reading, so prior decisions, patterns and surprises recalled from the palace never reached planning. The planner is now told to read it whenever it exists; phases without it plan as before. (#5960) (#5994)
  • Human-recorded UAT results are no longer lost when execute-phase re-runs — a phase that already had a filled UAT file had it reset to all-pending and committed when verification again needed human checks; new items are now appended and existing results kept. (#5845) (#6081)

Full Changelog: v1.20.1...v1.21.0

Don't miss a new gsd-core release

NewReleases is sending notifications on new releases.