gsd-pi 2.70.1 → 2.71.0-dev.4c35d99
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 +57 -17
- package/dist/cli.js +29 -3
- package/dist/headless-events.d.ts +2 -0
- package/dist/headless-events.js +7 -0
- package/dist/headless.js +16 -3
- package/dist/mcp-server.js +40 -17
- package/dist/provider-migrations.d.ts +10 -0
- package/dist/provider-migrations.js +12 -0
- package/dist/resource-loader.js +139 -13
- package/dist/resources/GSD-WORKFLOW.md +1 -1
- package/dist/resources/agents/debugger.md +58 -0
- package/dist/resources/agents/doc-writer.md +43 -0
- package/dist/resources/agents/git-ops.md +56 -0
- package/dist/resources/agents/javascript-pro.md +46 -271
- package/dist/resources/agents/planner.md +55 -0
- package/dist/resources/agents/refactorer.md +47 -0
- package/dist/resources/agents/reviewer.md +48 -0
- package/dist/resources/agents/security.md +59 -0
- package/dist/resources/agents/tester.md +50 -0
- package/dist/resources/agents/typescript-pro.md +41 -235
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +242 -40
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +5 -1
- package/dist/resources/extensions/gsd/auto/session.js +11 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
- package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
- package/dist/resources/extensions/gsd/auto-start.js +37 -18
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +56 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
- package/dist/resources/extensions/gsd/commands/context.js +15 -6
- package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
- package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
- package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/forensics.js +19 -6
- package/dist/resources/extensions/gsd/gate-registry.js +208 -0
- package/dist/resources/extensions/gsd/gsd-db.js +41 -0
- package/dist/resources/extensions/gsd/guided-flow.js +17 -20
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/metrics.js +1 -0
- package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
- package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
- package/dist/resources/extensions/gsd/notification-store.js +56 -5
- package/dist/resources/extensions/gsd/notification-widget.js +5 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
- package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +5 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/dist/resources/extensions/gsd/prompts/execute-task.md +22 -19
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
- package/dist/resources/extensions/gsd/session-model-override.js +25 -0
- package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
- package/dist/resources/extensions/gsd/state.js +241 -332
- package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +38 -1
- package/dist/resources/extensions/gsd/workflow-events.js +25 -13
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +1 -1
- package/dist/resources/extensions/ollama/index.js +13 -5
- package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
- package/dist/resources/extensions/subagent/agents.js +8 -0
- package/dist/resources/extensions/subagent/index.js +17 -0
- package/dist/resources/skills/create-skill/SKILL.md +2 -0
- package/dist/startup-model-validation.d.ts +0 -1
- package/dist/startup-model-validation.js +6 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- 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.js +2 -2
- 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 +3 -3
- 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/boot/route.js +1 -1
- 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.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.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.js +2 -2
- 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.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.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.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.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.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/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.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.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.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.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.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.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.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.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.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/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.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.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.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.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.js +2 -2
- 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.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.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.js +2 -2
- 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.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.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.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.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.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.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.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.js +2 -2
- 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.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.js +2 -2
- 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.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.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.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.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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- 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/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +23 -3
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +192 -44
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +22 -12
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +247 -41
- package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
- package/packages/mcp-server/src/workflow-tools.ts +32 -12
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +7 -4
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
- package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
- package/packages/pi-ai/src/providers/anthropic.ts +8 -4
- package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +388 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
- 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/model-resolver-initial-model-auth.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -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 +43 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +175 -25
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- 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 +62 -5
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +468 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
- package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +205 -31
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +70 -5
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
- package/packages/pi-tui/dist/components/input.d.ts +2 -0
- package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/input.js +7 -4
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +17 -1
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
- package/packages/pi-tui/src/components/input.ts +7 -4
- package/packages/pi-tui/src/components/markdown.ts +22 -1
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +1 -1
- package/src/resources/agents/debugger.md +58 -0
- package/src/resources/agents/doc-writer.md +43 -0
- package/src/resources/agents/git-ops.md +56 -0
- package/src/resources/agents/javascript-pro.md +46 -271
- package/src/resources/agents/planner.md +55 -0
- package/src/resources/agents/refactorer.md +47 -0
- package/src/resources/agents/reviewer.md +48 -0
- package/src/resources/agents/security.md +59 -0
- package/src/resources/agents/tester.md +50 -0
- package/src/resources/agents/typescript-pro.md +41 -235
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +288 -39
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +330 -2
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop.ts +45 -1
- package/src/resources/extensions/gsd/auto/phases.ts +6 -0
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
- package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
- package/src/resources/extensions/gsd/auto-start.ts +44 -20
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +72 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
- package/src/resources/extensions/gsd/commands/context.ts +16 -5
- package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
- package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
- package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/forensics.ts +23 -7
- package/src/resources/extensions/gsd/gate-registry.ts +251 -0
- package/src/resources/extensions/gsd/gsd-db.ts +51 -0
- package/src/resources/extensions/gsd/guided-flow.ts +17 -19
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/metrics.ts +12 -1
- package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
- package/src/resources/extensions/gsd/notification-store.ts +54 -5
- package/src/resources/extensions/gsd/notification-widget.ts +5 -14
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
- package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
- package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +5 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/src/resources/extensions/gsd/prompts/execute-task.md +22 -19
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +3 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
- package/src/resources/extensions/gsd/session-model-override.ts +36 -0
- package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
- package/src/resources/extensions/gsd/state.ts +285 -344
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +64 -26
- package/src/resources/extensions/gsd/types.ts +26 -0
- package/src/resources/extensions/gsd/workflow-events.ts +34 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
- package/src/resources/extensions/ollama/index.ts +13 -3
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
- package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
- package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
- package/src/resources/extensions/subagent/agents.ts +10 -0
- package/src/resources/extensions/subagent/index.ts +18 -0
- package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
- package/src/resources/skills/create-skill/SKILL.md +2 -0
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_ssgManifest.js +0 -0
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
insertSlice,
|
|
59
59
|
insertTask,
|
|
60
60
|
updateTaskStatus,
|
|
61
|
-
|
|
61
|
+
getPendingGateCountForTurn,
|
|
62
62
|
type MilestoneRow,
|
|
63
63
|
type SliceRow,
|
|
64
64
|
type TaskRow,
|
|
@@ -322,17 +322,8 @@ const isStatusDone = isClosedStatus;
|
|
|
322
322
|
*
|
|
323
323
|
* Must produce field-identical GSDState to _deriveStateImpl() for the same project.
|
|
324
324
|
*/
|
|
325
|
-
|
|
326
|
-
const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
|
|
327
|
-
|
|
325
|
+
function reconcileDiskToDb(basePath: string): MilestoneRow[] {
|
|
328
326
|
let allMilestones = getAllMilestones();
|
|
329
|
-
|
|
330
|
-
// Incremental disk→DB sync: milestone directories created outside the DB
|
|
331
|
-
// write path (via /gsd queue, manual mkdir, or complete-milestone writing the
|
|
332
|
-
// next CONTEXT.md) are never inserted by the initial migration guard in
|
|
333
|
-
// auto-start.ts because that guard only runs when gsd.db doesn't exist yet.
|
|
334
|
-
// Reconcile here so deriveStateFromDb never silently misses queued milestones.
|
|
335
|
-
// insertMilestone uses INSERT OR IGNORE, so this is safe to call every time.
|
|
336
327
|
const dbIdSet = new Set(allMilestones.map(m => m.id));
|
|
337
328
|
const diskIds = findMilestoneIds(basePath);
|
|
338
329
|
let synced = false;
|
|
@@ -344,11 +335,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
344
335
|
}
|
|
345
336
|
if (synced) allMilestones = getAllMilestones();
|
|
346
337
|
|
|
347
|
-
// Disk→DB slice reconciliation (#2533): slices defined in ROADMAP.md but
|
|
348
|
-
// missing from the DB cause permanent "No slice eligible" blocks because
|
|
349
|
-
// the dependency resolver only sees DB rows. Parse each milestone's roadmap
|
|
350
|
-
// and insert any missing slices, checking SUMMARY files to set correct status.
|
|
351
|
-
// insertSlice uses INSERT OR IGNORE, so existing rows are never overwritten.
|
|
352
338
|
for (const mid of diskIds) {
|
|
353
339
|
if (isGhostMilestone(basePath, mid)) continue;
|
|
354
340
|
const roadmapPath = resolveMilestoneFile(basePath, mid, "ROADMAP");
|
|
@@ -373,93 +359,43 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
373
359
|
});
|
|
374
360
|
}
|
|
375
361
|
}
|
|
362
|
+
return allMilestones;
|
|
363
|
+
}
|
|
376
364
|
|
|
377
|
-
|
|
378
|
-
// the DB. This happens when milestones were created before the DB migration
|
|
379
|
-
// or were manually added to the filesystem. Without this, disk-only
|
|
380
|
-
// milestones are invisible after migration (#2416).
|
|
381
|
-
const dbMilestoneIds = new Set(allMilestones.map(m => m.id));
|
|
382
|
-
const diskMilestoneIds = findMilestoneIds(basePath);
|
|
383
|
-
for (const diskId of diskMilestoneIds) {
|
|
384
|
-
if (!dbMilestoneIds.has(diskId)) {
|
|
385
|
-
// Synthesize a minimal MilestoneRow for the disk-only milestone.
|
|
386
|
-
// Title and status will be resolved from disk files in the loop below.
|
|
387
|
-
allMilestones.push({
|
|
388
|
-
id: diskId,
|
|
389
|
-
title: diskId,
|
|
390
|
-
status: 'active',
|
|
391
|
-
depends_on: [] as string[],
|
|
392
|
-
created_at: new Date().toISOString(),
|
|
393
|
-
} as MilestoneRow);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
// Re-sort so milestones follow queue order (same as dispatch guard) (#2556)
|
|
397
|
-
const customOrder = loadQueueOrder(basePath);
|
|
398
|
-
const sortedIds = sortByQueueOrder(allMilestones.map(m => m.id), customOrder);
|
|
399
|
-
const byId = new Map(allMilestones.map(m => [m.id, m]));
|
|
400
|
-
allMilestones.length = 0;
|
|
401
|
-
for (const id of sortedIds) allMilestones.push(byId.get(id)!);
|
|
402
|
-
|
|
403
|
-
// Parallel worker isolation: when locked, filter to just the locked milestone
|
|
404
|
-
const milestoneLock = process.env.GSD_MILESTONE_LOCK;
|
|
405
|
-
const milestones = milestoneLock
|
|
406
|
-
? allMilestones.filter(m => m.id === milestoneLock)
|
|
407
|
-
: allMilestones;
|
|
408
|
-
|
|
409
|
-
if (milestones.length === 0) {
|
|
410
|
-
return {
|
|
411
|
-
activeMilestone: null,
|
|
412
|
-
activeSlice: null,
|
|
413
|
-
activeTask: null,
|
|
414
|
-
phase: 'pre-planning',
|
|
415
|
-
recentDecisions: [],
|
|
416
|
-
blockers: [],
|
|
417
|
-
nextAction: 'No milestones found. Run /gsd to create one.',
|
|
418
|
-
registry: [],
|
|
419
|
-
requirements,
|
|
420
|
-
progress: { milestones: { done: 0, total: 0 } },
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Phase 1: Build completeness set (which milestones count as "done" for dep resolution)
|
|
365
|
+
function buildCompletenessSet(basePath: string, milestones: MilestoneRow[]) {
|
|
425
366
|
const completeMilestoneIds = new Set<string>();
|
|
426
367
|
const parkedMilestoneIds = new Set<string>();
|
|
427
368
|
|
|
428
369
|
for (const m of milestones) {
|
|
429
|
-
// Check disk for PARKED flag (not stored in DB status reliably — disk is truth for flag files)
|
|
430
370
|
const parkedFile = resolveMilestoneFile(basePath, m.id, "PARKED");
|
|
431
371
|
if (parkedFile || m.status === 'parked') {
|
|
432
372
|
parkedMilestoneIds.add(m.id);
|
|
433
373
|
continue;
|
|
434
374
|
}
|
|
435
|
-
|
|
436
375
|
if (isStatusDone(m.status)) {
|
|
437
376
|
completeMilestoneIds.add(m.id);
|
|
438
377
|
continue;
|
|
439
378
|
}
|
|
440
|
-
|
|
441
|
-
// Check if milestone has a summary on disk (terminal artifact per #864)
|
|
442
379
|
const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
|
|
443
380
|
if (summaryFile) {
|
|
444
381
|
completeMilestoneIds.add(m.id);
|
|
445
382
|
continue;
|
|
446
383
|
}
|
|
447
|
-
|
|
448
|
-
// Milestones with all slices done but no SUMMARY file are in
|
|
449
|
-
// validating/completing state — intentionally NOT added to
|
|
450
|
-
// completeMilestoneIds. The SUMMARY file (checked above) is the
|
|
451
|
-
// terminal artifact that proves completion per #864.
|
|
452
384
|
}
|
|
385
|
+
return { completeMilestoneIds, parkedMilestoneIds };
|
|
386
|
+
}
|
|
453
387
|
|
|
454
|
-
|
|
388
|
+
async function buildRegistryAndFindActive(
|
|
389
|
+
basePath: string,
|
|
390
|
+
milestones: MilestoneRow[],
|
|
391
|
+
completeMilestoneIds: Set<string>,
|
|
392
|
+
parkedMilestoneIds: Set<string>
|
|
393
|
+
) {
|
|
455
394
|
const registry: MilestoneRegistryEntry[] = [];
|
|
456
395
|
let activeMilestone: ActiveRef | null = null;
|
|
457
396
|
let activeMilestoneSlices: SliceRow[] = [];
|
|
458
397
|
let activeMilestoneFound = false;
|
|
459
398
|
let activeMilestoneHasDraft = false;
|
|
460
|
-
// Queued shells (DB row, no slices, no content files) are deferred during
|
|
461
|
-
// the main loop so they don't eclipse real active milestones (#3470).
|
|
462
|
-
// If no real active milestone is found, the first deferred shell is promoted.
|
|
463
399
|
let firstDeferredQueuedShell: { id: string; title: string; deps: string[] } | null = null;
|
|
464
400
|
|
|
465
401
|
for (const m of milestones) {
|
|
@@ -468,19 +404,14 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
468
404
|
continue;
|
|
469
405
|
}
|
|
470
406
|
|
|
471
|
-
// Ghost milestone check: no slices in DB AND no substantive files on disk.
|
|
472
|
-
// Skip queued milestones — they are handled by the deferred-shell logic below (#3470).
|
|
473
407
|
const slices = getMilestoneSlices(m.id);
|
|
474
408
|
if (slices.length === 0 && !isStatusDone(m.status) && m.status !== 'queued') {
|
|
475
|
-
// Check disk for ghost detection
|
|
476
409
|
if (isGhostMilestone(basePath, m.id)) continue;
|
|
477
410
|
}
|
|
478
411
|
|
|
479
412
|
const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
|
|
480
413
|
|
|
481
|
-
// Determine if this milestone is complete
|
|
482
414
|
if (completeMilestoneIds.has(m.id) || (summaryFile !== null)) {
|
|
483
|
-
// Get title from DB or summary
|
|
484
415
|
let title = stripMilestonePrefix(m.title) || m.id;
|
|
485
416
|
if (summaryFile && !m.title) {
|
|
486
417
|
const summaryContent = await loadFile(summaryFile);
|
|
@@ -489,14 +420,12 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
489
420
|
}
|
|
490
421
|
}
|
|
491
422
|
registry.push({ id: m.id, title, status: 'complete' });
|
|
492
|
-
completeMilestoneIds.add(m.id);
|
|
423
|
+
completeMilestoneIds.add(m.id);
|
|
493
424
|
continue;
|
|
494
425
|
}
|
|
495
426
|
|
|
496
|
-
// Not complete — determine if it should be active
|
|
497
427
|
const allSlicesDone = slices.length > 0 && slices.every(s => isStatusDone(s.status));
|
|
498
428
|
|
|
499
|
-
// Get title — prefer DB, fall back to context file extraction
|
|
500
429
|
let title = stripMilestonePrefix(m.title) || m.id;
|
|
501
430
|
if (title === m.id) {
|
|
502
431
|
const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
@@ -507,7 +436,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
507
436
|
}
|
|
508
437
|
|
|
509
438
|
if (!activeMilestoneFound) {
|
|
510
|
-
// Check milestone-level dependencies
|
|
511
439
|
const deps = m.depends_on;
|
|
512
440
|
const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
|
|
513
441
|
|
|
@@ -516,11 +444,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
516
444
|
continue;
|
|
517
445
|
}
|
|
518
446
|
|
|
519
|
-
// Defer queued shell milestones with no substantive content (#3470).
|
|
520
|
-
// A queued milestone with no slices and no context/draft file is a
|
|
521
|
-
// placeholder that should not block later real active milestones.
|
|
522
|
-
// If no real active milestone is found after the loop, the first
|
|
523
|
-
// deferred shell is promoted to active (#2921).
|
|
524
447
|
if (m.status === 'queued' && slices.length === 0) {
|
|
525
448
|
const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
526
449
|
const draftFile = resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
|
|
@@ -533,14 +456,12 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
533
456
|
}
|
|
534
457
|
}
|
|
535
458
|
|
|
536
|
-
// Handle all-slices-done case (validating/completing)
|
|
537
459
|
if (allSlicesDone) {
|
|
538
460
|
const validationFile = resolveMilestoneFile(basePath, m.id, "VALIDATION");
|
|
539
461
|
const validationContent = validationFile ? await loadFile(validationFile) : null;
|
|
540
462
|
const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
|
|
541
463
|
|
|
542
464
|
if (!validationTerminal || (validationTerminal && !summaryFile)) {
|
|
543
|
-
// Validating or completing — still active
|
|
544
465
|
activeMilestone = { id: m.id, title };
|
|
545
466
|
activeMilestoneSlices = slices;
|
|
546
467
|
activeMilestoneFound = true;
|
|
@@ -549,7 +470,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
549
470
|
}
|
|
550
471
|
}
|
|
551
472
|
|
|
552
|
-
// Check for context draft (needs-discussion phase)
|
|
553
473
|
const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
554
474
|
const draftFile = resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
|
|
555
475
|
if (!contextFile && draftFile) activeMilestoneHasDraft = true;
|
|
@@ -559,13 +479,11 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
559
479
|
activeMilestoneFound = true;
|
|
560
480
|
registry.push({ id: m.id, title, status: 'active', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
|
|
561
481
|
} else {
|
|
562
|
-
// After active milestone found — rest are pending
|
|
563
482
|
const deps = m.depends_on;
|
|
564
483
|
registry.push({ id: m.id, title, status: 'pending', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
|
|
565
484
|
}
|
|
566
485
|
}
|
|
567
486
|
|
|
568
|
-
// Promote deferred queued shell if no real active milestone was found (#3470/#2921).
|
|
569
487
|
if (!activeMilestoneFound && firstDeferredQueuedShell) {
|
|
570
488
|
const shell = firstDeferredQueuedShell;
|
|
571
489
|
activeMilestone = { id: shell.id, title: shell.title };
|
|
@@ -575,221 +493,141 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
575
493
|
if (entry) entry.status = 'active';
|
|
576
494
|
}
|
|
577
495
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
total: registry.length,
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
// ── No active milestone ──────────────────────────────────────────────
|
|
584
|
-
if (!activeMilestone) {
|
|
585
|
-
const pendingEntries = registry.filter(e => e.status === 'pending');
|
|
586
|
-
const parkedEntries = registry.filter(e => e.status === 'parked');
|
|
587
|
-
|
|
588
|
-
if (pendingEntries.length > 0) {
|
|
589
|
-
const blockerDetails = pendingEntries
|
|
590
|
-
.filter(e => e.dependsOn && e.dependsOn.length > 0)
|
|
591
|
-
.map(e => `${e.id} is waiting on unmet deps: ${e.dependsOn!.join(', ')}`);
|
|
592
|
-
return {
|
|
593
|
-
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
594
|
-
phase: 'blocked',
|
|
595
|
-
recentDecisions: [], blockers: blockerDetails.length > 0
|
|
596
|
-
? blockerDetails
|
|
597
|
-
: ['All remaining milestones are dep-blocked but no deps listed — check CONTEXT.md files'],
|
|
598
|
-
nextAction: 'Resolve milestone dependencies before proceeding.',
|
|
599
|
-
registry, requirements,
|
|
600
|
-
progress: { milestones: milestoneProgress },
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (parkedEntries.length > 0) {
|
|
605
|
-
const parkedIds = parkedEntries.map(e => e.id).join(', ');
|
|
606
|
-
return {
|
|
607
|
-
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
608
|
-
phase: 'pre-planning',
|
|
609
|
-
recentDecisions: [], blockers: [],
|
|
610
|
-
nextAction: `All remaining milestones are parked (${parkedIds}). Run /gsd unpark <id> or create a new milestone.`,
|
|
611
|
-
registry, requirements,
|
|
612
|
-
progress: { milestones: milestoneProgress },
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (registry.length === 0) {
|
|
617
|
-
return {
|
|
618
|
-
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
619
|
-
phase: 'pre-planning',
|
|
620
|
-
recentDecisions: [], blockers: [],
|
|
621
|
-
nextAction: 'No milestones found. Run /gsd to create one.',
|
|
622
|
-
registry: [], requirements,
|
|
623
|
-
progress: { milestones: { done: 0, total: 0 } },
|
|
624
|
-
};
|
|
625
|
-
}
|
|
496
|
+
return { registry, activeMilestone, activeMilestoneSlices, activeMilestoneHasDraft };
|
|
497
|
+
}
|
|
626
498
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
499
|
+
function handleNoActiveMilestone(
|
|
500
|
+
registry: MilestoneRegistryEntry[],
|
|
501
|
+
requirements: any,
|
|
502
|
+
milestoneProgress: { done: number, total: number }
|
|
503
|
+
): GSDState {
|
|
504
|
+
const pendingEntries = registry.filter(e => e.status === 'pending');
|
|
505
|
+
const parkedEntries = registry.filter(e => e.status === 'parked');
|
|
506
|
+
|
|
507
|
+
if (pendingEntries.length > 0) {
|
|
508
|
+
const blockerDetails = pendingEntries
|
|
509
|
+
.filter(e => e.dependsOn && e.dependsOn.length > 0)
|
|
510
|
+
.map(e => `${e.id} is waiting on unmet deps: ${e.dependsOn!.join(', ')}`);
|
|
633
511
|
return {
|
|
634
|
-
activeMilestone: null,
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
nextAction:
|
|
512
|
+
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
513
|
+
phase: 'blocked',
|
|
514
|
+
recentDecisions: [], blockers: blockerDetails.length > 0
|
|
515
|
+
? blockerDetails
|
|
516
|
+
: ['All remaining milestones are dep-blocked but no deps listed — check CONTEXT.md files'],
|
|
517
|
+
nextAction: 'Resolve milestone dependencies before proceeding.',
|
|
640
518
|
registry, requirements,
|
|
641
519
|
progress: { milestones: milestoneProgress },
|
|
642
520
|
};
|
|
643
521
|
}
|
|
644
522
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if (activeMilestoneSlices.length === 0) {
|
|
649
|
-
if (!hasRoadmap) {
|
|
650
|
-
const phase = activeMilestoneHasDraft ? 'needs-discussion' as const : 'pre-planning' as const;
|
|
651
|
-
const nextAction = activeMilestoneHasDraft
|
|
652
|
-
? `Discuss draft context for milestone ${activeMilestone.id}.`
|
|
653
|
-
: `Plan milestone ${activeMilestone.id}.`;
|
|
654
|
-
return {
|
|
655
|
-
activeMilestone, activeSlice: null, activeTask: null,
|
|
656
|
-
phase, recentDecisions: [], blockers: [],
|
|
657
|
-
nextAction, registry, requirements,
|
|
658
|
-
progress: { milestones: milestoneProgress },
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Has roadmap file but zero slices in DB — pre-planning (zero-slice roadmap guard)
|
|
523
|
+
if (parkedEntries.length > 0) {
|
|
524
|
+
const parkedIds = parkedEntries.map(e => e.id).join(', ');
|
|
663
525
|
return {
|
|
664
|
-
activeMilestone, activeSlice: null, activeTask: null,
|
|
526
|
+
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
665
527
|
phase: 'pre-planning',
|
|
666
528
|
recentDecisions: [], blockers: [],
|
|
667
|
-
nextAction: `
|
|
529
|
+
nextAction: `All remaining milestones are parked (${parkedIds}). Run /gsd unpark <id> or create a new milestone.`,
|
|
668
530
|
registry, requirements,
|
|
669
|
-
progress: {
|
|
670
|
-
milestones: milestoneProgress,
|
|
671
|
-
slices: { done: 0, total: 0 },
|
|
672
|
-
},
|
|
531
|
+
progress: { milestones: milestoneProgress },
|
|
673
532
|
};
|
|
674
533
|
}
|
|
675
534
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
done: activeMilestoneSlices.length,
|
|
685
|
-
total: activeMilestoneSlices.length,
|
|
535
|
+
if (registry.length === 0) {
|
|
536
|
+
return {
|
|
537
|
+
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
538
|
+
phase: 'pre-planning',
|
|
539
|
+
recentDecisions: [], blockers: [],
|
|
540
|
+
nextAction: 'No milestones found. Run /gsd to create one.',
|
|
541
|
+
registry: [], requirements,
|
|
542
|
+
progress: { milestones: { done: 0, total: 0 } },
|
|
686
543
|
};
|
|
544
|
+
}
|
|
687
545
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
546
|
+
const lastEntry = registry[registry.length - 1];
|
|
547
|
+
const activeReqs = requirements.active ?? 0;
|
|
548
|
+
const completionNote = activeReqs > 0
|
|
549
|
+
? `All milestones complete. ${activeReqs} active requirement${activeReqs === 1 ? '' : 's'} in REQUIREMENTS.md ${activeReqs === 1 ? 'has' : 'have'} not been mapped to a milestone.`
|
|
550
|
+
: 'All milestones complete.';
|
|
551
|
+
return {
|
|
552
|
+
activeMilestone: null,
|
|
553
|
+
lastCompletedMilestone: lastEntry ? { id: lastEntry.id, title: lastEntry.title } : null,
|
|
554
|
+
activeSlice: null, activeTask: null,
|
|
555
|
+
phase: 'complete',
|
|
556
|
+
recentDecisions: [], blockers: [],
|
|
557
|
+
nextAction: completionNote,
|
|
558
|
+
registry, requirements,
|
|
559
|
+
progress: { milestones: milestoneProgress },
|
|
560
|
+
};
|
|
561
|
+
}
|
|
700
562
|
|
|
563
|
+
async function handleAllSlicesDone(
|
|
564
|
+
basePath: string,
|
|
565
|
+
activeMilestone: ActiveRef,
|
|
566
|
+
registry: MilestoneRegistryEntry[],
|
|
567
|
+
requirements: any,
|
|
568
|
+
milestoneProgress: { done: number, total: number },
|
|
569
|
+
sliceProgress: { done: number, total: number }
|
|
570
|
+
): Promise<GSDState> {
|
|
571
|
+
const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
|
|
572
|
+
const validationContent = validationFile ? await loadFile(validationFile) : null;
|
|
573
|
+
const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
|
|
574
|
+
const verdict = validationContent ? extractVerdict(validationContent) : undefined;
|
|
575
|
+
|
|
576
|
+
if (!validationTerminal || verdict === 'needs-remediation') {
|
|
701
577
|
return {
|
|
702
578
|
activeMilestone, activeSlice: null, activeTask: null,
|
|
703
|
-
phase: '
|
|
579
|
+
phase: 'validating-milestone',
|
|
704
580
|
recentDecisions: [], blockers: [],
|
|
705
|
-
nextAction: `
|
|
581
|
+
nextAction: `Validate milestone ${activeMilestone.id} before completion.`,
|
|
706
582
|
registry, requirements,
|
|
707
583
|
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
708
584
|
};
|
|
709
585
|
}
|
|
710
586
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
587
|
+
return {
|
|
588
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
589
|
+
phase: 'completing-milestone',
|
|
590
|
+
recentDecisions: [], blockers: [],
|
|
591
|
+
nextAction: `All slices complete in ${activeMilestone.id}. Write milestone summary.`,
|
|
592
|
+
registry, requirements,
|
|
593
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
715
594
|
};
|
|
595
|
+
}
|
|
716
596
|
|
|
597
|
+
function resolveSliceDependencies(activeMilestoneSlices: SliceRow[]): { activeSlice: ActiveRef | null, activeSliceRow: SliceRow | null } {
|
|
717
598
|
const doneSliceIds = new Set(
|
|
718
599
|
activeMilestoneSlices.filter(s => isStatusDone(s.status)).map(s => s.id)
|
|
719
600
|
);
|
|
720
601
|
|
|
721
|
-
let activeSlice: ActiveRef | null = null;
|
|
722
|
-
let activeSliceRow: SliceRow | null = null;
|
|
723
|
-
|
|
724
|
-
// ── Slice-level parallel worker isolation ─────────────────────────────
|
|
725
|
-
// When GSD_SLICE_LOCK is set, this process is a parallel worker scoped
|
|
726
|
-
// to a single slice. Override activeSlice to only the locked slice ID.
|
|
727
602
|
const sliceLock = process.env.GSD_SLICE_LOCK;
|
|
728
603
|
if (sliceLock) {
|
|
729
604
|
const lockedSlice = activeMilestoneSlices.find(s => s.id === sliceLock);
|
|
730
605
|
if (lockedSlice) {
|
|
731
|
-
activeSlice
|
|
732
|
-
activeSliceRow = lockedSlice;
|
|
606
|
+
return { activeSlice: { id: lockedSlice.id, title: lockedSlice.title }, activeSliceRow: lockedSlice };
|
|
733
607
|
} else {
|
|
734
608
|
logWarning("state", `GSD_SLICE_LOCK=${sliceLock} not found in active slices — worker has no assigned work`);
|
|
735
|
-
|
|
736
|
-
return {
|
|
737
|
-
activeMilestone, activeSlice: null, activeTask: null,
|
|
738
|
-
phase: 'blocked',
|
|
739
|
-
recentDecisions: [], blockers: [`GSD_SLICE_LOCK=${sliceLock} not found in active milestone slices`],
|
|
740
|
-
nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
|
|
741
|
-
registry, requirements,
|
|
742
|
-
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
743
|
-
};
|
|
609
|
+
return { activeSlice: null, activeSliceRow: null };
|
|
744
610
|
}
|
|
745
|
-
} else {
|
|
746
|
-
for (const s of activeMilestoneSlices) {
|
|
747
|
-
if (isStatusDone(s.status)) continue;
|
|
748
|
-
// #2661: Skip deferred slices — a decision explicitly deferred this work.
|
|
749
|
-
// Without this guard the dispatcher would keep dispatching deferred slices
|
|
750
|
-
// because DECISIONS.md is only contextual, not authoritative for dispatch.
|
|
751
|
-
if (isDeferredStatus(s.status)) continue;
|
|
752
|
-
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
753
|
-
activeSlice = { id: s.id, title: s.title };
|
|
754
|
-
activeSliceRow = s;
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
if (!activeSlice) {
|
|
761
|
-
return {
|
|
762
|
-
activeMilestone, activeSlice: null, activeTask: null,
|
|
763
|
-
phase: 'blocked',
|
|
764
|
-
recentDecisions: [], blockers: ['No slice eligible — check dependency ordering'],
|
|
765
|
-
nextAction: 'Resolve dependency blockers or plan next slice.',
|
|
766
|
-
registry, requirements,
|
|
767
|
-
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
768
|
-
};
|
|
769
611
|
}
|
|
770
612
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
recentDecisions: [], blockers: [],
|
|
778
|
-
nextAction: `Plan slice ${activeSlice.id} (${activeSlice.title}).`,
|
|
779
|
-
registry, requirements,
|
|
780
|
-
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
781
|
-
};
|
|
613
|
+
for (const s of activeMilestoneSlices) {
|
|
614
|
+
if (isStatusDone(s.status)) continue;
|
|
615
|
+
if (isDeferredStatus(s.status)) continue;
|
|
616
|
+
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
617
|
+
return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
|
|
618
|
+
}
|
|
782
619
|
}
|
|
620
|
+
return { activeSlice: null, activeSliceRow: null };
|
|
621
|
+
}
|
|
783
622
|
|
|
784
|
-
|
|
785
|
-
|
|
623
|
+
async function reconcileSliceTasks(
|
|
624
|
+
basePath: string,
|
|
625
|
+
milestoneId: string,
|
|
626
|
+
sliceId: string,
|
|
627
|
+
planFile: string
|
|
628
|
+
): Promise<TaskRow[]> {
|
|
629
|
+
let tasks = getSliceTasks(milestoneId, sliceId);
|
|
786
630
|
|
|
787
|
-
// ── Reconcile missing tasks: plan file has tasks but DB is empty (#3600) ──
|
|
788
|
-
// When the planning agent writes S##-PLAN.md with task entries but never
|
|
789
|
-
// calls the gsd_plan_slice persistence tool, the DB has zero task rows
|
|
790
|
-
// even though the plan file contains valid tasks. Without this reconciliation,
|
|
791
|
-
// deriveState returns phase='planning' forever — the dispatcher re-dispatches
|
|
792
|
-
// plan-slice in an infinite loop.
|
|
793
631
|
if (tasks.length === 0 && planFile) {
|
|
794
632
|
try {
|
|
795
633
|
const planContent = await loadFile(planFile);
|
|
@@ -801,53 +639,188 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
801
639
|
try {
|
|
802
640
|
insertTask({
|
|
803
641
|
id: t.id,
|
|
804
|
-
sliceId
|
|
805
|
-
milestoneId
|
|
642
|
+
sliceId,
|
|
643
|
+
milestoneId,
|
|
806
644
|
title: t.title,
|
|
807
645
|
status: t.done ? 'complete' : 'pending',
|
|
808
646
|
sequence: i + 1,
|
|
809
647
|
});
|
|
810
648
|
} catch (insertErr) {
|
|
811
|
-
// Task may already exist from a partial previous import — skip
|
|
812
649
|
logWarning("reconcile", `failed to insert task ${t.id} from plan file: ${insertErr instanceof Error ? insertErr.message : String(insertErr)}`);
|
|
813
650
|
}
|
|
814
651
|
}
|
|
815
|
-
tasks = getSliceTasks(
|
|
816
|
-
logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${
|
|
652
|
+
tasks = getSliceTasks(milestoneId, sliceId);
|
|
653
|
+
logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${milestoneId}/${sliceId} — DB was empty (#3600)`, { mid: milestoneId, sid: sliceId });
|
|
817
654
|
}
|
|
818
655
|
}
|
|
819
656
|
} catch (err) {
|
|
820
|
-
|
|
821
|
-
logError("reconcile", `plan-file task import failed for ${activeMilestone.id}/${activeSlice.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
657
|
+
logError("reconcile", `plan-file task import failed for ${milestoneId}/${sliceId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
822
658
|
}
|
|
823
659
|
}
|
|
824
660
|
|
|
825
|
-
// ── Reconcile stale task status (#2514) ──────────────────────────────
|
|
826
|
-
// When a session disconnects after the agent writes SUMMARY + VERIFY
|
|
827
|
-
// artifacts but before postUnitPostVerification updates the DB, tasks
|
|
828
|
-
// remain "pending" in the DB despite being complete on disk. Without
|
|
829
|
-
// reconciliation, deriveState keeps returning the stale task as active,
|
|
830
|
-
// causing the dispatcher to re-dispatch the same completed task forever.
|
|
831
661
|
let reconciled = false;
|
|
832
662
|
for (const t of tasks) {
|
|
833
663
|
if (isStatusDone(t.status)) continue;
|
|
834
|
-
const summaryPath = resolveTaskFile(basePath,
|
|
664
|
+
const summaryPath = resolveTaskFile(basePath, milestoneId, sliceId, t.id, "SUMMARY");
|
|
835
665
|
if (summaryPath && existsSync(summaryPath)) {
|
|
836
666
|
try {
|
|
837
|
-
updateTaskStatus(
|
|
838
|
-
logWarning("reconcile", `task ${
|
|
667
|
+
updateTaskStatus(milestoneId, sliceId, t.id, "complete");
|
|
668
|
+
logWarning("reconcile", `task ${milestoneId}/${sliceId}/${t.id} status reconciled from "${t.status}" to "complete" (#2514)`, { mid: milestoneId, sid: sliceId, tid: t.id });
|
|
839
669
|
reconciled = true;
|
|
840
670
|
} catch (e) {
|
|
841
|
-
// DB write failed — continue with stale status rather than crash
|
|
842
671
|
logError("reconcile", `failed to update task ${t.id}`, { tid: t.id, error: (e as Error).message });
|
|
843
672
|
}
|
|
844
673
|
}
|
|
845
674
|
}
|
|
846
|
-
// Re-fetch tasks if any were reconciled so downstream logic sees fresh status
|
|
847
675
|
if (reconciled) {
|
|
848
|
-
tasks = getSliceTasks(
|
|
676
|
+
tasks = getSliceTasks(milestoneId, sliceId);
|
|
677
|
+
}
|
|
678
|
+
return tasks;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async function detectBlockers(basePath: string, milestoneId: string, sliceId: string, tasks: TaskRow[]): Promise<string | null> {
|
|
682
|
+
const completedTasks = tasks.filter(t => isStatusDone(t.status));
|
|
683
|
+
for (const ct of completedTasks) {
|
|
684
|
+
if (ct.blocker_discovered) {
|
|
685
|
+
return ct.id;
|
|
686
|
+
}
|
|
687
|
+
const summaryFile = resolveTaskFile(basePath, milestoneId, sliceId, ct.id, "SUMMARY");
|
|
688
|
+
if (!summaryFile) continue;
|
|
689
|
+
const summaryContent = await loadFile(summaryFile);
|
|
690
|
+
if (!summaryContent) continue;
|
|
691
|
+
const summary = parseSummary(summaryContent);
|
|
692
|
+
if (summary.frontmatter.blocker_discovered) {
|
|
693
|
+
return ct.id;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function checkReplanTrigger(basePath: string, milestoneId: string, sliceId: string): boolean {
|
|
700
|
+
const sliceRow = getSlice(milestoneId, sliceId);
|
|
701
|
+
const dbTriggered = !!sliceRow?.replan_triggered_at;
|
|
702
|
+
const diskTriggered = !dbTriggered &&
|
|
703
|
+
!!resolveSliceFile(basePath, milestoneId, sliceId, "REPLAN-TRIGGER");
|
|
704
|
+
return dbTriggered || diskTriggered;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async function checkInterruptedWork(basePath: string, milestoneId: string, sliceId: string): Promise<boolean> {
|
|
708
|
+
const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
|
|
709
|
+
const continueFile = sDir ? resolveSliceFile(basePath, milestoneId, sliceId, "CONTINUE") : null;
|
|
710
|
+
return !!(continueFile && await loadFile(continueFile)) ||
|
|
711
|
+
!!(sDir && await loadFile(join(sDir, "continue.md")));
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
715
|
+
const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
|
|
716
|
+
|
|
717
|
+
let allMilestones = reconcileDiskToDb(basePath);
|
|
718
|
+
|
|
719
|
+
const customOrder = loadQueueOrder(basePath);
|
|
720
|
+
const sortedIds = sortByQueueOrder(allMilestones.map(m => m.id), customOrder);
|
|
721
|
+
const byId = new Map(allMilestones.map(m => [m.id, m]));
|
|
722
|
+
allMilestones.length = 0;
|
|
723
|
+
for (const id of sortedIds) allMilestones.push(byId.get(id)!);
|
|
724
|
+
|
|
725
|
+
const milestoneLock = process.env.GSD_MILESTONE_LOCK;
|
|
726
|
+
const milestones = milestoneLock
|
|
727
|
+
? allMilestones.filter(m => m.id === milestoneLock)
|
|
728
|
+
: allMilestones;
|
|
729
|
+
|
|
730
|
+
if (milestones.length === 0) {
|
|
731
|
+
return {
|
|
732
|
+
activeMilestone: null, activeSlice: null, activeTask: null,
|
|
733
|
+
phase: 'pre-planning', recentDecisions: [], blockers: [],
|
|
734
|
+
nextAction: 'No milestones found. Run /gsd to create one.',
|
|
735
|
+
registry: [], requirements,
|
|
736
|
+
progress: { milestones: { done: 0, total: 0 } },
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const { completeMilestoneIds, parkedMilestoneIds } = buildCompletenessSet(basePath, milestones);
|
|
741
|
+
|
|
742
|
+
const registryContext = await buildRegistryAndFindActive(basePath, milestones, completeMilestoneIds, parkedMilestoneIds);
|
|
743
|
+
const { registry, activeMilestone, activeMilestoneSlices, activeMilestoneHasDraft } = registryContext;
|
|
744
|
+
|
|
745
|
+
const milestoneProgress = {
|
|
746
|
+
done: registry.filter(e => e.status === 'complete').length,
|
|
747
|
+
total: registry.length,
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
if (!activeMilestone) {
|
|
751
|
+
return handleNoActiveMilestone(registry, requirements, milestoneProgress);
|
|
849
752
|
}
|
|
850
753
|
|
|
754
|
+
const hasRoadmap = resolveMilestoneFile(basePath, activeMilestone.id, "ROADMAP") !== null;
|
|
755
|
+
|
|
756
|
+
if (activeMilestoneSlices.length === 0) {
|
|
757
|
+
if (!hasRoadmap) {
|
|
758
|
+
const phase = activeMilestoneHasDraft ? 'needs-discussion' as const : 'pre-planning' as const;
|
|
759
|
+
const nextAction = activeMilestoneHasDraft
|
|
760
|
+
? `Discuss draft context for milestone ${activeMilestone.id}.`
|
|
761
|
+
: `Plan milestone ${activeMilestone.id}.`;
|
|
762
|
+
return {
|
|
763
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
764
|
+
phase, recentDecisions: [], blockers: [],
|
|
765
|
+
nextAction, registry, requirements,
|
|
766
|
+
progress: { milestones: milestoneProgress },
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return {
|
|
771
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
772
|
+
phase: 'pre-planning', recentDecisions: [], blockers: [],
|
|
773
|
+
nextAction: `Milestone ${activeMilestone.id} has a roadmap but no slices defined. Add slices to the roadmap.`,
|
|
774
|
+
registry, requirements,
|
|
775
|
+
progress: { milestones: milestoneProgress, slices: { done: 0, total: 0 } },
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const allSlicesDone = activeMilestoneSlices.every(s => isStatusDone(s.status));
|
|
780
|
+
const sliceProgress = {
|
|
781
|
+
done: activeMilestoneSlices.filter(s => isStatusDone(s.status)).length,
|
|
782
|
+
total: activeMilestoneSlices.length,
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
if (allSlicesDone) {
|
|
786
|
+
return handleAllSlicesDone(basePath, activeMilestone, registry, requirements, milestoneProgress, sliceProgress);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const activeSliceContext = resolveSliceDependencies(activeMilestoneSlices);
|
|
790
|
+
if (!activeSliceContext.activeSlice) {
|
|
791
|
+
// If locked slice wasn't found, it returns null but logs warning, we need to return 'blocked'
|
|
792
|
+
if (process.env.GSD_SLICE_LOCK) {
|
|
793
|
+
return {
|
|
794
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
795
|
+
phase: 'blocked', recentDecisions: [], blockers: [`GSD_SLICE_LOCK=${process.env.GSD_SLICE_LOCK} not found in active milestone slices`],
|
|
796
|
+
nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
|
|
797
|
+
registry, requirements,
|
|
798
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
return {
|
|
802
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
803
|
+
phase: 'blocked', recentDecisions: [], blockers: ['No slice eligible — check dependency ordering'],
|
|
804
|
+
nextAction: 'Resolve dependency blockers or plan next slice.',
|
|
805
|
+
registry, requirements,
|
|
806
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
const { activeSlice } = activeSliceContext;
|
|
810
|
+
|
|
811
|
+
const planFile = resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "PLAN");
|
|
812
|
+
if (!planFile) {
|
|
813
|
+
return {
|
|
814
|
+
activeMilestone, activeSlice, activeTask: null,
|
|
815
|
+
phase: 'planning', recentDecisions: [], blockers: [],
|
|
816
|
+
nextAction: `Plan slice ${activeSlice.id} (${activeSlice.title}).`,
|
|
817
|
+
registry, requirements,
|
|
818
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const tasks = await reconcileSliceTasks(basePath, activeMilestone.id, activeSlice.id, planFile);
|
|
823
|
+
|
|
851
824
|
const taskProgress = {
|
|
852
825
|
done: tasks.filter(t => isStatusDone(t.status)).length,
|
|
853
826
|
total: tasks.length,
|
|
@@ -856,23 +829,19 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
856
829
|
const activeTaskRow = tasks.find(t => !isStatusDone(t.status));
|
|
857
830
|
|
|
858
831
|
if (!activeTaskRow && tasks.length > 0) {
|
|
859
|
-
// All tasks done but slice not marked complete → summarizing
|
|
860
832
|
return {
|
|
861
833
|
activeMilestone, activeSlice, activeTask: null,
|
|
862
|
-
phase: 'summarizing',
|
|
863
|
-
recentDecisions: [], blockers: [],
|
|
834
|
+
phase: 'summarizing', recentDecisions: [], blockers: [],
|
|
864
835
|
nextAction: `All tasks done in ${activeSlice.id}. Write slice summary and complete slice.`,
|
|
865
836
|
registry, requirements,
|
|
866
837
|
progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
|
|
867
838
|
};
|
|
868
839
|
}
|
|
869
840
|
|
|
870
|
-
// Empty plan — no tasks defined yet
|
|
871
841
|
if (!activeTaskRow) {
|
|
872
842
|
return {
|
|
873
843
|
activeMilestone, activeSlice, activeTask: null,
|
|
874
|
-
phase: 'planning',
|
|
875
|
-
recentDecisions: [], blockers: [],
|
|
844
|
+
phase: 'planning', recentDecisions: [], blockers: [],
|
|
876
845
|
nextAction: `Slice ${activeSlice.id} has a plan file but no tasks. Add tasks to the plan.`,
|
|
877
846
|
registry, requirements,
|
|
878
847
|
progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
|
|
@@ -881,15 +850,13 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
881
850
|
|
|
882
851
|
const activeTask: ActiveRef = { id: activeTaskRow.id, title: activeTaskRow.title };
|
|
883
852
|
|
|
884
|
-
// ── Task plan file check (#909) ─────────────────────────────────────
|
|
885
853
|
const tasksDir = resolveTasksDir(basePath, activeMilestone.id, activeSlice.id);
|
|
886
854
|
if (tasksDir && existsSync(tasksDir) && tasks.length > 0) {
|
|
887
855
|
const allFiles = readdirSync(tasksDir).filter(f => f.endsWith(".md"));
|
|
888
856
|
if (allFiles.length === 0) {
|
|
889
857
|
return {
|
|
890
858
|
activeMilestone, activeSlice, activeTask: null,
|
|
891
|
-
phase: 'planning',
|
|
892
|
-
recentDecisions: [], blockers: [],
|
|
859
|
+
phase: 'planning', recentDecisions: [], blockers: [],
|
|
893
860
|
nextAction: `Task plan files missing for ${activeSlice.id}. Run plan-slice to generate task plans.`,
|
|
894
861
|
registry, requirements,
|
|
895
862
|
progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
|
|
@@ -898,50 +865,34 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
898
865
|
}
|
|
899
866
|
|
|
900
867
|
// ── Quality gate evaluation check ──────────────────────────────────
|
|
901
|
-
//
|
|
902
|
-
//
|
|
868
|
+
// Pause before execution only when gates owned by the `gate-evaluate`
|
|
869
|
+
// turn (Q3/Q4) are still pending. Q8 is also `scope:"slice"` but is
|
|
870
|
+
// owned by `complete-slice`, so it must NOT block the evaluating-gates
|
|
871
|
+
// phase — otherwise auto-loop stalls forever waiting for a gate that
|
|
872
|
+
// this turn never evaluates. See gate-registry.ts for the ownership map.
|
|
903
873
|
// Slices with zero gate rows (pre-feature or simple) skip straight through.
|
|
904
|
-
const pendingGateCount =
|
|
874
|
+
const pendingGateCount = getPendingGateCountForTurn(
|
|
875
|
+
activeMilestone.id,
|
|
876
|
+
activeSlice.id,
|
|
877
|
+
"gate-evaluate",
|
|
878
|
+
);
|
|
905
879
|
if (pendingGateCount > 0) {
|
|
906
880
|
return {
|
|
907
881
|
activeMilestone, activeSlice, activeTask: null,
|
|
908
|
-
phase: 'evaluating-gates',
|
|
909
|
-
recentDecisions: [], blockers: [],
|
|
882
|
+
phase: 'evaluating-gates', recentDecisions: [], blockers: [],
|
|
910
883
|
nextAction: `Evaluate ${pendingGateCount} quality gate(s) for ${activeSlice.id} before execution.`,
|
|
911
884
|
registry, requirements,
|
|
912
885
|
progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
|
|
913
886
|
};
|
|
914
887
|
}
|
|
915
888
|
|
|
916
|
-
|
|
917
|
-
const completedTasks = tasks.filter(t => isStatusDone(t.status));
|
|
918
|
-
let blockerTaskId: string | null = null;
|
|
919
|
-
for (const ct of completedTasks) {
|
|
920
|
-
if (ct.blocker_discovered) {
|
|
921
|
-
blockerTaskId = ct.id;
|
|
922
|
-
break;
|
|
923
|
-
}
|
|
924
|
-
// Also check disk summary in case DB doesn't have the flag
|
|
925
|
-
const summaryFile = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, ct.id, "SUMMARY");
|
|
926
|
-
if (!summaryFile) continue;
|
|
927
|
-
const summaryContent = await loadFile(summaryFile);
|
|
928
|
-
if (!summaryContent) continue;
|
|
929
|
-
const summary = parseSummary(summaryContent);
|
|
930
|
-
if (summary.frontmatter.blocker_discovered) {
|
|
931
|
-
blockerTaskId = ct.id;
|
|
932
|
-
break;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
|
|
889
|
+
const blockerTaskId = await detectBlockers(basePath, activeMilestone.id, activeSlice.id, tasks);
|
|
936
890
|
if (blockerTaskId) {
|
|
937
|
-
// Loop protection: if replan_history has entries for this slice, a replan
|
|
938
|
-
// was already performed — don't re-enter replanning phase.
|
|
939
891
|
const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
|
|
940
892
|
if (replanHistory.length === 0) {
|
|
941
893
|
return {
|
|
942
894
|
activeMilestone, activeSlice, activeTask,
|
|
943
|
-
phase: 'replanning-slice',
|
|
944
|
-
recentDecisions: [],
|
|
895
|
+
phase: 'replanning-slice', recentDecisions: [],
|
|
945
896
|
blockers: [`Task ${blockerTaskId} discovered a blocker requiring slice replan`],
|
|
946
897
|
nextAction: `Task ${blockerTaskId} reported blocker_discovered. Replan slice ${activeSlice.id} before continuing.`,
|
|
947
898
|
activeWorkspace: undefined,
|
|
@@ -951,22 +902,14 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
951
902
|
}
|
|
952
903
|
}
|
|
953
904
|
|
|
954
|
-
// ── REPLAN-TRIGGER detection ─────────────────────────────────────────
|
|
955
905
|
if (!blockerTaskId) {
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
// was best-effort and failed (triage-resolution.ts dual-write gap).
|
|
959
|
-
const dbTriggered = !!sliceRow?.replan_triggered_at;
|
|
960
|
-
const diskTriggered = !dbTriggered &&
|
|
961
|
-
!!resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "REPLAN-TRIGGER");
|
|
962
|
-
if (dbTriggered || diskTriggered) {
|
|
963
|
-
// Loop protection: if replan_history has entries, replan was already done
|
|
906
|
+
const isTriggered = checkReplanTrigger(basePath, activeMilestone.id, activeSlice.id);
|
|
907
|
+
if (isTriggered) {
|
|
964
908
|
const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
|
|
965
909
|
if (replanHistory.length === 0) {
|
|
966
910
|
return {
|
|
967
911
|
activeMilestone, activeSlice, activeTask,
|
|
968
|
-
phase: 'replanning-slice',
|
|
969
|
-
recentDecisions: [],
|
|
912
|
+
phase: 'replanning-slice', recentDecisions: [],
|
|
970
913
|
blockers: ['Triage replan trigger detected — slice replan required'],
|
|
971
914
|
nextAction: `Triage replan triggered for slice ${activeSlice.id}. Replan before continuing.`,
|
|
972
915
|
activeWorkspace: undefined,
|
|
@@ -977,16 +920,11 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
977
920
|
}
|
|
978
921
|
}
|
|
979
922
|
|
|
980
|
-
|
|
981
|
-
const sDir = resolveSlicePath(basePath, activeMilestone.id, activeSlice.id);
|
|
982
|
-
const continueFile = sDir ? resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "CONTINUE") : null;
|
|
983
|
-
const hasInterrupted = !!(continueFile && await loadFile(continueFile)) ||
|
|
984
|
-
!!(sDir && await loadFile(join(sDir, "continue.md")));
|
|
923
|
+
const hasInterrupted = await checkInterruptedWork(basePath, activeMilestone.id, activeSlice.id);
|
|
985
924
|
|
|
986
925
|
return {
|
|
987
926
|
activeMilestone, activeSlice, activeTask,
|
|
988
|
-
phase: 'executing',
|
|
989
|
-
recentDecisions: [], blockers: [],
|
|
927
|
+
phase: 'executing', recentDecisions: [], blockers: [],
|
|
990
928
|
nextAction: hasInterrupted
|
|
991
929
|
? `Resume interrupted work on ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}. Read continue.md first.`
|
|
992
930
|
: `Execute ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}.`,
|
|
@@ -995,11 +933,14 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
995
933
|
};
|
|
996
934
|
}
|
|
997
935
|
|
|
936
|
+
|
|
998
937
|
// LEGACY: Filesystem-based state derivation for unmigrated projects.
|
|
999
938
|
// DB-backed projects use deriveStateFromDb() above. Target: extract to
|
|
1000
939
|
// state-legacy.ts when all projects are DB-backed.
|
|
1001
940
|
export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
1002
|
-
const
|
|
941
|
+
const diskIds = findMilestoneIds(basePath);
|
|
942
|
+
const customOrder = loadQueueOrder(basePath);
|
|
943
|
+
const milestoneIds = sortByQueueOrder(diskIds, customOrder);
|
|
1003
944
|
|
|
1004
945
|
// ── Parallel worker isolation ──────────────────────────────────────────
|
|
1005
946
|
// When GSD_MILESTONE_LOCK is set, this process is a parallel worker
|