* 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>
Mirror of code, issues, and PRs from the upstream gsd-build/get-shit-done,
which appears compromised or abandoned (maintainer unreachable since
2026-04-01; $GSD token linked to rug-pull).
- Adds rebrand notice block at top of English README
- Removes $GSD token badge and @gsd_foundation X badge (keeps Discord)
- Renames npm packages: get-shit-done-cc -> get-shit-done-redux,
@gsd-build/sdk -> @gsd-redux/sdk
- Updates all repo URLs across docs, workflows, package.json, bin/
- Updates ci@gsd-build -> ci@gsd-redux in workflow git identities
- Leaves CHANGELOG and .changeset/* alone (historical, time-stamped)
* test(installer): add regression tests for #3670 migration lock self-deadlock
- T1: same-process PID re-entry reclamation (primary regression)
- T2: dead-PID stale lock reclamation
- T3: unlinkSync EPERM surfaces (not silently swallowed via force:true)
- T4: counter-test — normal round-trip still works
- T5: counter-test — genuinely-held live lock still errors clearly
- Update existing 'reports lock release failures' test to mock
fs.unlinkSync (not fs.rmSync) matching the fixed release path
Windows wall-clock deadlock repro depends on Docker matrix Windows runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(installer): break migration lock self-deadlock on Windows (#3670)
Root cause: `acquireInstallMigrationLock` release closure called
`fs.rmSync(lockPath, { force: true })`. On Windows NTFS, a file
recently closed via `closeSync(fd)` may still return EPERM from
`unlink` until the OS fully releases the handle. The `force: true`
flag silently swallows EPERM, leaving the lock file on disk. The
subsequent `runInstallerMigrations` call in the same install()
invocation hits EEXIST, spins for 30 s, then throws
"installer migration lock is held".
Fix:
1. Release closure uses `fs.unlinkSync` (not rmSync+force) so
EPERM propagates via releaseError instead of being swallowed.
2. `acquireInstallMigrationLock` closes the fd before writing the
payload (path-based write), eliminating the open handle that
caused the deferred EPERM on Windows.
3. Stale-lock reclamation: on EEXIST, parse the on-disk PID and
reclaim immediately if it matches process.pid (same-process
re-entry, the primary #3670 failure mode) or if the PID is
dead (ESRCH). Live alien PIDs still trigger the 30 s timeout.
4. Error message on a genuinely-held lock now includes the holder
PID and acquiredAt timestamp for operator diagnostics.
No public API change. All callers of runInstallerMigrations are
inside installer-migrations.cjs and bin/install.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(3670): update changeset to reference PR #3765
* fix(3670): timeout on reclaim-unlink failure to prevent spin-loop regression
When unlinkSync throws (e.g. Windows EPERM on an open handle) in the
same-PID / dead-PID reclamation path, the original code continued
unconditionally — bypassing the timeout check and reintroducing the
exact deadlock the PR is supposed to fix.
Guard the continue behind a `reclaimed` flag: only loop back to
openSync if unlink SUCCEEDED. On failure, fall through to the existing
bounded sleep + timeout, which surfaces "installer migration lock is
held" within lockTimeoutMs instead of spinning indefinitely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(3670): tighten T5 — assert bounded failure when reclaim unlink fails on live lock
The old T5 accepted BOTH success and throw, which allowed over-reclamation
of a genuinely un-reclaimable lock to pass undetected.
Rewrite T5 to force deterministically unreclaimable conditions:
- Pre-seed lock with process.pid (triggers isSameProcess path)
- Mock fs.unlinkSync via mock.method() to throw EPERM for the lock file
With the production fix: reclaimed=false → falls through to timeout →
throws "installer migration lock is held" within ~200ms.
Without the production fix: unlink throws but continue runs anyway →
process spins and eventually OOMs (confirmed RED: 136s runtime, V8 heap
exhaustion from infinite readLockFile + new Error() allocations).
assert.throws() now makes success a hard failure, closing the
over-reclamation gap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(3670): clean up orphan lock file when writeFileSync fails after closeSync
If closeSync(fd) succeeds (fd=null) but the subsequent writeFileSync
throws, the empty lock file was left on disk. readLockFile returns null
for an empty/invalid-JSON file, so the stale-lock reclamation path
skips it, causing the next acquire attempt to spin to timeout.
Track ownership with lockCreatedByUs flag; add a second cleanup branch
in the catch block for the fd-already-closed case.
Also fix changeset body to use the bold-prefix format required by all
other fragments in .changeset/.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>