Commit graph

1758 commits

Author SHA1 Message Date
Derek Pearson
80a09a3503 fix: add activeEngineId and activeRunDir to PausedSessionMetadata interface 2026-03-22 12:27:13 -04:00
Derek Pearson
7c7e800609 Merge branch 'main' into fix/stale-interrupted-session-resume 2026-03-22 12:26:54 -04:00
Derek Pearson
97e66f8aeb Merge branch 'main' of https://github.com/gsd-build/gsd-2 2026-03-22 12:20:58 -04:00
Lex Christopherson
e35bc2fe15 fix(tests): wrap rmSync cleanup in try/catch for Windows EPERM
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>
2026-03-22 10:12:42 -06:00
Lex Christopherson
6923ddd519 fix(tests): add maxRetries to rmSync cleanup for Windows EPERM compatibility
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>
2026-03-22 10:03:46 -06:00
Copilot
21b2b6c795 fix: recursive key sorting in tool-call loop guard hash function (#1962)
* 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>
2026-03-22 09:54:03 -06:00
Lex Christopherson
17a2f55edb fix: use path.sep for cross-platform path traversal guards and test assertions
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>
2026-03-22 09:39:21 -06:00
Lex Christopherson
97241ea19c fix(tests): use cross-platform path split in run-manager timestamp test
The test split on "/" which fails on Windows where paths use "\".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 09:31:08 -06:00
TÂCHES
2aa01b8a60 feat(gsd): declarative workflow engine — YAML-defined workflows through the auto-loop (#2024)
* docs(S01): add slice plan

* feat(S01/T01): Created four engine abstraction layer files: engine-type…

- src/resources/extensions/gsd/engine-types.ts
- src/resources/extensions/gsd/workflow-engine.ts
- src/resources/extensions/gsd/execution-policy.ts
- src/resources/extensions/gsd/engine-resolver.ts

* test(S01/T02): Added activeEngineId to AutoSession lifecycle (property,…

- src/resources/extensions/gsd/auto/session.ts
- src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts

* chore(M001/S02): auto-commit after research-slice

* docs(S02): add slice plan

* feat(S02/T01): Created DevWorkflowEngine and DevExecutionPolicy classes…

- src/resources/extensions/gsd/dev-workflow-engine.ts
- src/resources/extensions/gsd/dev-execution-policy.ts
- src/resources/extensions/gsd/engine-resolver.ts
- src/resources/extensions/gsd/auto.ts

* test(S02/T02): Added 18 contract tests for dev engine wrapper and updat…

- src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts
- src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts

* docs(S03): add slice plan

* chore(S03/T01): Added definition-loader.ts with V1 YAML schema validati…

- src/resources/extensions/gsd/definition-loader.ts
- src/resources/extensions/gsd/tests/definition-loader.test.ts

* feat(S03/T02): Added graph.ts with YAML I/O, DAG dependency queries, im…

- src/resources/extensions/gsd/graph.ts
- src/resources/extensions/gsd/tests/graph-operations.test.ts

* docs(S04): add slice plan

* test(S04/T01): Created run-manager with createRun/listRuns, CustomWorkf…

- src/resources/extensions/gsd/run-manager.ts
- src/resources/extensions/gsd/custom-workflow-engine.ts
- src/resources/extensions/gsd/custom-execution-policy.ts
- src/resources/extensions/gsd/tests/run-manager.test.ts
- src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts

* feat(S04/T02): Extended engine-resolver with custom engine branch, adde…

- src/resources/extensions/gsd/engine-resolver.ts
- src/resources/extensions/gsd/auto/session.ts
- src/resources/extensions/gsd/auto.ts
- src/resources/extensions/gsd/auto-dashboard.ts

* test(S04/T03): Added polymorphic custom engine dispatch path to autoLoo…

- src/resources/extensions/gsd/auto/loop.ts
- src/resources/extensions/gsd/auto/phases.ts
- src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts

* docs(S05): add slice plan

* feat(S05/T01): Created custom-verification.ts with four policy handlers…

- src/resources/extensions/gsd/custom-verification.ts
- src/resources/extensions/gsd/custom-execution-policy.ts
- src/resources/extensions/gsd/engine-resolver.ts
- src/resources/extensions/gsd/tests/custom-verification.test.ts

* feat(S05/T02): Created context-injector.ts with injectContext() that re…

- src/resources/extensions/gsd/context-injector.ts
- src/resources/extensions/gsd/custom-workflow-engine.ts
- src/resources/extensions/gsd/tests/context-injector.test.ts

* docs(S06): add slice plan

* test(S06/T01): Wired expandIteration() into resolveDispatch() with DEFI…

- src/resources/extensions/gsd/custom-workflow-engine.ts
- src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts
- src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts

* docs(S07): add slice plan

* feat(S07/T01): Added six `/gsd workflow` subcommands (new, run, list, v…

- src/resources/extensions/gsd/commands/handlers/workflow.ts
- src/resources/extensions/gsd/commands/catalog.ts
- src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts

* fix(S07/T02): Added updateProgressWidget call in custom engine path and…

- src/resources/extensions/gsd/auto/loop.ts
- src/resources/extensions/gsd/dashboard-overlay.ts
- src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts

* docs(S08): add slice plan

* docs(S08/T01): Created 7-file router-pattern skill for conversational Y…

- src/resources/skills/create-workflow/SKILL.md
- src/resources/skills/create-workflow/workflows/create-from-scratch.md
- src/resources/skills/create-workflow/workflows/create-from-template.md
- src/resources/skills/create-workflow/references/yaml-schema-v1.md
- src/resources/skills/create-workflow/references/verification-policies.md
- src/resources/skills/create-workflow/references/feature-patterns.md
- src/resources/skills/create-workflow/templates/workflow-definition.yaml

* test(S08/T02): Created 3 example workflow definitions (blog-post-pipeli…

- src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml
- src/resources/skills/create-workflow/templates/code-audit.yaml
- src/resources/skills/create-workflow/templates/release-checklist.yaml
- src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts

* docs(S09): add slice plan

* test(S09/T01): Comprehensive e2e integration test proving the full work…

- src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts

* chore: remove .gsd/ artifacts from tracking (already in .gitignore)

* fix(skills): resolve broken cross-references in create-workflow workflow files

Paths in workflows/ referenced references/ as siblings, but they need
../references/ since they're resolved relative to the workflows/ directory.

* fix: resolve typecheck failures — .ts→.js imports, MapIterator.some(), LoadedGSDPreferences unwrap, constructor args

- Convert .ts import extensions to .js in source files to match codebase
  convention (tests keep .ts since tsconfig.extensions allows it)
- Use [...idCounts.values()].some() instead of MapIterator.some()
- Unwrap LoadedGSDPreferences.preferences for DispatchContext.prefs
- Pass runDir to CustomExecutionPolicy constructor in tests

* fix: add codeFilesChanged to mergeMilestoneToMain mock (synced with main)

* fix(tests): write DEFINITION.yaml in integration tests, fix error message assertion

Root cause: S06 (iterate) added DEFINITION.yaml reading to
resolveDispatch(), but S04's integration tests only wrote GRAPH.yaml.
The missing file threw ENOENT, swallowed by the blanket catch, causing
steps to stay 'pending' silently.

Fixes:
- custom-engine-loop-integration: write DEFINITION.yaml in all 5 tests
- custom-workflow-engine: verify test creates temp dir with definition
- dev-engine-wrapper: update error regex — resolver validates activeRunDir
  before engine ID, so 'Unknown engine' is never reached

* fix: address 13 audit findings from self-review of workflow engine PR

Critical:
- Fix verify-before-reconcile ordering — verify step output before
  marking complete in GRAPH.yaml, so failed verification triggers retry
- Fix GSD_ENGINE_BYPASS kill switch — check env var in autoLoop before
  entering custom engine block instead of throwing from resolveEngine
- Add shell-command injection guard with suspicious pattern detection

High:
- Add ReDoS timeout guard (5s) for iterate regex patterns
- Centralize DEFINITION.yaml parsing into readFrozenDefinition() with
  schema: "core" restriction, eliminating 3 independent parse+cast sites
- Persist activeEngineId/activeRunDir in paused-session.json and restore
  on resume so custom workflows survive /exit
- Clean up engine state on startAuto failure in workflow run handler

Medium:
- Coerce params values to strings in definition-loader (YAML numbers/bools)
- Add path traversal guard (resolve + startsWith) in context-injector
  and custom-verification content-heuristic
- Use function replacer in expandIteration to prevent $ escaping bugs

Low:
- Fix skill docs CLI syntax (remove --param prefix)
- Use resolveProjectRoot instead of process.cwd() in catalog completions
- Rename isHookUnit → skipArtifactVerification for clarity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 08:51:47 -06:00
Matt Haynes
28e3c2e72c fix: prevent SIGTSTP crash on Windows (#2018) 2026-03-22 06:47:07 -06:00
Derek Pearson
3c9c6817dc Merge branch 'main' of https://github.com/gsd-build/gsd-2 2026-03-22 04:22:01 -04:00
Iouri Goussev
642c0f5a9e test: fix Assertion Roulette, Eager Test, and contract test regressions (#1938)
* 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>
2026-03-21 21:24:15 -06:00
djimenez18
32bbf07d0b Fix command for browser-based web interface (#1932)
Corrected the command for running GSD in the browser from 'pi --web' to 'gsd --web'.
2026-03-21 21:23:45 -06:00
Lex Christopherson
61e473b32c fix: add missing codeFilesChanged to journal integration test mock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 21:12:38 -06:00
TÂCHES
d40ed851a2 fix(repo-identity): use native realpath on Windows to resolve 8.3 short paths (#1960)
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>
2026-03-21 21:08:58 -06:00
Tom Boucher
09f3a5f970 fix(doctor): gate roadmap checkbox on summary existing on disk, not issue detection (#1915)
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>
2026-03-21 21:06:53 -06:00
Tom Boucher
53d7350e0d fix: warn when milestone merge contains only metadata and no code (#1906) (#1927)
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>
2026-03-21 21:05:56 -06:00
TÂCHES
79e49aa413 fix(worktree): resolve 8.3 short paths and use shell mode for .bat hooks on Windows (#1956)
Resolve Windows 8.3 short paths (RUNNER~1 → runneradmin) via realpathSync.native()
and use shell mode for .bat/.cmd files in worktree post-create hooks. Fixes
pre-existing windows-portability CI failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 20:52:30 -06:00
Derek Pearson
c81e982c8c Merge branch 'main' of https://github.com/gsd-build/gsd-2 2026-03-21 22:16:41 -04:00
Derek Pearson
71c202c792 Merge remote-tracking branch 'upstream/main' into fix/stale-interrupted-session-resume
# Conflicts:
#	src/resources/extensions/gsd/auto.ts
2026-03-21 22:09:45 -04:00
Derek Pearson
bd4da09510 Merge remote-tracking branch 'origin/main' into fix/stale-interrupted-session-resume 2026-03-21 22:04:19 -04:00
Derek Pearson
3b9b109a4e fix(gsd): address QA round 4
- Add unitType/unitId round-trip test for paused-session metadata.
- Add backward-compatibility test for legacy metadata without
  unitType/unitId fields.
2026-03-21 21:45:44 -04:00
Derek Pearson
690bcbd79c fix(gsd): address QA round 3
- Set resourceVersionOnStart on paused-session resume so resource
  staleness detection works for resumed sessions.
- Re-register setLevelChangeCallback on resume so health-level
  transition notifications fire after process restart.
- Persist unitType/unitId in paused-session metadata and restore them
  on resume so recovery synthesis framing text shows the actual unit
  instead of "unknown"/"unknown".
- Check worktreePath existence before showing "(worktree)" in resume
  notification to avoid misleading the user when the worktree was
  already torn down.
- Fix showDiscuss skip_milestone to use step:false (matching
  discuss_draft and discuss_fresh) instead of hardcoded step:true.
2026-03-21 21:27:44 -04:00
Derek Pearson
364cc7dcbc fix(gsd): address QA round 2
- Initialize autoStartTime, originalModelId, originalModelProvider, and
  autoModeStartModel on file-based paused-session resume path so
  dashboard elapsed time, model restore on stop, and error-recovery
  model fallback all work correctly.
- Validate worktreePath existence before using it as assessment base;
  fall back to project root when the worktree has been torn down.
- Add tests for paused-session-only (no lock) with resumable disk state
  and for worktreePath fallback when the directory no longer exists.
2026-03-21 21:11:11 -04:00
Derek Pearson
9e357f8a85 fix(gsd): address QA round 1
- Re-derive state from project root after stale/recoverable cleanup in
  guided flow; the assessment may have derived state from a worktree
  path that was cleaned up, leading to stale state being used downstream.
- Add test for the "none" classification path (clean startup with no
  lock and no paused session) to close coverage gap.
2026-03-21 20:57:21 -04:00
Derek Pearson
d3db0cb411 fix(gsd): address review feedback from trek-e
- Remove redundant `void verboseMode` suppression (parameter is used at line 364)
- Simplify hasStrongRecoverySignal: `hasResumableDiskState || recoveryToolCallCount > 0`
  (the previous expression had a redundant third disjunct)
- Remove extra blank line in auto.ts between stepMode assignment and lock-clear block
2026-03-21 20:48:12 -04:00
TÂCHES
60885610ac feat(gsd): unified rule registry, event journal, journal query tool, and tool naming convention (#1928)
Unify dispatch rules and hooks into a flat rule registry, add structured event journal with causal tracing, expose journal query as an LLM tool, and adopt gsd_concept_action tool naming.

- RuleRegistry class absorbs dispatch rules + hooks into UnifiedRule objects with common when/where/then shape
- post-unit-hooks.ts refactored from 524 lines → 90-line thin facade delegating to the registry
- Event journal emits structured JSONL events with per-iteration flowId grouping and causedBy chains
- gsd_journal_query LLM-callable tool for AI self-debugging of autonomous runs
- 4 DB tools renamed to gsd_concept_action pattern with backward-compatible aliases
- 164 new tests, zero regressions

Closes #1763, closes #1764, closes #1766

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:47:41 -06:00
Derek Pearson
71f14269a5 fix(gsd): assess recovery from paused worktree state
Use paused-session worktree metadata when deriving interrupted-session state so
worktree isolation decisions follow the authoritative disk state without
changing /gsd discuss auto handoff semantics.
2026-03-21 20:36:03 -04:00
Jeremy McSpadden
bdd1e765f5 feat(ci): PR risk checker — classify changed files by system and surface risk level (#1930) 2026-03-21 18:12:01 -06:00
Derek Pearson
3bb3ca8020 fix(gsd): satisfy extension typecheck for interrupted recovery
Remove a dead running-state comparison after the early return in startAuto so
extension typechecking passes alongside the interrupted-session regressions.
2026-03-21 20:08:05 -04:00
Derek Pearson
11e126a1a6 fix(gsd): restore hook dispatch export and guided flow imports
Remove a duplicate guided-flow import that broke module loading and restore the
public dispatchHookUnit export so /gsd run-hook continues to work.
2026-03-21 19:52:22 -04:00
Derek Pearson
7a384a45d1 fix(gsd): clear stale paused metadata in guided flow
Remove stale paused-session state when guided entry suppresses interrupted
resume prompts and keep discuss-triggered auto resumes in step mode.
2026-03-21 19:32:59 -04:00
Jeremy McSpadden
c1a35dd1b3 feat: ADR attribution — distinguish human vs agent vs collaborative decisions (#1830)
* feat: add made_by attribution field to decisions (human/agent/collaborative)

Add a 'made_by' field to the Decision type that tracks whether a
decision was made by the human, the agent, or collaboratively. This
enables ADR-style accountability — you can always tell who actually
made each call.

Schema:
- New DecisionMadeBy type: 'human' | 'agent' | 'collaborative'
- DB schema v3 → v4: ALTER TABLE decisions ADD COLUMN made_by
- Existing decisions default to 'agent' (backward compatible)
- DECISIONS.md gains a 'Made By' column
- Parser handles old 7-column format gracefully (defaults to 'agent')

Surfaces updated:
- gsd_save_decision tool accepts optional made_by parameter
- Markdown generator/parser round-trips the new column
- Prompt formatter shows attribution in LLM context
- Compact formatter includes made_by in pipe-separated output
- Worktree reconciliation includes made_by in conflict detection + merge

Tests: 476 assertions across 9 test suites, all passing.

* fix(gsd-db): resolve CI failures and address review findings

- Update memory-store.test.ts to expect schema version 4
- Recreate active_decisions view in v4 migration to pick up new made_by column
- Handle missing made_by column in older worktrees during reconciliation
- Optimize VALID_MADE_BY Set by moving it outside the parser loop

* fix(types): resolve missing made_by property errors in context-store and tests
2026-03-21 15:26:28 -06:00
Tom Boucher
f4db25b9b8 fix(web): persist auth token in sessionStorage to survive page refreshes (#1877)
Next.js 16 auto-detects web/proxy.ts as middleware, gating all /api/*
routes behind bearer token validation. The token was only cached in
memory (lost on page refresh) and extracted from the URL hash fragment
(cleared after first extraction). This caused 401 errors on page
refresh and broke the sendBeacon shutdown call which cannot set
custom headers.

Changes:
- Persist the auth token to sessionStorage after extracting from the
  URL fragment so it survives page refreshes within the same tab
- Fall back to sessionStorage when the URL hash is absent (refresh,
  bookmark without hash)
- Pass the auth token as a _token query parameter in the sendBeacon
  shutdown call since sendBeacon cannot set Authorization headers
- Add regression tests for token persistence, sessionStorage fallback,
  and sendBeacon authentication

Fixes #1851

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:25:27 -06:00
Iouri Goussev
e0011a897a test: replace shape-only assertions with value checks (#1875)
Several test files used assert.ok(Array.isArray(x)) or assert.ok(result)
patterns that verify structure/existence without checking actual values.
These pass even when the code returns wrong data.

- web-diagnostics-contract: Array.isArray() checks → deepEqual([], [])
  for fields constructed as empty; DoctorFixResult uses deepEqual(["fix1"])
  instead of Array.isArray + length; InstanceType<typeof GSDWorkspaceStore>
  for type assertions from dynamic import
- skill-lifecycle: computeStaleAvoidList → deepEqual(result, []) since
  nonexistent path must return empty
- blob-store: remove redundant assert.ok(retrieved) before deepEqual
- discovery-cache: assert.ok(entry) existence check → verify models[0].id

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:25:10 -06:00
Tom Boucher
747e29b9b4 fix: clean up SQUASH_MSG after squash-merge and guard worktree teardown against uncommitted changes (#1868)
Three changes to prevent data loss and persistent doctor errors in the
worktree merge-back lifecycle:

1. After nativeCommit in mergeMilestoneToMain, explicitly delete
   .git/SQUASH_MSG. The native libgit2 path and git commit -F - on
   some versions do not auto-remove it, causing doctor to report
   corrupt_merge_state on every run.

2. Before worktree removal (step 11), check for uncommitted changes
   and force a final auto-commit if dirty. This prevents code files
   written by task agents from being destroyed by git worktree remove.

3. Invalidate the nativeHasChanges 10-second cache before the
   post-unit auto-commit in auto-post-unit.ts. A stale false result
   causes autoCommit to skip staging entirely.

Fixes #1853

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:24:53 -06:00
Tom Boucher
99032444eb fix: populate RecoveryContext in hook unit supervision to prevent crash on stalled tool recovery (#1867)
The buildRecoveryContext callback in auto/phases.ts returned an empty
object instead of a valid RecoveryContext. When the idle watchdog detected
a stalled tool and called recoverTimedOutUnit, basePath was undefined,
causing join(undefined, ".gsd") to throw "The path argument must be of
type string. Received undefined". The error left the session permanently
hung because the unit promise was never resolved.

Fixes #1855

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:24:37 -06:00
Tom Boucher
f79de8a583 fix: resolve worktree path from git registry when .gsd/ symlink is shadowed (#1866)
When .gsd/ is a symlink to an external state directory, git registers
worktrees at the resolved (real) path. If syncStateToProjectRoot later
creates a real .gsd/ directory that shadows the symlink, worktreePath()
computes a local path that diverges from git's registered path. The
stale local directory passes existsSync but is not a git worktree, so
nativeWorktreeRemove fails silently.

removeWorktree now queries nativeWorktreeList to find the actual
git-registered path by matching on branch name before attempting
removal, falling back to the computed path if the lookup fails.

Fixes #1852

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:24:21 -06:00
Tom Boucher
2e04253c0b fix: resolve Node v24 web boot failure — ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING (#1864)
Node v24 forbids --experimental-strip-types for files under node_modules/.
When GSD is globally installed, all src/ files live under node_modules/gsd-pi/,
causing every subprocess worker to crash with ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING.

Bug 1: Extract resolveTypeStrippingFlag() into src/web/ts-subprocess-flags.ts.
When the package root is under node_modules/ and Node >= 22.7, the function
returns --experimental-transform-types (which handles node_modules paths).
All 15 service files and cli-entry.ts now call this function instead of
hardcoding --experimental-strip-types.

Bug 2: waitForBootReady() now tracks consecutive 5xx responses and aborts
after 3 in a row, including the response body in the error message.
Connection-level errors (transient during cold start) reset the counter.

Bug 3: The /api/boot route handler now wraps collectBootPayload() in
try/catch and returns { error: message } with status 500, matching the
error response pattern used by other API routes.

Fixes #1849

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:24:07 -06:00
Tom Boucher
7140ee0f53 feat: add /gsd fast command and gate service tier icon to supported models (#1848) (#1862)
Add `/gsd fast [on|off|flex|status]` command for toggling OpenAI service
tiers, with `supportsServiceTier()` gating so the status bar icon only
appears on models that actually support service tiers (gpt-5.4 variants).

Fixes #1848

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:23:54 -06:00
Iouri Goussev
0dd7176af6 test: replace shape-only assertions with value checks in preferences and routing-history (#1842)
- preferences.test.ts: hook config tests were testing Math.max/min and a
  locally-constructed Set (testing JS builtins, not production code); replaced
  with validatePreferences calls that exercise real clamping in
  preferences-validation.ts and action validation for pre_dispatch_hooks.
  assert.ok(prefs) existence checks replaced with assert.notEqual(prefs, null).
- routing-history.test.ts: removed assert.ok(history) and assert.ok(pattern)
  guards that only verified object existence; assertions now go directly to
  the values that matter.
2026-03-21 15:23:26 -06:00
Tom Boucher
b49cb8cbad fix(auto): broaden worktree health check to all ecosystems (#1860)
* fix(auto): use PROJECT_FILES from detection.ts in worktree health check

The worktree health check introduced in #1833 hard-coded package.json
and src/ as the only valid project markers, blocking auto-mode dispatch
for Rust (Cargo.toml), Go (go.mod), Python (pyproject.toml), and 14
other ecosystems. Replace the JS-centric heuristic with the shared
PROJECT_FILES array from detection.ts which already covers 17+
ecosystems.

Fixes #1843

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update test assertion to match new project files message

The health check now says "no recognized project files" instead of
"no package.json or src/" after broadening to PROJECT_FILES.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:22:34 -06:00
Tom Boucher
358dc1da6b fix(doctor): cascade slice uncheck when task_done_missing_summary unchecks tasks (#1850) (#1858)
When tasks are [x] done but no T##-SUMMARY.md exists, doctor unchecks
the tasks but left the slice [x] done in the roadmap. The state machine
skips done slices, so unchecked tasks never re-execute and doctor fires
again on every start — infinite loop.

After unchecking tasks via task_done_missing_summary, also uncheck the
slice in the roadmap so the state machine re-enters the executing phase.

Fixes #1850

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:22:23 -06:00
Tom Boucher
1ad1b5d061 fix: defend exit path against ESM module cache mismatch (#1854)
* fix: defend exit path against ESM module cache mismatch (#1839)

Wrap the stopAuto import/call in exit-command.ts with try/catch so
that a mid-session gsd-pi update (which causes stale ESM cache for
native-git-bridge.js exports) does not crash the /exit handler.
A warning is emitted instead. The user's work is already saved;
this path is cleanup only.

Fixes #1839

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use "warning" not "warn" for notify severity type

TS2345: "warn" is not in the severity union type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update test assertion to match "warning" severity

The source was corrected to "warning" (valid union type), test must match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:22:13 -06:00
Andrew
3f8d7921ca feat: add --host, --port, --allowed-origins flags for web mode (#1847) (#1873)
Wire CLI flags through parseCliArgs → runWebCliBranch → launchWebMode
so users can bind to a custom host/port and whitelist CORS origins for
LAN/Tailscale access.

- Add webHost, webPort, webAllowedOrigins to CliFlags
- Parse --host, --port (validated 1-65535), --allowed-origins (csv)
- Forward into launchWebMode options
- Set GSD_WEB_ALLOWED_ORIGINS in subprocess env when provided
- Add allowedOrigins to WebModeLaunchOptions

Usage: gsd --web --host 0.0.0.0 --port 8080 --allowed-origins http://192.168.1.10:8080

Closes #1847
2026-03-21 15:22:01 -06:00
TÂCHES
8bed02c077 fix: escape parentheses in paths before bash shell-out, fix __extensionDir fallback (#1872)
Closes #1437
2026-03-21 15:14:40 -06:00
TÂCHES
77b220e9e5 fix: use PowerShell Start-Process for Windows browser launch, prevent URL wrapping (#1870)
Closes #1574
2026-03-21 15:12:24 -06:00
TÂCHES
0188b8eaa8 fix: clear stale unit state and restore CWD when step-wizard exits auto-loop (#1869)
Closes #1698
2026-03-21 15:12:16 -06:00
TÂCHES
05a535598d fix: prevent cross-project state leak in brand-new directories (#1639) (#1861)
When GSD is launched in a new empty directory that happens to be inside
an existing git repo (e.g. mkdir ~/Projects/newproject where ~/Projects
has a .git), repoIdentity() resolves to the parent repo's hash and
loads milestones from an unrelated project.

Add isInheritedRepo() to detect when basePath inherits a parent repo's
git root without having its own .gsd. When detected, git init creates
an independent repo so the directory gets a unique identity hash.
Legitimate subdirectory access (cd src/ inside an existing GSD project)
is preserved — the check only triggers when the parent repo has no .gsd.

Closes #1639

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 14:58:25 -06:00
Tom Boucher
7054e5dde6 fix: reconcile worktree HEAD with milestone branch ref before squash merge (#1846) (#1859)
When the worktree HEAD detaches and advances past the named milestone
branch, the branch ref becomes stale. The squash merge only captured
the stale ref, silently orphaning all commits between the branch ref
and the actual worktree HEAD.

Before the squash merge, compare the milestone branch ref with the
worktree's actual HEAD. If the branch ref is an ancestor of the
worktree HEAD, fast-forward the branch ref. If they have diverged,
throw a clear error instead of silently losing commits.

Guarded by worktreeCwd !== originalBasePath_ so non-worktree merge
paths (e.g. parallel-merge) are unaffected.

Fixes #1846
2026-03-21 14:58:16 -06:00