Commit graph

2484 commits

Author SHA1 Message Date
Jeremy
ee87924636 fix(gsd): gate steer worktree routing on active session, fix messaging
Address adversarial review findings:

1. [high] Override routing now requires an active auto-mode session
   (in-process or remote via checkRemoteAutoSession) before writing
   to a worktree path. Previously, any existing worktree directory
   would receive the override even if no agent was running there —
   a leftover worktree from a previous session would silently eat
   the override.

2. [medium] Success messages now report the actual resolved override
   location (worktree vs project root .gsd/OVERRIDES.md) so operators
   know exactly where to look during recovery or manual rewrite.

Additional tests cover: inactive worktree fallback, double-gate
(autoRunning + valid .git), and getAutoWorktreePath null on missing .git.

Closes #3476
2026-04-04 15:37:13 -05:00
Jeremy
bd863e3e21 fix(gsd): resolve steer overrides to worktree path when worktree is active
handleSteer used process.cwd() as the base path for appendOverride,
which writes to project/.gsd/OVERRIDES.md. When auto-mode runs in a
worktree, it reads from worktree/.gsd/ — so overrides written from a
second terminal were never seen by the agent.

Now checks for an active worktree via getAutoWorktreePath and writes
the override there when one exists, falling back to the project root
when no worktree is active.

Closes #3476
2026-04-04 15:25:26 -05:00
Jeremy McSpadden
2acf5292d0 Merge pull request #3508 from jeremymcs/fix/audit-log-hardening
fix(gsd): harden audit log persistence and demote probe warnings
2026-04-04 15:12:14 -05:00
Jeremy McSpadden
1c4219ee2e Merge pull request #3510 from jeremymcs/feat/codebase-map-enhancements
feat(gsd): enhance /gsd codebase with preferences, --collapse-threshold, and auto-init
2026-04-04 15:11:36 -05:00
Jeremy
4ddb9ca8a5 fix(gsd): add codebase validation in validatePreferences so preferences are not silently dropped
The codebase preferences block was accepted as a known key but never
validated or assigned in validatePreferences(), causing all user-configured
codebase defaults to be silently discarded. Adds validation for
exclude_patterns (string[]), max_files (positive int), and collapse_threshold
(positive int) with unknown-key warnings and 4 new tests.
2026-04-04 15:01:15 -05:00
Jeremy
bbe67da02c feat(gsd): enhance /gsd codebase with preferences, --collapse-threshold, and auto-init
Add configurable codebase map options via preferences.md (exclude_patterns,
max_files, collapse_threshold), expose --collapse-threshold as a CLI flag,
and auto-generate CODEBASE.md during project init for instant agent orientation.

Closes #3509
2026-04-04 14:51:51 -05:00
Jeremy
a290708573 fix(test): update db-path-worktree-symlink test for simplified diagnostic logging
The ensureDbOpen catch block now logs via logWarning with error message
instead of structured diagnostic object. Update source-level assertion
to match the new pattern.
2026-04-04 14:33:12 -05:00
Jeremy
6eb532bf9d fix(gsd): update tests for errors-only audit persistence, fix empty catch blocks
Update existing workflow-logger tests to use logError for audit
persistence assertions (warnings are now ephemeral). Add void
expression to empty catch blocks in detectMainBranch to satisfy
the no-empty-catch CI check.
2026-04-04 14:29:00 -05:00
Jeremy
10cd4a12c5 test(gsd): add workflow-logger audit persistence tests
Covers error-only persistence policy, warning ephemeral behavior,
message truncation, context field allowlist sanitization, and
mixed severity filtering.
2026-04-04 14:22:56 -05:00
Jeremy
2396ecf1db fix(gsd): harden audit log persistence — errors-only, sanitized, demote probe warnings
Only persist error-severity entries to audit-log.jsonl (warnings stay
ephemeral in stderr + buffer). Sanitize persisted entries with message
truncation and context field allowlisting. Demote expected main/master
branch probe failures to silent control flow. Remove JSON.stringify of
diagnostic objects embedding cwd/paths in warning messages.

Addresses Codex adversarial review findings on workflow-logger migration.
2026-04-04 14:19:36 -05:00
Jeremy McSpadden
104d103d14 Merge pull request #3501 from jeremymcs/fix/upgrade-kotlin-lsp
fix: upgrade Kotlin LSP to official Kotlin/kotlin-lsp
2026-04-04 14:04:40 -05:00
Jeremy McSpadden
7a1c6213a0 Merge pull request #3507 from jeremymcs/refactor/workflow-logger-migration
refactor(gsd): migrate all catch blocks to centralized workflow-logger
2026-04-04 14:04:26 -05:00
Jeremy McSpadden
1a21915572 Merge pull request #3505 from jeremymcs/pr-3496
fix(gsd): fail-closed stop guard, harden backtrack parsing, fix prompt params
2026-04-04 13:59:04 -05:00
Jeremy
64fe364fdb fix(gsd): address adversarial review findings on workflow-logger migration
workflow-events.ts: stop logging raw event line content to audit log —
log byte length only to avoid persisting potentially sensitive payload
fragments to .gsd/audit-log.jsonl.

parallel-orchestrator.ts: revert worker NDJSON parse failure to silent
drop — non-JSON lines (progress text, tool output) are expected in
worker stdout and logging each one creates I/O pressure and audit log
bloat in the parallel execution hot path.
2026-04-04 13:53:16 -05:00
Jeremy
3d6d72c04d refactor(gsd): migrate all catch blocks to centralized workflow-logger
Replace raw process.stderr.write(), console.error(), and empty catch
blocks across 50 GSD files with structured logWarning/logError calls
from the centralized workflow-logger system.

Add 13 new LogComponent types to cover all subsystems: recovery,
session, prompt, dashboard, timer, worktree, command, parallel, fs,
bootstrap, guided, registry, renderer.

Every migrated catch block now automatically:
- Shows in terminal (stderr) with component tag
- Gets buffered for auto-loop stuck-detection summary
- Persists to .gsd/audit-log.jsonl for post-mortem analysis

Update regression test to verify catch blocks use workflow-logger
instead of raw stderr/console, covering auto-mode files and all
explicitly migrated infrastructure files.

Closes #3506
Supersedes the approach in #3496
2026-04-04 13:42:55 -05:00
Jeremy
abe887de10 fix(gsd): fail-closed stop guard, harden backtrack parsing, fix prompt params
- Stop/backtrack guard now calls pauseAuto before marking captures executed,
  and returns break on any exception to prevent silently dropping user halt intent
- Backtrack target parsing excludes current milestone ID and rejects ambiguous
  multi-target strings instead of guessing first match
- Fixed gsd_skip_slice parameter names in rethink prompt (milestone_id → milestoneId)
2026-04-04 13:09:16 -05:00
Tibsfox
4f896cc561 fix(gsd): add diagnostic logging to empty catch blocks in auto-mode
Auto-mode has empty catch blocks across 11 files that silently
swallow errors. When these operations fail (DB writes, git commands,
file sync, worktree operations), the error is lost and downstream
systems see stale or inconsistent state — leading to stuck loops,
phantom milestones, and silent data loss.

Replace every empty catch with a process.stderr.write() call that
logs the operation context and error message. Format:

  gsd [filename]: <operation> failed: <error.message>

For catches already annotated with /* non-fatal */ or /* best-effort */
comments, the logging is added alongside the annotation to preserve
the original intent while making failures observable.

Adds a regression test that scans all auto-mode source files and
asserts no empty catch blocks remain.

Files modified (11):
  auto-worktree.ts, auto.ts, auto-recovery.ts, auto-prompts.ts,
  auto-dashboard.ts, auto-start.ts, auto-timers.ts, auto-post-unit.ts,
  auto-dispatch.ts, auto-unit-closeout.ts, auto/phases.ts

No behavioral changes — only diagnostic output added.

Addresses #3348, addresses #3345
2026-04-04 10:38:54 -07:00
Jeremy McSpadden
d07f573799 Merge pull request #3499 from jeremymcs/test/state-machine-edge-cases
test(gsd): fill state machine E2E verification gaps
2026-04-04 11:57:17 -05:00
Jeremy
4df55a51c8 fix(lsp): add legacy alias for renamed kotlin-language-server key
Users with existing lsp.json overrides referencing the old
"kotlin-language-server" key would silently lose their Kotlin
LSP config after the rename to "kotlin-lsp". LEGACY_ALIASES
map remaps old keys during mergeServers() so overrides still
merge correctly.
2026-04-04 11:45:58 -05:00
Jeremy McSpadden
7365b85b4a Merge pull request #3503 from jeremymcs/fix/interview-notes-loop
fix: break infinite notes loop on "None of the above"
2026-04-04 11:37:46 -05:00
Jeremy
1e31ca4b29 ci: trigger CI re-run 2026-04-04 11:23:59 -05:00
Jeremy
e0884375e6 test: add regression test for interview-ui notes loop (#3502)
Exercises the goNextOrSubmit → notes auto-open path to ensure:
- Enter after typing a note advances instead of looping
- Empty notes still trigger the auto-open
- Normal option selection is unaffected

Fixes #3502
2026-04-04 11:22:15 -05:00
Jeremy
f153745c4f fix: break infinite notes loop when selecting "None of the above"
goNextOrSubmit() unconditionally reopened the notes field whenever the
cursor sat on the "None of the above" slot, even after the user had
already typed a note and pressed Enter. This trapped users in an
endless loop where Enter always bounced back to notes mode.

Add a `!states[currentIdx].notes` guard so the auto-open only fires
when notes are still empty.

Fixes #3502
2026-04-04 11:12:17 -05:00
Jeremy
851bb0bebe fix(pi-coding-agent): upgrade Kotlin LSP to official Kotlin/kotlin-lsp
Closes #3493
2026-04-04 10:45:15 -05:00
Jeremy
3f9fa9351f fix(test): use correct RequirementCounts type fields in edge case tests
Replace non-existent `invalidated` field with the correct type fields
(`outOfScope`, `blocked`, `total`) to pass typecheck.
2026-04-04 10:25:00 -05:00
Jeremy
181243a933 chore: init gsd 2026-04-04 10:00:43 -05:00
Jeremy
62cc474002 test(gsd): fill state machine E2E verification gaps (#3498)
Add 102 integration tests across two new files covering state machine
edge cases, runtime failures, and boundary conditions not exercised
by the existing live-validation suite.

Closes #3498
2026-04-04 10:00:07 -05:00
github-actions[bot]
6aaa244742 release: v2.60.0 2026-04-04 14:46:08 +00:00
Tom Boucher
a061e3c276 feat: stop/backtrack capture classifications for milestone regression (#3488)
* feat: add stop/backtrack capture classifications for milestone regression (#3487)

Adds 4-layer methodology for halting auto-mode and backtracking to
previous milestones when captures indicate the user wants to stop or
that a milestone missed critical features:

1. Type layer: "stop" and "backtrack" classification types in captures.ts
2. Guard layer: pre-dispatch stop check in runGuards() pauses auto-mode
   before the next unit dispatches
3. Resolution layer: executeBacktrack() writes BACKTRACK-TRIGGER.md and
   milestone regression markers for state machine detection
4. Protection layer: revertExecutorResolvedCaptures() detects and reverts
   captures silenced by non-triage agents (resolved without classification)

Also adds fast-path stop detection in auto-post-unit.ts that pattern-matches
pending capture text for stop keywords without waiting for triage.

Closes #3487

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add slice-level skip with gsd_skip_slice tool (#3477)

Adds "skipped" as a closed status alongside "complete" and "done":

- status-guards.ts: isClosedStatus() recognizes "skipped"
- state.ts: isStatusDone() recognizes "skipped"
- gsd-db.ts: getActiveSliceFromDb() skips slices with status "skipped"
- db-tools.ts: new gsd_skip_slice tool for rethink and manual use
- rethink.md: added "Skip a slice" operation to rethink prompt
- rethink.ts: buildRethinkData shows skipped slice counts

Skipped slices satisfy dependencies for downstream slices, allowing
auto-mode to advance past them. Slice data is preserved for reference.

Relates to #3477

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve 4 issues found in adversarial review of PR #3488

1. triage-ui.ts: Restore stop/backtrack entries in CLASSIFICATION_LABELS
   and ALL_CLASSIFICATIONS — the Record<Classification, ...> type requires
   all union members, and runtime lookups would crash on stop/backtrack.
   Also auto-confirm stop/backtrack in the triage confirmation flow
   (matching the triage-captures.md prompt directive).

2. triage-resolution.ts: Replace require("node:fs") in clearBacktrackTrigger
   with ESM import of unlinkSync — consistent with the rest of the codebase.

3. auto-post-unit.ts: Anchor STOP_PATTERN regex to start-of-string (^) to
   prevent false positives on captures like "add a pause button" or "stop
   the timer from re-rendering" which are feature descriptions, not halt
   directives.

4. status-guards.test.ts: Add missing test case for isClosedStatus("skipped")
   to cover the new status value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update tool-naming test count for gsd_skip_slice

The new gsd_skip_slice tool (no alias) brings the total from 29 to 30.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 01:40:33 -04:00
Tom Boucher
7d5bf63b2d feat: GSD context optimization with model routing and context masking
* docs: add context optimization design spec, implementation plan, and pi-layer research

- Spec: 6-change design for GSD extension context optimization
- Plan: 9-task TDD implementation plan with exact file paths and code
- Pi-layer doc: 10 infrastructure opportunities (research only, not planned)

Part of #3171, #3406, #3452, #3433.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(context): add observation masking for auto-mode sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(context): add phase handoff anchors for auto-mode

Introduces PhaseAnchor read/write utilities so downstream agents can
inherit decisions, blockers, and intent written at phase boundaries
without re-inferring from conversation history.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(context): add capability-aware model routing and context management preferences

Implement ADR-004 Phase 2 capability scoring with 7-dimension model
profiles, task requirement vectors, and weighted scoring. Add
ContextManagementConfig preferences for observation masking thresholds.
Wire capability scoring into auto-model-selection dispatch path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(context): wire observation masking, phase anchors, and tool truncation

Register observation masker in before_provider_request hook to replace
old tool results with placeholders during auto-mode. Add tool result
truncation (configurable via context_management.tool_result_max_chars).
Inject phase handoff anchors into prompt builders so downstream phases
inherit decisions from research/planning. Write anchors after successful
phase completion. Update ADR-004 status to Implemented.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: remove internal planning artifacts from PR

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add capability routing, observation masking, and context management

Update dynamic-model-routing.md with capability-aware scoring section.
Update token-optimization.md with observation masking, tool truncation,
and phase handoff anchor documentation. Update configuration.md with
context_management preference block and capability_routing flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Merge branch 'main' into feat/gsd-context-optimization

* fix: add context_management to known keys and prevent tool truncation state corruption

- Add missing 'context_management' to KNOWN_PREFERENCE_KEYS set so users
  don't get spurious unknown-key warnings when configuring it.
- Replace in-place mutation of tool result content with immutable spread
  to prevent corrupting shared conversation message objects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add stop and backtrack to triage-ui classification labels

The Classification type gained stop and backtrack variants from main
but triage-ui.ts was not updated, causing a TypeScript build failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: context masker and tool truncation operate on correct pi-ai message format

The observation masker and tool result truncation in before_provider_request
were checking m.type === "toolResult" but the actual pi-ai payload uses
m.role === "toolResult" with content as TextContent[] arrays (not strings).
bashExecution messages are converted to {role:"user"} by convertToLlm before
the hook fires, so checking m.type === "bashExecution" was a no-op.

- Fix context-masker to match on role, handle array content, detect bash
  results by their "Ran `" prefix
- Fix register-hooks truncation to operate on role:"toolResult" with
  array content blocks
- Update tests to use correct pi-ai LLM payload format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 01:02:35 -04:00
Jeremy McSpadden
bb47f5a087 Merge pull request #2287 from jeremymcs/worktree-btw-implementation
feat: add /btw skill — ephemeral side questions from conversation context
2026-04-03 15:18:03 -05:00
Jeremy McSpadden
df93cdc43c Merge pull request #3432 from deseltrus/fix/slash-command-session-routing
fix: route non-builtin slash commands after TUI dispatch
2026-04-03 05:08:01 -05:00
github-actions[bot]
7589509156 release: v2.59.0 2026-04-03 06:18:06 +00:00
deseltrus
b7e0173e50 fix: route non-builtin slash commands after TUI dispatch
The TUI slash dispatcher started treating any unrecognized /command as handled before session.prompt() could resolve extension commands, prompt templates, or /skill:* inputs. That blocked valid non-builtin slash commands and also let /export swallow unrelated /export* prefixes.

Move unknown-command detection to the interactive entry points, allow only known builtins or session-resolved slash commands through, gate /skill:* on the skill-command setting, and tighten /export matching to exact command tokens.
2026-04-03 06:44:09 +02:00
Jeremy McSpadden
db4fa32854 Merge pull request #3426 from justinwyer/fix/configurable-security-overrides
fix(security): add configurable overrides for command allowlist and SSRF blocklist
2026-04-02 12:29:57 -05:00
Justin Wyer
95875c41c5 refactor(test): consolidate regression and override tests into #666 test files
Move regression tests and override tests from standalone files into
the existing test files introduced by PR #666:

- resolve-config-value.test.ts: add REGRESSION #666 describe block
  and setAllowedCommandPrefixes override tests
- url-utils.test.ts: add REGRESSION #666 describe block and
  setFetchAllowedUrls override tests
- Delete: regression-666.test.ts, resolve-config-value-override.test.ts,
  url-utils-override.test.ts

Same 59 tests, fewer files, tests live next to the code they test.
2026-04-02 14:06:19 +02:00
Justin Wyer
d5f581fe6b test: add regression tests for #666 (fails on main, passes on fix)
Two regression tests that prove the bug introduced by PR #666:

1. Non-default credential tool (sops) is silently blocked by the
   hardcoded SAFE_COMMAND_PREFIXES with no way to override.
2. Private IP URL is silently blocked by isBlockedUrl() with no
   way to allowlist.

Both tests use dynamic import to check for the override functions,
so they run cleanly on both main (where they fail) and this branch
(where they pass). Verified in a git worktree of main.
2026-04-02 14:03:34 +02:00
Justin Wyer
5ae78d8f8b docs: document command allowlist and fetch_page URL blocking
- custom-models.md: add Command Allowlist section under Value Resolution
  explaining the restriction, default list, and how to override via
  allowedCommandPrefixes setting or GSD_ALLOWED_COMMAND_PREFIXES env var

- configuration.md: add URL Blocking (fetch_page) section documenting
  what's blocked by default, why, and how to allowlist specific hosts
  via fetchAllowedUrls setting or GSD_FETCH_ALLOWED_URLS env var

- configuration.md: add both env vars to the Environment Variables table
2026-04-02 13:55:07 +02:00
Justin Wyer
71caa18552 fix(security): add configurable overrides for command allowlist and SSRF blocklist
PR #666 introduced hardcoded SAFE_COMMAND_PREFIXES and SSRF URL
blocklists with no override mechanism. Users with non-standard
credential tools (sops, doppler, age, infisical) or needing to fetch
from internal URLs (self-hosted docs, VPN services) were silently
blocked with no recourse.

Add two global-only settings (ignored in project-level settings.json
to preserve the security property against malicious repos):

- allowedCommandPrefixes: replaces the built-in command allowlist
- fetchAllowedUrls: exempts hostnames from SSRF blocking

Both also support env var overrides (GSD_ALLOWED_COMMAND_PREFIXES,
GSD_FETCH_ALLOWED_URLS) for CI/container environments. Env vars
take precedence over settings.json.

Security model: global-only keys are stripped from project settings
at load time via stripGlobalOnlyKeys(), applied at all three
assignment points for this.projectSettings. The merge function
stays untouched — no future caller can accidentally skip stripping.

15 new tests covering override behavior, cache invalidation,
allowlist exemptions, and global-only enforcement.
2026-04-02 13:45:05 +02:00
Jeremy McSpadden
46d5fa56af Merge pull request #2312 from jeremymcs/fix/tui-review
fix(tui): comprehensive TUI review — layout, flow, rendering, and state fixes
2026-04-01 16:38:31 -05:00
Jeremy
a3a2f2e3b3 test(tui): update provider-manager tests for confirmation-based removal
Tests now match the new hasAuth guard and double-press r confirmation
flow introduced in the TUI review PR.
2026-04-01 16:24:14 -05:00
Jeremy McSpadden
d0555857c2 Merge pull request #2976 from jeremymcs/splash-header-updates-clean
feat(splash): add remote channel indicator to tools row
2026-04-01 16:14:23 -05:00
Jeremy McSpadden
b2abff3ce5 Merge pull request #3138 from jeremymcs/claude/add-stale-commit-check-GIbgw
feat(doctor): stale commit safety check with gsd snapshot and auto-cleanup
2026-04-01 14:22:21 -05:00
Jeremy McSpadden
03bb723dfb Merge pull request #3204 from jeremymcs/fix/stream-re-catch-all-json-parse
fix(error-classifier): catch-all V8 JSON.parse pattern replaces STREAM_RE whack-a-mole
2026-04-01 14:22:07 -05:00
Jeremy
f7cb3ec07b chore(merge): resolve conflict with upstream/main for PR #3204
Keep catch-all STREAM_RE from PR; upstream's 5-variant whack-a-mole is
superseded by the /in JSON at position \d+/ pattern. Also drop the now-
stale comment about checking stream before server/connection (no longer
needed since catch-all avoids those false-positive overlaps).
2026-04-01 14:05:28 -05:00
Jeremy
d929e9ceed chore(merge): resolve conflicts with upstream/main for PR #3138
- auto-worktree.ts: take upstream's MERGE_HEAD cleanup wording/order
- state.ts: take upstream's inline disk→DB reconciliation (#2631)
  over the simpler "always call deriveStateFromDb" approach
2026-04-01 14:04:16 -05:00
Tom Boucher
6e7721406e Merge pull request #2680 from jeremymcs/refactor/vscode-extension-design
feat(vscode): sidebar redesign, SCM provider, checkpoints, diagnostics [3/3]
2026-04-01 11:51:36 -04:00
Tom Boucher
0415f41eee Merge pull request #3116 from jeremymcs/refactor/planning-tier-heavy
refactor(complexity): reclassify planning phases from standard to heavy tier
2026-04-01 11:49:11 -04:00
Tom Boucher
adaa661a87 Merge pull request #3401 from gsd-build/claude/resolve-pr-3322-conflict-GQXIc
fix(worktree): resolve merge conflict for PR #3322 — adopt comprehensive pre-merge cleanup
2026-04-01 11:31:31 -04:00
Claude
1edf172463 test(worktree): add regression test for SQUASH_MSG/MERGE_MSG pre-merge cleanup (#2912)
Satisfies CI require-tests gate by adding a test that verifies the
comprehensive pre-merge cleanup (step 7b) removes stale SQUASH_MSG and
MERGE_MSG files — the enhancement over the prior MERGE_HEAD-only cleanup.

https://claude.ai/code/session_01SSHD9RNwVGNxAJZEgNZpgZ
2026-04-01 15:19:18 +00:00