gsd-pi 2.80.0-dev.2848d9009 → 2.80.0-dev.3065f7bfa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +178 -108
- package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +20 -30
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
- package/dist/resources/extensions/gsd/auto-prompts.js +103 -16
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
- package/dist/resources/extensions/gsd/auto-start.js +197 -6
- package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +86 -26
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +42 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +33 -20
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +259 -28
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
- package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
- package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
- package/dist/resources/extensions/gsd/git-service.js +36 -4
- package/dist/resources/extensions/gsd/gsd-db.js +46 -13
- package/dist/resources/extensions/gsd/guided-flow.js +99 -38
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/migrate/command.js +40 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
- package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
- package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/dist/resources/extensions/gsd/quick.js +34 -2
- package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
- package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
- package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +3 -3
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +22 -17
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
- package/packages/mcp-server/src/workflow-tools.ts +30 -16
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +2 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
- package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
- package/packages/pi-agent-core/dist/token-audit.js +221 -0
- package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
- package/packages/pi-agent-core/dist/types.d.ts +9 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-agent-core/src/agent.ts +8 -0
- package/packages/pi-agent-core/src/index.ts +2 -0
- package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
- package/packages/pi-agent-core/src/token-audit.ts +287 -0
- package/packages/pi-agent-core/src/types.ts +14 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +44 -7
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +6 -6
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +5 -3
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +39 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +25 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +13 -5
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +53 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +48 -7
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +3 -1
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
- package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +5 -3
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +8 -5
- package/packages/pi-coding-agent/src/core/extensions/types.ts +42 -1
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
- package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
- package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +4 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +22 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +3 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +18 -8
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/tui.ts +20 -8
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
- package/src/resources/extensions/gsd/auto/loop.ts +50 -8
- package/src/resources/extensions/gsd/auto/phases.ts +251 -172
- package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +25 -30
- package/src/resources/extensions/gsd/auto/session.ts +8 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +112 -15
- package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
- package/src/resources/extensions/gsd/auto-start.ts +230 -9
- package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
- package/src/resources/extensions/gsd/auto.ts +98 -21
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +49 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +34 -19
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +305 -28
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
- package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
- package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
- package/src/resources/extensions/gsd/git-service.ts +46 -8
- package/src/resources/extensions/gsd/gsd-db.ts +50 -13
- package/src/resources/extensions/gsd/guided-flow.ts +123 -45
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/migrate/command.ts +47 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
- package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/src/resources/extensions/gsd/quick.ts +37 -2
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +417 -10
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
- package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
- package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
- package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
- package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
- package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
- package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
- /package/dist/web/standalone/.next/static/{vaQ47E8aNlbbNEDoeME6G → O2WvnXROue6l7nAp_4dhC}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vaQ47E8aNlbbNEDoeME6G → O2WvnXROue6l7nAp_4dhC}/_ssgManifest.js +0 -0
|
@@ -13,6 +13,9 @@ import { parseRoadmap, parsePlan } from '../parsers-legacy.ts';
|
|
|
13
13
|
import { parseSummary } from '../files.ts';
|
|
14
14
|
import { deriveState } from '../state.ts';
|
|
15
15
|
import { invalidateAllCaches } from '../cache.ts';
|
|
16
|
+
import { ensureDbOpen } from '../bootstrap/dynamic-tools.ts';
|
|
17
|
+
import { closeDatabase, getAllMilestones } from '../gsd-db.ts';
|
|
18
|
+
import { importWrittenMigrationToDb } from '../migrate/command.ts';
|
|
16
19
|
import type {
|
|
17
20
|
GSDProject,
|
|
18
21
|
GSDMilestone,
|
|
@@ -250,6 +253,51 @@ test('Scenario 1: Incomplete project — write, parse, deriveState', async () =>
|
|
|
250
253
|
}
|
|
251
254
|
});
|
|
252
255
|
|
|
256
|
+
test('Scenario 1b: written migration imports into authoritative DB state', async () => {
|
|
257
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-writer-db-import-'));
|
|
258
|
+
try {
|
|
259
|
+
const project = buildIncompleteProject();
|
|
260
|
+
await writeGSDDirectory(project, base);
|
|
261
|
+
|
|
262
|
+
assert.equal(await ensureDbOpen(base), true, 'db import: ensureDbOpen creates authoritative DB');
|
|
263
|
+
|
|
264
|
+
invalidateAllCaches();
|
|
265
|
+
const before = await deriveState(base);
|
|
266
|
+
assert.equal(before.activeMilestone, null, 'db import: markdown-only migration is invisible before DB import');
|
|
267
|
+
|
|
268
|
+
const imported = await importWrittenMigrationToDb(base);
|
|
269
|
+
assert.deepStrictEqual(imported.hierarchy, { milestones: 1, slices: 2, tasks: 3 }, 'db import: hierarchy counts');
|
|
270
|
+
|
|
271
|
+
invalidateAllCaches();
|
|
272
|
+
const after = await deriveState(base);
|
|
273
|
+
assert.deepStrictEqual(after.phase, 'executing', 'db import: deriveState sees imported DB hierarchy');
|
|
274
|
+
assert.deepStrictEqual(after.activeMilestone?.id, 'M001', 'db import: active milestone');
|
|
275
|
+
assert.deepStrictEqual(after.activeSlice?.id, 'S02', 'db import: active slice');
|
|
276
|
+
assert.deepStrictEqual(after.activeTask?.id, 'T03', 'db import: active task');
|
|
277
|
+
} finally {
|
|
278
|
+
closeDatabase();
|
|
279
|
+
rmSync(base, { recursive: true, force: true });
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('Scenario 1c: DB import verification fails when preview counts do not match', async () => {
|
|
284
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-writer-db-check-'));
|
|
285
|
+
try {
|
|
286
|
+
const project = buildIncompleteProject();
|
|
287
|
+
await writeGSDDirectory(project, base);
|
|
288
|
+
|
|
289
|
+
const preview = generatePreview(project);
|
|
290
|
+
await assert.rejects(
|
|
291
|
+
() => importWrittenMigrationToDb(base, { ...preview, totalTasks: preview.totalTasks + 1 }),
|
|
292
|
+
/migration DB import verification failed: tasks 3\/4/,
|
|
293
|
+
);
|
|
294
|
+
assert.deepStrictEqual(getAllMilestones(), [], 'db import: failed verification rolls back hierarchy rewrite');
|
|
295
|
+
} finally {
|
|
296
|
+
closeDatabase();
|
|
297
|
+
rmSync(base, { recursive: true, force: true });
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
253
301
|
// ─── Scenario 2: Fully complete project ────────────────────────────────
|
|
254
302
|
|
|
255
303
|
test('Scenario 2: Fully complete project — deriveState phase', async () => {
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { ensureDbOpen } from "../bootstrap/dynamic-tools.ts";
|
|
8
|
+
import {
|
|
9
|
+
_getAdapter,
|
|
10
|
+
closeDatabase,
|
|
11
|
+
getAllMilestones,
|
|
12
|
+
getSliceTasks,
|
|
13
|
+
} from "../gsd-db.ts";
|
|
14
|
+
import {
|
|
15
|
+
autoImportMarkdownHierarchyIfDbMismatch,
|
|
16
|
+
countMarkdownHierarchy,
|
|
17
|
+
} from "../migration-auto-check.ts";
|
|
18
|
+
import { writeGSDDirectory } from "../migrate/writer.ts";
|
|
19
|
+
import type { GSDProject } from "../migrate/types.ts";
|
|
20
|
+
|
|
21
|
+
function makeBase(): string {
|
|
22
|
+
return mkdtempSync(join(tmpdir(), "gsd-migration-auto-check-"));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cleanup(base: string): void {
|
|
26
|
+
closeDatabase();
|
|
27
|
+
rmSync(base, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function projectFixture(): GSDProject {
|
|
31
|
+
return {
|
|
32
|
+
projectContent: "# Legacy Project\n",
|
|
33
|
+
decisionsContent: "",
|
|
34
|
+
requirements: [],
|
|
35
|
+
milestones: [
|
|
36
|
+
{
|
|
37
|
+
id: "M001",
|
|
38
|
+
title: "Legacy Milestone",
|
|
39
|
+
vision: "Carry forward previous work",
|
|
40
|
+
successCriteria: ["Existing task is visible"],
|
|
41
|
+
research: null,
|
|
42
|
+
boundaryMap: [],
|
|
43
|
+
slices: [
|
|
44
|
+
{
|
|
45
|
+
id: "S01",
|
|
46
|
+
title: "Legacy Slice",
|
|
47
|
+
risk: "medium",
|
|
48
|
+
depends: [],
|
|
49
|
+
done: false,
|
|
50
|
+
demo: "Legacy slice demo",
|
|
51
|
+
goal: "Legacy slice demo",
|
|
52
|
+
research: null,
|
|
53
|
+
summary: null,
|
|
54
|
+
tasks: [
|
|
55
|
+
{
|
|
56
|
+
id: "T01",
|
|
57
|
+
title: "Legacy Task",
|
|
58
|
+
description: "Task carried from markdown",
|
|
59
|
+
done: false,
|
|
60
|
+
estimate: "",
|
|
61
|
+
files: ["src/index.ts"],
|
|
62
|
+
mustHaves: [],
|
|
63
|
+
summary: null,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
test("migration auto-check imports markdown hierarchy when DB is empty", async () => {
|
|
74
|
+
const base = makeBase();
|
|
75
|
+
try {
|
|
76
|
+
await writeGSDDirectory(projectFixture(), base);
|
|
77
|
+
assert.deepEqual(countMarkdownHierarchy(base), { milestones: 1, slices: 1, tasks: 1 });
|
|
78
|
+
|
|
79
|
+
assert.equal(await ensureDbOpen(base), true);
|
|
80
|
+
assert.equal(getAllMilestones().length, 0, "fresh authoritative DB starts empty");
|
|
81
|
+
|
|
82
|
+
const result = await autoImportMarkdownHierarchyIfDbMismatch(base);
|
|
83
|
+
assert.equal(result.action, "imported");
|
|
84
|
+
assert.equal(result.reason, "db-empty");
|
|
85
|
+
assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 1 });
|
|
86
|
+
assert.equal(getAllMilestones().length, 1);
|
|
87
|
+
assert.equal(getSliceTasks("M001", "S01").length, 1);
|
|
88
|
+
} finally {
|
|
89
|
+
cleanup(base);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("migration auto-check repairs DB hierarchy count mismatch", async () => {
|
|
94
|
+
const base = makeBase();
|
|
95
|
+
try {
|
|
96
|
+
await writeGSDDirectory(projectFixture(), base);
|
|
97
|
+
await autoImportMarkdownHierarchyIfDbMismatch(base);
|
|
98
|
+
|
|
99
|
+
_getAdapter()!.prepare("DELETE FROM tasks WHERE milestone_id = ? AND slice_id = ? AND id = ?").run("M001", "S01", "T01");
|
|
100
|
+
assert.equal(getSliceTasks("M001", "S01").length, 0, "test fixture simulates stale DB task count");
|
|
101
|
+
|
|
102
|
+
const result = await autoImportMarkdownHierarchyIfDbMismatch(base);
|
|
103
|
+
assert.equal(result.action, "imported");
|
|
104
|
+
assert.equal(result.reason, "count-mismatch");
|
|
105
|
+
assert.deepEqual(result.beforeDb, { milestones: 1, slices: 1, tasks: 0 });
|
|
106
|
+
assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 1 });
|
|
107
|
+
assert.equal(getSliceTasks("M001", "S01").length, 1);
|
|
108
|
+
} finally {
|
|
109
|
+
cleanup(base);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("migration auto-check leaves matching DB hierarchy alone", async () => {
|
|
114
|
+
const base = makeBase();
|
|
115
|
+
try {
|
|
116
|
+
await writeGSDDirectory(projectFixture(), base);
|
|
117
|
+
await autoImportMarkdownHierarchyIfDbMismatch(base);
|
|
118
|
+
|
|
119
|
+
const result = await autoImportMarkdownHierarchyIfDbMismatch(base);
|
|
120
|
+
assert.equal(result.action, "none");
|
|
121
|
+
assert.equal(result.reason, "in-sync");
|
|
122
|
+
assert.deepEqual(result.markdown, { milestones: 1, slices: 1, tasks: 1 });
|
|
123
|
+
assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 1 });
|
|
124
|
+
} finally {
|
|
125
|
+
cleanup(base);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts
|
|
2
|
+
// Regression: postflight stash pop must run even when mergeAndExit throws.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import { _runMilestoneMergeWithStashRestore } from "../auto/phases.js";
|
|
8
|
+
import type { IterationContext } from "../auto/types.js";
|
|
9
|
+
import { MergeConflictError } from "../git-service.js";
|
|
10
|
+
import type {
|
|
11
|
+
PostflightResult,
|
|
12
|
+
PreflightResult,
|
|
13
|
+
} from "../clean-root-preflight.js";
|
|
14
|
+
|
|
15
|
+
interface CallLog {
|
|
16
|
+
preflightCalls: number;
|
|
17
|
+
mergeCalls: number;
|
|
18
|
+
postflightCalls: number;
|
|
19
|
+
stopAutoCalls: Array<string | undefined>;
|
|
20
|
+
notifyCalls: Array<{ message: string; level: string }>;
|
|
21
|
+
milestoneMergedInPhases: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildIc(opts: {
|
|
25
|
+
preflightResult: PreflightResult;
|
|
26
|
+
mergeBehavior: "succeed" | (() => never);
|
|
27
|
+
postflightResult: PostflightResult;
|
|
28
|
+
}): { ic: IterationContext; log: CallLog } {
|
|
29
|
+
const log: CallLog = {
|
|
30
|
+
preflightCalls: 0,
|
|
31
|
+
mergeCalls: 0,
|
|
32
|
+
postflightCalls: 0,
|
|
33
|
+
stopAutoCalls: [],
|
|
34
|
+
notifyCalls: [],
|
|
35
|
+
milestoneMergedInPhases: false,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const session = {
|
|
39
|
+
basePath: "/tmp/proj",
|
|
40
|
+
originalBasePath: "/tmp/proj",
|
|
41
|
+
get milestoneMergedInPhases() {
|
|
42
|
+
return log.milestoneMergedInPhases;
|
|
43
|
+
},
|
|
44
|
+
set milestoneMergedInPhases(v: boolean) {
|
|
45
|
+
log.milestoneMergedInPhases = v;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const ctx = {
|
|
50
|
+
ui: {
|
|
51
|
+
notify: (message: string, level: string) => {
|
|
52
|
+
log.notifyCalls.push({ message, level });
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const deps = {
|
|
58
|
+
preflightCleanRoot: () => {
|
|
59
|
+
log.preflightCalls += 1;
|
|
60
|
+
return opts.preflightResult;
|
|
61
|
+
},
|
|
62
|
+
postflightPopStash: () => {
|
|
63
|
+
log.postflightCalls += 1;
|
|
64
|
+
return opts.postflightResult;
|
|
65
|
+
},
|
|
66
|
+
resolver: {
|
|
67
|
+
mergeAndExit: () => {
|
|
68
|
+
log.mergeCalls += 1;
|
|
69
|
+
if (opts.mergeBehavior !== "succeed") {
|
|
70
|
+
opts.mergeBehavior();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
stopAuto: async (_c?: unknown, _p?: unknown, reason?: string) => {
|
|
75
|
+
log.stopAutoCalls.push(reason);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const ic = {
|
|
80
|
+
ctx,
|
|
81
|
+
pi: {},
|
|
82
|
+
s: session,
|
|
83
|
+
deps,
|
|
84
|
+
} as unknown as IterationContext;
|
|
85
|
+
|
|
86
|
+
return { ic, log };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const STASH_PUSHED: PreflightResult = {
|
|
90
|
+
stashPushed: true,
|
|
91
|
+
stashMarker: "gsd-preflight-stash:M002:42:1700000000000:abc",
|
|
92
|
+
summary: "Stashed uncommitted changes before merge (milestone M002).",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const STASH_NOT_PUSHED: PreflightResult = {
|
|
96
|
+
stashPushed: false,
|
|
97
|
+
summary: "",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const POP_OK: PostflightResult = {
|
|
101
|
+
restored: true,
|
|
102
|
+
needsManualRecovery: false,
|
|
103
|
+
message: "Restored stashed changes after milestone M002 merge.",
|
|
104
|
+
stashRef: "stash@{0}",
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const POP_NEEDS_RECOVERY: PostflightResult = {
|
|
108
|
+
restored: false,
|
|
109
|
+
needsManualRecovery: true,
|
|
110
|
+
message: "git stash pop stash@{0} failed: conflict in lib/models.ts",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
test("happy path: merge succeeds and stash is popped", async () => {
|
|
114
|
+
const { ic, log } = buildIc({
|
|
115
|
+
preflightResult: STASH_PUSHED,
|
|
116
|
+
mergeBehavior: "succeed",
|
|
117
|
+
postflightResult: POP_OK,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
|
|
121
|
+
|
|
122
|
+
assert.equal(result, null, "happy path returns null (loop continues)");
|
|
123
|
+
assert.equal(log.preflightCalls, 1);
|
|
124
|
+
assert.equal(log.mergeCalls, 1);
|
|
125
|
+
assert.equal(log.postflightCalls, 1, "postflight pop must run on success");
|
|
126
|
+
assert.equal(log.stopAutoCalls.length, 0, "no stopAuto on happy path");
|
|
127
|
+
assert.equal(log.milestoneMergedInPhases, true, "merge flag set");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("regression #5538-followup: postflight pop runs even when mergeAndExit throws non-conflict error", async () => {
|
|
131
|
+
// The original bug: when mergeAndExit threw, the catch block called
|
|
132
|
+
// stopAuto + return break BEFORE postflight pop ran. The user's
|
|
133
|
+
// gsd-preflight-stash:M00x stash was orphaned. This test exercises that
|
|
134
|
+
// exact scenario and asserts the pop is now invoked.
|
|
135
|
+
const { ic, log } = buildIc({
|
|
136
|
+
preflightResult: STASH_PUSHED,
|
|
137
|
+
mergeBehavior: () => {
|
|
138
|
+
throw new Error("native git merge failed: index lock present");
|
|
139
|
+
},
|
|
140
|
+
postflightResult: POP_OK,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
|
|
144
|
+
|
|
145
|
+
assert.deepEqual(result, { action: "break", reason: "merge-failed" });
|
|
146
|
+
assert.equal(log.mergeCalls, 1);
|
|
147
|
+
assert.equal(
|
|
148
|
+
log.postflightCalls,
|
|
149
|
+
1,
|
|
150
|
+
"postflight pop must run even on merge failure (was the bug)",
|
|
151
|
+
);
|
|
152
|
+
assert.equal(log.stopAutoCalls.length, 1);
|
|
153
|
+
assert.match(log.stopAutoCalls[0] ?? "", /Merge error on milestone M002/);
|
|
154
|
+
assert.equal(
|
|
155
|
+
log.milestoneMergedInPhases,
|
|
156
|
+
false,
|
|
157
|
+
"merge flag must NOT be set when merge throws",
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("regression #5538-followup: postflight pop runs even when mergeAndExit throws MergeConflictError", async () => {
|
|
162
|
+
const { ic, log } = buildIc({
|
|
163
|
+
preflightResult: STASH_PUSHED,
|
|
164
|
+
mergeBehavior: () => {
|
|
165
|
+
throw new MergeConflictError(
|
|
166
|
+
["lib/models.ts", "app/page.tsx"],
|
|
167
|
+
"squash",
|
|
168
|
+
"milestone/M002",
|
|
169
|
+
"main",
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
postflightResult: POP_OK,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
|
|
176
|
+
|
|
177
|
+
assert.deepEqual(result, { action: "break", reason: "merge-conflict" });
|
|
178
|
+
assert.equal(log.mergeCalls, 1);
|
|
179
|
+
assert.equal(
|
|
180
|
+
log.postflightCalls,
|
|
181
|
+
1,
|
|
182
|
+
"postflight pop must run on merge conflict (was the bug)",
|
|
183
|
+
);
|
|
184
|
+
assert.equal(log.stopAutoCalls.length, 1);
|
|
185
|
+
assert.match(log.stopAutoCalls[0] ?? "", /Merge conflict on milestone M002/);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("clean tree: no stash to pop, merge succeeds, no pop attempted", async () => {
|
|
189
|
+
const { ic, log } = buildIc({
|
|
190
|
+
preflightResult: STASH_NOT_PUSHED,
|
|
191
|
+
mergeBehavior: "succeed",
|
|
192
|
+
postflightResult: POP_OK,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
|
|
196
|
+
|
|
197
|
+
assert.equal(result, null);
|
|
198
|
+
assert.equal(log.postflightCalls, 0, "no pop when nothing was stashed");
|
|
199
|
+
assert.equal(log.milestoneMergedInPhases, true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("merge succeeds but stash pop needs manual recovery -> postflight-stash-restore-failed break", async () => {
|
|
203
|
+
const { ic, log } = buildIc({
|
|
204
|
+
preflightResult: STASH_PUSHED,
|
|
205
|
+
mergeBehavior: "succeed",
|
|
206
|
+
postflightResult: POP_NEEDS_RECOVERY,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
|
|
210
|
+
|
|
211
|
+
assert.deepEqual(result, {
|
|
212
|
+
action: "break",
|
|
213
|
+
reason: "postflight-stash-restore-failed",
|
|
214
|
+
});
|
|
215
|
+
assert.equal(log.postflightCalls, 1);
|
|
216
|
+
assert.equal(log.stopAutoCalls.length, 1);
|
|
217
|
+
assert.match(
|
|
218
|
+
log.stopAutoCalls[0] ?? "",
|
|
219
|
+
/Post-merge stash restore failed for milestone M002/,
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("merge error is reported even when stash pop also failed (merge-error takes priority)", async () => {
|
|
224
|
+
const { ic, log } = buildIc({
|
|
225
|
+
preflightResult: STASH_PUSHED,
|
|
226
|
+
mergeBehavior: () => {
|
|
227
|
+
throw new Error("network unreachable during push");
|
|
228
|
+
},
|
|
229
|
+
postflightResult: POP_NEEDS_RECOVERY,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
|
|
233
|
+
|
|
234
|
+
assert.deepEqual(result, { action: "break", reason: "merge-failed" });
|
|
235
|
+
assert.equal(log.postflightCalls, 1, "postflight pop still attempted");
|
|
236
|
+
assert.equal(log.stopAutoCalls.length, 1, "stopAuto called once, not twice");
|
|
237
|
+
assert.match(
|
|
238
|
+
log.stopAutoCalls[0] ?? "",
|
|
239
|
+
/Merge error/,
|
|
240
|
+
"stopAuto message reflects merge error, not stash failure",
|
|
241
|
+
);
|
|
242
|
+
});
|
|
@@ -12,11 +12,18 @@
|
|
|
12
12
|
|
|
13
13
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
14
14
|
import assert from "node:assert/strict";
|
|
15
|
-
import { chmodSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
15
|
+
import { chmodSync, existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { tmpdir } from "node:os";
|
|
18
18
|
import { execFileSync } from "node:child_process";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
assertWorktreeMaterialized,
|
|
21
|
+
nativeBranchDelete,
|
|
22
|
+
nativeCommit,
|
|
23
|
+
nativeIsRepo,
|
|
24
|
+
nativeResetHard,
|
|
25
|
+
nativeWorktreeAdd,
|
|
26
|
+
} from "../native-git-bridge.js";
|
|
20
27
|
|
|
21
28
|
// Note: prior static-analysis tests that scanned native-git-bridge.ts for
|
|
22
29
|
// the raw shell-spawn pattern were removed under #4827 — the integration
|
|
@@ -115,4 +122,29 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
|
|
|
115
122
|
/GSD_GIT_ERROR|git branch -D does-not-exist failed/,
|
|
116
123
|
);
|
|
117
124
|
});
|
|
125
|
+
|
|
126
|
+
test("assertWorktreeMaterialized rejects directories without a .git file", (t) => {
|
|
127
|
+
const dir = mkdtempSync(join(tmpdir(), "ngb-worktree-missing-git-"));
|
|
128
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
129
|
+
|
|
130
|
+
assert.throws(
|
|
131
|
+
() => assertWorktreeMaterialized(dir),
|
|
132
|
+
/missing \.git file/,
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("nativeWorktreeAdd materializes a valid .git marker", (t) => {
|
|
137
|
+
const wtPath = join(repo, ".gsd", "worktrees", "M001");
|
|
138
|
+
t.after(() => {
|
|
139
|
+
try { git(["worktree", "remove", "--force", wtPath], repo); } catch { /* noop */ }
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
nativeWorktreeAdd(repo, wtPath, "milestone/M001", true, "HEAD");
|
|
143
|
+
|
|
144
|
+
assert.equal(
|
|
145
|
+
existsSync(join(wtPath, ".git")),
|
|
146
|
+
true,
|
|
147
|
+
"created worktree must have the .git file required by later health checks",
|
|
148
|
+
);
|
|
149
|
+
});
|
|
118
150
|
});
|
|
@@ -110,6 +110,9 @@ function makeDeps(overrides?: Partial<WorktreeResolverDeps>): WorktreeResolverDe
|
|
|
110
110
|
calls.push({ fn: "getCurrentBranch", args: [basePath] });
|
|
111
111
|
return "main";
|
|
112
112
|
},
|
|
113
|
+
checkoutBranch: (basePath: string, branch: string) => {
|
|
114
|
+
calls.push({ fn: "checkoutBranch", args: [basePath, branch] });
|
|
115
|
+
},
|
|
113
116
|
autoWorktreeBranch: (milestoneId: string) => `milestone/${milestoneId}`,
|
|
114
117
|
resolveMilestoneFile: (basePath: string, milestoneId: string, fileType: string) => {
|
|
115
118
|
calls.push({ fn: "resolveMilestoneFile", args: [basePath, milestoneId, fileType] });
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts
|
|
2
|
+
// Regression: bootstrap must actively merge orphan completed-but-unmerged
|
|
3
|
+
// milestones, not just seed `s.currentMilestoneId` (the seed approach was
|
|
4
|
+
// silently overwritten at auto-start.ts:948 — caught in audit of PR #5549).
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
|
|
9
|
+
import { _mergeOrphanCompletedMilestone } from "../auto-start.js";
|
|
10
|
+
import type { WorktreeResolver } from "../worktree-resolver.js";
|
|
11
|
+
|
|
12
|
+
interface FakeResolverState {
|
|
13
|
+
mergeCalls: Array<{ milestoneId: string }>;
|
|
14
|
+
shouldThrow?: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function fakeResolver(state: FakeResolverState): WorktreeResolver {
|
|
18
|
+
return {
|
|
19
|
+
mergeAndExit: (milestoneId: string) => {
|
|
20
|
+
state.mergeCalls.push({ milestoneId });
|
|
21
|
+
if (state.shouldThrow) throw state.shouldThrow;
|
|
22
|
+
},
|
|
23
|
+
} as unknown as WorktreeResolver;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface FakeUiState {
|
|
27
|
+
notifications: Array<{ message: string; level: string }>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function fakeUi(state: FakeUiState): {
|
|
31
|
+
notify: (msg: string, level?: "info" | "warning" | "error" | "success") => void;
|
|
32
|
+
} {
|
|
33
|
+
return {
|
|
34
|
+
notify: (message: string, level?: "info" | "warning" | "error" | "success") => {
|
|
35
|
+
state.notifications.push({ message, level: level ?? "info" });
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
test("happy path: orphan merge runs, returns merged:true, emits info notify", () => {
|
|
41
|
+
const resolverState: FakeResolverState = { mergeCalls: [] };
|
|
42
|
+
const uiState: FakeUiState = { notifications: [] };
|
|
43
|
+
const result = _mergeOrphanCompletedMilestone(
|
|
44
|
+
fakeResolver(resolverState),
|
|
45
|
+
"M002",
|
|
46
|
+
fakeUi(uiState),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
assert.deepEqual(result, { merged: true });
|
|
50
|
+
assert.deepEqual(resolverState.mergeCalls, [{ milestoneId: "M002" }]);
|
|
51
|
+
assert.equal(uiState.notifications.length, 1);
|
|
52
|
+
assert.deepEqual(uiState.notifications[0], {
|
|
53
|
+
message: "Detected unmerged completed milestone M002. Merging now.",
|
|
54
|
+
level: "info",
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("regression: mergeAndExit throwing (e.g. wrong-branch from PR #5549 commit 5) does not bubble out", () => {
|
|
59
|
+
// Commit 5 (68ef58a3c) made `_mergeBranchMode` throw on wrong branch
|
|
60
|
+
// instead of silently returning false. If `_mergeOrphanCompletedMilestone`
|
|
61
|
+
// didn't catch the throw, bootstrap would surface an unhandled exception
|
|
62
|
+
// to the slash-command caller — the exact regression risk that motivated
|
|
63
|
+
// wrapping in try/catch.
|
|
64
|
+
const boom = new Error("dirty working tree blocks checkout");
|
|
65
|
+
const resolverState: FakeResolverState = { mergeCalls: [], shouldThrow: boom };
|
|
66
|
+
const uiState: FakeUiState = { notifications: [] };
|
|
67
|
+
|
|
68
|
+
const result = _mergeOrphanCompletedMilestone(
|
|
69
|
+
fakeResolver(resolverState),
|
|
70
|
+
"M002",
|
|
71
|
+
fakeUi(uiState),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
assert.equal(result.merged, false);
|
|
75
|
+
assert.equal(result.error, boom);
|
|
76
|
+
|
|
77
|
+
// First notify announces the merge attempt; second notify reports the failure.
|
|
78
|
+
assert.equal(uiState.notifications.length, 2);
|
|
79
|
+
assert.equal(uiState.notifications[0].level, "info");
|
|
80
|
+
assert.equal(uiState.notifications[1].level, "warning");
|
|
81
|
+
assert.match(
|
|
82
|
+
uiState.notifications[1].message,
|
|
83
|
+
/Could not merge orphan milestone M002/,
|
|
84
|
+
);
|
|
85
|
+
assert.match(uiState.notifications[1].message, /dirty working tree blocks checkout/);
|
|
86
|
+
assert.match(uiState.notifications[1].message, /Resolve manually/);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("non-Error thrown values are still captured and notified", () => {
|
|
90
|
+
// Defensive: thrown strings, numbers, etc. must not crash the formatter.
|
|
91
|
+
const resolverState: FakeResolverState = {
|
|
92
|
+
mergeCalls: [],
|
|
93
|
+
// mimic a thrown non-Error value
|
|
94
|
+
shouldThrow: "git lock held",
|
|
95
|
+
};
|
|
96
|
+
const uiState: FakeUiState = { notifications: [] };
|
|
97
|
+
|
|
98
|
+
const result = _mergeOrphanCompletedMilestone(
|
|
99
|
+
fakeResolver(resolverState),
|
|
100
|
+
"M002",
|
|
101
|
+
fakeUi(uiState),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
assert.equal(result.merged, false);
|
|
105
|
+
assert.equal(result.error, resolverState.shouldThrow);
|
|
106
|
+
assert.equal(uiState.notifications[1].level, "warning");
|
|
107
|
+
assert.match(uiState.notifications[1].message, /git lock held/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("the mergeAndExit call receives a notify-bound NotifyCtx the resolver can invoke", () => {
|
|
111
|
+
// The resolver's NotifyCtx must be wired to ui.notify so user-facing
|
|
112
|
+
// messages from inside mergeAndExit (e.g. "Milestone Mxxx merged") still
|
|
113
|
+
// reach the UI. Verify by having the fake resolver invoke the ctx.notify.
|
|
114
|
+
const uiState: FakeUiState = { notifications: [] };
|
|
115
|
+
const ui = fakeUi(uiState);
|
|
116
|
+
|
|
117
|
+
const resolver = {
|
|
118
|
+
mergeAndExit: (_milestoneId: string, ctx: { notify: (msg: string, level?: "info" | "warning" | "error" | "success") => void }) => {
|
|
119
|
+
ctx.notify("inner success message", "success");
|
|
120
|
+
},
|
|
121
|
+
} as unknown as WorktreeResolver;
|
|
122
|
+
|
|
123
|
+
const result = _mergeOrphanCompletedMilestone(resolver, "M002", ui);
|
|
124
|
+
|
|
125
|
+
assert.equal(result.merged, true);
|
|
126
|
+
// 1: outer "Detected unmerged completed milestone..."
|
|
127
|
+
// 2: inner "inner success message" emitted via the bound ctx.notify
|
|
128
|
+
assert.equal(uiState.notifications.length, 2);
|
|
129
|
+
assert.deepEqual(uiState.notifications[1], {
|
|
130
|
+
message: "inner success message",
|
|
131
|
+
level: "success",
|
|
132
|
+
});
|
|
133
|
+
});
|