insertTask() used INSERT OR REPLACE which in SQLite does DELETE + INSERT,
zeroing planning columns (description, estimate, inputs, expected_output)
when callers like handleCompleteTask didn't pass them. Changed to
ON CONFLICT ... DO UPDATE SET with CASE/NULLIF preservation for planning
columns.
Removed post-unit migrateFromMarkdown hook that re-imported a lossy
markdown subset after every auto-mode unit, overwriting DB planning data.
Startup migration in auto-start.ts and dynamic-tools.ts remains.
Removed vestigial "MUST write file" prompt instructions that conflict with
the DB-backed tool workflow. Removed Steps section duplication in task plan
renderer that re-rendered description as garbled bullets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The observability validator checked for markdown headings (## Observability / Diagnostics,
## Observability Impact) that the DB-backed renderer never produces, causing false-positive
warnings on every dispatch. Removed entirely — the DB schema enforces structure at write time.
The worktree health check blocked execution in directories without recognized project files
(package.json, Cargo.toml, etc.), preventing greenfield projects from scaffolding. Downgraded
to a warning — .git check remains as the hard gate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Local tsconfig excludes src/resources/ but CI compiles everything.
Record<string, unknown> for params broke handler calls since handlers
expect typed params (validated at runtime). Keep params: any with
eslint-disable annotation, type all other executor params properly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tool executor lambdas now use proper types (string, Record<string, unknown>,
AbortSignal | undefined) instead of any for all parameters.
registerAlias toolDef param also properly typed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- github-sync/sync.ts: import parseRoadmap/parsePlan from parsers-legacy
- auto-worktree.ts: replace dangling roadmap.title with getMilestone() DB query
- markdown-renderer.ts: add explicit type annotations on lazy-loaded parser callbacks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
plan-task.ts was the only planning tool handler not wrapping its
insertTask/upsertTaskPlanning calls in a transaction(), risking partial
DB state if the upsert failed after insert. Matches the pattern used by
plan-slice, replan-slice, reassess-roadmap, and plan-milestone.
Also removes 80 .gsd/ working artifacts that were force-added despite
being in .gitignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When `gsd --web` exits uncleanly (terminal closed, crash), the spawned
server process survives as an orphan bound to port 3000. On re-launch,
the new server gets EADDRINUSE and the 3-minute boot-ready poll hangs.
Add `cleanupStaleInstance()` that checks the instance registry for a
previous entry matching the same cwd and kills its process before
reserving a port. This makes re-launches succeed immediately instead
of timing out after 180 seconds.
Fixes#1934
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On non-English systems (e.g. LANG=de_DE.UTF-8), git produces localized
stderr output. GSD's stderr.includes() guards are hardcoded to English
strings and never match, causing every git add with exclusions to throw
GSD_GIT_ERROR and merge failures to be misclassified.
- Add LC_ALL: "C" to GIT_NO_PROMPT_ENV in git-constants.ts
- Add env: GIT_NO_PROMPT_ENV to nativeMergeSquash fallback execFileSync
- Add regression tests for both fixes
Fixes#1997
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The forensics prompt suggested `gh issue create` but the agent's
system-level tool rules preferred the `github_issues` tool, which has
no repo parameter and always targets the user's current repository.
Add an explicit constraint forbidding `github_issues` and requiring
the `bash` tool with `gh issue create --repo gsd-build/gsd-2`.
Fixes#2067
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When .gsd is a symlink (external state projects), autoCommit silently
drops new milestone artifacts because:
1. nativeAddAllWithExclusions falls back to plain `git add -A` (symlink
pathspec rejection: "beyond a symbolic link")
2. `.gsd` is in .gitignore, so new .gsd/ files are invisible to git add
`git add -f` also fails through symlinks, so this fix uses
`git hash-object -w` + `git update-index --add --cacheinfo` to bypass
the symlink restriction entirely, staging each milestone artifact by
hashing its content and inserting the blob directly into the index.
Includes a reproduction test that creates a repo with .gsd as a symlink,
adds new files under .gsd/milestones/, and verifies they are staged.
Fixes#2104
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- auto-recovery, idle-recovery, validate-milestone tests: assert
gsd recover instead of gsd doctor in remediation steps
- derive-state-crossval test C: add task summary files so migration
consistency check doesn't downgrade tasks to pending
- md-importer: slice auto-upgrade now requires slice summary to exist
(all tasks done without slice summary = summarizing, not complete)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Pre-migration consistency check: migrateHierarchyToDb() validates
task done+summary agreement and auto-upgrades slice status when all
tasks are genuinely complete — prevents importing bad markdown state.
2. buildLoopRemediationSteps: all branches updated to reference
gsd undo-task, gsd reset-slice, and gsd recover instead of manual
checkbox editing and gsd doctor reconciliation.
3. DB/disk render split: complete-task and complete-slice handlers
roll back DB status if disk render fails, keeping deriveState()
and verifyExpectedArtifact() consistent.
4. Pre-upgrade worktree reconciliation: syncWorktreeStateBack() detects
local gsd.db copies from pre-WAL worktrees and reconciles hierarchy
data into the project root DB before file sync.
5. Dead COMPLETION_TRANSITION_CODES removed: empty Set export deleted
from doctor-types.ts, dead guard in doctor.ts shouldFix() removed.
6. (Merged with fix 2 — all branches updated.)
7. Stale state.ts comment replaced: removed misleading "intentionally
do NOT load from SQLite DB" note, replaced with accurate description
of filesystem fallback role.
Test fixes: schema version assertions (6→7), tool count (10→12),
doctor behavior assertions updated to match new state-transition model.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The milestone type only accepts { id, title?, status?, depends_on?[] } —
`seq` is not a valid property and caused TS2353 typecheck failures in CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
maxRetries doesn't help with EPERM (only EBUSY/EMFILE/ENFILE).
Windows holds directory handles after close, making rmSync fail
in afterEach. Swallowing the error is safe — OS cleans temp dirs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows holds file handles briefly after close, causing EPERM on
rmSync in afterEach cleanup. Node's maxRetries/retryDelay options
handle this by retrying after a short delay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Initial plan
* fix: use recursive-sort replacer in hashToolCall to preserve nested properties
The array replacer in JSON.stringify acted as a property-name whitelist at
every nesting level, stripping all nested object properties and causing
structurally different tool calls to produce identical hashes. This led to
false-positive loop detection for tools with nested/array arguments like
ask_user_questions, plan_clarify, browser_batch, etc.
Replace with a function replacer that recursively sorts object keys while
preserving array order and primitive values.
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/c10384bc-a2f9-46b8-8380-43ea451ed39d
* fix: add missing codeFilesChanged to mergeMilestoneToMain mock in journal-integration test
Pre-existing typecheck failure: the mock was missing the codeFilesChanged
property added to the mergeMilestoneToMain return type.
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/debb019f-2fc8-4c76-b809-ecfe48993eff
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Path traversal guards used hardcoded "/" separator which fails on Windows
where resolve() produces backslash paths. Test assertions also used
forward-slash path fragments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add assertion messages to fix Assertion Roulette in GSD tests
Add descriptive messages to multi-assertion tests where a bare failure
output ("expected true, got false") wouldn't identify which assertion
broke. Affected tests: auto-secrets-gate, search-tavily, search-provider-
command, tavily-helpers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: fix Eager Test smell in captures and worktree-manager tests
- Split captures: loadPendingCaptures test — extracted loadAllCaptures
assertion into its own focused test
- Refactor worktree-manager: replace monolithic main() script with 11
isolated test() calls, each with its own repo setup via helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: add assertion messages to remaining test files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: fix contract test gate, dynamic roots, and shared fetch helpers
- Fix reject-notice sub-test gated on outcome.kind (actual) instead of
expectedKind (map value) in web-command-parity-contract.test.ts
- Restore dynamic loop over registered non-gsd passthrough roots with
an explicit count assertion so new registrations fail loudly
- Extract normalizeHeaders/parseJsonBody to src/tests/fetch-test-helpers.ts
and import in both search-tavily and llm-context-tavily tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Use realpathSync.native() on Windows in canonicalizeExistingPath to resolve
8.3 short names (RUNNER~1 → runneradmin). Fixes isInheritedRepo path
comparison failures on Windows CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The roadmap-done condition checked whether the missing-summary issue was
detected in the issues array, but at fixLevel="task" the summary is
detected and never fixed (deferred via COMPLETION_TRANSITION_CODES).
This caused the roadmap checkbox to be marked without the summary on
disk, making deriveState() skip the summarizing phase and hard-stop at
validating-milestone.
Replace the issues.some() fallback with an existsSync re-check so the
roadmap is only marked when the summary actually exists — either
pre-existing or created earlier in the same doctor run.
Fixes#1910
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mergeMilestoneToMain now detects when the squash-merge commit contains
only .gsd/ metadata files and no actual code changes. The worktree
resolver surfaces a clear warning so users know the milestone summary
may describe planned work that was never implemented.
The complete-milestone prompt now requires the LLM to verify code
changes exist on the branch before declaring verification passed.
Fixes#1906
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>