gsd-pi 2.44.0-dev.d25d507 → 2.45.0-dev.1afbdaa
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/help-text.js +1 -1
- package/dist/loader.js +34 -0
- package/dist/resources/extensions/gsd/activity-log.js +7 -0
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +63 -77
- package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -11
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +23 -5
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
- package/dist/resources/extensions/gsd/auto.js +42 -60
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/context.js +0 -4
- package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
- package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
- package/dist/resources/extensions/gsd/db-writer.js +40 -22
- package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
- package/dist/resources/extensions/gsd/doctor.js +13 -3
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +28 -4
- package/dist/resources/extensions/gsd/guided-flow.js +1 -2
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences.js +8 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
- package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/repo-identity.js +45 -7
- package/dist/resources/extensions/gsd/rethink.js +115 -0
- package/dist/resources/extensions/gsd/session-lock.js +1 -3
- package/dist/resources/extensions/gsd/state.js +48 -3
- package/dist/resources/extensions/gsd/sync-lock.js +89 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
- package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
- package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
- package/dist/resources/extensions/gsd/workflow-events.js +102 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
- package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
- package/dist/resources/extensions/gsd/write-intercept.js +84 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/resources/extensions/voice/index.js +11 -16
- package/dist/resources/extensions/voice/linux-ready.js +67 -0
- 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 +2 -2
- 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 +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/page.js +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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- 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 +4 -4
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +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 +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/4024.11ca5c01938e5948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
- package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
- package/package.json +2 -1
- package/packages/native/dist/stream-process/index.js +2 -2
- package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
- package/packages/native/src/stream-process/index.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +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 +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.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 +7 -3
- 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/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
- package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +1 -0
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
- package/src/resources/extensions/gsd/auto/phases.ts +69 -91
- package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -18
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +26 -5
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
- package/src/resources/extensions/gsd/auto.ts +44 -86
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/context.ts +0 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
- package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
- package/src/resources/extensions/gsd/db-writer.ts +41 -27
- package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
- package/src/resources/extensions/gsd/doctor-types.ts +7 -1
- package/src/resources/extensions/gsd/doctor.ts +13 -4
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +32 -4
- package/src/resources/extensions/gsd/guided-flow.ts +1 -2
- package/src/resources/extensions/gsd/journal.ts +6 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
- package/src/resources/extensions/gsd/preferences-types.ts +2 -2
- package/src/resources/extensions/gsd/preferences.ts +7 -3
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
- package/src/resources/extensions/gsd/prompts/queue.md +2 -2
- package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/repo-identity.ts +46 -7
- package/src/resources/extensions/gsd/rethink.ts +154 -0
- package/src/resources/extensions/gsd/session-lock.ts +0 -4
- package/src/resources/extensions/gsd/state.ts +49 -1
- package/src/resources/extensions/gsd/sync-lock.ts +94 -0
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
- package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/types.ts +8 -0
- package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
- package/src/resources/extensions/gsd/workflow-events.ts +154 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
- package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
- package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
- package/src/resources/extensions/gsd/write-intercept.ts +90 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- package/src/resources/extensions/voice/index.ts +11 -21
- package/src/resources/extensions/voice/linux-ready.ts +87 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
- package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
- package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
- /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2358: Survivor branch recovery skipped in phase=complete.
|
|
3
|
+
*
|
|
4
|
+
* When bootstrapAutoSession finds a survivor milestone branch and the derived
|
|
5
|
+
* state phase is "complete", recovery/finalization is skipped entirely because
|
|
6
|
+
* the survivor branch detection only triggers when phase === "pre-planning".
|
|
7
|
+
* The milestone finalization (merge, cleanup) never runs, leaving the worktree
|
|
8
|
+
* and branch alive.
|
|
9
|
+
*
|
|
10
|
+
* The fix broadens the survivor branch detection to also check phase === "complete",
|
|
11
|
+
* and adds a finalization path that runs mergeAndExit before falling through to
|
|
12
|
+
* the normal "complete" handling.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
16
|
+
|
|
17
|
+
const { assertTrue, assertEq, report } = createTestContext();
|
|
18
|
+
|
|
19
|
+
// ═══ Test: survivor branch detection conditions ══════════════════════════════
|
|
20
|
+
|
|
21
|
+
// The survivor branch detection block in auto-start.ts checks:
|
|
22
|
+
// state.activeMilestone &&
|
|
23
|
+
// state.phase === "pre-planning" && // <-- BUG: too restrictive
|
|
24
|
+
// shouldUseWorktreeIsolation() &&
|
|
25
|
+
// !detectWorktreeName(base) &&
|
|
26
|
+
// !base.includes(...)
|
|
27
|
+
//
|
|
28
|
+
// The fix should also include state.phase === "complete".
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
console.log("\n=== #2358: survivor branch should be detected in phase=complete ===");
|
|
32
|
+
|
|
33
|
+
// Simulate the condition check before the fix (only pre-planning)
|
|
34
|
+
const phasesBeforeFix = ["pre-planning"];
|
|
35
|
+
const phasesAfterFix = ["pre-planning", "complete"];
|
|
36
|
+
|
|
37
|
+
const testPhase = "complete";
|
|
38
|
+
|
|
39
|
+
const detectedBefore = phasesBeforeFix.includes(testPhase);
|
|
40
|
+
assertEq(detectedBefore, false, "before fix: phase=complete should NOT trigger survivor detection");
|
|
41
|
+
|
|
42
|
+
const detectedAfter = phasesAfterFix.includes(testPhase);
|
|
43
|
+
assertEq(detectedAfter, true, "after fix: phase=complete SHOULD trigger survivor detection");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ═══ Test: pre-planning survivor detection still works ═══════════════════════
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
console.log("\n=== #2358: pre-planning survivor detection is not broken ===");
|
|
50
|
+
|
|
51
|
+
const phasesAfterFix = ["pre-planning", "complete"];
|
|
52
|
+
const testPhase = "pre-planning";
|
|
53
|
+
|
|
54
|
+
const detected = phasesAfterFix.includes(testPhase);
|
|
55
|
+
assertEq(detected, true, "pre-planning should still trigger survivor detection after fix");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ═══ Test: other phases do NOT trigger survivor detection ════════════════════
|
|
59
|
+
|
|
60
|
+
{
|
|
61
|
+
console.log("\n=== #2358: other phases should NOT trigger survivor detection ===");
|
|
62
|
+
|
|
63
|
+
const phasesAfterFix = ["pre-planning", "complete"];
|
|
64
|
+
|
|
65
|
+
for (const phase of ["planning", "executing", "blocked", "needs-discussion"]) {
|
|
66
|
+
const detected = phasesAfterFix.includes(phase);
|
|
67
|
+
assertEq(detected, false, `phase=${phase} should NOT trigger survivor detection`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ═══ Test: phase=complete + hasSurvivorBranch should trigger finalization ═════
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
console.log("\n=== #2358: phase=complete + survivor branch triggers finalization path ===");
|
|
75
|
+
|
|
76
|
+
// Simulate the decision logic after the fix:
|
|
77
|
+
// if (hasSurvivorBranch && state.phase === "complete") -> finalize
|
|
78
|
+
// if (hasSurvivorBranch && state.phase === "needs-discussion") -> discuss
|
|
79
|
+
// if (!hasSurvivorBranch && state.phase === "complete") -> showSmartEntry
|
|
80
|
+
|
|
81
|
+
const scenarios = [
|
|
82
|
+
{ hasSurvivorBranch: true, phase: "complete", expected: "finalize" },
|
|
83
|
+
{ hasSurvivorBranch: true, phase: "needs-discussion", expected: "discuss" },
|
|
84
|
+
{ hasSurvivorBranch: true, phase: "pre-planning", expected: "continue" },
|
|
85
|
+
{ hasSurvivorBranch: false, phase: "complete", expected: "showSmartEntry" },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
for (const { hasSurvivorBranch, phase, expected } of scenarios) {
|
|
89
|
+
let result: string;
|
|
90
|
+
if (hasSurvivorBranch && phase === "complete") {
|
|
91
|
+
result = "finalize";
|
|
92
|
+
} else if (hasSurvivorBranch && phase === "needs-discussion") {
|
|
93
|
+
result = "discuss";
|
|
94
|
+
} else if (!hasSurvivorBranch && (!phase || phase === "complete")) {
|
|
95
|
+
result = "showSmartEntry";
|
|
96
|
+
} else {
|
|
97
|
+
result = "continue";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
assertEq(
|
|
101
|
+
result,
|
|
102
|
+
expected,
|
|
103
|
+
`hasSurvivorBranch=${hasSurvivorBranch}, phase=${phase} -> expected ${expected}, got ${result}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
report();
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// GSD Extension — sync-lock unit tests
|
|
2
|
+
// Tests acquireSyncLock() and releaseSyncLock().
|
|
3
|
+
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import * as os from 'node:os';
|
|
9
|
+
import { acquireSyncLock, releaseSyncLock } from '../sync-lock.ts';
|
|
10
|
+
|
|
11
|
+
function tempDir(): string {
|
|
12
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-sync-lock-'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function cleanupDir(dirPath: string): void {
|
|
16
|
+
try { fs.rmSync(dirPath, { recursive: true, force: true }); } catch { /* best effort */ }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── acquireSyncLock ─────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
test('sync-lock: acquireSyncLock returns { acquired: true } when no lock exists', () => {
|
|
22
|
+
const base = tempDir();
|
|
23
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
24
|
+
try {
|
|
25
|
+
const result = acquireSyncLock(base);
|
|
26
|
+
assert.strictEqual(result.acquired, true);
|
|
27
|
+
} finally {
|
|
28
|
+
cleanupDir(base);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('sync-lock: acquireSyncLock creates lock file at .gsd/sync.lock', () => {
|
|
33
|
+
const base = tempDir();
|
|
34
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
35
|
+
try {
|
|
36
|
+
acquireSyncLock(base);
|
|
37
|
+
const lockPath = path.join(base, '.gsd', 'sync.lock');
|
|
38
|
+
assert.ok(fs.existsSync(lockPath), 'sync.lock should exist after acquire');
|
|
39
|
+
} finally {
|
|
40
|
+
cleanupDir(base);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('sync-lock: lock file contains pid and acquired_at fields', () => {
|
|
45
|
+
const base = tempDir();
|
|
46
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
47
|
+
try {
|
|
48
|
+
acquireSyncLock(base);
|
|
49
|
+
const lockPath = path.join(base, '.gsd', 'sync.lock');
|
|
50
|
+
const content = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));
|
|
51
|
+
assert.strictEqual(typeof content.pid, 'number');
|
|
52
|
+
assert.strictEqual(typeof content.acquired_at, 'string');
|
|
53
|
+
} finally {
|
|
54
|
+
cleanupDir(base);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// ─── releaseSyncLock ─────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
test('sync-lock: releaseSyncLock removes lock file', () => {
|
|
61
|
+
const base = tempDir();
|
|
62
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
63
|
+
try {
|
|
64
|
+
acquireSyncLock(base);
|
|
65
|
+
const lockPath = path.join(base, '.gsd', 'sync.lock');
|
|
66
|
+
assert.ok(fs.existsSync(lockPath), 'lock file should exist before release');
|
|
67
|
+
releaseSyncLock(base);
|
|
68
|
+
assert.ok(!fs.existsSync(lockPath), 'lock file should not exist after release');
|
|
69
|
+
} finally {
|
|
70
|
+
cleanupDir(base);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('sync-lock: releaseSyncLock is a no-op when no lock file exists', () => {
|
|
75
|
+
const base = tempDir();
|
|
76
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
77
|
+
try {
|
|
78
|
+
// Should not throw
|
|
79
|
+
releaseSyncLock(base);
|
|
80
|
+
} finally {
|
|
81
|
+
cleanupDir(base);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ─── acquire → release → re-acquire round-trip ───────────────────────────
|
|
86
|
+
|
|
87
|
+
test('sync-lock: can re-acquire after release', () => {
|
|
88
|
+
const base = tempDir();
|
|
89
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
90
|
+
try {
|
|
91
|
+
const r1 = acquireSyncLock(base);
|
|
92
|
+
assert.strictEqual(r1.acquired, true, 'first acquire should succeed');
|
|
93
|
+
releaseSyncLock(base);
|
|
94
|
+
const r2 = acquireSyncLock(base);
|
|
95
|
+
assert.strictEqual(r2.acquired, true, 're-acquire after release should succeed');
|
|
96
|
+
releaseSyncLock(base);
|
|
97
|
+
} finally {
|
|
98
|
+
cleanupDir(base);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ─── stale lock override ─────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
test('sync-lock: overrides stale lock file (mtime backdated)', (t) => {
|
|
105
|
+
const base = tempDir();
|
|
106
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
107
|
+
const lockPath = path.join(base, '.gsd', 'sync.lock');
|
|
108
|
+
try {
|
|
109
|
+
// Write a lock file with a very old mtime (simulating staleness)
|
|
110
|
+
fs.writeFileSync(lockPath, JSON.stringify({ pid: 99999, acquired_at: new Date(0).toISOString() }));
|
|
111
|
+
// Backdate mtime by 2 minutes
|
|
112
|
+
const staleTime = new Date(Date.now() - 120_000);
|
|
113
|
+
fs.utimesSync(lockPath, staleTime, staleTime);
|
|
114
|
+
|
|
115
|
+
// Should override stale lock and acquire
|
|
116
|
+
const result = acquireSyncLock(base, 500);
|
|
117
|
+
assert.strictEqual(result.acquired, true, 'should acquire over stale lock');
|
|
118
|
+
releaseSyncLock(base);
|
|
119
|
+
} finally {
|
|
120
|
+
cleanupDir(base);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminated-transient.test.ts — Regression test for #2309.
|
|
3
|
+
*
|
|
4
|
+
* classifyProviderError should treat 'terminated' errors (process killed,
|
|
5
|
+
* connection reset) as transient with auto-resume, not permanent.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { classifyProviderError } from "../provider-error-pause.ts";
|
|
11
|
+
|
|
12
|
+
test("#2309: 'terminated' errors should be classified as transient", () => {
|
|
13
|
+
const result = classifyProviderError("terminated");
|
|
14
|
+
assert.equal(result.isTransient, true, "'terminated' should be transient");
|
|
15
|
+
assert.equal(result.isRateLimit, false, "'terminated' is not a rate limit");
|
|
16
|
+
assert.ok(result.suggestedDelayMs > 0, "'terminated' should have a retry delay");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("#2309: 'connection reset' errors should be classified as transient", () => {
|
|
20
|
+
const result = classifyProviderError("connection reset by peer");
|
|
21
|
+
assert.equal(result.isTransient, true, "'connection reset' should be transient");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("#2309: 'other side closed' errors should be classified as transient", () => {
|
|
25
|
+
const result = classifyProviderError("other side closed the connection");
|
|
26
|
+
assert.equal(result.isTransient, true, "'other side closed' should be transient");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("#2309: 'fetch failed' errors should be classified as transient", () => {
|
|
30
|
+
const result = classifyProviderError("fetch failed: network error");
|
|
31
|
+
assert.equal(result.isTransient, true, "'fetch failed' should be transient");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("#2309: 'connection refused' errors should be classified as transient", () => {
|
|
35
|
+
const result = classifyProviderError("ECONNREFUSED: connection refused");
|
|
36
|
+
assert.equal(result.isTransient, true, "'connection refused' should be transient");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("#2309: permanent errors are still permanent", () => {
|
|
40
|
+
const authResult = classifyProviderError("unauthorized: invalid API key");
|
|
41
|
+
assert.equal(authResult.isTransient, false, "auth errors should stay permanent");
|
|
42
|
+
assert.equal(authResult.suggestedDelayMs, 0, "permanent errors have no delay");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("#2309: rate limits are still transient", () => {
|
|
46
|
+
const rlResult = classifyProviderError("rate limit exceeded (429)");
|
|
47
|
+
assert.equal(rlResult.isTransient, true, "rate limits are still transient");
|
|
48
|
+
assert.equal(rlResult.isRateLimit, true, "rate limits are flagged as rate limits");
|
|
49
|
+
});
|
|
@@ -34,6 +34,7 @@ const RENAME_MAP: Array<{ canonical: string; alias: string }> = [
|
|
|
34
34
|
{ canonical: "gsd_replan_slice", alias: "gsd_slice_replan" },
|
|
35
35
|
{ canonical: "gsd_reassess_roadmap", alias: "gsd_roadmap_reassess" },
|
|
36
36
|
{ canonical: "gsd_complete_milestone", alias: "gsd_milestone_complete" },
|
|
37
|
+
{ canonical: "gsd_validate_milestone", alias: "gsd_milestone_validate" },
|
|
37
38
|
];
|
|
38
39
|
|
|
39
40
|
// ─── Registration count ──────────────────────────────────────────────────────
|
|
@@ -43,7 +44,7 @@ console.log('\n── Tool naming: registration count ──');
|
|
|
43
44
|
const pi = makeMockPi();
|
|
44
45
|
registerDbTools(pi);
|
|
45
46
|
|
|
46
|
-
assert.deepStrictEqual(pi.tools.length,
|
|
47
|
+
assert.deepStrictEqual(pi.tools.length, 26, 'Should register exactly 26 tools (13 canonical + 13 aliases)');
|
|
47
48
|
|
|
48
49
|
// ─── Both names exist for each pair ──────────────────────────────────────────
|
|
49
50
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// GSD — unit-ownership tests
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
claimUnit,
|
|
12
|
+
releaseUnit,
|
|
13
|
+
getOwner,
|
|
14
|
+
checkOwnership,
|
|
15
|
+
taskUnitKey,
|
|
16
|
+
sliceUnitKey,
|
|
17
|
+
} from '../unit-ownership.ts';
|
|
18
|
+
|
|
19
|
+
function makeTmpBase(): string {
|
|
20
|
+
return mkdtempSync(join(tmpdir(), 'gsd-ownership-'));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cleanup(base: string): void {
|
|
24
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── Key builders ────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
test('taskUnitKey: builds correct key', () => {
|
|
30
|
+
assert.equal(taskUnitKey('M001', 'S01', 'T01'), 'M001/S01/T01');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('sliceUnitKey: builds correct key', () => {
|
|
34
|
+
assert.equal(sliceUnitKey('M001', 'S01'), 'M001/S01');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ─── Claim / get / release ───────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
test('claimUnit: creates claim file and records agent', () => {
|
|
40
|
+
const base = makeTmpBase();
|
|
41
|
+
try {
|
|
42
|
+
claimUnit(base, 'M001/S01/T01', 'executor-01');
|
|
43
|
+
|
|
44
|
+
assert.ok(existsSync(join(base, '.gsd', 'unit-claims.json')), 'claim file should exist');
|
|
45
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'executor-01');
|
|
46
|
+
} finally {
|
|
47
|
+
cleanup(base);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('claimUnit: overwrites existing claim (last writer wins)', () => {
|
|
52
|
+
const base = makeTmpBase();
|
|
53
|
+
try {
|
|
54
|
+
claimUnit(base, 'M001/S01/T01', 'executor-01');
|
|
55
|
+
claimUnit(base, 'M001/S01/T01', 'executor-02');
|
|
56
|
+
|
|
57
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'executor-02');
|
|
58
|
+
} finally {
|
|
59
|
+
cleanup(base);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('claimUnit: multiple units can be claimed independently', () => {
|
|
64
|
+
const base = makeTmpBase();
|
|
65
|
+
try {
|
|
66
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
67
|
+
claimUnit(base, 'M001/S01/T02', 'agent-b');
|
|
68
|
+
|
|
69
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'agent-a');
|
|
70
|
+
assert.equal(getOwner(base, 'M001/S01/T02'), 'agent-b');
|
|
71
|
+
} finally {
|
|
72
|
+
cleanup(base);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('getOwner: returns null when no claim file exists', () => {
|
|
77
|
+
const base = makeTmpBase();
|
|
78
|
+
try {
|
|
79
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), null);
|
|
80
|
+
} finally {
|
|
81
|
+
cleanup(base);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('getOwner: returns null for unclaimed unit', () => {
|
|
86
|
+
const base = makeTmpBase();
|
|
87
|
+
try {
|
|
88
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
89
|
+
assert.equal(getOwner(base, 'M001/S01/T99'), null);
|
|
90
|
+
} finally {
|
|
91
|
+
cleanup(base);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('releaseUnit: removes claim', () => {
|
|
96
|
+
const base = makeTmpBase();
|
|
97
|
+
try {
|
|
98
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
99
|
+
releaseUnit(base, 'M001/S01/T01');
|
|
100
|
+
|
|
101
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), null);
|
|
102
|
+
} finally {
|
|
103
|
+
cleanup(base);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('releaseUnit: no-op for non-existent claim', () => {
|
|
108
|
+
const base = makeTmpBase();
|
|
109
|
+
try {
|
|
110
|
+
// Should not throw
|
|
111
|
+
releaseUnit(base, 'M001/S01/T01');
|
|
112
|
+
} finally {
|
|
113
|
+
cleanup(base);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ─── checkOwnership ──────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
test('checkOwnership: returns null when no actorName provided (opt-in)', () => {
|
|
120
|
+
const base = makeTmpBase();
|
|
121
|
+
try {
|
|
122
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
123
|
+
|
|
124
|
+
// No actorName → ownership not enforced
|
|
125
|
+
assert.equal(checkOwnership(base, 'M001/S01/T01', undefined), null);
|
|
126
|
+
} finally {
|
|
127
|
+
cleanup(base);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('checkOwnership: returns null when no claim file exists', () => {
|
|
132
|
+
const base = makeTmpBase();
|
|
133
|
+
try {
|
|
134
|
+
assert.equal(checkOwnership(base, 'M001/S01/T01', 'agent-a'), null);
|
|
135
|
+
} finally {
|
|
136
|
+
cleanup(base);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('checkOwnership: returns null when unit is unclaimed', () => {
|
|
141
|
+
const base = makeTmpBase();
|
|
142
|
+
try {
|
|
143
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
144
|
+
|
|
145
|
+
// Different unit, unclaimed
|
|
146
|
+
assert.equal(checkOwnership(base, 'M001/S01/T99', 'agent-b'), null);
|
|
147
|
+
} finally {
|
|
148
|
+
cleanup(base);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('checkOwnership: returns null when actor matches owner', () => {
|
|
153
|
+
const base = makeTmpBase();
|
|
154
|
+
try {
|
|
155
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
156
|
+
|
|
157
|
+
assert.equal(checkOwnership(base, 'M001/S01/T01', 'agent-a'), null);
|
|
158
|
+
} finally {
|
|
159
|
+
cleanup(base);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('checkOwnership: returns error string when actor does not match owner', () => {
|
|
164
|
+
const base = makeTmpBase();
|
|
165
|
+
try {
|
|
166
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
167
|
+
|
|
168
|
+
const err = checkOwnership(base, 'M001/S01/T01', 'agent-b');
|
|
169
|
+
assert.ok(err !== null, 'should return error');
|
|
170
|
+
assert.match(err!, /owned by agent-a/);
|
|
171
|
+
assert.match(err!, /not agent-b/);
|
|
172
|
+
} finally {
|
|
173
|
+
cleanup(base);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// GSD Extension — workflow-events unit tests
|
|
2
|
+
// Tests appendEvent, readEvents, findForkPoint, compactMilestoneEvents.
|
|
3
|
+
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import * as os from 'node:os';
|
|
9
|
+
import {
|
|
10
|
+
appendEvent,
|
|
11
|
+
readEvents,
|
|
12
|
+
findForkPoint,
|
|
13
|
+
compactMilestoneEvents,
|
|
14
|
+
type WorkflowEvent,
|
|
15
|
+
} from '../workflow-events.ts';
|
|
16
|
+
|
|
17
|
+
function tempDir(): string {
|
|
18
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-events-'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function cleanupDir(dirPath: string): void {
|
|
22
|
+
try { fs.rmSync(dirPath, { recursive: true, force: true }); } catch { /* best effort */ }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeEvent(cmd: string, params: Record<string, unknown> = {}): Omit<WorkflowEvent, 'hash' | 'session_id'> {
|
|
26
|
+
return { cmd, params, ts: new Date().toISOString(), actor: 'agent' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── appendEvent ─────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
test('workflow-events: appendEvent creates .gsd dir and event-log.jsonl', () => {
|
|
32
|
+
const base = tempDir();
|
|
33
|
+
try {
|
|
34
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M001', taskId: 'T01' }));
|
|
35
|
+
assert.ok(fs.existsSync(path.join(base, '.gsd', 'event-log.jsonl')));
|
|
36
|
+
} finally {
|
|
37
|
+
cleanupDir(base);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('workflow-events: appendEvent writes valid JSON line', () => {
|
|
42
|
+
const base = tempDir();
|
|
43
|
+
try {
|
|
44
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M001', taskId: 'T01' }));
|
|
45
|
+
const content = fs.readFileSync(path.join(base, '.gsd', 'event-log.jsonl'), 'utf-8');
|
|
46
|
+
const lines = content.trim().split('\n');
|
|
47
|
+
assert.strictEqual(lines.length, 1);
|
|
48
|
+
const parsed = JSON.parse(lines[0]!) as WorkflowEvent;
|
|
49
|
+
assert.strictEqual(parsed.cmd, 'complete-task');
|
|
50
|
+
assert.strictEqual(parsed.actor, 'agent');
|
|
51
|
+
assert.strictEqual(typeof parsed.hash, 'string');
|
|
52
|
+
assert.strictEqual(parsed.hash.length, 16);
|
|
53
|
+
} finally {
|
|
54
|
+
cleanupDir(base);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('workflow-events: appendEvent appends multiple events', () => {
|
|
59
|
+
const base = tempDir();
|
|
60
|
+
try {
|
|
61
|
+
appendEvent(base, makeEvent('complete-task', { taskId: 'T01' }));
|
|
62
|
+
appendEvent(base, makeEvent('complete-slice', { sliceId: 'S01' }));
|
|
63
|
+
const events = readEvents(path.join(base, '.gsd', 'event-log.jsonl'));
|
|
64
|
+
assert.strictEqual(events.length, 2);
|
|
65
|
+
assert.strictEqual(events[0]!.cmd, 'complete-task');
|
|
66
|
+
assert.strictEqual(events[1]!.cmd, 'complete-slice');
|
|
67
|
+
} finally {
|
|
68
|
+
cleanupDir(base);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('workflow-events: same cmd+params → same hash (deterministic)', () => {
|
|
73
|
+
const base = tempDir();
|
|
74
|
+
try {
|
|
75
|
+
appendEvent(base, makeEvent('plan-task', { milestoneId: 'M001', sliceId: 'S01' }));
|
|
76
|
+
appendEvent(base, makeEvent('plan-task', { milestoneId: 'M001', sliceId: 'S01' }));
|
|
77
|
+
const events = readEvents(path.join(base, '.gsd', 'event-log.jsonl'));
|
|
78
|
+
assert.strictEqual(events[0]!.hash, events[1]!.hash, 'identical cmd+params produce identical hash');
|
|
79
|
+
} finally {
|
|
80
|
+
cleanupDir(base);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('workflow-events: different params → different hash', () => {
|
|
85
|
+
const base = tempDir();
|
|
86
|
+
try {
|
|
87
|
+
appendEvent(base, makeEvent('complete-task', { taskId: 'T01' }));
|
|
88
|
+
appendEvent(base, makeEvent('complete-task', { taskId: 'T02' }));
|
|
89
|
+
const events = readEvents(path.join(base, '.gsd', 'event-log.jsonl'));
|
|
90
|
+
assert.notStrictEqual(events[0]!.hash, events[1]!.hash, 'different params produce different hash');
|
|
91
|
+
} finally {
|
|
92
|
+
cleanupDir(base);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ─── readEvents ──────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
test('workflow-events: readEvents returns [] for non-existent file', () => {
|
|
99
|
+
const result = readEvents('/nonexistent/path/event-log.jsonl');
|
|
100
|
+
assert.deepStrictEqual(result, []);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('workflow-events: readEvents skips corrupted lines', () => {
|
|
104
|
+
const base = tempDir();
|
|
105
|
+
try {
|
|
106
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
107
|
+
const logPath = path.join(base, '.gsd', 'event-log.jsonl');
|
|
108
|
+
// Write a valid line, a corrupted line, and another valid line
|
|
109
|
+
fs.writeFileSync(logPath,
|
|
110
|
+
'{"cmd":"complete-task","params":{},"ts":"2026-01-01T00:00:00Z","hash":"abcd1234abcd1234","actor":"agent"}\n' +
|
|
111
|
+
'NOT VALID JSON {{{{\n' +
|
|
112
|
+
'{"cmd":"plan-task","params":{},"ts":"2026-01-01T00:00:01Z","hash":"1234abcd1234abcd","actor":"system"}\n',
|
|
113
|
+
);
|
|
114
|
+
const events = readEvents(logPath);
|
|
115
|
+
assert.strictEqual(events.length, 2, 'should return 2 valid events, skipping the corrupted line');
|
|
116
|
+
assert.strictEqual(events[0]!.cmd, 'complete-task');
|
|
117
|
+
assert.strictEqual(events[1]!.cmd, 'plan-task');
|
|
118
|
+
} finally {
|
|
119
|
+
cleanupDir(base);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ─── findForkPoint ───────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
test('workflow-events: findForkPoint returns -1 for two empty logs', () => {
|
|
126
|
+
assert.strictEqual(findForkPoint([], []), -1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('workflow-events: findForkPoint returns -1 when first events differ', () => {
|
|
130
|
+
const e1 = { cmd: 'a', params: {}, ts: '', hash: 'hash1', actor: 'agent' } as WorkflowEvent;
|
|
131
|
+
const e2 = { cmd: 'b', params: {}, ts: '', hash: 'hash2', actor: 'agent' } as WorkflowEvent;
|
|
132
|
+
assert.strictEqual(findForkPoint([e1], [e2]), -1);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('workflow-events: findForkPoint returns 0 when only first event is common', () => {
|
|
136
|
+
const common = { cmd: 'a', params: {}, ts: '', hash: 'hash1', actor: 'agent' } as WorkflowEvent;
|
|
137
|
+
const eA = { cmd: 'b', params: {}, ts: '', hash: 'hash2', actor: 'agent' } as WorkflowEvent;
|
|
138
|
+
const eB = { cmd: 'c', params: {}, ts: '', hash: 'hash3', actor: 'agent' } as WorkflowEvent;
|
|
139
|
+
// logA: [common, eA], logB: [common, eB]
|
|
140
|
+
assert.strictEqual(findForkPoint([common, eA], [common, eB]), 0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('workflow-events: findForkPoint returns last common index for prefix relationship', () => {
|
|
144
|
+
const e1 = { cmd: 'a', params: {}, ts: '', hash: 'h1', actor: 'agent' } as WorkflowEvent;
|
|
145
|
+
const e2 = { cmd: 'b', params: {}, ts: '', hash: 'h2', actor: 'agent' } as WorkflowEvent;
|
|
146
|
+
const e3 = { cmd: 'c', params: {}, ts: '', hash: 'h3', actor: 'agent' } as WorkflowEvent;
|
|
147
|
+
// logA is a prefix of logB → fork point is last index of logA
|
|
148
|
+
assert.strictEqual(findForkPoint([e1, e2], [e1, e2, e3]), 1);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('workflow-events: findForkPoint handles equal logs', () => {
|
|
152
|
+
const e1 = { cmd: 'a', params: {}, ts: '', hash: 'h1', actor: 'agent' } as WorkflowEvent;
|
|
153
|
+
const e2 = { cmd: 'b', params: {}, ts: '', hash: 'h2', actor: 'agent' } as WorkflowEvent;
|
|
154
|
+
assert.strictEqual(findForkPoint([e1, e2], [e1, e2]), 1);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// ─── compactMilestoneEvents ──────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
test('workflow-events: compactMilestoneEvents returns { archived: 0 } when no matching events', () => {
|
|
160
|
+
const base = tempDir();
|
|
161
|
+
try {
|
|
162
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M002', taskId: 'T01' }));
|
|
163
|
+
const result = compactMilestoneEvents(base, 'M001');
|
|
164
|
+
assert.strictEqual(result.archived, 0);
|
|
165
|
+
} finally {
|
|
166
|
+
cleanupDir(base);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('workflow-events: compactMilestoneEvents archives milestone events', () => {
|
|
171
|
+
const base = tempDir();
|
|
172
|
+
try {
|
|
173
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M001', taskId: 'T01' }));
|
|
174
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M001', taskId: 'T02' }));
|
|
175
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M002', taskId: 'T03' }));
|
|
176
|
+
|
|
177
|
+
const result = compactMilestoneEvents(base, 'M001');
|
|
178
|
+
assert.strictEqual(result.archived, 2, 'should archive 2 M001 events');
|
|
179
|
+
|
|
180
|
+
// Archive file should exist
|
|
181
|
+
const archivePath = path.join(base, '.gsd', 'event-log-M001.jsonl.archived');
|
|
182
|
+
assert.ok(fs.existsSync(archivePath), 'archive file should exist');
|
|
183
|
+
const archived = readEvents(archivePath);
|
|
184
|
+
assert.strictEqual(archived.length, 2, 'archive file should have 2 events');
|
|
185
|
+
|
|
186
|
+
// Active log should retain only M002 event
|
|
187
|
+
const active = readEvents(path.join(base, '.gsd', 'event-log.jsonl'));
|
|
188
|
+
assert.strictEqual(active.length, 1, 'active log should have 1 remaining event');
|
|
189
|
+
assert.strictEqual((active[0]!.params as { milestoneId?: string }).milestoneId, 'M002');
|
|
190
|
+
} finally {
|
|
191
|
+
cleanupDir(base);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('workflow-events: compactMilestoneEvents empties active log when all events are from milestone', () => {
|
|
196
|
+
const base = tempDir();
|
|
197
|
+
try {
|
|
198
|
+
appendEvent(base, makeEvent('complete-task', { milestoneId: 'M001', taskId: 'T01' }));
|
|
199
|
+
compactMilestoneEvents(base, 'M001');
|
|
200
|
+
const active = readEvents(path.join(base, '.gsd', 'event-log.jsonl'));
|
|
201
|
+
assert.strictEqual(active.length, 0, 'active log should be empty after full compact');
|
|
202
|
+
} finally {
|
|
203
|
+
cleanupDir(base);
|
|
204
|
+
}
|
|
205
|
+
});
|