chore(#518): rename npm package + bin to @opengsd/gsd-core (#519)

* chore: rename npm package + bin to @opengsd/gsd-core (functional)

- package.json: name @opengsd/get-shit-done-redux → @opengsd/gsd-core,
  bin key get-shit-done-redux → gsd-core, repository/homepage/bugs URLs
- package-lock.json: regenerated (npm install --package-lock-only)
- tests/**, scripts/**, bin/**, .github/**, agents/**, commands/**,
  get-shit-done/bin/**, get-shit-done/workflows/**:
  applied the 4-rule replacement (scoped npm ref, GitHub repo path,
  bin/clone invocations) per #505 single-source refactor

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: sweep live references to @opengsd/gsd-core

Update all live documentation (README.md + translations, docs/**,
CONTRIBUTING.md, VERSIONING.md, SECURITY.md, CONTEXT.md,
docs/CANARY.md) to reflect the renamed package and repository.

Rules applied:
- @opengsd/get-shit-done-redux → @opengsd/gsd-core (scoped npm name)
- open-gsd/get-shit-done-redux → open-gsd/gsd-core (GitHub repo)
- GSD-redux/get-shit-done-redux → open-gsd/gsd-core (stale badge org)
- bare bin/clone refs → gsd-core

CHANGELOG.md, docs/adr/**, docs/RELEASE-*.md, docs/research/**,
and .changeset/** are preserved byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: add negative lookbehind to slash-command regex in bug-2954 test

The extractSlashReferences regex matched /gsd-core inside npm package
URLs (@opengsd/gsd-core), producing a false /gsd:core command reference.
Adding a negative lookbehind (?<![a-z]) excludes matches preceded by a
letter, so only standalone /gsd-<cmd> and /gsd:<cmd> tokens are found.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#518): add changeset for package rename

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(#518): update package-identity expectations to the renamed coordinates

The rebase regenerated the seam to @opengsd/gsd-core (bin gsd-core, repo
open-gsd/gsd-core). The #498 seam tests assert deriveIdentity against the REAL
package.json, so their expected literals must follow the rename. The drift-lint
unit test is left as-is — its SEAM is a self-consistent fixture and its
stale-literal detection cases would shift if altered; the live-repo scan in it
already passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-30 17:25:02 -04:00
committed by GitHub
parent 05cdec5f47
commit 79002a00cb
173 changed files with 662 additions and 655 deletions

View File

@@ -1,7 +1,7 @@
'use strict';
/**
* Regression tests for issue #26 (open-gsd/get-shit-done-redux).
* Regression tests for issue #26 (open-gsd/gsd-core).
* Three generator-pattern drift items: W005 phaseDirNameRe,
* W006-archived regex constants (PHASE_TOKEN_FROM_DIR_RE, MILESTONE_ARCHIVE_DIR_RE),
* I001 canonicalPlanStem.
@@ -11,7 +11,7 @@
* tests go RED.
*
* References:
* - Issue #26 (open-gsd/get-shit-done-redux) — three drift items + reproducer
* - Issue #26 (open-gsd/gsd-core) — three drift items + reproducer
* - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md)
* - PR #154 (issue #4) — generator pattern precedent
* - PR #156 (issue #6) — validate.ts generator scaffolding (#26 extends this)

View File

@@ -1,7 +1,7 @@
'use strict';
/**
* Regression test for issue #4 (open-gsd/get-shit-done-redux):
* Regression test for issue #4 (open-gsd/gsd-core):
* bin/lib/phase.cjs cmdPhaseComplete — non-idempotent and unclamped.
*
* Root cause (pre-fix):
@@ -21,7 +21,7 @@
* References:
* - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) — architectural foundation
* - /tmp/adr-3524-review-findings.md — architectural justification
* - Issue #4 (open-gsd/get-shit-done-redux)
* - Issue #4 (open-gsd/gsd-core)
*/
const { describe, test, beforeEach, afterEach } = require('node:test');

View File

@@ -1,7 +1,7 @@
'use strict';
/**
* Regression tests for issue #6 (open-gsd/get-shit-done-redux):
* Regression tests for issue #6 (open-gsd/gsd-core):
* Three validation behaviors present in validate.ts are missing from verify.cjs,
* producing silent false negatives on the CJS production path.
*
@@ -22,7 +22,7 @@
*
* References:
* - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md)
* - Issue #6 (open-gsd/get-shit-done-redux)
* - Issue #6 (open-gsd/gsd-core)
* - PR #154 (issue #4) — precedent for the generator pattern
*/

View File

@@ -8,7 +8,7 @@
* Validates that all agent .md files use the standardized <required_reading>
* pattern and that no legacy <files_to_read> blocks remain.
*
* See: https://github.com/open-gsd/get-shit-done-redux/issues/2168
* See: https://github.com/open-gsd/gsd-core/issues/2168
*/
const { test, describe } = require('node:test');

View File

@@ -19,7 +19,7 @@
* rationale in the PR, and make sure the bloat is not duplicated content
* that belongs in `get-shit-done/references/`.
*
* See: https://github.com/open-gsd/get-shit-done-redux/issues/2361
* See: https://github.com/open-gsd/gsd-core/issues/2361
*/
const { test, describe } = require('node:test');

View File

@@ -8,7 +8,7 @@ const path = require('node:path');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
const LEGACY_HINT = 'npx get-shit-done-cc@latest --claude --local';
const CURRENT_HINT = 'npx -y @opengsd/get-shit-done-redux@latest --claude --local';
const CURRENT_HINT = 'npx -y @opengsd/gsd-core@latest --claude --local';
function findMarkdownFiles(dir) {
const out = [];

View File

@@ -31,7 +31,7 @@ const fs = require('node:fs');
const REPO_ROOT = path.join(__dirname, '..');
const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf-8'));
const installPath = path.resolve(REPO_ROOT, pkg.bin['get-shit-done-redux']);
const installPath = path.resolve(REPO_ROOT, pkg.bin['gsd-core']);
const { stripStaleGsdHookBlocks } = require(installPath);
/**

View File

@@ -27,7 +27,7 @@ const fs = require('node:fs');
const REPO_ROOT = path.join(__dirname, '..');
const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf-8'));
const installPath = path.resolve(REPO_ROOT, pkg.bin['get-shit-done-redux']);
const installPath = path.resolve(REPO_ROOT, pkg.bin['gsd-core']);
const install = require(installPath);
// Build a minimal Claude command source whose description starts with the

View File

@@ -77,7 +77,9 @@ function listShippedSlashBaseNames() {
function extractSlashReferences(contents) {
const names = new Set();
const tokenRe = /\/gsd[:-]([a-z][a-z0-9-]*)/g;
// Negative lookbehind: must not be preceded by a letter (avoids matching npm scope
// paths like @opengsd/gsd-core where `/gsd-` appears inside a package URL).
const tokenRe = /(?<![a-z])\/gsd[:-]([a-z][a-z0-9-]*)/g;
let match;
while ((match = tokenRe.exec(contents)) !== null) {
names.add(match[1]);

View File

@@ -17,8 +17,8 @@ const { checkLatestVersion, CHECK_REASON, PACKAGE_NAME } = require(
// Tests use a pluggable spawn so no real npm process is invoked.
describe('Bug #2992: deterministic latest-version check', () => {
test('PACKAGE_NAME is the constant @opengsd/get-shit-done-redux (no callers can override)', () => {
assert.equal(PACKAGE_NAME, '@opengsd/get-shit-done-redux');
test('PACKAGE_NAME is the constant @opengsd/gsd-core (no callers can override)', () => {
assert.equal(PACKAGE_NAME, '@opengsd/gsd-core');
});
test('CHECK_REASON enum exposes the documented codes', () => {

View File

@@ -4,14 +4,14 @@
// Regression guard for bug #3130.
//
// Two failure modes were observed with the pre-fix npx invocation form:
// 1. Cache-stale: bare `npx -y @opengsd/get-shit-done-redux@latest` hits npx's local
// 1. Cache-stale: bare `npx -y @opengsd/gsd-core@latest` hits npx's local
// cache and may pull an older version instead of @latest.
// 2. Token-routing: Bash-tool wrappers misroute the `@` token in
// `@opengsd/get-shit-done-redux@latest`, causing npm to error with
// "Unknown command: @opengsd/get-shit-done-redux@latest".
// `@opengsd/gsd-core@latest`, causing npm to error with
// "Unknown command: @opengsd/gsd-core@latest".
//
// The robust form is:
// npx -y --package=@opengsd/get-shit-done-redux@latest -- get-shit-done-redux $ARGS
// npx -y --package=@opengsd/gsd-core@latest -- gsd-core $ARGS
//
// `--package=` forces a fresh registry fetch, bypassing the npx cache.
// `--` clearly delineates npx flags from the run-command, preventing
@@ -28,9 +28,9 @@ const UPDATE_WF = path.join(ROOT, 'get-shit-done', 'workflows', 'update.md');
const src = fs.readFileSync(UPDATE_WF, 'utf8');
test('bug #3130: update.md contains no bare npx invocations (cache-stale form)', () => {
// Any occurrence of `npx -y @opengsd/get-shit-done-redux@latest` without `--package=`
// Any occurrence of `npx -y @opengsd/gsd-core@latest` without `--package=`
// is the stale form that triggers the two failure modes.
const stale = (src.match(/npx -y @opengsd\/get-shit-done-redux@latest[^\n]*/g) || []);
const stale = (src.match(/npx -y @opengsd\/gsd-core@latest[^\n]*/g) || []);
assert.deepEqual(
stale,
[],
@@ -40,7 +40,7 @@ test('bug #3130: update.md contains no bare npx invocations (cache-stale form)',
test('bug #3130: update.md has >=3 robust npx invocations (--package= + -- separator)', () => {
// Three sibling invocations: local, global, and unknown/fallback.
const robust = (src.match(/npx -y --package=@opengsd\/get-shit-done-redux@latest -- get-shit-done-redux/g) || []);
const robust = (src.match(/npx -y --package=@opengsd\/gsd-core@latest -- gsd-core/g) || []);
assert.ok(
robust.length >= 3,
`Expected >=3 robust npx invocations in update.md, found ${robust.length}`,

View File

@@ -17,7 +17,7 @@
* ignored by Steps 2-6 on non-GSD projects. The block was dead-but-noisy.
*
* Fix: gate the runtime bash detection on a positive "is-this-the-framework-
* repo" check (package.json name === "@opengsd/get-shit-done-redux") so it runs ONLY when
* repo" check (package.json name === "@opengsd/gsd-core") so it runs ONLY when
* analysing the GSD framework's own repo, OR remove the block entirely if no
* downstream consumers exist.
*
@@ -88,7 +88,7 @@ describe('bug #3290 — Group A: layout-detection block must be gated or absent'
// A valid gate checks package.json name or an equivalent positive signal
// that the current project IS the GSD framework's own repo.
const hasFrameworkGate =
content.includes('@opengsd/get-shit-done-redux') ||
content.includes('@opengsd/gsd-core') ||
content.includes('is-this-the-framework') ||
content.includes('framework repo') ||
content.includes('Only run') ||
@@ -102,7 +102,7 @@ describe('bug #3290 — Group A: layout-detection block must be gated or absent'
'bash block (`ls -d .kilo ... || echo unknown`) with no surrounding ' +
'framework-repo gate (#3290). ' +
'Either remove the block entirely, or wrap it in a check like:\n' +
' if [[ "$(jq -r \'.name // ""\' package.json 2>/dev/null)" == "@opengsd/get-shit-done-redux" ]]; then\n' +
' if [[ "$(jq -r \'.name // ""\' package.json 2>/dev/null)" == "@opengsd/gsd-core" ]]; then\n' +
' # ... detection block ...\n' +
' fi'
);

View File

@@ -11,7 +11,7 @@ process.env.GSD_TEST_MODE = '1';
* commands from skills/<name>/SKILL.md. Prior installer logic ("Codex now
* discovers official skills from .agents/skills") was based on an assumption
* that does not match the shipping Codex CLI behavior, leaving users with
* workflows on disk and no $gsd-* entrypoints after `npx @opengsd/get-shit-done-redux
* workflows on disk and no $gsd-* entrypoints after `npx @opengsd/gsd-core
* --codex --global`.
*
* Fix: re-wire copyCommandsAsCodexSkills() back into the install dispatch path

View File

@@ -1,5 +1,5 @@
/**
* Regression test for #3610: fresh `npx @opengsd/get-shit-done-redux@latest --codex`
* Regression test for #3610: fresh `npx @opengsd/gsd-core@latest --codex`
* hard-aborts when the target ~/.codex/hooks/ contains the bundled GSD
* hook files (`gsd-check-update-worker.js`, `gsd-prompt-guard.js`, …)
* left over from a previous version. The installer-migration report

View File

@@ -42,7 +42,7 @@
* truly un-reclaimable lock, must still throw with a useful message naming
* the holder PID. (This guards against over-reclamation.)
*
* @see https://github.com/open-gsd/get-shit-done-redux/issues/3670
* @see https://github.com/open-gsd/gsd-core/issues/3670
*/
'use strict';

View File

@@ -1,9 +1,9 @@
/**
* Regression test for #378 / #498: the SessionStart update worker must end up
* querying the SCOPED package name (@opengsd/get-shit-done-redux) when it asks
* querying the SCOPED package name (@opengsd/gsd-core) when it asks
* npm for the latest version.
*
* Background (#378): the worker once hardcoded the unscoped 'get-shit-done-redux',
* Background (#378): the worker once hardcoded the unscoped 'gsd-core',
* which 404s from the registry, leaving update_available permanently false.
*
* Original #378 fix derived the name from `require('../package.json').name`.
@@ -26,7 +26,7 @@
* 3. Structural: worker delegates to check-latest-version's
* `checkLatestVersion` rather than calling `npm view` itself.
* 4. Single-source: check-latest-version's PACKAGE_NAME === the seam's
* packageName === the scoped '@opengsd/get-shit-done-redux'.
* packageName === the scoped '@opengsd/gsd-core'.
*
* Source-grep policy: this test reads hook source via readFileSync. The repo's
* lint-no-source-grep rule targets bin/lib/get-shit-done — hooks/ is out of
@@ -63,16 +63,16 @@ describe('bug #378 / #498: update worker queries the scoped name via the seam',
assert.ok(fs.existsSync(WORKER_PATH), `worker not found at ${WORKER_PATH}`);
});
test('package.json name is the scoped @opengsd/get-shit-done-redux', () => {
test('package.json name is the scoped @opengsd/gsd-core', () => {
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
assert.equal(pkg.name, '@opengsd/get-shit-done-redux');
assert.equal(pkg.name, '@opengsd/gsd-core');
});
test('worker does NOT hardcode the unscoped get-shit-done-redux as a string literal', () => {
test('worker does NOT hardcode the unscoped gsd-core as a string literal', () => {
assert.doesNotMatch(
workerCodeOnly(),
/['"]get-shit-done-redux['"]/,
"Worker must not pass the unscoped 'get-shit-done-redux' to npm — it 404s.",
/['"]gsd-core['"]/,
"Worker must not pass the unscoped 'gsd-core' to npm — it 404s.",
);
});
@@ -100,6 +100,6 @@ describe('bug #378 / #498: update worker queries the scoped name via the seam',
test('check-latest-version PACKAGE_NAME is single-sourced from the seam', () => {
assert.equal(PACKAGE_NAME, SEAM.packageName);
assert.equal(SEAM.packageName, '@opengsd/get-shit-done-redux');
assert.equal(SEAM.packageName, '@opengsd/gsd-core');
});
});

View File

@@ -1,7 +1,7 @@
'use strict';
/**
* Regression tests for issue #416 (open-gsd/get-shit-done-redux).
* Regression tests for issue #416 (open-gsd/gsd-core).
*
* Bug: getActiveMilestoneArchiveDir falls back to the newest archive directory
* when STATE.md names a milestone that has no matching archive yet, producing

View File

@@ -109,7 +109,7 @@ describe('changeset github release notes: tag-range renderer (#3382)', () => {
fromRef: 'v1.0.0',
toRef: 'v1.0.1',
repoSlug: 'example/project',
installCommand: 'npx @opengsd/get-shit-done-redux@latest',
installCommand: 'npx @opengsd/gsd-core@latest',
});
assert.equal(fs.readFileSync(output, 'utf8'), generated.body);
});

View File

@@ -75,7 +75,7 @@ describe('changeset serialize: multi-line bullet parsing (#3496)', () => {
test('a multi-line bullet with linked release header is parsed correctly', () => {
const text = [
'## [1.42.1](https://github.com/open-gsd/get-shit-done-redux/compare/v1.41.0...v1.42.1) - 2026-05-15',
'## [1.42.1](https://github.com/open-gsd/gsd-core/compare/v1.41.0...v1.42.1) - 2026-05-15',
'',
'### Fixed',
'',

View File

@@ -7,7 +7,7 @@
* Regression tests for bug #1991
*
* Cline is listed in GSD documentation as a supported runtime but was
* completely absent from bin/install.js. Running `npx @opengsd/get-shit-done-redux`
* completely absent from bin/install.js. Running `npx @opengsd/gsd-core`
* did not show Cline as an option in the interactive menu.
*
* Fixed: Cline is now a first-class runtime that:

View File

@@ -143,25 +143,25 @@ describe('forensics workflow', () => {
);
});
test('workflow submits issues to open-gsd/get-shit-done-redux, not the current repo', () => {
test('workflow submits issues to open-gsd/gsd-core, not the current repo', () => {
const content = fs.readFileSync(workflowPath, 'utf-8');
// Scope check to the gh issue create invocation — a whole-file search would
// pass even if gh issue create lacked --repo, because gh label list also
// contains the repo string.
assert.match(
content,
/gh issue create[\s\S]{0,250}--repo\s+open-gsd\/get-shit-done-redux/,
'gh issue create must use --repo open-gsd/get-shit-done-redux to avoid submitting to the user\'s current project repo'
/gh issue create[\s\S]{0,250}--repo\s+open-gsd\/gsd-core/,
'gh issue create must use --repo open-gsd/gsd-core to avoid submitting to the user\'s current project repo'
);
});
test('workflow checks bug label in open-gsd/get-shit-done-redux, not the current repo', () => {
test('workflow checks bug label in open-gsd/gsd-core, not the current repo', () => {
const content = fs.readFileSync(workflowPath, 'utf-8');
// Regex is more robust than a fixed-length slice to formatting changes
assert.match(
content,
/gh label list[\s\S]{0,250}--repo\s+open-gsd\/get-shit-done-redux/,
'gh label list must target open-gsd/get-shit-done-redux'
/gh label list[\s\S]{0,250}--repo\s+open-gsd\/gsd-core/,
'gh label list must target open-gsd/gsd-core'
);
});

View File

@@ -53,9 +53,9 @@ describe('Issue #498: deriveIdentity (pure, package.json -> coordinates)', () =>
test('derives the real GSD coordinates from the repo package.json', () => {
const real = require(path.join(ROOT, 'package.json'));
const id = deriveIdentity(real);
assert.equal(id.packageName, '@opengsd/get-shit-done-redux');
assert.equal(id.binName, 'get-shit-done-redux');
assert.equal(id.repoSlug, 'open-gsd/get-shit-done-redux');
assert.equal(id.packageName, '@opengsd/gsd-core');
assert.equal(id.binName, 'gsd-core');
assert.equal(id.repoSlug, 'open-gsd/gsd-core');
});
});
@@ -78,7 +78,7 @@ describe('Issue #498: formatManualInstall (the npx fallback command)', () => {
const id = deriveIdentity(require(path.join(ROOT, 'package.json')));
assert.equal(
formatManualInstall({ packageName: id.packageName, binName: id.binName, scope: 'global', runtime: 'claude' }),
'npx -y --package=@opengsd/get-shit-done-redux@latest -- get-shit-done-redux --claude --global',
'npx -y --package=@opengsd/gsd-core@latest -- gsd-core --claude --global',
);
});
});
@@ -98,16 +98,16 @@ describe('Issue #498: generated runtime module (baked, drift-checked)', () => {
test('requiring the generated module exposes the real coordinates', () => {
const id = require(GENERATED);
assert.equal(id.packageName, '@opengsd/get-shit-done-redux');
assert.equal(id.binName, 'get-shit-done-redux');
assert.equal(id.repoSlug, 'open-gsd/get-shit-done-redux');
assert.equal(id.packageName, '@opengsd/gsd-core');
assert.equal(id.binName, 'gsd-core');
assert.equal(id.repoSlug, 'open-gsd/gsd-core');
});
test('generated manualInstallCommand closes over the baked coordinates', () => {
const id = require(GENERATED);
assert.equal(
id.manualInstallCommand({ scope: 'global', runtime: 'claude' }),
'npx -y --package=@opengsd/get-shit-done-redux@latest -- get-shit-done-redux --claude --global',
'npx -y --package=@opengsd/gsd-core@latest -- gsd-core --claude --global',
);
});
});

View File

@@ -4,7 +4,7 @@
* Lint guard: the package name must be single-sourced from package-identity.cjs.
*
* Scans runtime files (bin/install.js, get-shit-done/bin/**, scripts/*.cjs)
* and FAILS if the literal `@opengsd/get-shit-done-redux` appears in a
* and FAILS if the literal `@opengsd/gsd-core` appears in a
* non-comment, non-identity-module line. This enforces that a future rename
* is a one-file change in package.json (#516).
*
@@ -24,7 +24,7 @@ const path = require('node:path');
const { execSync } = require('node:child_process');
const ROOT = path.join(__dirname, '..');
const LITERAL = '@opengsd/get-shit-done-redux';
const LITERAL = '@opengsd/gsd-core';
const IDENTITY_MODULE = path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'package-identity.cjs');
// Files to scan: bin/install.js + everything under get-shit-done/bin/ + touched scripts
@@ -75,7 +75,7 @@ function isCommentLine(trimmed) {
);
}
test('no hardcoded package-name literals in runtime non-comment code lines (#516)', () => {
test('no hardcoded @opengsd/gsd-core literals in runtime non-comment code lines (#516)', () => {
const files = getRuntimeFiles();
const violations = [];
@@ -98,7 +98,7 @@ test('no hardcoded package-name literals in runtime non-comment code lines (#516
assert.deepEqual(
violations,
[],
`Found ${violations.length} hardcoded package-name literal(s) in non-comment code lines:\n` +
`Found ${violations.length} hardcoded @opengsd/gsd-core literal(s) in non-comment code lines:\n` +
violations.map(v => ` ${v}`).join('\n') +
'\n\nReplace each with the PACKAGE_NAME imported from get-shit-done/bin/lib/package-identity.cjs'
);

View File

@@ -97,7 +97,7 @@ describe('release-tarball-smoke', () => {
// ── Test C — happy lifecycle ───────────────────────────────────────────────
// Verifies that the installed package has all expected command .md files and
// that each command resolves a workflow .md file that also exists.
// Also verifies that `get-shit-done-redux --local --claude` (init) succeeds in
// Also verifies that `gsd-core --local --claude` (init) succeeds in
// the fixtureDir and creates the expected .claude/ directories.
test('C: happy lifecycle — command + workflow files resolve OK', () => {
const result = runSmoke({

View File

@@ -14,7 +14,7 @@
* 4. findProjectRoot detects .git at same level as .planning/
* 5. @file: handoff present in all workflows that call init
*
* Regression tests for: https://github.com/open-gsd/get-shit-done-redux/issues/1343
* Regression tests for: https://github.com/open-gsd/gsd-core/issues/1343
*/
const { test, describe } = require('node:test');

View File

@@ -7,7 +7,7 @@ process.env.GSD_TEST_MODE = '1';
* .github/workflows/*.yml file has an effective `shell:` directive that is
* H1-policy-compliant (native shell per OS).
*
* H1 policy (LOCKED — open-gsd/get-shit-done-redux):
* H1 policy (LOCKED — open-gsd/gsd-core):
* ubuntu-* → bash (runner default, no pin needed)
* macos-* → zsh (must be pinned explicitly)
* windows-* → pwsh (runner default, no pin needed)

View File

@@ -22,8 +22,8 @@
* pattern introduced by #2551).
*
* See:
* - https://github.com/open-gsd/get-shit-done-redux/issues/2551 (this test)
* - https://github.com/open-gsd/get-shit-done-redux/issues/2361 (agent budget)
* - https://github.com/open-gsd/gsd-core/issues/2551 (this test)
* - https://github.com/open-gsd/gsd-core/issues/2361 (agent budget)
*/
const { test, describe } = require('node:test');