fix(#2423): finalize job syncs next package.json after final release (#2437)

* fix(release): finalize job calls sync-next-version.cjs to bump next after final release (#2423)

The release pipeline's 'finalize' job shipped X.Y.0 to npm 'latest' and
merged to main, but never bumped 'next' to match. 'scripts/sync-next-version.cjs'
exists exactly for this — its docstring promises to run 'for every release
type (rc / hotfix / final)' — but it was wired only into the 'rc' job
(release.yml:479), not 'finalize'. As a result, after 1.7.0 shipped on
2026-07-15, 'next' stayed at 1.7.0-rc.6 and every npm script banner on
'next' (and feature branches cut from it) reported the stale rc version.

Regression of #1104 — closed incomplete (covered rc only, not final).

This patch:
  - adds a 'Sync next branch to the published release' step to the
    'finalize' job, mirroring the rc job's pattern at line 479 (uses
    VERSION from inputs.version rather than PRE_VERSION from steps.prerelease,
    since finalize does not run the prerelease step);
  - gates it on !inputs.dry_run + continue-on-error:true (matches rc);
  - adds tests/release-finalize-syncs-next-version.test.cjs — a structural
    YAML assertion that fails against the pre-fix workflow and passes after.
    Failing-first demonstrated during development; the test parses job blocks
    by indentation rather than grep so it stays valid as the file grows.

Root-cause diagnosis: scripts/sync-next-version.cjs:14 docstring admits
'used by release.yml's rc job, which has no next-targeting PR of its own'.
release.yml:506-685 (finalize job) had no sync-next-version step before
this patch. Every prior rc.N release has a matching 'chore: sync next
package version to 1.7.0-rc.N' commit; there is no such commit for 1.7.0.

* chore(release): sync next package version to 1.7.0 (#2423)

Replays the canonical 'chore: sync next package version to <v>' commit
that the release pipeline's rc job auto-produces via scripts/sync-next-version.cjs,
for the 1.7.0 final release that shipped on 2026-07-15 (commit dd4c90f82
'chore: finalize v1.7.0' on main). Without this, 'next' (and every feature
branch cut from it) carried 1.7.0-rc.6 indefinitely and reported it in
every npm script banner (e.g. 'lint:ci').

Bumps 43 synchronized manifests via the npm 'version' lifecycle hook
(scripts/sync-manifest-versions.cjs --stage + scripts/gen-capability-registry.cjs
--write), matching the file set of commit 27f69cc48 ('chore: sync next
package version to 1.7.0-rc.6') and commit dd4c90f82 ('chore: finalize
v1.7.0').

This is the immediate Layer-1 repair for #2423. Layer-2 (prevent recurrence)
is the workflow patch in the previous commit; Layer-3 (regression test)
ships with it. Future X.Y.0 final releases will produce this commit
automatically once the workflow fix lands.

* test(release): tighten #2423 dry-run gate assertion to the sync step

Code review of fix/2423 found that test #3 ('gates sync-next-version on
!inputs.dry_run') asserted too loosely: it scanned the entire finalize
block for any '!inputs.dry_run' line, so it would still pass if the
gate were stripped from the sync-next-version step specifically — the
exact regression the test name promises to catch. The finalize job has
multiple steps with their own !inputs.dry_run gates (e.g. Verify
publish), so the loose version masked the very bug it claimed to detect.

Tighten by extracting the specific YAML step block containing
'scripts/sync-next-version.cjs' and asserting the gate appears within
THAT step's lines, not anywhere in the job. Verified the tightened test:

  - PASSES against the post-fix workflow (sync step has its own gate)
  - FAILS when the sync step's gate is stripped (even when other steps
    in finalize retain their own !inputs.dry_run gates) — the exact
    regression that previously slipped through

Adds extractStepBlockContaining(jobBlock, marker) helper alongside the
existing extractJobBlock(text, jobName). Reuses the same indentation-
based parsing, so it stays valid as the file grows.

* chore(changeset): backfill pr:2437 in .changeset/sturdy-ibex-jump.md

CLAUDE.md changeset convention: 'Use placeholder pr:0 during initial commit.
Backfill immediately after gh api POST /pulls returns the real number.'

PR #2437 created from branch fix/2423-release-finalize-sync-next-version.

* fix(#2423): add see #2423 to allow-test-rule exemption per ADR-456

CI lint-allow-test-rule-refs failed on PR #2437: ADR-456 requires new
allow-test-rule exemptions added after the ADR's acceptance to include a
tracking issue number in the comment, in the form
  // allow-test-rule: <reason> (see #NNN)
The exemption added in commit 976c8b0a2 lacked this ref. Fixed.

Verified locally:
  node scripts/lint-allow-test-rule-refs.cjs
    → ok lint-allow-test-rule-refs: 173 grandfathered exemption(s) tracked, no novel untracked offenders
This commit is contained in:
Tom Boucher
2026-07-19 14:33:19 -04:00
committed by GitHub
parent 40ce95f882
commit 6b196ef638
46 changed files with 232 additions and 98 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2437
---
**`npm run lint:ci` (and every npm script banner) on `next` and feature branches cut from `next` no longer reports a stale pre-release version after a final release** — the release pipeline's `finalize` job shipped `X.Y.0` to npm `latest` but never bumped `next` to match, so `next` carried the last `rc.N` placeholder indefinitely (observed: `1.7.0-rc.6` lingering after `1.7.0` shipped). The `finalize` job now runs `scripts/sync-next-version.cjs` — the same step the `rc` job already ran — keeping `next` at the last published release for every release type as `scripts/sync-next-version.cjs:6-9` always promised. (#2423)

View File

@@ -9,7 +9,7 @@
{
"name": "gsd-core",
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"source": "./",
"author": {
"name": "open-gsd",

View File

@@ -1,7 +1,7 @@
{
"name": "gsd-core",
"displayName": "GSD Core",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
"author": {
"name": "open-gsd",

View File

@@ -669,6 +669,21 @@ jobs:
VERSION: ${{ inputs.version }}
run: node scripts/verify-npm-publish.cjs --package @opengsd/gsd-core --version "$VERSION" --dist-tag latest
# Regression #2423: keep `next` at the last published release for final
# releases, not just rc/hotfix. Without this, `next` drifts to whatever
# rc.N the release branch forked from, and every npm script banner on
# `next` (and feature branches cut from it) reports the stale rc version
# — e.g. `lint:ci` reported `@opengsd/gsd-core@1.7.0-rc.6` after 1.7.0
# shipped. Mirrors the rc job's sync step at line ~479. Idempotent: a
# no-op when `next` is already at the target version.
- name: Sync next branch to the published release
if: ${{ !inputs.dry_run }}
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GSD_BOT_PR_TOKEN || secrets.GITHUB_TOKEN }}
VERSION: ${{ inputs.version }}
run: node scripts/sync-next-version.cjs "$VERSION"
- name: Summary
env:
VERSION: ${{ inputs.version }}

View File

@@ -1,7 +1,7 @@
{
"id": "ai-integration",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "AI design contract",
"description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "antigravity",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat skill layout; tier-1 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "assumption-delta",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Assumption-delta architecture checkpoint",
"description": "Rarely-firing advisory checkpoint that triggers when a phase makes something plural, optional, or chosen that used to be singular, required, or derived. Surfaces one identity-model question (promote the new general representation to primary, or add it alongside?) so a silent primary-key drift does not accumulate into a later user-facing bug. Non-blocking; fires only on a detected signal.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "audit",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Audit",
"description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "augment",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Augment Code",
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "claude-orchestration",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Claude orchestration (Workflow backend)",
"description": "Default-off, BETA, claude-only capability that adopts Claude Code's Workflow tool (the engine behind /effort ultracode) as an optional parallel-execution backend for the GSD loop. When the runtime exposes the Workflow tool and claude_orchestration.execution_backend resolves to 'workflow', execute-phase emits a generated Workflow script (waves -> parallel() barriers, plans -> agent({ agentType: 'gsd-executor', isolation: 'worktree' }), files_modified overlap -> separate sequential stages, resumeFromRunId wired to the phase run id, shared token budget) that composes the SAME gsd-executor agent and worktree isolation the inline path uses, restoring the wave parallelism the #853 backgrounded-agent nesting limitation forces inline on Claude Code. (The plan-checker and verifier remain inline until separately wired — this capability delivers the parallel-execution backend, not those gates.) Also folds the ultraplan plan-offload under one runtime gate (plan:* surface). On any runtime lacking the Workflow tool, or when the capability is disabled, behaviour is byte-identical to today (inline/manual dispatch). Detection + emission live in gsd-core/bin/lib/claude-orchestration.cjs (pure, fail-closed). Mirrors the existing gsd-ultraplan-phase BETA-isolation posture.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "claude",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Claude Code",
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "cline",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Cline",
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "code-review",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Code review",
"description": "Source-file code review and review-fix workflow support for completed execution work.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "codebuddy",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "CodeBuddy",
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "codex",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "OpenAI Codex CLI",
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "copilot",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "GitHub Copilot",
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "cursor",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Cursor",
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "drift",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Drift detection gates",
"description": "Drift detection gates for the planning loop. At execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md). At plan:pre: a non-blocking, warn-only codebase drift gate (gated on workflow.plan_drift_precheck) that flags a stale codebase map before planning, so plans are authored against a fresh STRUCTURE.md instead of discovering drift mid-execution.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "external-job",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Async external-job scheduler adapter",
"description": "Default-off producer of the async external-job manifest (#1164). At execute:wave:post an executor can externalize long-running compute (SLURM first, scheduler-pluggable), commit a .planning/async-jobs/<job>.json manifest, defer SUMMARY.md, and return external_job_waiting. The core loop (#1165) consumes the manifest; this capability is the only thing that writes it. NOTE on contribution point: #1164 specifies execute:wave:pre, but execute-phase.md only dispatches execute:wave:post today (wave:pre is declared in the loop host contract but not rendered); wiring wave:pre dispatch is a core-loop change #1164 explicitly puts out of scope, so this capability registers at wave:post and the executor honors the runtime_budget classification guidance before running any tagged task. The adapter (scripts/slurm-adapter.cjs) reads external_job.submit_timeout_ms / poll_timeout_ms / artifact_dir through the canonical capability-config seam (env override > config > registry default).",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "gap-analysis",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Post-planning gap analysis",
"description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
"tier": "standard",

View File

@@ -1,7 +1,7 @@
{
"id": "graphify",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Knowledge graph",
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "hermes",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Hermes Agent",
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "intel",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Codebase intelligence",
"description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "kilo",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Kilo Code",
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "kimi",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "mempalace",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "MemPalace memory",
"description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "nyquist",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Nyquist validation",
"description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "opencode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat commands/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "pattern-mapper",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Pattern mapping",
"description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "pi",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "pi",
"description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.cjs; no shared-settings hook surface; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "profile-pipeline",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Developer profiling pipeline",
"description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "qwen",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Qwen Code",
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "research",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Phase research",
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
"tier": "standard",

View File

@@ -1,7 +1,7 @@
{
"id": "schema-gate",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Schema push detection gate",
"description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "security",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Security enforcement",
"description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "tdd",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Test-driven development",
"description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "trae",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Trae IDE",
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "ui",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "UI design contracts",
"description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
"tier": "full",

View File

@@ -1,7 +1,7 @@
{
"id": "vscode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "VS Code",
"description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "windsurf",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); tier-2 support.",
"tier": "core",

View File

@@ -1,7 +1,7 @@
{
"id": "zcode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "ZCode",
"description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
"tier": "core",

View File

@@ -10,7 +10,7 @@ const capabilities = {
"ai-integration": {
"id": "ai-integration",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "AI design contract",
"description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
"tier": "full",
@@ -95,7 +95,7 @@ const capabilities = {
"antigravity": {
"id": "antigravity",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat skill layout; tier-1 support.",
"tier": "core",
@@ -196,7 +196,7 @@ const capabilities = {
"assumption-delta": {
"id": "assumption-delta",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Assumption-delta architecture checkpoint",
"description": "Rarely-firing advisory checkpoint that triggers when a phase makes something plural, optional, or chosen that used to be singular, required, or derived. Surfaces one identity-model question (promote the new general representation to primary, or add it alongside?) so a silent primary-key drift does not accumulate into a later user-facing bug. Non-blocking; fires only on a detected signal.",
"tier": "full",
@@ -242,7 +242,7 @@ const capabilities = {
"audit": {
"id": "audit",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Audit",
"description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
"tier": "full",
@@ -279,7 +279,7 @@ const capabilities = {
"augment": {
"id": "augment",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Augment Code",
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -386,7 +386,7 @@ const capabilities = {
"claude": {
"id": "claude",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Claude Code",
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
"tier": "core",
@@ -491,7 +491,7 @@ const capabilities = {
"claude-orchestration": {
"id": "claude-orchestration",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Claude orchestration (Workflow backend)",
"description": "Default-off, BETA, claude-only capability that adopts Claude Code's Workflow tool (the engine behind /effort ultracode) as an optional parallel-execution backend for the GSD loop. When the runtime exposes the Workflow tool and claude_orchestration.execution_backend resolves to 'workflow', execute-phase emits a generated Workflow script (waves -> parallel() barriers, plans -> agent({ agentType: 'gsd-executor', isolation: 'worktree' }), files_modified overlap -> separate sequential stages, resumeFromRunId wired to the phase run id, shared token budget) that composes the SAME gsd-executor agent and worktree isolation the inline path uses, restoring the wave parallelism the #853 backgrounded-agent nesting limitation forces inline on Claude Code. (The plan-checker and verifier remain inline until separately wired — this capability delivers the parallel-execution backend, not those gates.) Also folds the ultraplan plan-offload under one runtime gate (plan:* surface). On any runtime lacking the Workflow tool, or when the capability is disabled, behaviour is byte-identical to today (inline/manual dispatch). Detection + emission live in gsd-core/bin/lib/claude-orchestration.cjs (pure, fail-closed). Mirrors the existing gsd-ultraplan-phase BETA-isolation posture.",
"tier": "full",
@@ -579,7 +579,7 @@ const capabilities = {
"cline": {
"id": "cline",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Cline",
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
"tier": "core",
@@ -648,7 +648,7 @@ const capabilities = {
"code-review": {
"id": "code-review",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Code review",
"description": "Source-file code review and review-fix workflow support for completed execution work.",
"tier": "full",
@@ -709,7 +709,7 @@ const capabilities = {
"codebuddy": {
"id": "codebuddy",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "CodeBuddy",
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -820,7 +820,7 @@ const capabilities = {
"codex": {
"id": "codex",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "OpenAI Codex CLI",
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
"tier": "core",
@@ -905,7 +905,7 @@ const capabilities = {
"copilot": {
"id": "copilot",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "GitHub Copilot",
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
"tier": "core",
@@ -998,7 +998,7 @@ const capabilities = {
"cursor": {
"id": "cursor",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Cursor",
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
"tier": "core",
@@ -1119,7 +1119,7 @@ const capabilities = {
"drift": {
"id": "drift",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Drift detection gates",
"description": "Drift detection gates for the planning loop. At execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md). At plan:pre: a non-blocking, warn-only codebase drift gate (gated on workflow.plan_drift_precheck) that flags a stale codebase map before planning, so plans are authored against a fresh STRUCTURE.md instead of discovering drift mid-execution.",
"tier": "full",
@@ -1197,7 +1197,7 @@ const capabilities = {
"external-job": {
"id": "external-job",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Async external-job scheduler adapter",
"description": "Default-off producer of the async external-job manifest (#1164). At execute:wave:post an executor can externalize long-running compute (SLURM first, scheduler-pluggable), commit a .planning/async-jobs/<job>.json manifest, defer SUMMARY.md, and return external_job_waiting. The core loop (#1165) consumes the manifest; this capability is the only thing that writes it. NOTE on contribution point: #1164 specifies execute:wave:pre, but execute-phase.md only dispatches execute:wave:post today (wave:pre is declared in the loop host contract but not rendered); wiring wave:pre dispatch is a core-loop change #1164 explicitly puts out of scope, so this capability registers at wave:post and the executor honors the runtime_budget classification guidance before running any tagged task. The adapter (scripts/slurm-adapter.cjs) reads external_job.submit_timeout_ms / poll_timeout_ms / artifact_dir through the canonical capability-config seam (env override > config > registry default).",
"tier": "full",
@@ -1280,7 +1280,7 @@ const capabilities = {
"gap-analysis": {
"id": "gap-analysis",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Post-planning gap analysis",
"description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
"tier": "standard",
@@ -1321,7 +1321,7 @@ const capabilities = {
"graphify": {
"id": "graphify",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Knowledge graph",
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
"tier": "full",
@@ -1362,7 +1362,7 @@ const capabilities = {
"hermes": {
"id": "hermes",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Hermes Agent",
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -1451,7 +1451,7 @@ const capabilities = {
"intel": {
"id": "intel",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Codebase intelligence",
"description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
"tier": "full",
@@ -1503,7 +1503,7 @@ const capabilities = {
"kilo": {
"id": "kilo",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Kilo Code",
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
"tier": "core",
@@ -1610,7 +1610,7 @@ const capabilities = {
"kimi": {
"id": "kimi",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; tier-2 support.",
"tier": "core",
@@ -1698,7 +1698,7 @@ const capabilities = {
"mempalace": {
"id": "mempalace",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "MemPalace memory",
"description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
"tier": "full",
@@ -1872,7 +1872,7 @@ const capabilities = {
"nyquist": {
"id": "nyquist",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Nyquist validation",
"description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
"tier": "full",
@@ -1922,7 +1922,7 @@ const capabilities = {
"opencode": {
"id": "opencode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat commands/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
"tier": "core",
@@ -2028,7 +2028,7 @@ const capabilities = {
"pattern-mapper": {
"id": "pattern-mapper",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Pattern mapping",
"description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
"tier": "full",
@@ -2082,7 +2082,7 @@ const capabilities = {
"pi": {
"id": "pi",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "pi",
"description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.cjs; no shared-settings hook surface; tier-2 support.",
"tier": "core",
@@ -2142,7 +2142,7 @@ const capabilities = {
"profile-pipeline": {
"id": "profile-pipeline",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Developer profiling pipeline",
"description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
"tier": "full",
@@ -2219,7 +2219,7 @@ const capabilities = {
"qwen": {
"id": "qwen",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Qwen Code",
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -2324,7 +2324,7 @@ const capabilities = {
"research": {
"id": "research",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Phase research",
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
"tier": "standard",
@@ -2376,7 +2376,7 @@ const capabilities = {
"schema-gate": {
"id": "schema-gate",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Schema push detection gate",
"description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
"tier": "full",
@@ -2422,7 +2422,7 @@ const capabilities = {
"security": {
"id": "security",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Security enforcement",
"description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
"tier": "full",
@@ -2521,7 +2521,7 @@ const capabilities = {
"tdd": {
"id": "tdd",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Test-driven development",
"description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
"tier": "full",
@@ -2574,7 +2574,7 @@ const capabilities = {
"trae": {
"id": "trae",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Trae IDE",
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
"tier": "core",
@@ -2664,7 +2664,7 @@ const capabilities = {
"ui": {
"id": "ui",
"role": "feature",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "UI design contracts",
"description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
"tier": "full",
@@ -2759,7 +2759,7 @@ const capabilities = {
"vscode": {
"id": "vscode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "VS Code",
"description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
"tier": "core",
@@ -2810,7 +2810,7 @@ const capabilities = {
"windsurf": {
"id": "windsurf",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); tier-2 support.",
"tier": "core",
@@ -2895,7 +2895,7 @@ const capabilities = {
"zcode": {
"id": "zcode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "ZCode",
"description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
"tier": "core",
@@ -3907,7 +3907,7 @@ const runtimes = {
"antigravity": {
"id": "antigravity",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat skill layout; tier-1 support.",
"tier": "core",
@@ -4008,7 +4008,7 @@ const runtimes = {
"augment": {
"id": "augment",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Augment Code",
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -4115,7 +4115,7 @@ const runtimes = {
"claude": {
"id": "claude",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Claude Code",
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
"tier": "core",
@@ -4220,7 +4220,7 @@ const runtimes = {
"cline": {
"id": "cline",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Cline",
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
"tier": "core",
@@ -4289,7 +4289,7 @@ const runtimes = {
"codebuddy": {
"id": "codebuddy",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "CodeBuddy",
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -4400,7 +4400,7 @@ const runtimes = {
"codex": {
"id": "codex",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "OpenAI Codex CLI",
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
"tier": "core",
@@ -4485,7 +4485,7 @@ const runtimes = {
"copilot": {
"id": "copilot",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "GitHub Copilot",
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
"tier": "core",
@@ -4578,7 +4578,7 @@ const runtimes = {
"cursor": {
"id": "cursor",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Cursor",
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
"tier": "core",
@@ -4699,7 +4699,7 @@ const runtimes = {
"hermes": {
"id": "hermes",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Hermes Agent",
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -4788,7 +4788,7 @@ const runtimes = {
"kilo": {
"id": "kilo",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Kilo Code",
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
"tier": "core",
@@ -4895,7 +4895,7 @@ const runtimes = {
"kimi": {
"id": "kimi",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; tier-2 support.",
"tier": "core",
@@ -4983,7 +4983,7 @@ const runtimes = {
"opencode": {
"id": "opencode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat commands/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
"tier": "core",
@@ -5089,7 +5089,7 @@ const runtimes = {
"pi": {
"id": "pi",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "pi",
"description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.cjs; no shared-settings hook surface; tier-2 support.",
"tier": "core",
@@ -5149,7 +5149,7 @@ const runtimes = {
"qwen": {
"id": "qwen",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Qwen Code",
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
@@ -5254,7 +5254,7 @@ const runtimes = {
"trae": {
"id": "trae",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Trae IDE",
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
"tier": "core",
@@ -5344,7 +5344,7 @@ const runtimes = {
"vscode": {
"id": "vscode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "VS Code",
"description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
"tier": "core",
@@ -5395,7 +5395,7 @@ const runtimes = {
"windsurf": {
"id": "windsurf",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); tier-2 support.",
"tier": "core",
@@ -5480,7 +5480,7 @@ const runtimes = {
"zcode": {
"id": "zcode",
"role": "runtime",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"title": "ZCode",
"description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
"tier": "core",

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@opengsd/gsd-core",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@opengsd/gsd-core",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"license": "MIT",
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.84",

View File

@@ -1,6 +1,6 @@
{
"name": "@opengsd/gsd-core",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
"main": ".opencode/plugins/gsd-core.js",
"bin": {

View File

@@ -0,0 +1,114 @@
// allow-test-rule: source-text-is-the-product (see #2423)
// .github/workflows/release.yml is the deployed CI contract; asserting that
// the finalize job wires in scripts/sync-next-version.cjs is only expressible
// against the workflow text. See regression #2423 — the finalize job shipped
// 1.7.0 to npm latest without bumping next, which sat stale at 1.7.0-rc.6 and
// leaked into every npm script banner (e.g. `lint:ci`).
'use strict';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const RELEASE_WORKFLOW = path.join(__dirname, '..', '.github', 'workflows', 'release.yml');
/**
* Extract a top-level job block from a GitHub Actions workflow YAML file.
*
* Jobs sit at column 2 (`^ <job>:`). Returns the lines from the job header
* through the line before the next top-level key (column 0) or the next job.
* @param {string} text - workflow file text
* @param {string} jobName - job key to extract
* @returns {string[]} lines belonging to the job (including its header)
*/
function extractJobBlock(text, jobName) {
const lines = text.split(/\r?\n/);
const startIdx = lines.findIndex((l) => new RegExp(`^\\s{2}${jobName}:\\s*$`).test(l));
assert.ok(startIdx >= 0, `job '${jobName}' not found in release.yml`);
let endIdx = lines.length;
for (let i = startIdx + 1; i < lines.length; i++) {
// Next top-level key (column 0, non-blank, non-comment) ends the job block.
if (/^\S/.test(lines[i])) {
endIdx = i;
break;
}
}
return lines.slice(startIdx, endIdx);
}
/**
* Extract the YAML step block (within a job) that contains `marker` text.
*
* Steps begin at `^ - name:` (6-space indent for a job at column 2).
* Returns the lines from the step's `- name:` line through the line before
* the next `- name:` at the same indent (or end of the job block).
*
* Used to assert invariants about a SPECIFIC step rather than the whole job,
* so a test like "the sync step is gated on !inputs.dry_run" can't pass by
* accident because some OTHER step in the same job happens to have that gate.
* @param {string[]} jobBlock - lines of the containing job (from extractJobBlock)
* @param {string} marker - substring identifying the target step
* @returns {string[]} lines of the matching step (including its `- name:` header)
*/
function extractStepBlockContaining(jobBlock, marker) {
const stepStarts = [];
for (let i = 0; i < jobBlock.length; i++) {
if (/^\s{6}- name:/.test(jobBlock[i])) {
stepStarts.push(i);
}
}
for (let s = 0; s < stepStarts.length; s++) {
const start = stepStarts[s];
const end = s + 1 < stepStarts.length ? stepStarts[s + 1] : jobBlock.length;
const stepLines = jobBlock.slice(start, end);
if (stepLines.some((l) => l.includes(marker))) {
return stepLines;
}
}
return [];
}
describe('release-finalize-syncs-next-version (regression #2423)', () => {
const text = fs.readFileSync(RELEASE_WORKFLOW, 'utf8');
test('the rc job still wires sync-next-version.cjs (control — must not regress)', () => {
const rcBlock = extractJobBlock(text, 'rc');
const hits = rcBlock.filter((l) => l.includes('scripts/sync-next-version.cjs'));
assert.notStrictEqual(hits.length, 0,
'rc job must call scripts/sync-next-version.cjs (lost during refactor?)');
});
test('the finalize job calls scripts/sync-next-version.cjs after publishing', () => {
const finalizeBlock = extractJobBlock(text, 'finalize');
const stepLines = finalizeBlock.filter((l) => l.includes('scripts/sync-next-version.cjs'));
assert.notStrictEqual(stepLines.length, 0,
[
'finalize job must call scripts/sync-next-version.cjs to bump next after',
'a final release (regression #2423). Without it, next drifts to whatever',
'rc.N version the release branch started from, and every npm script banner',
'on next (and feature branches cut from it) reports the stale rc version.',
].join(' '));
});
test('the finalize job gates sync-next-version on !inputs.dry_run', () => {
// A dry-run finalize must not open a sync PR. We assert this against the
// SPECIFIC step that runs sync-next-version.cjs (not the whole finalize
// block) so the test cannot pass by accident if some OTHER step in the
// finalize job happens to have a !inputs.dry_run gate. Regression of the
// loose-invariant version of this test, caught during #2423 code review.
const finalizeBlock = extractJobBlock(text, 'finalize');
const syncStep = extractStepBlockContaining(finalizeBlock, 'scripts/sync-next-version.cjs');
assert.notStrictEqual(syncStep.length, 0,
'cannot locate the sync-next-version step within the finalize job — has the step shape changed?');
const stepHasGate = syncStep.some((l) => l.includes('!inputs.dry_run'));
assert.ok(stepHasGate,
[
'the sync-next-version step itself must be gated on !inputs.dry_run',
'(regression of the loose-invariant test that passed when ANY step in',
'finalize had the gate). Without this guard, a dry-run finalize would',
'open a real chore: sync next package version PR against next.',
].join(' '));
});
});

View File

@@ -2,7 +2,7 @@
"name": "gsd-core-vscode",
"displayName": "GSD Core",
"description": "GSD orchestration engine embedded in VS Code (ADR-1239 IDE profile).",
"version": "1.7.0-rc.6",
"version": "1.7.0",
"publisher": "opengsd",
"engines": {
"vscode": "^1.105.0"