gsd-pi 2.65.0 → 2.66.0
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/dist/mcp-server.js +6 -2
- package/dist/resources/extensions/browser-tools/capture.js +20 -1
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/dist/resources/extensions/gsd/auto/finalize-timeout.js +2 -0
- package/dist/resources/extensions/gsd/auto/loop.js +2 -2
- package/dist/resources/extensions/gsd/auto/phases.js +48 -5
- package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto/types.js +2 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
- package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
- package/dist/resources/extensions/gsd/auto-start.js +175 -12
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
- package/dist/resources/extensions/gsd/auto.js +21 -15
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
- package/dist/resources/extensions/gsd/commands/context.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -2
- package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +312 -0
- package/dist/resources/extensions/gsd/db-writer.js +13 -3
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/dist/resources/extensions/gsd/doctor.js +2 -1
- package/dist/resources/extensions/gsd/files.js +17 -0
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +47 -4
- package/dist/resources/extensions/gsd/guided-flow.js +220 -29
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/json-persistence.js +5 -2
- package/dist/resources/extensions/gsd/md-importer.js +14 -7
- package/dist/resources/extensions/gsd/notification-overlay.js +1 -1
- package/dist/resources/extensions/gsd/notification-widget.js +2 -1
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +26 -5
- package/dist/resources/extensions/gsd/preferences-types.js +3 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -2
- package/dist/resources/extensions/gsd/preparation.js +1092 -0
- package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
- package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/dist/resources/extensions/gsd/prompts/system.md +2 -2
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/dist/resources/extensions/gsd/quick.js +19 -15
- package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
- package/dist/resources/extensions/gsd/session-lock.js +23 -1
- package/dist/resources/extensions/gsd/state.js +115 -28
- package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
- package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
- package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
- package/dist/resources/extensions/gsd/undo.js +3 -2
- package/dist/resources/extensions/gsd/workflow-events.js +1 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
- package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
- package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
- package/dist/resources/extensions/gsd/worktree.js +9 -0
- package/dist/resources/extensions/shared/interview-ui.js +1 -1
- package/dist/resources/extensions/subagent/agents.js +19 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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 +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.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 +20 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-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 +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
- package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/packages/pi-tui/dist/components/image.d.ts +2 -0
- package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/image.js +4 -0
- package/packages/pi-tui/dist/components/image.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
- package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/image.test.js +32 -0
- package/packages/pi-tui/dist/components/image.test.js.map +1 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +3 -1
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/components/image.test.ts +36 -0
- package/packages/pi-tui/src/components/image.ts +5 -0
- package/packages/pi-tui/src/tui.ts +3 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/capture.ts +19 -1
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
- package/src/resources/extensions/gsd/auto/loop.ts +2 -2
- package/src/resources/extensions/gsd/auto/phases.ts +68 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto/types.ts +5 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
- package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
- package/src/resources/extensions/gsd/auto-start.ts +188 -10
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
- package/src/resources/extensions/gsd/auto.ts +19 -8
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
- package/src/resources/extensions/gsd/commands/context.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +26 -2
- package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +331 -0
- package/src/resources/extensions/gsd/db-writer.ts +11 -3
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/src/resources/extensions/gsd/doctor.ts +2 -1
- package/src/resources/extensions/gsd/files.ts +19 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +46 -4
- package/src/resources/extensions/gsd/guided-flow.ts +254 -30
- package/src/resources/extensions/gsd/index.ts +1 -0
- package/src/resources/extensions/gsd/json-persistence.ts +6 -3
- package/src/resources/extensions/gsd/md-importer.ts +13 -6
- package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
- package/src/resources/extensions/gsd/notification-widget.ts +2 -1
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
- package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -7
- package/src/resources/extensions/gsd/preferences-types.ts +25 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
- package/src/resources/extensions/gsd/preferences.ts +9 -2
- package/src/resources/extensions/gsd/preparation.ts +1419 -0
- package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/src/resources/extensions/gsd/prompts/queue.md +2 -0
- package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/src/resources/extensions/gsd/prompts/system.md +2 -2
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/src/resources/extensions/gsd/quick.ts +20 -15
- package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
- package/src/resources/extensions/gsd/session-lock.ts +17 -1
- package/src/resources/extensions/gsd/state.ts +115 -26
- package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
- package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
- package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
- package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +284 -20
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
- package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
- package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
- package/src/resources/extensions/gsd/types.ts +4 -0
- package/src/resources/extensions/gsd/undo.ts +3 -2
- package/src/resources/extensions/gsd/workflow-events.ts +5 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
- package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
- package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
- package/src/resources/extensions/gsd/worktree.ts +10 -0
- package/src/resources/extensions/shared/interview-ui.ts +1 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
- package/src/resources/extensions/subagent/agents.ts +30 -6
- package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
- /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → Bdk1mnQugYZh7ZxuXUYvc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → Bdk1mnQugYZh7ZxuXUYvc}/_ssgManifest.js +0 -0
|
@@ -163,16 +163,15 @@ test("deriveState returns completing-milestone when VALIDATION exists with termi
|
|
|
163
163
|
}
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
test("deriveState treats needs-remediation as terminal —
|
|
166
|
+
test("deriveState treats needs-remediation as non-terminal — re-enters validating-milestone (#832)", async () => {
|
|
167
167
|
const base = makeTmpBase();
|
|
168
168
|
try {
|
|
169
169
|
writeRoadmap(base, "M001", ALL_DONE_ROADMAP);
|
|
170
170
|
writeValidation(base, "M001", "---\nverdict: needs-remediation\nremediation_round: 0\n---\n\n# Validation\nNeeds fixes.");
|
|
171
171
|
|
|
172
172
|
const state = await deriveState(base);
|
|
173
|
-
// needs-remediation
|
|
174
|
-
|
|
175
|
-
assert.notEqual(state.phase, "validating-milestone");
|
|
173
|
+
// needs-remediation routes back to validating-milestone for re-validation
|
|
174
|
+
assert.equal(state.phase, "validating-milestone");
|
|
176
175
|
assert.equal(state.activeMilestone?.id, "M001");
|
|
177
176
|
} finally {
|
|
178
177
|
cleanup(base);
|
|
@@ -80,3 +80,18 @@ test("isVerificationNotApplicable: 'Verify API response times under load' requir
|
|
|
80
80
|
test("isVerificationNotApplicable: 'Monitor error rates for 24h' requires verification", () => {
|
|
81
81
|
assert.equal(isVerificationNotApplicable("Monitor error rates for 24h"), false);
|
|
82
82
|
});
|
|
83
|
+
|
|
84
|
+
// Regression: #3634 — "Not provided." default from plan-milestone
|
|
85
|
+
test("isVerificationNotApplicable: 'Not provided.' is not applicable (#3634)", () => {
|
|
86
|
+
assert.equal(isVerificationNotApplicable("Not provided."), true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("isVerificationNotApplicable: 'Not provided' (no period) is not applicable (#3634)", () => {
|
|
90
|
+
assert.equal(isVerificationNotApplicable("Not provided"), true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("isVerificationNotApplicable: trailing period does not defeat match (#3634)", () => {
|
|
94
|
+
assert.equal(isVerificationNotApplicable("None required."), true);
|
|
95
|
+
assert.equal(isVerificationNotApplicable("N/A."), true);
|
|
96
|
+
assert.equal(isVerificationNotApplicable("Not applicable."), true);
|
|
97
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #3607 — tighten verifyExpectedArtifact legacy branch
|
|
3
|
+
*
|
|
4
|
+
* The legacy (pre-migration) fallback in verifyExpectedArtifact previously
|
|
5
|
+
* accepted either a heading match (### T01 --) or a checked checkbox as proof
|
|
6
|
+
* that gsd_complete_task ran. A heading alone does not prove completion —
|
|
7
|
+
* it could result from a rogue write.
|
|
8
|
+
*
|
|
9
|
+
* The fix removes the hdRe heading regex and requires only a checked checkbox
|
|
10
|
+
* (cbRe) in the legacy branch, ensuring that only actual tool-completed tasks
|
|
11
|
+
* are treated as verified.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it } from 'node:test'
|
|
15
|
+
import assert from 'node:assert/strict'
|
|
16
|
+
import { readFileSync } from 'node:fs'
|
|
17
|
+
import { resolve } from 'node:path'
|
|
18
|
+
|
|
19
|
+
const src = readFileSync(
|
|
20
|
+
resolve(process.cwd(), 'src', 'resources', 'extensions', 'gsd', 'auto-recovery.ts'),
|
|
21
|
+
'utf-8',
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
describe('verifyExpectedArtifact legacy branch tightened (#3607)', () => {
|
|
25
|
+
it('legacy branch does NOT define hdRe heading regex', () => {
|
|
26
|
+
// Find the legacy fallback section
|
|
27
|
+
const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
|
|
28
|
+
assert.ok(legacyIdx !== -1, 'LEGACY comment must exist')
|
|
29
|
+
|
|
30
|
+
// Check the code within a reasonable window after the LEGACY comment
|
|
31
|
+
const legacyBlock = src.slice(legacyIdx, legacyIdx + 600)
|
|
32
|
+
|
|
33
|
+
assert.ok(
|
|
34
|
+
!legacyBlock.includes('hdRe'),
|
|
35
|
+
'hdRe heading regex must NOT exist in legacy branch — heading alone is not proof of completion',
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('legacy branch requires checked checkbox via cbRe', () => {
|
|
40
|
+
const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
|
|
41
|
+
assert.ok(legacyIdx !== -1)
|
|
42
|
+
|
|
43
|
+
const legacyBlock = src.slice(legacyIdx, legacyIdx + 600)
|
|
44
|
+
|
|
45
|
+
assert.ok(
|
|
46
|
+
legacyBlock.includes('cbRe'),
|
|
47
|
+
'cbRe checked-checkbox regex must exist in legacy branch',
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
// cbRe must match checked checkboxes [x] or [X]
|
|
51
|
+
assert.ok(
|
|
52
|
+
legacyBlock.includes('[xX]'),
|
|
53
|
+
'cbRe must match both [x] and [X] checkbox variants',
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('legacy branch returns false when no plan file exists', () => {
|
|
58
|
+
const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
|
|
59
|
+
assert.ok(legacyIdx !== -1)
|
|
60
|
+
|
|
61
|
+
const legacyBlock = src.slice(legacyIdx, legacyIdx + 1000)
|
|
62
|
+
|
|
63
|
+
// The else branch: no plan file means cannot verify
|
|
64
|
+
assert.ok(
|
|
65
|
+
legacyBlock.includes('no plan file'),
|
|
66
|
+
'missing plan file must be handled with return false',
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('DB available but task not found returns false', () => {
|
|
71
|
+
const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
|
|
72
|
+
assert.ok(legacyIdx !== -1)
|
|
73
|
+
|
|
74
|
+
const legacyBlock = src.slice(legacyIdx, legacyIdx + 1000)
|
|
75
|
+
|
|
76
|
+
assert.ok(
|
|
77
|
+
legacyBlock.includes('DB available but task row not found'),
|
|
78
|
+
'must handle case where DB is available but task row is missing',
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
// The comment should be followed by a return false
|
|
82
|
+
const commentIdx = legacyBlock.indexOf('DB available but task row not found')
|
|
83
|
+
const afterComment = legacyBlock.slice(commentIdx, commentIdx + 200)
|
|
84
|
+
assert.ok(
|
|
85
|
+
afterComment.includes('return false'),
|
|
86
|
+
'missing task row when DB available must return false',
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// GSD State Machine — Wave 1 Critical Regression Tests
|
|
2
|
+
// Validates fixes for event log format mismatch, skipped milestone status,
|
|
3
|
+
// dead code removal, and replan disk-file fallback.
|
|
4
|
+
|
|
5
|
+
import { describe, test } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { extractEntityKey } from "../workflow-reconcile.js";
|
|
8
|
+
import { isClosedStatus } from "../status-guards.js";
|
|
9
|
+
import type { WorkflowEvent } from "../workflow-events.js";
|
|
10
|
+
|
|
11
|
+
// ── Fix 1: Event log cmd format — hyphens and underscores both accepted ──
|
|
12
|
+
|
|
13
|
+
describe("extractEntityKey normalizes cmd format", () => {
|
|
14
|
+
const baseEvent = { params: {}, ts: "", hash: "", actor: "agent" as const, session_id: "" };
|
|
15
|
+
|
|
16
|
+
test("accepts hyphenated complete-task", () => {
|
|
17
|
+
const event: WorkflowEvent = { ...baseEvent, cmd: "complete-task", params: { taskId: "T01" } };
|
|
18
|
+
const key = extractEntityKey(event);
|
|
19
|
+
assert.deepStrictEqual(key, { type: "task", id: "T01" });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("accepts underscored complete_task (legacy)", () => {
|
|
23
|
+
const event: WorkflowEvent = { ...baseEvent, cmd: "complete_task", params: { taskId: "T01" } };
|
|
24
|
+
const key = extractEntityKey(event);
|
|
25
|
+
assert.deepStrictEqual(key, { type: "task", id: "T01" });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("accepts hyphenated complete-slice", () => {
|
|
29
|
+
const event: WorkflowEvent = { ...baseEvent, cmd: "complete-slice", params: { sliceId: "S01" } };
|
|
30
|
+
const key = extractEntityKey(event);
|
|
31
|
+
assert.deepStrictEqual(key, { type: "slice", id: "S01" });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("accepts hyphenated complete-milestone", () => {
|
|
35
|
+
const event: WorkflowEvent = { ...baseEvent, cmd: "complete-milestone", params: { milestoneId: "M001" } };
|
|
36
|
+
const key = extractEntityKey(event);
|
|
37
|
+
assert.deepStrictEqual(key, { type: "milestone", id: "M001" });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ── Fix 3: getActiveMilestoneId must skip "skipped" milestones ──
|
|
42
|
+
|
|
43
|
+
describe("isClosedStatus includes skipped", () => {
|
|
44
|
+
test("complete is closed", () => assert.ok(isClosedStatus("complete")));
|
|
45
|
+
test("done is closed", () => assert.ok(isClosedStatus("done")));
|
|
46
|
+
test("skipped is closed", () => assert.ok(isClosedStatus("skipped")));
|
|
47
|
+
test("pending is not closed", () => assert.ok(!isClosedStatus("pending")));
|
|
48
|
+
test("active is not closed", () => assert.ok(!isClosedStatus("active")));
|
|
49
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// GSD State Machine — Wave 2 Event Log Regression Tests
|
|
2
|
+
// Validates fixes for appendEvent isolation, entity replay handlers,
|
|
3
|
+
// and post-reconcile cache invalidation.
|
|
4
|
+
|
|
5
|
+
import { describe, test } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { extractEntityKey } from "../workflow-reconcile.js";
|
|
8
|
+
import type { WorkflowEvent } from "../workflow-events.js";
|
|
9
|
+
|
|
10
|
+
const base = { params: {}, ts: "", hash: "", actor: "agent" as const, session_id: "" };
|
|
11
|
+
|
|
12
|
+
// ── Fix 8: New entity event types handled by extractEntityKey ──
|
|
13
|
+
|
|
14
|
+
describe("extractEntityKey handles plan events", () => {
|
|
15
|
+
test("plan-milestone → milestone type", () => {
|
|
16
|
+
const event: WorkflowEvent = { ...base, cmd: "plan-milestone", params: { milestoneId: "M001" } };
|
|
17
|
+
const key = extractEntityKey(event);
|
|
18
|
+
assert.deepStrictEqual(key, { type: "milestone", id: "M001" });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("plan-task → task type", () => {
|
|
22
|
+
const event: WorkflowEvent = { ...base, cmd: "plan-task", params: { taskId: "T01" } };
|
|
23
|
+
const key = extractEntityKey(event);
|
|
24
|
+
assert.deepStrictEqual(key, { type: "task", id: "T01" });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("plan-slice preserves slice_plan type (conflict isolation)", () => {
|
|
28
|
+
const event: WorkflowEvent = { ...base, cmd: "plan-slice", params: { sliceId: "S01" } };
|
|
29
|
+
const key = extractEntityKey(event);
|
|
30
|
+
assert.deepStrictEqual(key, { type: "slice_plan", id: "S01" });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("replan-slice → slice type", () => {
|
|
34
|
+
const event: WorkflowEvent = { ...base, cmd: "replan-slice", params: { sliceId: "S01" } };
|
|
35
|
+
const key = extractEntityKey(event);
|
|
36
|
+
assert.deepStrictEqual(key, { type: "slice", id: "S01" });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ── Fix 8b: Unknown commands return null (don't crash) ──
|
|
41
|
+
|
|
42
|
+
describe("extractEntityKey handles unknown commands gracefully", () => {
|
|
43
|
+
test("unknown-command returns null", () => {
|
|
44
|
+
const event: WorkflowEvent = { ...base, cmd: "unknown-future-cmd", params: { foo: "bar" } };
|
|
45
|
+
const key = extractEntityKey(event);
|
|
46
|
+
assert.strictEqual(key, null);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// GSD State Machine — Wave 3 Session Regression Tests
|
|
2
|
+
// Validates tri-state hasImplementationArtifacts and AutoSession.consecutiveCompleteBootstraps.
|
|
3
|
+
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { hasImplementationArtifacts } from "../auto-recovery.js";
|
|
7
|
+
import { AutoSession } from "../auto/session.js";
|
|
8
|
+
|
|
9
|
+
// ── Fix 9: hasImplementationArtifacts returns tri-state ──
|
|
10
|
+
|
|
11
|
+
describe("hasImplementationArtifacts tri-state return", () => {
|
|
12
|
+
test("returns 'unknown' for non-git directory", () => {
|
|
13
|
+
const result = hasImplementationArtifacts("/tmp/not-a-git-repo-" + Date.now());
|
|
14
|
+
assert.strictEqual(result, "unknown");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("return type is one of present/absent/unknown", () => {
|
|
18
|
+
const result = hasImplementationArtifacts(process.cwd());
|
|
19
|
+
assert.ok(
|
|
20
|
+
result === "present" || result === "absent" || result === "unknown",
|
|
21
|
+
`Expected present/absent/unknown, got: ${result}`,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// ── Fix 11: consecutiveCompleteBootstraps is per-session ──
|
|
27
|
+
|
|
28
|
+
describe("AutoSession.consecutiveCompleteBootstraps", () => {
|
|
29
|
+
test("initial value is 0", () => {
|
|
30
|
+
const s = new AutoSession();
|
|
31
|
+
assert.strictEqual(s.consecutiveCompleteBootstraps, 0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("reset() clears the counter", () => {
|
|
35
|
+
const s = new AutoSession();
|
|
36
|
+
s.consecutiveCompleteBootstraps = 5;
|
|
37
|
+
s.reset();
|
|
38
|
+
assert.strictEqual(s.consecutiveCompleteBootstraps, 0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("two sessions have independent counters", () => {
|
|
42
|
+
const s1 = new AutoSession();
|
|
43
|
+
const s2 = new AutoSession();
|
|
44
|
+
s1.consecutiveCompleteBootstraps = 3;
|
|
45
|
+
assert.strictEqual(s2.consecutiveCompleteBootstraps, 0);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// GSD State Machine — Wave 4 Write Safety Regression Tests
|
|
2
|
+
// Validates randomized tmp suffix in json-persistence and atomic writes.
|
|
3
|
+
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { saveJsonFile, loadJsonFile } from "../json-persistence.js";
|
|
10
|
+
|
|
11
|
+
// ── Fix 15: json-persistence uses randomized tmp suffix ──
|
|
12
|
+
|
|
13
|
+
describe("saveJsonFile atomic write", () => {
|
|
14
|
+
test("writes JSON file correctly", () => {
|
|
15
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-json-test-"));
|
|
16
|
+
try {
|
|
17
|
+
const file = join(tmp, "test.json");
|
|
18
|
+
saveJsonFile(file, { key: "value" });
|
|
19
|
+
const content = JSON.parse(readFileSync(file, "utf-8"));
|
|
20
|
+
assert.deepStrictEqual(content, { key: "value" });
|
|
21
|
+
} finally {
|
|
22
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("no .tmp file left after successful write", () => {
|
|
27
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-json-test-"));
|
|
28
|
+
try {
|
|
29
|
+
const file = join(tmp, "test.json");
|
|
30
|
+
saveJsonFile(file, { data: 123 });
|
|
31
|
+
const files = readdirSync(tmp);
|
|
32
|
+
const tmpFiles = files.filter((f: string) => f.includes(".tmp"));
|
|
33
|
+
assert.strictEqual(tmpFiles.length, 0, "No .tmp files should remain after write");
|
|
34
|
+
} finally {
|
|
35
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("concurrent writes don't corrupt data", () => {
|
|
40
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-json-test-"));
|
|
41
|
+
try {
|
|
42
|
+
const file = join(tmp, "shared.json");
|
|
43
|
+
// Write two different values rapidly — both should succeed without corruption
|
|
44
|
+
saveJsonFile(file, { writer: "first" });
|
|
45
|
+
saveJsonFile(file, { writer: "second" });
|
|
46
|
+
const content = JSON.parse(readFileSync(file, "utf-8"));
|
|
47
|
+
assert.strictEqual(content.writer, "second");
|
|
48
|
+
} finally {
|
|
49
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("round-trip through loadJsonFile", () => {
|
|
54
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-json-test-"));
|
|
55
|
+
try {
|
|
56
|
+
const file = join(tmp, "roundtrip.json");
|
|
57
|
+
const data = { items: [1, 2, 3], name: "test" };
|
|
58
|
+
saveJsonFile(file, data);
|
|
59
|
+
const loaded = loadJsonFile(
|
|
60
|
+
file,
|
|
61
|
+
(d): d is typeof data => typeof d === "object" && d !== null && "items" in d,
|
|
62
|
+
() => ({ items: [], name: "" }),
|
|
63
|
+
);
|
|
64
|
+
assert.deepStrictEqual(loaded.items, [1, 2, 3]);
|
|
65
|
+
assert.strictEqual(loaded.name, "test");
|
|
66
|
+
} finally {
|
|
67
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// GSD State Machine — Wave 5 Consistency Regression Tests
|
|
2
|
+
// Validates isClosedStatus usage in projections, upsertDecision seq preservation,
|
|
3
|
+
// event schema versioning, and replay round-trip with mixed cmd formats.
|
|
4
|
+
|
|
5
|
+
import { describe, test } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { isClosedStatus } from "../status-guards.js";
|
|
11
|
+
import { openDatabase, closeDatabase, upsertDecision, _getAdapter, insertMilestone, insertSlice, insertTask, getTask } from "../gsd-db.js";
|
|
12
|
+
import { extractEntityKey } from "../workflow-reconcile.js";
|
|
13
|
+
import type { WorkflowEvent } from "../workflow-events.js";
|
|
14
|
+
|
|
15
|
+
// ── Fix 19: isClosedStatus covers all closed statuses ──
|
|
16
|
+
|
|
17
|
+
describe("isClosedStatus used by projections", () => {
|
|
18
|
+
test("skipped is closed (projections now show checked)", () => {
|
|
19
|
+
assert.ok(isClosedStatus("skipped"));
|
|
20
|
+
});
|
|
21
|
+
test("complete is closed", () => {
|
|
22
|
+
assert.ok(isClosedStatus("complete"));
|
|
23
|
+
});
|
|
24
|
+
test("done is closed", () => {
|
|
25
|
+
assert.ok(isClosedStatus("done"));
|
|
26
|
+
});
|
|
27
|
+
test("in-progress is not closed", () => {
|
|
28
|
+
assert.ok(!isClosedStatus("in-progress"));
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ── Fix 20: upsertDecision preserves seq on update ──
|
|
33
|
+
|
|
34
|
+
describe("upsertDecision preserves seq column", () => {
|
|
35
|
+
test("seq is preserved when decision is re-upserted", () => {
|
|
36
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-upsert-test-"));
|
|
37
|
+
const dbPath = join(tmp, "gsd.db");
|
|
38
|
+
try {
|
|
39
|
+
openDatabase(dbPath);
|
|
40
|
+
const adapter = _getAdapter();
|
|
41
|
+
assert.ok(adapter, "adapter must be available");
|
|
42
|
+
|
|
43
|
+
// Insert two decisions
|
|
44
|
+
upsertDecision({
|
|
45
|
+
id: "D001", when_context: "ctx1", scope: "s1",
|
|
46
|
+
decision: "d1", choice: "c1", rationale: "r1",
|
|
47
|
+
revisable: "yes", made_by: "agent", superseded_by: null,
|
|
48
|
+
});
|
|
49
|
+
upsertDecision({
|
|
50
|
+
id: "D002", when_context: "ctx2", scope: "s2",
|
|
51
|
+
decision: "d2", choice: "c2", rationale: "r2",
|
|
52
|
+
revisable: "yes", made_by: "agent", superseded_by: null,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Get original seq values
|
|
56
|
+
const rows1 = adapter.prepare("SELECT id, seq FROM decisions ORDER BY seq").all() as Array<{ id: string; seq: number }>;
|
|
57
|
+
assert.strictEqual(rows1[0].id, "D001");
|
|
58
|
+
assert.strictEqual(rows1[1].id, "D002");
|
|
59
|
+
const d001OriginalSeq = rows1[0].seq;
|
|
60
|
+
|
|
61
|
+
// Re-upsert D001 with updated content
|
|
62
|
+
upsertDecision({
|
|
63
|
+
id: "D001", when_context: "updated", scope: "s1",
|
|
64
|
+
decision: "d1-updated", choice: "c1", rationale: "r1",
|
|
65
|
+
revisable: "yes", made_by: "agent", superseded_by: null,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Verify seq is preserved (not moved to end)
|
|
69
|
+
const rows2 = adapter.prepare("SELECT id, seq FROM decisions ORDER BY seq").all() as Array<{ id: string; seq: number }>;
|
|
70
|
+
assert.strictEqual(rows2[0].id, "D001", "D001 should still be first by seq");
|
|
71
|
+
assert.strictEqual(rows2[0].seq, d001OriginalSeq, "D001 seq should be preserved");
|
|
72
|
+
assert.strictEqual(rows2[1].id, "D002", "D002 should still be second");
|
|
73
|
+
|
|
74
|
+
// Verify content was updated
|
|
75
|
+
const updated = adapter.prepare("SELECT decision FROM decisions WHERE id = 'D001'").get() as { decision: string };
|
|
76
|
+
assert.strictEqual(updated.decision, "d1-updated");
|
|
77
|
+
|
|
78
|
+
closeDatabase();
|
|
79
|
+
} finally {
|
|
80
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ── Fix 23: Event schema versioning ──
|
|
86
|
+
|
|
87
|
+
describe("WorkflowEvent v field", () => {
|
|
88
|
+
test("appendEvent includes v:2 in output", async () => {
|
|
89
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-event-v-test-"));
|
|
90
|
+
try {
|
|
91
|
+
const { appendEvent } = await import("../workflow-events.js");
|
|
92
|
+
appendEvent(tmp, {
|
|
93
|
+
cmd: "test-event",
|
|
94
|
+
params: { foo: "bar" },
|
|
95
|
+
ts: new Date().toISOString(),
|
|
96
|
+
actor: "system",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const logPath = join(tmp, ".gsd", "event-log.jsonl");
|
|
100
|
+
const line = readFileSync(logPath, "utf-8").trim();
|
|
101
|
+
const event = JSON.parse(line);
|
|
102
|
+
assert.strictEqual(event.v, 2, "New events should have v:2");
|
|
103
|
+
assert.strictEqual(event.cmd, "test-event");
|
|
104
|
+
} finally {
|
|
105
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ── Fix 19 (behavior-level): Projection rendering with skipped tasks ──
|
|
111
|
+
|
|
112
|
+
describe("isClosedStatus drives projection checkbox logic", () => {
|
|
113
|
+
test("skipped task produces checked checkbox via isClosedStatus", () => {
|
|
114
|
+
// This tests the behavior contract that projections rely on:
|
|
115
|
+
// workflow-projections.ts uses isClosedStatus() to determine checkbox state.
|
|
116
|
+
// "skipped" tasks must render as [x], not [ ].
|
|
117
|
+
const statuses = ["complete", "done", "skipped"];
|
|
118
|
+
for (const status of statuses) {
|
|
119
|
+
assert.ok(
|
|
120
|
+
isClosedStatus(status),
|
|
121
|
+
`status "${status}" must be closed so projections render [x]`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
// Non-closed statuses must render as [ ]
|
|
125
|
+
for (const status of ["pending", "in-progress", "blocked", "active"]) {
|
|
126
|
+
assert.ok(
|
|
127
|
+
!isClosedStatus(status),
|
|
128
|
+
`status "${status}" must NOT be closed so projections render [ ]`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── extractEntityKey: underscored cmds are recognized (Wave 5 scope) ──
|
|
135
|
+
// Note: hyphenated cmd normalization is in Wave 1. These tests validate
|
|
136
|
+
// the underscored format that Wave 5's extractEntityKey handles directly.
|
|
137
|
+
|
|
138
|
+
describe("extractEntityKey recognizes underscored cmds", () => {
|
|
139
|
+
const base: WorkflowEvent = { cmd: "", params: {}, ts: "", hash: "", actor: "agent", session_id: "" };
|
|
140
|
+
|
|
141
|
+
test("complete_task → task entity", () => {
|
|
142
|
+
const key = extractEntityKey({ ...base, cmd: "complete_task", params: { taskId: "T01" } });
|
|
143
|
+
assert.deepStrictEqual(key, { type: "task", id: "T01" });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("complete_slice → slice entity", () => {
|
|
147
|
+
const key = extractEntityKey({ ...base, cmd: "complete_slice", params: { sliceId: "S01" } });
|
|
148
|
+
assert.deepStrictEqual(key, { type: "slice", id: "S01" });
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("plan_slice → slice_plan entity (distinct from complete)", () => {
|
|
152
|
+
const key = extractEntityKey({ ...base, cmd: "plan_slice", params: { sliceId: "S01" } });
|
|
153
|
+
assert.deepStrictEqual(key, { type: "slice_plan", id: "S01" });
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("save_decision → decision entity", () => {
|
|
157
|
+
const key = extractEntityKey({ ...base, cmd: "save_decision", params: { scope: "s", decision: "d" } });
|
|
158
|
+
assert.deepStrictEqual(key, { type: "decision", id: "s:d" });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("unknown cmd returns null (not crash)", () => {
|
|
162
|
+
const key = extractEntityKey({ ...base, cmd: "future_unknown_cmd", params: {} });
|
|
163
|
+
assert.strictEqual(key, null);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker model override — tests for parallel.worker_model preference.
|
|
3
|
+
*
|
|
4
|
+
* Verifies validation, resolveParallelConfig pass-through, and type definitions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import test from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { join, dirname } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
|
|
15
|
+
const typesSrc = readFileSync(join(__dirname, "..", "types.ts"), "utf-8");
|
|
16
|
+
const validationSrc = readFileSync(join(__dirname, "..", "preferences-validation.ts"), "utf-8");
|
|
17
|
+
const preferencesSrc = readFileSync(join(__dirname, "..", "preferences.ts"), "utf-8");
|
|
18
|
+
|
|
19
|
+
// ─── Type definition ──────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
test("ParallelConfig includes worker_model optional field", () => {
|
|
22
|
+
assert.ok(
|
|
23
|
+
typesSrc.includes("worker_model?: string"),
|
|
24
|
+
"ParallelConfig should have optional worker_model field",
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ─── Validation ───────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
test("validatePreferences accepts valid worker_model string", () => {
|
|
31
|
+
assert.ok(
|
|
32
|
+
validationSrc.includes("p.worker_model"),
|
|
33
|
+
"validation should check parallel.worker_model",
|
|
34
|
+
);
|
|
35
|
+
assert.ok(
|
|
36
|
+
validationSrc.includes('parallel.worker_model must be a non-empty string'),
|
|
37
|
+
"validation should reject invalid worker_model",
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ─── resolveParallelConfig ────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
test("resolveParallelConfig passes through worker_model", () => {
|
|
44
|
+
assert.ok(
|
|
45
|
+
preferencesSrc.includes("worker_model: prefs?.parallel?.worker_model"),
|
|
46
|
+
"resolveParallelConfig should pass through worker_model",
|
|
47
|
+
);
|
|
48
|
+
});
|
|
@@ -90,18 +90,21 @@ describe("workflow-logger audit persistence", () => {
|
|
|
90
90
|
assert.ok(ctx, "context should exist");
|
|
91
91
|
assert.equal(ctx.fn, "saveDecisionToDb");
|
|
92
92
|
assert.equal(ctx.tool, "gsd_decision_save");
|
|
93
|
-
assert.equal(ctx.error,
|
|
93
|
+
assert.equal(ctx.error, "SQLITE_BUSY: database is locked", "error key should be preserved in persisted context");
|
|
94
94
|
assert.equal(ctx.file, undefined, "file key must be stripped from persisted context");
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
test("persisted errors
|
|
97
|
+
test("persisted errors preserve error key but strip other unsafe keys", () => {
|
|
98
98
|
logError("bootstrap", "ensureDbOpen failed", {
|
|
99
99
|
error: "ENOENT",
|
|
100
100
|
cwd: "/home/user/project",
|
|
101
101
|
});
|
|
102
102
|
const lines = readAuditLines(tmp);
|
|
103
103
|
assert.equal(lines.length, 1);
|
|
104
|
-
|
|
104
|
+
const ctx = lines[0].context as Record<string, string>;
|
|
105
|
+
assert.ok(ctx, "context should exist when error key is present");
|
|
106
|
+
assert.equal(ctx.error, "ENOENT", "error key should be preserved");
|
|
107
|
+
assert.equal(ctx.cwd, undefined, "cwd key must be stripped");
|
|
105
108
|
});
|
|
106
109
|
|
|
107
110
|
test("mixed warnings and errors only persist errors", () => {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree-expected-warnings.test.ts — #3665
|
|
3
|
+
*
|
|
4
|
+
* Verify that auto-worktree.ts and worktree-manager.ts suppress expected
|
|
5
|
+
* ENOENT and EISDIR conditions instead of logging misleading warnings.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { join, dirname } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const autoWorktreeFile = join(__dirname, "..", "auto-worktree.ts");
|
|
16
|
+
const worktreeManagerFile = join(__dirname, "..", "worktree-manager.ts");
|
|
17
|
+
|
|
18
|
+
describe("worktree expected-condition warning suppression (#3665)", () => {
|
|
19
|
+
const autoSource = readFileSync(autoWorktreeFile, "utf-8");
|
|
20
|
+
|
|
21
|
+
test("auto-worktree.ts checks for ENOENT before logging unlink warning", () => {
|
|
22
|
+
assert.match(autoSource, /code\s*!==\s*["']ENOENT["']/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("auto-worktree.ts checks for EISDIR before logging unlink warning", () => {
|
|
26
|
+
assert.match(autoSource, /code\s*!==\s*["']EISDIR["']/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("auto-worktree.ts references issue #3597", () => {
|
|
30
|
+
assert.match(autoSource, /#3597/);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const managerSource = readFileSync(worktreeManagerFile, "utf-8");
|
|
34
|
+
|
|
35
|
+
test("worktree-manager.ts checks isDirectory() before reading .git file", () => {
|
|
36
|
+
assert.match(managerSource, /lstatSync\(gitPath\)\.isDirectory\(\)/);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -26,9 +26,11 @@ import {
|
|
|
26
26
|
getSliceBranchName,
|
|
27
27
|
autoCommitCurrentBranch,
|
|
28
28
|
SLICE_BRANCH_RE,
|
|
29
|
+
_resetServiceCache,
|
|
29
30
|
} from "../worktree.ts";
|
|
30
31
|
|
|
31
32
|
import { deriveState } from "../state.ts";
|
|
33
|
+
import { _clearGsdRootCache } from "../paths.ts";
|
|
32
34
|
import { describe, test } from 'node:test';
|
|
33
35
|
import assert from 'node:assert/strict';
|
|
34
36
|
|
|
@@ -74,6 +76,14 @@ run("git add .", base);
|
|
|
74
76
|
run('git commit -m "chore: init"', base);
|
|
75
77
|
|
|
76
78
|
describe('worktree-integration', async () => {
|
|
79
|
+
// Isolate from user's global preferences (which may have git.main_branch set).
|
|
80
|
+
// Reset caches so getService() creates a fresh instance with empty preferences.
|
|
81
|
+
const originalHome = process.env.HOME;
|
|
82
|
+
const fakeHome = mkdtempSync(join(tmpdir(), "gsd-fake-home-"));
|
|
83
|
+
process.env.HOME = fakeHome;
|
|
84
|
+
_clearGsdRootCache();
|
|
85
|
+
_resetServiceCache();
|
|
86
|
+
|
|
77
87
|
// ── Verify main tree baseline ──────────────────────────────────────────────
|
|
78
88
|
|
|
79
89
|
console.log("\n=== Main tree baseline ===");
|
|
@@ -197,4 +207,10 @@ describe('worktree-integration', async () => {
|
|
|
197
207
|
assert.deepStrictEqual(listWorktrees(base).length, 0, "all worktrees removed");
|
|
198
208
|
|
|
199
209
|
rmSync(base, { recursive: true, force: true });
|
|
210
|
+
|
|
211
|
+
// Restore HOME and reset caches
|
|
212
|
+
process.env.HOME = originalHome;
|
|
213
|
+
_clearGsdRootCache();
|
|
214
|
+
_resetServiceCache();
|
|
215
|
+
rmSync(fakeHome, { recursive: true, force: true });
|
|
200
216
|
});
|