Tom Boucher 0f60266042 fix(#2723): reconcile emitted manifest families as a set, not a count (#2750)
* fix(#2723): reconcile emitted manifest families as a set, not a shared count

EXPECTED_MANIFEST_COUNT was a single literal 19 asserted against both the
baseline (built at the base ref) and the current tree (built at PR HEAD).
Those sides legitimately differ by one family whenever a PR adds or removes
a runtime, so no value satisfied both: 19 rejected the current side, 20
rejected the baseline side. Every runtime-adding PR was hard-blocked.

Replace the shared literal with three independent signals - the derived
family set, the recorded fixture set, and the families present at the base
ref - reconciled as sets in both directions. A family may appear or vanish
only when the diff plausibly touches the runtime registry, and the failure
names the family rather than a count. An absolute floor catches the
uniformly shrunken universe a same-count self-check passes vacuously.

Found by tracing #2005 (Qoder runtime) through the gate during the ADR-2719
dual-run window.

* fix(#2723): read the baseline family set from the ref, not HEAD's registry

Review found three defects in the first cut.

Blocker: baselineManifestsAtRef enumerated MANIFEST_FAMILIES, which is imported
at module load and therefore describes PR HEAD. A runtime REMOVED by the PR is
already absent from that list, so the base ref was never asked for it, the
baseline silently omitted a family that genuinely existed, and the dropped-family
check could never fire in production - while its unit tests passed, because they
inject the baseline directly. Enumerate from the ref with git ls-tree instead.

Also: narrow the registry-signal set to the two surfaces that actually define the
family set, since every extra path widens what excuses an unattributed delta; drop
the ack bypass, which was a one-sided escape hatch making removals easier to wave
through than additions; and gate the derived/fixtures inputs so malformed values
return a verdict rather than an unhandled TypeError.

* fix(#2723): filter prototype-shaped family names read from git output

baselineFamilyNamesAtRef derives object keys from git ls-tree output rather
than a trusted constant, so a fixture committed as __proto__.json would turn
the manifests[name] assignment into a prototype write. Compared inline rather
than through a Set, which is the form the prototype-pollution analysis
recognizes.

* fix(#2723): require an exact capability path depth and make the ref test hermetic

The remote runner went red on both linux lanes with two real defects.

The capability signal matched by prefix+suffix, so 'capabilities/capability.json'
(no runtime segment) and 'capabilities/a/b/capability.json' (wrong depth) both
attributed a family change and would have excused an unattributed delta. Anchored
to an exact single-segment pattern.

The ref-derivation test reached for this repo's root commit, which is not stable:
the remote runner shallow-clones, so rev-list --max-parents=0 returns the grafted
boundary carrying every fixture, and this repo has two root commits locally anyway.
It now builds its own git repo containing a family absent from the current registry
- the real discriminator, and one the root-commit version could never assert.

Lint then caught a third: the test called t.after() without declaring t.

* fix(#2723): stop asserting ref enumeration against the ambient checkout

The remote runner returned [] for the repo's own HEAD while every hermetic
temp-repo assertion in the same test passed. That is this function's documented
behavior when git cannot read the ref - the runner works from a shallow clone
under a bind-mounted workdir - so the assertion was testing the checkout rather
than the code.

Dropped it. The temp repo already proves the property that matters, and proves it
more strongly: it contains a family absent from the current registry, which a
registry-derived implementation could never report. The ambient path stays covered
by the real-tree test, which skips explicitly when no base ref is resolvable.

A git failure is not silently permissive downstream: baselineManifestsAtRef returns
null on an empty family set and the real-tree test asserts the baseline is non-empty.
2026-07-28 07:41:13 -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%