@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.965d1788
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 +1 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
- package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
- package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
- package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
- package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
- package/dist/resources/extensions/gsd/auto-verification.js +5 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
- package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -19
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
- package/dist/resources/extensions/gsd/commands-context.js +19 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
- package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
- package/dist/resources/extensions/gsd/db/queries.js +60 -0
- package/dist/resources/extensions/gsd/db-workspace.js +55 -3
- package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
- package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
- package/dist/resources/extensions/gsd/forensics.js +2 -32
- package/dist/resources/extensions/gsd/git-service.js +5 -4
- package/dist/resources/extensions/gsd/gitignore.js +3 -0
- package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
- package/dist/resources/extensions/gsd/health-widget.js +55 -29
- package/dist/resources/extensions/gsd/json-persistence.js +20 -0
- package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
- package/dist/resources/extensions/gsd/layout-policy.js +3 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
- package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
- package/dist/resources/extensions/gsd/metrics.js +4 -4
- package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
- package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
- package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/quick.js +45 -2
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
- package/dist/resources/extensions/gsd/session-forensics.js +11 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
- package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
- package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
- package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
- package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
- package/dist/resources/extensions/gsd/state.js +12 -611
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
- package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
- package/dist/resources/extensions/gsd/undo.js +43 -6
- package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
- package/dist/resources/extensions/gsd/unit-registry.js +32 -4
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
- package/dist/resources/extensions/gsd/verification-gate.js +7 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +4 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
- 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 +12 -12
- 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 +9 -9
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/page_client-reference-manifest.js +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/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- 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 +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +1 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +2 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +3 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts +2 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +2 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
- package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
- package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +1 -1
- package/packages/mcp-server/dist/server.d.ts +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +3 -3
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +34 -20
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/README.md +3 -2
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme-schema.d.ts +75 -75
- package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/theme/theme-schema.js +1 -1
- package/pkg/dist/theme/theme-schema.js.map +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +11 -7
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
- package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
- package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
- package/src/resources/extensions/gsd/auto-verification.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
- package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -19
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
- package/src/resources/extensions/gsd/commands-context.ts +18 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
- package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
- package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
- package/src/resources/extensions/gsd/db/queries.ts +79 -0
- package/src/resources/extensions/gsd/db-workspace.ts +61 -3
- package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
- package/src/resources/extensions/gsd/forensics.ts +2 -33
- package/src/resources/extensions/gsd/git-service.ts +6 -5
- package/src/resources/extensions/gsd/gitignore.ts +3 -0
- package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
- package/src/resources/extensions/gsd/health-widget.ts +69 -32
- package/src/resources/extensions/gsd/json-persistence.ts +20 -0
- package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
- package/src/resources/extensions/gsd/layout-policy.ts +2 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
- package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
- package/src/resources/extensions/gsd/metrics.ts +4 -4
- package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
- package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
- package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/quick.ts +43 -2
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
- package/src/resources/extensions/gsd/session-forensics.ts +11 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
- package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
- package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
- package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
- package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
- package/src/resources/extensions/gsd/state.ts +32 -732
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
- package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
- package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +130 -3
- package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
- package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
- package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
- package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
- package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
- package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
- package/src/resources/extensions/gsd/undo.ts +47 -6
- package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
- package/src/resources/extensions/gsd/unit-registry.ts +32 -4
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
- package/src/resources/extensions/gsd/verification-gate.ts +8 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
- package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
- package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
- package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → -ji77QJck2IKIwrGza1Bs}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → -ji77QJck2IKIwrGza1Bs}/_ssgManifest.js +0 -0
|
@@ -29,21 +29,22 @@ test("#2007 bug 1: MAX_ARTIFACT_VERIFICATION_RETRIES constant is defined", () =>
|
|
|
29
29
|
assert.equal(MAX_ARTIFACT_VERIFICATION_RETRIES, 3);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
test("#2007 bug 1: retry state
|
|
32
|
+
test("#2007 bug 1: retry state keeps attempt separate from failure context", () => {
|
|
33
33
|
const s = new AutoSession();
|
|
34
34
|
const retryKey = "execute-task:M001/S01/T01";
|
|
35
|
+
const failureContext = "Missing expected artifact";
|
|
35
36
|
|
|
36
37
|
for (let attempt = 1; attempt <= MAX_ARTIFACT_VERIFICATION_RETRIES; attempt++) {
|
|
37
38
|
s.verificationRetryCount.set(retryKey, attempt);
|
|
38
39
|
s.pendingVerificationRetry = {
|
|
39
40
|
unitId: "M001/S01/T01",
|
|
40
|
-
failureContext
|
|
41
|
+
failureContext,
|
|
41
42
|
attempt,
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
assert.equal(s.verificationRetryCount.get(retryKey), attempt);
|
|
45
46
|
assert.equal(s.pendingVerificationRetry.attempt, attempt);
|
|
46
|
-
assert.
|
|
47
|
+
assert.equal(s.pendingVerificationRetry.failureContext, failureContext);
|
|
47
48
|
}
|
|
48
49
|
});
|
|
49
50
|
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "nod
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
|
|
7
|
-
import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
|
|
7
|
+
import { resolveExpectedArtifactPath, resolveSliceResearchLocation, resolveExistingSliceResearchPath } from "../auto-artifact-paths.ts";
|
|
8
8
|
import { clearPathCache, _clearGsdRootCache, isLegacyMilestonesLayout, milestonesDir } from "../paths.ts";
|
|
9
9
|
|
|
10
10
|
test("worktree artifact resolution falls back to project .gsd artifacts", () => {
|
|
@@ -359,3 +359,100 @@ test("canonical worktree + project-root legacy dir produces legacy filename (#bu
|
|
|
359
359
|
rmSync(root, { recursive: true, force: true });
|
|
360
360
|
}
|
|
361
361
|
});
|
|
362
|
+
|
|
363
|
+
// ── resolveSliceResearchLocation / resolveExistingSliceResearchPath ───────────
|
|
364
|
+
//
|
|
365
|
+
// Added in the code-quality consolidation PR as the shared dual-path resolver
|
|
366
|
+
// for slice RESEARCH files (worktree projection first, then canonical path
|
|
367
|
+
// fallback). These tests guard: missing file → null pair; existing legacy-
|
|
368
|
+
// layout file → correct absolute and relative paths.
|
|
369
|
+
|
|
370
|
+
test("resolveSliceResearchLocation returns null pair when no RESEARCH file exists", () => {
|
|
371
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-research-missing-")));
|
|
372
|
+
try {
|
|
373
|
+
const milestoneDir = join(root, ".gsd", "milestones", "M001");
|
|
374
|
+
mkdirSync(join(milestoneDir, "slices", "S01"), { recursive: true });
|
|
375
|
+
// Content-bearing dir so it is not treated as META-only, but no RESEARCH file.
|
|
376
|
+
writeFileSync(join(milestoneDir, "M001-CONTEXT.md"), "# context\n");
|
|
377
|
+
|
|
378
|
+
_clearGsdRootCache();
|
|
379
|
+
clearPathCache();
|
|
380
|
+
|
|
381
|
+
const result = resolveSliceResearchLocation(root, "M001", "S01");
|
|
382
|
+
assert.strictEqual(result.absolutePath, null, "absolutePath must be null when no RESEARCH exists");
|
|
383
|
+
assert.strictEqual(result.relativePath, null, "relativePath must be null when no RESEARCH exists");
|
|
384
|
+
} finally {
|
|
385
|
+
_clearGsdRootCache();
|
|
386
|
+
clearPathCache();
|
|
387
|
+
rmSync(root, { recursive: true, force: true });
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test("resolveExistingSliceResearchPath returns null when no RESEARCH file exists", () => {
|
|
392
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-research-null-")));
|
|
393
|
+
try {
|
|
394
|
+
const milestoneDir = join(root, ".gsd", "milestones", "M001");
|
|
395
|
+
mkdirSync(join(milestoneDir, "slices", "S01"), { recursive: true });
|
|
396
|
+
writeFileSync(join(milestoneDir, "M001-CONTEXT.md"), "# context\n");
|
|
397
|
+
|
|
398
|
+
_clearGsdRootCache();
|
|
399
|
+
clearPathCache();
|
|
400
|
+
|
|
401
|
+
assert.strictEqual(
|
|
402
|
+
resolveExistingSliceResearchPath(root, "M001", "S01"),
|
|
403
|
+
null,
|
|
404
|
+
"must return null when RESEARCH file is absent",
|
|
405
|
+
);
|
|
406
|
+
} finally {
|
|
407
|
+
_clearGsdRootCache();
|
|
408
|
+
clearPathCache();
|
|
409
|
+
rmSync(root, { recursive: true, force: true });
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("resolveSliceResearchLocation finds existing RESEARCH in legacy layout", () => {
|
|
414
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-research-legacy-")));
|
|
415
|
+
try {
|
|
416
|
+
const milestoneDir = join(root, ".gsd", "milestones", "M001");
|
|
417
|
+
const sliceDir = join(milestoneDir, "slices", "S01");
|
|
418
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
419
|
+
writeFileSync(join(milestoneDir, "M001-CONTEXT.md"), "# context\n");
|
|
420
|
+
const researchFile = join(sliceDir, "S01-RESEARCH.md");
|
|
421
|
+
writeFileSync(researchFile, "# slice research\n");
|
|
422
|
+
|
|
423
|
+
_clearGsdRootCache();
|
|
424
|
+
clearPathCache();
|
|
425
|
+
|
|
426
|
+
const result = resolveSliceResearchLocation(root, "M001", "S01");
|
|
427
|
+
assert.ok(result.absolutePath !== null, "absolutePath must be non-null when RESEARCH exists");
|
|
428
|
+
assert.ok(result.relativePath !== null, "relativePath must be non-null when RESEARCH exists");
|
|
429
|
+
assert.equal(result.absolutePath, researchFile, "absolutePath must point to the RESEARCH file");
|
|
430
|
+
} finally {
|
|
431
|
+
_clearGsdRootCache();
|
|
432
|
+
clearPathCache();
|
|
433
|
+
rmSync(root, { recursive: true, force: true });
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("resolveExistingSliceResearchPath returns absolute path when RESEARCH exists (legacy layout)", () => {
|
|
438
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-research-existing-")));
|
|
439
|
+
try {
|
|
440
|
+
const milestoneDir = join(root, ".gsd", "milestones", "M001");
|
|
441
|
+
const sliceDir = join(milestoneDir, "slices", "S01");
|
|
442
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
443
|
+
writeFileSync(join(milestoneDir, "M001-CONTEXT.md"), "# context\n");
|
|
444
|
+
const researchFile = join(sliceDir, "S01-RESEARCH.md");
|
|
445
|
+
writeFileSync(researchFile, "# research\n");
|
|
446
|
+
|
|
447
|
+
_clearGsdRootCache();
|
|
448
|
+
clearPathCache();
|
|
449
|
+
|
|
450
|
+
const result = resolveExistingSliceResearchPath(root, "M001", "S01");
|
|
451
|
+
assert.ok(result !== null, "must return non-null when RESEARCH exists");
|
|
452
|
+
assert.equal(result, researchFile, "must return the absolute path to the RESEARCH file");
|
|
453
|
+
} finally {
|
|
454
|
+
_clearGsdRootCache();
|
|
455
|
+
clearPathCache();
|
|
456
|
+
rmSync(root, { recursive: true, force: true });
|
|
457
|
+
}
|
|
458
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// File Purpose: Tests auto-mode artifact verification and recovery behavior.
|
|
3
3
|
import test, { afterEach } from "node:test";
|
|
4
4
|
import assert from "node:assert/strict";
|
|
5
|
-
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { chmodSync, mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
@@ -964,6 +964,45 @@ function makeGitBase(): string {
|
|
|
964
964
|
return base;
|
|
965
965
|
}
|
|
966
966
|
|
|
967
|
+
function shellQuote(value: string): string {
|
|
968
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function withLoggedGitCommands<T>(base: string, action: () => T): { result: T; commands: string[] } {
|
|
972
|
+
const realGit = execFileSync("which", ["git"], {
|
|
973
|
+
encoding: "utf-8",
|
|
974
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
975
|
+
}).trim().split(/\r?\n/)[0];
|
|
976
|
+
if (!realGit) throw new Error("Unable to resolve git executable for invocation logging test");
|
|
977
|
+
const binDir = join(base, ".git-wrapper-bin");
|
|
978
|
+
const logFile = join(base, "git-invocations.log");
|
|
979
|
+
mkdirSync(binDir, { recursive: true });
|
|
980
|
+
const wrapper = join(binDir, "git");
|
|
981
|
+
writeFileSync(
|
|
982
|
+
wrapper,
|
|
983
|
+
[
|
|
984
|
+
"#!/usr/bin/env bash",
|
|
985
|
+
`printf '%s\\n' "$1" >> ${shellQuote(logFile)}`,
|
|
986
|
+
`exec ${shellQuote(realGit)} "$@"`,
|
|
987
|
+
"",
|
|
988
|
+
].join("\n"),
|
|
989
|
+
);
|
|
990
|
+
chmodSync(wrapper, 0o755);
|
|
991
|
+
|
|
992
|
+
const originalPath = process.env.PATH;
|
|
993
|
+
process.env.PATH = originalPath ? `${binDir}:${originalPath}` : binDir;
|
|
994
|
+
try {
|
|
995
|
+
const result = action();
|
|
996
|
+
const commands = existsSync(logFile)
|
|
997
|
+
? readFileSync(logFile, "utf-8").split(/\r?\n/).filter(Boolean)
|
|
998
|
+
: [];
|
|
999
|
+
return { result, commands };
|
|
1000
|
+
} finally {
|
|
1001
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
1002
|
+
else process.env.PATH = originalPath;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
967
1006
|
test("hasImplementationArtifacts returns false when only .gsd/ files committed (#1703)", () => {
|
|
968
1007
|
const base = makeGitBase();
|
|
969
1008
|
try {
|
|
@@ -1344,6 +1383,77 @@ test("hasImplementationArtifacts finds implementation commits when .gsd/ is giti
|
|
|
1344
1383
|
}
|
|
1345
1384
|
});
|
|
1346
1385
|
|
|
1386
|
+
test("hasImplementationArtifacts scans GSD-tagged history without per-commit diff-tree forks (#892)", { skip: process.platform === "win32" }, () => {
|
|
1387
|
+
const base = makeGitBase();
|
|
1388
|
+
try {
|
|
1389
|
+
writeFileSync(join(base, ".git", "info", "exclude"), ".gsd/\n");
|
|
1390
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
|
|
1391
|
+
writeFileSync(
|
|
1392
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-SUMMARY.md"),
|
|
1393
|
+
"# Summary",
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
mkdirSync(join(base, "src"), { recursive: true });
|
|
1397
|
+
for (let i = 0; i < 3; i++) {
|
|
1398
|
+
writeFileSync(join(base, "src", `feature-${i}.ts`), `export const feature${i} = true;\n`);
|
|
1399
|
+
execFileSync("git", ["add", "src"], { cwd: base, stdio: "ignore" });
|
|
1400
|
+
execFileSync(
|
|
1401
|
+
"git",
|
|
1402
|
+
["commit", "-m", `feat: materialize M001 evidence ${i}\n\nGSD-Task: S01/T01`],
|
|
1403
|
+
{ cwd: base, stdio: "ignore" },
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
const { result, commands } = withLoggedGitCommands(base, () => hasImplementationArtifacts(base, "M001"));
|
|
1408
|
+
assert.equal(result, "present", "milestone-tagged commits should still prove implementation evidence");
|
|
1409
|
+
assert.ok(commands.includes("log"), "milestone evidence fallback should scan history with git log");
|
|
1410
|
+
assert.equal(commands.filter((command) => command === "diff-tree").length, 0);
|
|
1411
|
+
} finally {
|
|
1412
|
+
cleanup(base);
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
test("hasImplementationArtifacts backfill scans commit records without per-commit diff-tree forks (#892)", { skip: process.platform === "win32" }, () => {
|
|
1417
|
+
const base = makeGitBase();
|
|
1418
|
+
try {
|
|
1419
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
1420
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1421
|
+
insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
|
|
1422
|
+
insertSlice({
|
|
1423
|
+
id: "S01",
|
|
1424
|
+
milestoneId: "M001",
|
|
1425
|
+
title: "Slice One",
|
|
1426
|
+
status: "complete",
|
|
1427
|
+
risk: "low",
|
|
1428
|
+
depends: [],
|
|
1429
|
+
});
|
|
1430
|
+
insertTask({
|
|
1431
|
+
id: "T01",
|
|
1432
|
+
sliceId: "S01",
|
|
1433
|
+
milestoneId: "M001",
|
|
1434
|
+
title: "Task One",
|
|
1435
|
+
status: "complete",
|
|
1436
|
+
keyFiles: ["src/expected-0.ts", "src/expected-1.ts"],
|
|
1437
|
+
planning: { files: ["src/expected-0.ts", "src/expected-1.ts"] },
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
mkdirSync(join(base, "src"), { recursive: true });
|
|
1441
|
+
for (let i = 0; i < 2; i++) {
|
|
1442
|
+
writeFileSync(join(base, "src", `expected-${i}.ts`), `export const expected${i} = true;\n`);
|
|
1443
|
+
execFileSync("git", ["add", "src"], { cwd: base, stdio: "ignore" });
|
|
1444
|
+
execFileSync("git", ["commit", "-m", `feat: untagged implementation ${i}`], { cwd: base, stdio: "ignore" });
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
const { result, commands } = withLoggedGitCommands(base, () => hasImplementationArtifacts(base, "M001"));
|
|
1448
|
+
assert.equal(result, "present", "completed task file hints should still backfill untagged commits");
|
|
1449
|
+
assert.equal(getMilestoneCommitAttributionShas("M001").length, 2);
|
|
1450
|
+
assert.ok(commands.includes("log"), "backfill should scan commit records with git log");
|
|
1451
|
+
assert.equal(commands.filter((command) => command === "diff-tree").length, 0);
|
|
1452
|
+
} finally {
|
|
1453
|
+
cleanup(base);
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1347
1457
|
test("hasImplementationArtifacts binds GSD-Task trailer to milestone via DB state when .gsd/ is gitignored", () => {
|
|
1348
1458
|
const base = makeGitBase();
|
|
1349
1459
|
try {
|
|
@@ -101,6 +101,32 @@ test("analyzeSessionContext buckets injections, tool results, and loaded skills"
|
|
|
101
101
|
assert.equal(result.subagentSpawns, 1);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
test("analyzeSessionContext bounds large assistant tool-call arguments", () => {
|
|
105
|
+
const result = analyzeSessionContext(sessionEntries({
|
|
106
|
+
role: "assistant",
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: "toolCall",
|
|
110
|
+
id: "tc-save",
|
|
111
|
+
name: "gsd_summary_save",
|
|
112
|
+
arguments: {
|
|
113
|
+
path: ".gsd/milestones/M001/M001-SUMMARY.md",
|
|
114
|
+
artifact_type: "SUMMARY",
|
|
115
|
+
content: "x".repeat(20_000),
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
timestamp: TS,
|
|
120
|
+
}), PROVIDER);
|
|
121
|
+
|
|
122
|
+
const assistant = result.conversationSections.find((section) => section.label === "Assistant responses");
|
|
123
|
+
assert.ok(assistant);
|
|
124
|
+
assert.ok(
|
|
125
|
+
assistant.tokens < 200,
|
|
126
|
+
`assistant tool-call arguments should be redacted before token counting, got ${assistant.tokens} tokens`,
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
104
130
|
test("formatContextReport lists skills and subagents", () => {
|
|
105
131
|
const report = buildContextBreakdown({
|
|
106
132
|
modelLabel: "claude-code/claude-sonnet-4-6",
|
|
@@ -460,6 +460,7 @@ describe("Batch 3 prompt templates resolve", () => {
|
|
|
460
460
|
const out = loadPrompt("code-review", { scope: "s", depth: "deep", fixMode: "off", reviewId: "001" });
|
|
461
461
|
assert.match(out, /deep/);
|
|
462
462
|
assert.match(out, /001/);
|
|
463
|
+
assert.match(out, /diff-first/i, "code-review prompt should require diff-first context gathering");
|
|
463
464
|
});
|
|
464
465
|
test("review.md loads with target + reviewers", () => {
|
|
465
466
|
const out = loadPrompt("review", { target: "t", reviewers: "claude, codex" });
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
3
7
|
|
|
4
8
|
import {
|
|
5
9
|
formatCleanKeepReason,
|
|
10
|
+
handleWorktree,
|
|
6
11
|
type WorktreeStatus,
|
|
7
12
|
} from "../commands-worktree.ts";
|
|
13
|
+
import { withCommandCwd } from "../commands/context.ts";
|
|
14
|
+
import { createWorktree } from "../worktree-manager.ts";
|
|
15
|
+
import {
|
|
16
|
+
disableDebug,
|
|
17
|
+
enableDebug,
|
|
18
|
+
getDebugCounters,
|
|
19
|
+
} from "../debug-logger.ts";
|
|
8
20
|
|
|
9
21
|
function mkStatus(over: Partial<WorktreeStatus>): WorktreeStatus {
|
|
10
22
|
const name = over.name ?? "feat-x";
|
|
@@ -22,6 +34,45 @@ function mkStatus(over: Partial<WorktreeStatus>): WorktreeStatus {
|
|
|
22
34
|
};
|
|
23
35
|
}
|
|
24
36
|
|
|
37
|
+
function git(cwd: string, args: string[]): string {
|
|
38
|
+
return execFileSync("git", args, {
|
|
39
|
+
cwd,
|
|
40
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
41
|
+
encoding: "utf-8",
|
|
42
|
+
}).trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function makeRepo(): string {
|
|
46
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-worktree-command-"));
|
|
47
|
+
git(base, ["init", "-b", "main"]);
|
|
48
|
+
git(base, ["config", "user.name", "Test User"]);
|
|
49
|
+
git(base, ["config", "user.email", "test@example.com"]);
|
|
50
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
51
|
+
writeFileSync(join(base, "README.md"), "# Test\n", "utf-8");
|
|
52
|
+
git(base, ["add", "."]);
|
|
53
|
+
git(base, ["commit", "-m", "chore: init"]);
|
|
54
|
+
return base;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function createCommittedWorktree(base: string, name: string): void {
|
|
58
|
+
const wt = createWorktree(base, name);
|
|
59
|
+
writeFileSync(join(wt.path, `${name}.txt`), `${name}\n`, "utf-8");
|
|
60
|
+
git(wt.path, ["add", "."]);
|
|
61
|
+
git(wt.path, ["commit", "-m", `feat: ${name}`]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function createMockCtx() {
|
|
65
|
+
const notifications: { message: string; level: string }[] = [];
|
|
66
|
+
return {
|
|
67
|
+
notifications,
|
|
68
|
+
ui: {
|
|
69
|
+
notify(message: string, level: string) {
|
|
70
|
+
notifications.push({ message, level });
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
25
76
|
test("clean keep reason shows uncommitted-only worktrees clearly", () => {
|
|
26
77
|
const reason = formatCleanKeepReason(mkStatus({ uncommitted: true }));
|
|
27
78
|
assert.equal(reason, "uncommitted changes");
|
|
@@ -46,3 +97,32 @@ test("clean keep reason uses singular form for a single changed file", () => {
|
|
|
46
97
|
const reason = formatCleanKeepReason(mkStatus({ filesChanged: 1, uncommitted: false }));
|
|
47
98
|
assert.equal(reason, "1 changed file");
|
|
48
99
|
});
|
|
100
|
+
|
|
101
|
+
test("worktree list detects main branch once for the command", async (t) => {
|
|
102
|
+
if (process.env.GSD_ENABLE_NATIVE_GSD_GIT === "1") {
|
|
103
|
+
t.skip("git invocation regression is specific to the CLI fallback path");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const base = makeRepo();
|
|
108
|
+
try {
|
|
109
|
+
createCommittedWorktree(base, "feature-a");
|
|
110
|
+
createCommittedWorktree(base, "feature-b");
|
|
111
|
+
|
|
112
|
+
const ctx = createMockCtx();
|
|
113
|
+
enableDebug(base);
|
|
114
|
+
try {
|
|
115
|
+
await withCommandCwd(base, async () => {
|
|
116
|
+
await handleWorktree("list", ctx as any);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
assert.equal(ctx.notifications.length, 1);
|
|
120
|
+
assert.match(ctx.notifications[0].message, /Worktrees — 2/);
|
|
121
|
+
assert.equal(getDebugCounters().gitInvocations, 12);
|
|
122
|
+
} finally {
|
|
123
|
+
disableDebug();
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
rmSync(base, { recursive: true, force: true });
|
|
127
|
+
}
|
|
128
|
+
});
|
|
@@ -312,6 +312,34 @@ console.log('\n=== complete-milestone: incomplete slices block closeout ===');
|
|
|
312
312
|
cleanup(dbPath);
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
316
|
+
// complete-milestone: deferred slices are inactive for closeout
|
|
317
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
318
|
+
|
|
319
|
+
console.log('\n=== complete-milestone: deferred slices do not block closeout ===');
|
|
320
|
+
{
|
|
321
|
+
const dbPath = tempDbPath();
|
|
322
|
+
openDatabase(dbPath);
|
|
323
|
+
const { basePath } = createTempProject();
|
|
324
|
+
seedCompletedMilestone({ basePath, validationVerdict: 'pass' });
|
|
325
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Deferred Slice', status: 'deferred' });
|
|
326
|
+
insertTask({
|
|
327
|
+
id: 'T02',
|
|
328
|
+
sliceId: 'S02',
|
|
329
|
+
milestoneId: 'M001',
|
|
330
|
+
status: 'pending',
|
|
331
|
+
title: 'Deferred Slice Task',
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const result = await handleCompleteMilestone(makeValidParams(), basePath);
|
|
335
|
+
|
|
336
|
+
assertTrue(!('error' in result), 'deferred slice should not block milestone completion');
|
|
337
|
+
assertEq(getMilestone('M001')!.status, 'complete', 'milestone should complete with deferred inactive slice');
|
|
338
|
+
|
|
339
|
+
cleanupDir(basePath);
|
|
340
|
+
cleanup(dbPath);
|
|
341
|
+
}
|
|
342
|
+
|
|
315
343
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
316
344
|
// complete-milestone: deep task check (slice closed, task not)
|
|
317
345
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -7,6 +7,10 @@ import { join } from "node:path";
|
|
|
7
7
|
|
|
8
8
|
import { postUnitPreVerification } from "../auto-post-unit.ts";
|
|
9
9
|
import { AutoSession } from "../auto/session.ts";
|
|
10
|
+
import {
|
|
11
|
+
decideVerificationRetry,
|
|
12
|
+
hashVerificationFailureContext,
|
|
13
|
+
} from "../auto/verification-retry-policy.ts";
|
|
10
14
|
import { cleanup, makeTempRepo } from "./test-utils.ts";
|
|
11
15
|
|
|
12
16
|
function makePostUnitContext(base: string, s: AutoSession, notifications: string[]) {
|
|
@@ -227,3 +231,68 @@ test("complete-slice with gsd_replan_slice but no REPLAN artifact retries", asyn
|
|
|
227
231
|
cleanup(base);
|
|
228
232
|
}
|
|
229
233
|
});
|
|
234
|
+
|
|
235
|
+
test("artifact retry context stays stable across attempts while notifications show attempt count", async () => {
|
|
236
|
+
const base = makeTempRepo("gsd-artifact-retry-stable-context-");
|
|
237
|
+
try {
|
|
238
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
|
|
239
|
+
|
|
240
|
+
const s = new AutoSession();
|
|
241
|
+
s.active = true;
|
|
242
|
+
s.basePath = base;
|
|
243
|
+
s.currentUnit = { type: "complete-slice", id: "M001/S01", startedAt: Date.now() };
|
|
244
|
+
|
|
245
|
+
const notifications: string[] = [];
|
|
246
|
+
const pctx = makePostUnitContext(base, s, notifications);
|
|
247
|
+
const opts = {
|
|
248
|
+
skipSettleDelay: true,
|
|
249
|
+
skipWorktreeSync: true,
|
|
250
|
+
agentEndMessages: [
|
|
251
|
+
{
|
|
252
|
+
role: "toolResult",
|
|
253
|
+
toolName: "gsd_replan_slice",
|
|
254
|
+
isError: false,
|
|
255
|
+
content: "Slice replanned with reopened task T02.",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
assert.equal(await postUnitPreVerification(pctx, opts), "retry");
|
|
261
|
+
const firstRetry = s.pendingVerificationRetry;
|
|
262
|
+
assert.ok(firstRetry);
|
|
263
|
+
assert.equal(firstRetry.attempt, 1);
|
|
264
|
+
assert.doesNotMatch(firstRetry.failureContext, /\(attempt \d\/3\)\.$/);
|
|
265
|
+
const firstFailureHash = hashVerificationFailureContext(firstRetry.failureContext);
|
|
266
|
+
|
|
267
|
+
assert.equal(await postUnitPreVerification(pctx, opts), "retry");
|
|
268
|
+
const secondRetry = s.pendingVerificationRetry;
|
|
269
|
+
assert.ok(secondRetry);
|
|
270
|
+
assert.equal(secondRetry.attempt, 2);
|
|
271
|
+
assert.equal(secondRetry.failureContext, firstRetry.failureContext);
|
|
272
|
+
assert.equal(hashVerificationFailureContext(secondRetry.failureContext), firstFailureHash);
|
|
273
|
+
assert.deepEqual(
|
|
274
|
+
decideVerificationRetry({
|
|
275
|
+
unitType: s.currentUnit.type,
|
|
276
|
+
retryInfo: secondRetry,
|
|
277
|
+
previousFailureHash: firstFailureHash,
|
|
278
|
+
random: () => 0.5,
|
|
279
|
+
}),
|
|
280
|
+
{
|
|
281
|
+
action: "pause",
|
|
282
|
+
reason: "duplicate-failure-context",
|
|
283
|
+
key: "complete-slice:M001/S01",
|
|
284
|
+
failureHash: firstFailureHash,
|
|
285
|
+
},
|
|
286
|
+
);
|
|
287
|
+
assert.ok(
|
|
288
|
+
notifications.some((message) => message.includes("Retrying (attempt 1/3).")),
|
|
289
|
+
`expected first attempt notification, got: ${notifications.join("\n")}`,
|
|
290
|
+
);
|
|
291
|
+
assert.ok(
|
|
292
|
+
notifications.some((message) => message.includes("Retrying (attempt 2/3).")),
|
|
293
|
+
`expected second attempt notification, got: ${notifications.join("\n")}`,
|
|
294
|
+
);
|
|
295
|
+
} finally {
|
|
296
|
+
cleanup(base);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
@@ -203,6 +203,10 @@ console.log('\n=== complete-slice: handler happy path ===');
|
|
|
203
203
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second Slice', risk: 'low', depends: ['S01'], demo: 'advanced stuff', sequence: 2 });
|
|
204
204
|
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete', title: 'Task 1' });
|
|
205
205
|
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', status: 'complete', title: 'Task 2' });
|
|
206
|
+
insertTask({ id: 'T99', sliceId: 'S02', milestoneId: 'M001', status: 'complete', title: 'Sibling Task' });
|
|
207
|
+
const siblingSummaryPath = path.join(path.dirname(roadmapPath), 'T99-SUMMARY.md');
|
|
208
|
+
const siblingSummaryBefore = '# Existing sibling task summary\n\nDo not rewrite this file.\n';
|
|
209
|
+
fs.writeFileSync(siblingSummaryPath, siblingSummaryBefore);
|
|
206
210
|
|
|
207
211
|
const params = makeValidSliceParams();
|
|
208
212
|
const result = await handleCompleteSlice(params, basePath);
|
|
@@ -267,6 +271,13 @@ console.log('\n=== complete-slice: handler happy path ===');
|
|
|
267
271
|
// (e) Verify slice status is complete in DB
|
|
268
272
|
assertEq(sliceAfter!.status, 'complete', 'slice status should be complete in DB');
|
|
269
273
|
assertTrue(sliceAfter!.completed_at !== null, 'completed_at should be set in DB');
|
|
274
|
+
|
|
275
|
+
// (f) Verify unrelated completed task summaries are not re-rendered during slice completion
|
|
276
|
+
assertEq(
|
|
277
|
+
fs.readFileSync(siblingSummaryPath, 'utf-8'),
|
|
278
|
+
siblingSummaryBefore,
|
|
279
|
+
'complete-slice should not re-render sibling completed task summaries',
|
|
280
|
+
);
|
|
270
281
|
}
|
|
271
282
|
|
|
272
283
|
cleanupDir(basePath);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, afterEach } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync, promises as fsPromises } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
@@ -41,7 +41,7 @@ const VALID_PARAMS = {
|
|
|
41
41
|
],
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
describe("complete-task projection failures
|
|
44
|
+
describe("complete-task projection failures roll back DB completion", () => {
|
|
45
45
|
let base: string;
|
|
46
46
|
|
|
47
47
|
afterEach(() => {
|
|
@@ -75,7 +75,44 @@ describe("complete-task projection failures keep DB completion committed", () =>
|
|
|
75
75
|
assert.equal(rows.length, 2, "should have 2 evidence rows after success");
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
it("
|
|
78
|
+
it("reopens the workflow DB when it disappears between SUMMARY.md write and plan render", async (t) => {
|
|
79
|
+
base = makeTmpBase();
|
|
80
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
81
|
+
insertMilestone({ id: "M001" });
|
|
82
|
+
insertSlice({ id: "S01", milestoneId: "M001" });
|
|
83
|
+
|
|
84
|
+
const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
|
|
85
|
+
writeFileSync(
|
|
86
|
+
planPath,
|
|
87
|
+
"# S01 Plan\n\n## Tasks\n\n- [ ] **T01: Test task**\n",
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const originalRename = fsPromises.rename.bind(fsPromises);
|
|
91
|
+
let closedAfterSummaryWrite = false;
|
|
92
|
+
t.mock.method(fsPromises, "rename", async (...args: Parameters<typeof fsPromises.rename>) => {
|
|
93
|
+
await originalRename(...args);
|
|
94
|
+
const target = String(args[1]);
|
|
95
|
+
if (!closedAfterSummaryWrite && target.endsWith("T01-SUMMARY.md")) {
|
|
96
|
+
closedAfterSummaryWrite = true;
|
|
97
|
+
closeDatabase();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const result = await handleCompleteTask(VALID_PARAMS, base);
|
|
102
|
+
assert.ok(closedAfterSummaryWrite, "test fixture should close DB after SUMMARY.md atomic rename");
|
|
103
|
+
assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`);
|
|
104
|
+
|
|
105
|
+
const content = readFileSync(planPath, "utf-8");
|
|
106
|
+
assert.match(content, /\[x\][^\n]*\*\*T01\*\*/, "PLAN.md checkbox should be rendered after DB reopen");
|
|
107
|
+
|
|
108
|
+
const adapter = _getAdapter()!;
|
|
109
|
+
const task = adapter.prepare(
|
|
110
|
+
`SELECT status FROM tasks WHERE milestone_id = 'M001' AND slice_id = 'S01' AND id = 'T01'`,
|
|
111
|
+
).get() as { status: string } | undefined;
|
|
112
|
+
assert.equal(task?.status, "complete", "task should remain complete after successful projection");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("rolls back DB completion and clears verification_evidence when disk projection write fails", async () => {
|
|
79
116
|
base = makeTmpBase();
|
|
80
117
|
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
81
118
|
insertMilestone({ id: "M001" });
|
|
@@ -87,19 +124,22 @@ describe("complete-task projection failures keep DB completion committed", () =>
|
|
|
87
124
|
writeFileSync(tasksDir, "not-a-directory");
|
|
88
125
|
|
|
89
126
|
const result = await handleCompleteTask(VALID_PARAMS, base);
|
|
90
|
-
assert.ok(
|
|
91
|
-
assert.
|
|
127
|
+
assert.ok("error" in result, "expected rollback error when projection write fails");
|
|
128
|
+
assert.ok(
|
|
129
|
+
(result as { error: string }).error.includes("rolled completion back to pending"),
|
|
130
|
+
`error should mention rollback; got: ${"error" in result ? result.error : ""}`,
|
|
131
|
+
);
|
|
92
132
|
|
|
93
133
|
const adapter = _getAdapter()!;
|
|
94
134
|
const task = adapter.prepare(
|
|
95
135
|
`SELECT status FROM tasks WHERE milestone_id = 'M001' AND slice_id = 'S01' AND id = 'T01'`,
|
|
96
136
|
).get() as { status: string } | undefined;
|
|
97
|
-
assert.ok(task, "task row should still exist");
|
|
98
|
-
assert.equal(task!.status, "
|
|
137
|
+
assert.ok(task, "task row should still exist after rollback");
|
|
138
|
+
assert.equal(task!.status, "pending", "task status should be rolled back to pending");
|
|
99
139
|
|
|
100
140
|
const evidenceRows = adapter.prepare(
|
|
101
141
|
`SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'`,
|
|
102
142
|
).all();
|
|
103
|
-
assert.equal(evidenceRows.length,
|
|
143
|
+
assert.equal(evidenceRows.length, 0, "verification_evidence should be deleted after rollback");
|
|
104
144
|
});
|
|
105
145
|
});
|