PR #3564 narrowed the internal overlay to @gsd* prefixes only, which
dropped non-hoisted optional deps like @anthropic-ai/claude-agent-sdk
from the merged ~/.gsd/agent/node_modules directory. Revert to overlaying
all non-dotfile internal entries so optional deps resolve correctly.
- Use content fingerprint (packageRoot + sorted entry names from both
dirs) in .gsd-merged marker so pnpm add/remove triggers rebuild
- Restrict overlay loop to @gsd* scopes only, preventing accidental
shadowing of hoisted deps with internal versions
- Guard marker write behind linkedCount > 0 to avoid stamping success
on a broken/empty merged directory
- Log warnings when readdirSync fails on hoisted/internal roots
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pnpm's virtual-store layout doesn't hoist @gsd/* workspace scopes to
the parent node_modules, so the simple symlink-to-hoisted approach from
the original fix (#3529) left workspace packages unresolvable.
Detect when workspace scopes are missing from the hoisted root and
create a real node_modules directory with symlinks from both the hoisted
root (external deps) and internal root (workspace packages). A .gsd-merged
marker file skips rebuild on subsequent startups.
Restores behavioral tests deleted in the original PR and adds unit tests
for the pnpm merge path and scope detection logic.
Reported-by: @moekify
Fixes: #3564 (comment)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover the composable helpers extracted from deriveStateFromDb:
reconcileDiskToDb, buildCompletenessSet, buildRegistryAndFindActive,
handleNoActiveMilestone, resolveSliceDependencies, reconcileSliceTasks,
detectBlockers, checkReplanTrigger, checkInterruptedWork, and queue
order sorting.
Extracts the monolithic deriveStateFromDb function into distinct,
composable helper functions (reconcileDiskToDb, resolveSliceDependencies,
detectBlockers, etc.) inside state.ts.
Resolves technical debt identified during the code quality audit by
drastically reducing cyclomatic complexity while preserving the exact
type signature and logical behavior.
Also removes duplicate disk->DB reconciliation that could overwrite
milestone statuses.
Implements file-level advisory locking via proper-lockfile to ensure
atomic read-modify-write sequences in:
- compactMilestoneEvents (event-log.jsonl)
- custom-workflow-engine reconcile (GRAPH.yaml)
Fixes silent data loss when concurrent auto-mode or dashboard
sessions overlap in these operations.
This fixes an issue where MCP workflow tools (e.g. gsd_plan_slice) would return error details in their JSON response, but without setting the 'isError: true' flag at the top level of the tool response payload. This caused MCP clients (like Claude Code) to interpret failed validations (like empty tasks arrays) as successes and get stuck in infinite validation failure loops.
The depth verification gate and multi-milestone readiness gate
unconditionally referenced ask_user_questions. On transports where
structuredQuestionsAvailable is false, this would trap the flow in
re-ask loops.
Add {{structuredQuestionsAvailable}} conditionals to:
- Depth verification (true: ask_user_questions, false: plain text)
- Multi-milestone Phase 3 readiness gate
- Per-milestone technical assumption verification
The discuss.md prompt (used for /gsd new project creation) only asked
"What's the vision?" once and relied on LLM judgment for follow-ups.
This led to the agent asking a single question then jumping straight
to planning without gathering enough context.
Add explicit Question Rounds section with:
- 1-3 questions per round structure
- Conditional ask_user_questions vs plain text support
- Incremental persistence (CONTEXT-DRAFT save every 2 rounds)
- Depth-matching rule (1-2 rounds for simple, 4+ for large visions)
- Round cadence that drives toward the depth enforcement checklist
Thread structuredQuestionsAvailable through buildDiscussPrompt() and
prepareAndBuildDiscussPrompt() so the template variable resolves
correctly at runtime.
Closes#3976