Tom Boucher 40628d8050 fix(#4409): delete the fold's shadow copies of extractShellBlocks and collectWorkflowFiles (#4720)
* test(#4409): prove the fold shadows two module-scope helpers

Failing-first regression coverage for #4409.

tests/runtime-launcher-parity.test.cjs declares extractShellBlocks twice.
The module-scope copy (line 220) splits on /\r?\n/ and pushes
{index, lang, lines}; the folded copy (line 1315) splits on '\n' and
pushes {lines} only. The fold opens with a bare `{` at line 1226, so the
inner declaration shadows the outer one for everything inside it -- and
on a CRLF checkout every extracted line keeps a trailing \r.
collectWorkflowFiles is duplicated the same way and is currently
byte-identical, i.e. latent rather than active.

Every row walks an AST via espree, a declared devDependency. None reads a
.cjs and calls .includes(): that is local/no-source-grep's exact shape,
and it is also the wrong instrument -- "how many declarations exist" is a
construct count, and a regex would match the name inside a comment or a
string literal too.

Row 2 pins the rest of the corpus as an EXACT SORTED LIST of the 26
fold-shadowed helpers that remain in 15 other files, measured with this
same walker rather than guessed. A bare count was rejected: `27 !== 26`
names no offender and costs a round-trip to diagnose. Row 3 keeps that
baseline from rotting into strings that match nothing.

Row 4 is the behavioural half -- the defect a Windows user actually hits
-- and asserts the surviving extractShellBlocks splits on a regex that
tolerates \r, not on the bare '\n' literal.

Red round: rows 1, 2 and 4 fail; row 3 passes, since every baseline entry
is real today.

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

* fix(#4409): delete the fold's shadow copies of two helpers

Both folded copies are removed so the fold resolves the module-scope
pair. Deletion, not repair: patching the folded split to /\r?\n/ would
leave two definitions and the next edit could diverge them again, and the
issue asks for one definition per helper.

Verified safe before deleting, not assumed:
  - the fold's WORKFLOWS_DIR and SNIPPET_FILE are byte-identical to the
    module-scope ones, and escapeRegex resolves to the same
    gsd-core/bin/lib/pattern.cjs, so the surviving functions close over
    the same values the folded copies did;
  - the fold has exactly ONE call site (test (E)) and it consumes blocks
    only as blocks.flatMap(b => b.lines). The module-scope extractor
    returns {index, lang, lines}, a SUPERSET of the folded {lines}, so no
    caller is starved. The deepStrictEqual 11 lines below that call
    compares `missing`, an array of path strings -- not block objects.

Also removes the fold's now-dead `escapeRegex` re-require. It was used
only by the folded extractShellBlocks; lint:ci caught it as an unused
binding. The module-scope require stays -- the surviving extractor needs
it.

Applied by AST range rather than line numbers so the deletion boundaries
are exact.

Not fixed here, and not a deferral: the same walker finds 28 fold-shadowed
helpers across 16 test files, of which 16 DIVERGE from their module-scope
twin. The 15 diverged pairs outside this file are the same defect class,
but the tracker already drew this boundary -- #4337 removed three
runBashFile shadows "only because that PR had to edit all four
identically" and deliberately left these two to #4409. A diverged shadow
also cannot be deleted mechanically the way these could: its fold's tests
were written against its own copy. The new suite pins all 26 remaining as
an exact list so none can be forgotten.

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

* test(#4409): close five holes both reviewers found in the new suite

Review round. Every change is to the new suite; the fix itself is
untouched.

1. The corpus walk was NON-RECURSIVE, so 64 files under tests/{dispatch,
   qa,observability,health-diagnostic-rules,helpers} were invisible to the
   baseline guard. Now recursive: 1013 files parsed, up from 949. The
   baseline is unchanged at 26 -- coverage widened without moving the pin.

2. `catch { continue }` silently exempted any file espree could not parse,
   so the guard could go quietly blind on exactly the file that broke.
   Parse failures now propagate, and row 2 asserts the scanned count.

3. Only FunctionDeclaration was detected, so a `const helper = () => {}`
   shadow was invisible. Arrow and function-expression bindings now count
   too. Zero such shadows exist today; the hole was future-facing.

4. The old row 3 was VACUOUS -- subsumed by row 2, which already fails on a
   stale baseline entry because the lists stop matching. Deleted; its
   message folded into row 2.

5. The CRLF row inspected only declarations[0]. On the pre-fix tree that is
   the MODULE-SCOPE copy, which was already correct -- so only the
   length===1 assertion went red and the row never actually saw the bug. It
   now checks EVERY declaration, and pins the split to the one applied to
   that declaration's own content parameter rather than to whichever
   `.split()` appears first in the body.

Also removes the `allow-test-rule: source-text-is-the-product` marker. It
was inert: the readFileSync result feeds espree.parse, never a text method,
so local/no-source-grep has no candidate site to suppress. Proven by
running the rule through the Linter API with suppression neutralized -- 0
messages either way. A marker naming an exemption that does not exist is
misleading.

The divergence figure in the header comment now states its normalization.
Both reviewers were right and measured different things: over the 26
remaining shadows, whitespace-only normalization gives 16 diverged / 10
identical; stripping comments as well gives 15 / 11. Exactly one pair
differs only in its comments.

Control re-run against the pre-fix subject file with the new logic: 3 of 3
rows fail, 3 of 3 pass after. Every row is now load-bearing.

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

---------

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 03:50:42 -04:00

GSD Core

Git. Ship. Done.

English · Português · 简体中文 · 日本語 · 한국어

A light-weight meta-prompting, context engineering, and spec-driven development system for Claude Code, OpenCode, Antigravity CLI, Kimi CLI, Kilo, Codex, Copilot, Cursor, Windsurf, and more.

npm version npm downloads Tests Discord GitHub stars License


What is GSD Core

GSD Core is a context-engineering and spec-driven development framework that drives AI coding agents (Claude Code, Codex, Antigravity CLI, Kimi CLI, Copilot, Cursor, and more) through a disciplined phase loop. It solves context rot — the quality degradation that accumulates as an AI fills its context window — by running all heavy research, planning, and execution work in fresh-context subagents while keeping your main session lean.


How it works

Each milestone repeats the same five-step loop, one phase at a time:

  1. Discuss — capture implementation decisions before anything is planned
  2. Plan — research, decompose, and verify the plan fits a fresh context window
  3. Execute — run plans in parallel waves; each executor starts with a clean 200k-token context
  4. Verify — walk through what was built; diagnose and fix before declaring done
  5. Ship — create the PR, archive the phase, repeat for the next one

Quickstart

npx @opengsd/gsd-core@latest

The installer prompts for your runtime (Claude Code, OpenCode, Antigravity CLI, Kimi CLI, Kilo, Codex, Copilot, Cursor, Windsurf, and more) and whether to install globally or locally. The installer is required for cross-runtime compatibility — do not copy files from agents/ or commands/ directly.

On another runtime or without Node.js? See Install on your runtime.

Once installed, start a new project or onboard an existing repo:

/gsd-new-project   # greenfield project
/gsd-onboard       # existing codebase

New here? Follow Your first project for a guided walkthrough from install to first shipped phase, or Onboarding an existing codebase for brownfield setup.


Documentation

What's new in 1.7.0 → docs/whats-new-1.7.0.md

Tutorials — learning by doing:

How-to guides — task-focused recipes:

Reference — authoritative facts:

Explanation — concepts and design decisions:

Full index: docs/README.md. Other languages: 日本語 · 한국어 · Português · 简体中文.


Why it works

Most AI-coding setups fail at scale because context bloat silently degrades output quality, there is no shared memory between sessions, and nothing verifies that code actually works. GSD Core solves all three: heavy work runs in fresh subagents, structured artifacts like STATE.md and CONTEXT.md survive session boundaries, and the verify step walks through what was built and generates fix plans before a phase is declared done. See docs/explanation/context-engineering.md for the full reasoning.

Troubleshooting? See docs/how-to/recover-and-troubleshoot.md.


Community

Project Platform
gsd-opencode Original OpenCode port
Discord Community support

Star History

Star History Chart

License

MIT License. See LICENSE for details.


Claude Code is powerful. GSD Core makes it reliable.

Description
No description provided
Readme MIT 77 MiB
Languages
JavaScript 82.3%
TypeScript 17.4%
Shell 0.3%