* docs: add PRD and ADR for pi clean seam refactor
Introduces two new planning documents for extracting GSD-authored code
out of the vendored pi packages into dedicated @gsd/agent-core and
@gsd/agent-modes workspace packages, establishing a module-system-enforced
boundary that makes future pi-mono upstream updates significantly easier.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: rename ADR-009 to ADR-010, update cross-references
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs were causing version drift across the repo:
1. Root package.json was silently reverted from 2.74.0 → 2.73.1 during
commit b03c9401c (a CI optimization rebase). Tag v2.74.0 is already
published on npm, so the next release would have computed 2.73.2 —
lower than what's already out — and shipped a broken version.
2. scripts/bump-version.mjs only touches pi-coding-agent + pkg + native
platform shims. Other workspace packages drift independently:
- @gsd-build/mcp-server: stuck at 2.52.0 (22 minor versions behind)
- @gsd-build/rpc-client: stuck at 2.52.0
- @gsd/pi-ai, pi-tui, pi-agent-core: stuck at 0.57.1
- @gsd/native, @gsd-build/daemon: stuck at 0.1.0
Changes:
- Bump all non-private workspace packages to 2.74.0 to match the latest
release tag. Update daemon + mcp-server's internal rpc-client dep
from ^2.52.0 → ^2.74.0. Regenerate root lockfile.
- scripts/generate-changelog.mjs: compute newVersion from max(latest
stable tag, package.json) instead of package.json alone. Prevents
version regressions when package.json is accidentally clobbered by
rebases or merges.
- scripts/bump-version.mjs: extend to sync all eight non-private
workspace packages (daemon, mcp-server, native, pi-agent-core, pi-ai,
pi-coding-agent, pi-tui, rpc-client) including their internal deps
on each other. Private packages (studio, web) are left alone.
Studio and web remain on their own versioning (private: true, never
published). The native platform shims under native/npm/* are still
synced via native/scripts/sync-platform-versions.cjs from the root
version as before.
Ports the v1 graphify system to v2 as a native TypeScript implementation.
The knowledge graph builds semantic relationships between milestones, slices,
tasks, and knowledge entries — and injects relevant subgraphs automatically
into every agent dispatch prompt.
## Core implementation (packages/mcp-server/src/readers/graph.ts)
- `buildGraph(projectDir)` — walks all .gsd/ artifacts (STATE.md,
milestone PLANs, slice PLANs, KNOWLEDGE.md), extracts nodes and edges
with confidence tiers (EXTRACTED / INFERRED / AMBIGUOUS). Parse errors
skip the node rather than crashing.
- `writeGraph(gsdRoot, graph)` — atomic write via tmp file + rename.
- `writeSnapshot(gsdRoot)` — saves a diff baseline before each rebuild.
- `graphQuery(projectDir, term, budget?)` — BFS subgraph search with
case-insensitive matching on label + description; trims AMBIGUOUS edges
first, then INFERRED, respecting the token budget (default 4 000).
- `graphStatus(projectDir)` — freshness check; stale = older than 24 h.
- `graphDiff(projectDir)` — compares current graph to last snapshot,
returns added / removed / changed counts for nodes and edges.
## MCP tool (packages/mcp-server/src/server.ts)
Registers `gsd_graph` immediately after `gsd_knowledge` with four modes:
build | query | status | diff. All errors returned as isError: true.
## CLI subcommand (src/cli.ts, src/help-text.ts)
`gsd graph build|status|query <term>|diff` — follows the established
`if (cliFlags.messages[0] === '...')` dispatch pattern. Uses
`resolveGsdRoot()` for git-root-aware path resolution (not a naive
`.gsd` append). Help text updated with correct positional argument format.
## Auto-rebuild after slice completion
(src/resources/extensions/gsd/tools/complete-slice.ts)
Fire-and-forget `buildGraph → writeGraph` triggered after every slice
completion. Uses `@gsd-build/mcp-server` package import (not a relative
src path) and `resolveGsdRoot()` for correct path resolution in monorepos.
## Graph-aware dispatch injection
(src/resources/extensions/gsd/graph-context.ts,
src/resources/extensions/gsd/auto-prompts.ts)
`inlineGraphSubgraph(projectDir, term, { budget })` queries the graph and
formats the result as a `### Knowledge Graph Context` markdown block,
consistent with all other inlined context blocks. Adds a stale warning
annotation when the graph is older than 24 h. Returns null (graceful
skip) when graph.json is missing, the query returns zero nodes, or the
import fails — no agent dispatch is ever blocked by graph availability.
Injected into three prompt builders:
- `buildResearchSlicePrompt` — 3 000 token budget
- `buildPlanSlicePrompt` — 3 000 token budget
- `buildExecuteTaskPrompt` — 2 000 token budget
## Tests
- 22 tests for the core graph reader (graph.test.ts)
- 14 tests for the dispatch injection helper (graph-context.test.ts)
- All tests use real on-disk fixtures (no module mocking needed)
- Full suite: 6 318 passed, 0 failed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pipeline.yml was in CI's paths-ignore, so PRs that only touch pipeline.yml
never trigger CI — but 'build' is a required status check, deadlocking
those PRs forever. This also means pipeline.yml changes merge without
any code validation, which is how the dev-publish 'next: not found' bug
slipped through in the first place.
The dev-publish job splits build into build:core + build:web-host, but
web/ has its own lockfile and is not an npm workspace. The old 'npm run
build' step self-healed via build-web-if-stale.cjs; the direct
build:web-host call does not, causing 'next: not found'.
Matches the pattern already used in ci.yml.
- pr-branch: use execFileSync everywhere, validate --name with git check-ref-format,
cherry-pick --no-commit + path-level reset to strip .gsd/.planning/PLAN.md from
mixed commits, and assert no excluded paths leak into the final branch
- ship: argv-safe git push and gh pr create, validate base/current refs, and
resolve ROADMAP/SUMMARY via resolveMilestoneFile/resolveSliceFile
- add-tests: resolve slice SUMMARY via resolveSliceFile instead of hardcoded path
- Fix ProjectTotals field names (totalCost→cost, totalDuration→duration, etc.)
- Fix UnitMetrics field names (status→finishedAt, unitId→id)
- Fix ModelAggregate field name (count→units)
- Fix ActiveRef usage (no phase/slices fields)
- Remove commands-slice-mutation.ts (depends on single-writer engine)
- Remove slice mutation routes from catalog, do command, and workflow handler
- Update backlog promote to work without engine slice commands
Drop map-codebase from the v1→v2 parity PR: handler, prompt template,
catalog entry, nested completions, route in ops.ts, and keyword route
in the do command.
Add 12 commands that exist in GSDv1 but had no v2 equivalent:
High priority:
- ship: Create PR from milestone artifacts (title, body, metrics)
- add-slice: Append slice to roadmap via engine updateRoadmap()
- insert-slice: Insert slice at position with reordering
- remove-slice: Remove pending slice (--force for planned slices)
- do: Natural language routing via keyword matching (30 routes)
- session-report: Session cost/tokens/work summary (--json, --save)
Medium priority:
- backlog: Structured backlog with 999.x numbering (add/promote/remove)
- pr-branch: Clean PR branch filtering .gsd/ commits via cherry-pick
- add-tests: LLM-dispatched test generation for completed slices
- map-codebase: Codebase analysis (tech/arch/quality/concerns)
All slice mutations go through the engine's updateRoadmap() command,
preserving the single-writer architecture. No direct markdown edits.
Includes 46 unit tests across 6 test files, 2 prompt templates,
catalog entries with nested completions for all commands.
The workflow-logger per-unit buffer API (_resetLogs / drainAndSummarize /
formatForNotification) had zero callers outside tests, so accumulated
warnings never reached users as a consolidated post-unit alert and the
buffer leaked across units in the same Node process. Several state-layer
sites also silently swallowed errors that should have surfaced.
- auto/phases.ts: reset logger in runUnitPhase, drain + ctx.ui.notify in
runFinalize success path, drain in both finalize timeout branches so
timed-out unit logs don't bleed into the next iteration
- auto/detect-stuck.ts: enrich stuck reasons with summarizeLogs() so
recovery has the diagnostic context (read-only peek, no drain)
- auto.ts: call setLogBasePath(base) in startAuto to pin the audit log
on /clear resume and hot-reload paths that bypass dynamic-tools bootstrap
- workflow-manifest.ts: log snapshotState ROLLBACK failures (split-brain
signal) instead of silently swallowing them
- state.ts: log reconcileDiskToDb roadmap read failures instead of silent
continue
- workflow-projections.ts: log renderStateProjection DB handle probe
failures instead of silent return
New regression tests cover the phases.ts wiring (source-scan), setLogBasePath
in startAuto, detect-stuck enrichment runtime behavior (including the
read-only peek invariant), and the three silent-catch fixes.
Route every INSERT/UPDATE/DELETE/REPLACE against .gsd/gsd.db through typed
wrappers in gsd-db.ts and add a structural test that fails CI if a new
bypass appears. Previously 13 call sites across 10 modules reached into
_getAdapter() and issued raw write SQL, making the "single writer"
architecture unenforceable in-process.
New wrappers in gsd-db.ts: deleteDecisionById, deleteRequirementById,
deleteArtifactByPath, clearEngineHierarchy, insertOrIgnoreSlice,
insertOrIgnoreTask, setSliceReplanTriggeredAt, upsertQualityGate,
restoreManifest, bulkInsertLegacyHierarchy, readTransaction, and eight
memory-store helpers (insertMemoryRow, rewriteMemoryId, etc).
workflow-manifest.restore() is lifted verbatim into gsd-db.restoreManifest
with a type-only import of StateManifest to avoid circular runtime deps.
tools/workflow-tool-executors and workflow-manifest.snapshotState swap
their manual BEGIN DEFERRED/COMMIT/ROLLBACK dance for readTransaction().
unit-ownership.ts stays outside the invariant: it writes to a separate
.gsd/unit-claims.db by design.
tests/single-writer-invariant.test.ts walks every .ts file under gsd/
(excluding tests/ and the allowlist) and fails with a grouped violations
list on any regex match for .prepare/.exec raw writes, plus a positive
assertion that gsd-db.ts still exports each expected wrapper so the
structural test can't silently become a no-op.
https://claude.ai/code/session_01FZgXD3bjcddoFYsTEY6JhC
On Windows, execSync spawns cmd.exe which cannot resolve git when Git for
Windows is installed via MSYS2/bash but not in cmd.exe's PATH. This caused
every auto-commit to fail silently, leaving all milestone work uncommitted.
All other fallback paths in native-git-bridge already use execFileSync — the
three affected functions were the outliers. execFileSync resolves the binary
directly without a shell intermediary and supports identical stdio/input options.
Closes#4180
- Include dist/ and packages/*/dist/ in the TypeScript incremental cache
so that when tsbuildinfo indicates no changes, the compiled output files
are still present. Without this, tsc with incremental:true skips emission
when tsbuildinfo exists but dist/ is absent (fresh checkout + cache restore),
causing downstream packages like @gsd/pi-tui to fail resolving @gsd/native
subpath exports.
- Also hash source files in the cache key so dist is invalidated on code changes.
- Replace process.stderr.write with logWarning("bootstrap", ...) in catch blocks
to satisfy the workflow-logger coverage test (#3348).
- Update extension-bootstrap-isolation tests to match the new logWarning pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On Windows, static imports in register-shortcuts.ts (added in v2.72) can
fail at module load time, causing the entire GSD extension to silently fail
to register. This makes /gsd unavailable despite the welcome screen
suggesting it.
Three changes:
- index.ts: register /gsd command before importing register-extension.js,
wrapped in try-catch so bootstrap failures don't prevent core command
- register-extension.ts: remove duplicate registerGSDCommand call, wrap
non-critical registrations (tools, shortcuts, hooks) in individual
try-catch blocks so one failure doesn't prevent others
- Add structural contract tests verifying isolation properties
Closes#4168, closes#4172
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Read-side twin of #4175. `deriveStateFromDb` had a SUMMARY-file fallback
that could mark a milestone complete even when the DB row said otherwise,
allowing an orphan SUMMARY.md (crashed complete-milestone turn, partial
merge, manual edit) to cascade into a false auto-merge.
- buildCompletenessSet: drop SUMMARY fallback; only DB status decides.
- buildRegistryAndFindActive: remove SUMMARY from the completeness check;
still consult SUMMARY as a title fallback for DB-certified milestones.
- allSlicesDone branch: drop `!summaryFile` clause so a terminal-validation
+ orphan-SUMMARY path flows through to `completing-milestone` instead of
short-circuiting, letting complete-milestone re-run idempotently.
Regression tests: orphan SUMMARY with in-flight slice stays active; orphan
SUMMARY with all-slices-done + validation-terminal lands at
completing-milestone (does not report as already complete).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Guards the three cooperating fixes shipped in #4178 via source inspection
so a future refactor cannot silently reintroduce the false-merge path:
- stopAuto now uses the DB getMilestone() status as the authoritative
milestone-complete signal (falls back to SUMMARY presence only when
the project DB is unavailable).
- postUnitPreVerification pauses auto-mode for complete-milestone after
retries are exhausted instead of writing a stub blocker placeholder.
- recoverTimedOutUnit pauses complete-milestone instead of writing a
stub blocker placeholder.
Unblocks the CI lint / require-tests.sh gate on PR #4178.
When complete-milestone failed verification, auto-mode could end up merging
the worktree to main anyway and emit a metadata-only merge warning, creating
a misleading near-complete signal while the SUMMARY was never actually written.
The blocker-placeholder path for complete-milestone wrote a stub SUMMARY
without updating DB status, and stopAuto's SUMMARY-presence check treated
the stub as a legitimate completion signal.
- auto-post-unit.ts: skip blocker placeholder and pause auto-mode on
complete-milestone verification retry exhaustion.
- auto-timeout-recovery.ts: same guard for the idle/hard timeout path.
- auto.ts: make stopAuto Step 4 DB-authoritative (getMilestone.status ===
"complete") with SUMMARY-presence fallback only for DB-unavailable
legacy projects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>