gsd-pi 2.63.0-dev.026d309 → 2.63.0-dev.786f0ff
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 +46 -134
- package/dist/cli.js +48 -6
- package/dist/headless-query.js +11 -1
- package/dist/help-text.js +4 -1
- package/dist/onboarding.js +15 -8
- package/dist/resource-loader.js +18 -3
- package/dist/resources/extensions/cmux/index.js +21 -12
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
- package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -0
- package/dist/resources/extensions/gsd/auto/phases.js +157 -22
- package/dist/resources/extensions/gsd/auto/session.js +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +9 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +124 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
- package/dist/resources/extensions/gsd/auto-start.js +10 -21
- package/dist/resources/extensions/gsd/auto-timers.js +2 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
- package/dist/resources/extensions/gsd/auto.js +19 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
- package/dist/resources/extensions/gsd/constants.js +42 -0
- package/dist/resources/extensions/gsd/db-writer.js +72 -4
- package/dist/resources/extensions/gsd/forensics.js +20 -4
- package/dist/resources/extensions/gsd/gsd-db.js +64 -17
- package/dist/resources/extensions/gsd/guided-flow.js +19 -0
- package/dist/resources/extensions/gsd/metrics.js +27 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences.js +7 -2
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +4 -7
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
- package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
- package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
- package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
- package/dist/resources/extensions/gsd/state.js +74 -14
- package/dist/resources/extensions/gsd/status-guards.js +11 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
- package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
- package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
- package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
- package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
- package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
- package/dist/resources/extensions/mcp-client/auth.js +101 -0
- package/dist/resources/extensions/mcp-client/index.js +10 -1
- package/dist/resources/extensions/ollama/index.js +28 -22
- package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
- package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
- package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
- package/dist/resources/extensions/ollama/ollama-client.js +23 -32
- package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
- package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
- package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
- package/dist/update-cmd.js +4 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -20
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +20 -20
- package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
- 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/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +50 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
- package/packages/pi-agent-core/src/agent-loop.ts +53 -0
- package/packages/pi-ai/dist/types.d.ts +16 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/types.ts +18 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -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 +50 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.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-registry.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
- package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
- package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
- package/src/resources/extensions/cmux/index.ts +18 -12
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
- package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
- package/src/resources/extensions/gsd/auto/loop.ts +5 -0
- package/src/resources/extensions/gsd/auto/phases.ts +194 -33
- package/src/resources/extensions/gsd/auto/session.ts +14 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +11 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +141 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
- package/src/resources/extensions/gsd/auto-start.ts +11 -20
- package/src/resources/extensions/gsd/auto-timers.ts +2 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
- package/src/resources/extensions/gsd/auto.ts +22 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
- package/src/resources/extensions/gsd/constants.ts +44 -0
- package/src/resources/extensions/gsd/db-writer.ts +78 -4
- package/src/resources/extensions/gsd/forensics.ts +21 -5
- package/src/resources/extensions/gsd/gsd-db.ts +64 -17
- package/src/resources/extensions/gsd/guided-flow.ts +22 -0
- package/src/resources/extensions/gsd/metrics.ts +28 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences.ts +9 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +4 -7
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
- package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
- package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
- package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
- package/src/resources/extensions/gsd/state.ts +67 -12
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
- package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
- package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
- package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
- package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
- package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
- package/src/resources/extensions/gsd/types.ts +44 -22
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
- package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
- package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
- package/src/resources/extensions/mcp-client/auth.ts +149 -0
- package/src/resources/extensions/mcp-client/index.ts +16 -1
- package/src/resources/extensions/ollama/index.ts +26 -25
- package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
- package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
- package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
- package/src/resources/extensions/ollama/ollama-client.ts +30 -30
- package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
- package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
- package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
- package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
- package/src/resources/extensions/ollama/types.ts +23 -0
- package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
- /package/dist/web/standalone/.next/static/{TTlAguZQ5vR9EOv6G8cel → SDB1T-4NqkMjYirjjqQhr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{TTlAguZQ5vR9EOv6G8cel → SDB1T-4NqkMjYirjjqQhr}/_ssgManifest.js +0 -0
|
@@ -6,12 +6,13 @@ import { parseSummary, loadFile, parseRequirementCounts, parseContextDependsOn,
|
|
|
6
6
|
import { resolveMilestoneFile, resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTasksDir, resolveGsdRootFile, gsdRoot, } from './paths.js';
|
|
7
7
|
import { findMilestoneIds } from './milestone-ids.js';
|
|
8
8
|
import { loadQueueOrder, sortByQueueOrder } from './queue-order.js';
|
|
9
|
+
import { isDeferredStatus } from './status-guards.js';
|
|
9
10
|
import { nativeBatchParseGsdFiles } from './native-parser-bridge.js';
|
|
10
11
|
import { join, resolve } from 'path';
|
|
11
12
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
12
13
|
import { debugCount, debugTime } from './debug-logger.js';
|
|
13
|
-
import { extractVerdict } from './verdict-parser.js';
|
|
14
14
|
import { logWarning, logError } from './workflow-logger.js';
|
|
15
|
+
import { extractVerdict } from './verdict-parser.js';
|
|
15
16
|
import { isDbAvailable, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, updateTaskStatus, getPendingSliceGateCount, } from './gsd-db.js';
|
|
16
17
|
/**
|
|
17
18
|
* A "ghost" milestone directory contains only META.json (and no substantive
|
|
@@ -568,13 +569,43 @@ export async function deriveStateFromDb(basePath) {
|
|
|
568
569
|
const doneSliceIds = new Set(activeMilestoneSlices.filter(s => isStatusDone(s.status)).map(s => s.id));
|
|
569
570
|
let activeSlice = null;
|
|
570
571
|
let activeSliceRow = null;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
572
|
+
// ── Slice-level parallel worker isolation ─────────────────────────────
|
|
573
|
+
// When GSD_SLICE_LOCK is set, this process is a parallel worker scoped
|
|
574
|
+
// to a single slice. Override activeSlice to only the locked slice ID.
|
|
575
|
+
const sliceLock = process.env.GSD_SLICE_LOCK;
|
|
576
|
+
if (sliceLock) {
|
|
577
|
+
const lockedSlice = activeMilestoneSlices.find(s => s.id === sliceLock);
|
|
578
|
+
if (lockedSlice) {
|
|
579
|
+
activeSlice = { id: lockedSlice.id, title: lockedSlice.title };
|
|
580
|
+
activeSliceRow = lockedSlice;
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
logWarning("state", `GSD_SLICE_LOCK=${sliceLock} not found in active slices — worker has no assigned work`);
|
|
584
|
+
// Don't silently continue — this is a dispatch error
|
|
585
|
+
return {
|
|
586
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
587
|
+
phase: 'blocked',
|
|
588
|
+
recentDecisions: [], blockers: [`GSD_SLICE_LOCK=${sliceLock} not found in active milestone slices`],
|
|
589
|
+
nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
|
|
590
|
+
registry, requirements,
|
|
591
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
for (const s of activeMilestoneSlices) {
|
|
597
|
+
if (isStatusDone(s.status))
|
|
598
|
+
continue;
|
|
599
|
+
// #2661: Skip deferred slices — a decision explicitly deferred this work.
|
|
600
|
+
// Without this guard the dispatcher would keep dispatching deferred slices
|
|
601
|
+
// because DECISIONS.md is only contextual, not authoritative for dispatch.
|
|
602
|
+
if (isDeferredStatus(s.status))
|
|
603
|
+
continue;
|
|
604
|
+
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
605
|
+
activeSlice = { id: s.id, title: s.title };
|
|
606
|
+
activeSliceRow = s;
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
578
609
|
}
|
|
579
610
|
}
|
|
580
611
|
if (!activeSlice) {
|
|
@@ -1188,12 +1219,41 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1188
1219
|
// Find the active slice (first incomplete with deps satisfied)
|
|
1189
1220
|
const doneSliceIds = new Set(activeRoadmap.slices.filter(s => s.done).map(s => s.id));
|
|
1190
1221
|
let activeSlice = null;
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1222
|
+
// ── Slice-level parallel worker isolation ─────────────────────────────
|
|
1223
|
+
// When GSD_SLICE_LOCK is set, override activeSlice to only the locked slice.
|
|
1224
|
+
const sliceLockLegacy = process.env.GSD_SLICE_LOCK;
|
|
1225
|
+
if (sliceLockLegacy) {
|
|
1226
|
+
const lockedSlice = activeRoadmap.slices.find(s => s.id === sliceLockLegacy);
|
|
1227
|
+
if (lockedSlice) {
|
|
1228
|
+
activeSlice = { id: lockedSlice.id, title: lockedSlice.title };
|
|
1229
|
+
}
|
|
1230
|
+
else {
|
|
1231
|
+
logWarning("state", `GSD_SLICE_LOCK=${sliceLockLegacy} not found in active slices — worker has no assigned work`);
|
|
1232
|
+
return {
|
|
1233
|
+
activeMilestone,
|
|
1234
|
+
activeSlice: null,
|
|
1235
|
+
activeTask: null,
|
|
1236
|
+
phase: 'blocked',
|
|
1237
|
+
recentDecisions: [],
|
|
1238
|
+
blockers: [`GSD_SLICE_LOCK=${sliceLockLegacy} not found in active milestone slices`],
|
|
1239
|
+
nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
|
|
1240
|
+
registry,
|
|
1241
|
+
requirements,
|
|
1242
|
+
progress: {
|
|
1243
|
+
milestones: milestoneProgress,
|
|
1244
|
+
slices: sliceProgress,
|
|
1245
|
+
},
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
else {
|
|
1250
|
+
for (const s of activeRoadmap.slices) {
|
|
1251
|
+
if (s.done)
|
|
1252
|
+
continue;
|
|
1253
|
+
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
1254
|
+
activeSlice = { id: s.id, title: s.title };
|
|
1255
|
+
break;
|
|
1256
|
+
}
|
|
1197
1257
|
}
|
|
1198
1258
|
}
|
|
1199
1259
|
if (!activeSlice) {
|
|
@@ -11,3 +11,14 @@
|
|
|
11
11
|
export function isClosedStatus(status) {
|
|
12
12
|
return status === "complete" || status === "done" || status === "skipped";
|
|
13
13
|
}
|
|
14
|
+
/** Returns true when a slice status indicates it was deferred by a decision. */
|
|
15
|
+
export function isDeferredStatus(status) {
|
|
16
|
+
return status === "deferred";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns true when a slice should be skipped during active-slice selection.
|
|
20
|
+
* This includes both closed (complete/done) and deferred slices.
|
|
21
|
+
*/
|
|
22
|
+
export function isInactiveStatus(status) {
|
|
23
|
+
return isClosedStatus(status) || isDeferredStatus(status);
|
|
24
|
+
}
|
|
@@ -12,24 +12,29 @@ import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
|
12
12
|
import { isClosedStatus } from "../status-guards.js";
|
|
13
13
|
import { saveFile, clearParseCache } from "../files.js";
|
|
14
14
|
import { invalidateStateCache } from "../state.js";
|
|
15
|
-
import { renderAllProjections } from "../workflow-projections.js";
|
|
15
|
+
import { renderAllProjections, stripIdPrefix } from "../workflow-projections.js";
|
|
16
16
|
import { writeManifest } from "../workflow-manifest.js";
|
|
17
17
|
import { appendEvent } from "../workflow-events.js";
|
|
18
18
|
import { logWarning } from "../workflow-logger.js";
|
|
19
19
|
function renderMilestoneSummaryMarkdown(params) {
|
|
20
20
|
const now = new Date().toISOString();
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
const displayTitle = stripIdPrefix(params.title, params.milestoneId);
|
|
22
|
+
// Apply defaults for optional enrichment fields (#2771)
|
|
23
|
+
const keyDecisions = params.keyDecisions ?? [];
|
|
24
|
+
const keyFiles = params.keyFiles ?? [];
|
|
25
|
+
const lessonsLearned = params.lessonsLearned ?? [];
|
|
26
|
+
const keyDecisionsYaml = keyDecisions.length > 0
|
|
27
|
+
? keyDecisions.map(d => ` - ${d}`).join("\n")
|
|
23
28
|
: " - (none)";
|
|
24
|
-
const keyFilesYaml =
|
|
25
|
-
?
|
|
29
|
+
const keyFilesYaml = keyFiles.length > 0
|
|
30
|
+
? keyFiles.map(f => ` - ${f}`).join("\n")
|
|
26
31
|
: " - (none)";
|
|
27
|
-
const lessonsYaml =
|
|
28
|
-
?
|
|
32
|
+
const lessonsYaml = lessonsLearned.length > 0
|
|
33
|
+
? lessonsLearned.map(l => ` - ${l}`).join("\n")
|
|
29
34
|
: " - (none)";
|
|
30
35
|
return `---
|
|
31
36
|
id: ${params.milestoneId}
|
|
32
|
-
title: "${
|
|
37
|
+
title: "${displayTitle}"
|
|
33
38
|
status: complete
|
|
34
39
|
completed_at: ${now}
|
|
35
40
|
key_decisions:
|
|
@@ -40,7 +45,7 @@ lessons_learned:
|
|
|
40
45
|
${lessonsYaml}
|
|
41
46
|
---
|
|
42
47
|
|
|
43
|
-
# ${params.milestoneId}: ${
|
|
48
|
+
# ${params.milestoneId}: ${displayTitle}
|
|
44
49
|
|
|
45
50
|
**${params.oneLiner}**
|
|
46
51
|
|
|
@@ -50,15 +55,15 @@ ${params.narrative}
|
|
|
50
55
|
|
|
51
56
|
## Success Criteria Results
|
|
52
57
|
|
|
53
|
-
${params.successCriteriaResults}
|
|
58
|
+
${params.successCriteriaResults ?? "Not provided."}
|
|
54
59
|
|
|
55
60
|
## Definition of Done Results
|
|
56
61
|
|
|
57
|
-
${params.definitionOfDoneResults}
|
|
62
|
+
${params.definitionOfDoneResults ?? "Not provided."}
|
|
58
63
|
|
|
59
64
|
## Requirement Outcomes
|
|
60
65
|
|
|
61
|
-
${params.requirementOutcomes}
|
|
66
|
+
${params.requirementOutcomes ?? "Not provided."}
|
|
62
67
|
|
|
63
68
|
## Deviations
|
|
64
69
|
|
|
@@ -25,46 +25,60 @@ import { logWarning } from "../workflow-logger.js";
|
|
|
25
25
|
*/
|
|
26
26
|
function renderSliceSummaryMarkdown(params) {
|
|
27
27
|
const now = new Date().toISOString();
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Apply defaults for optional enrichment arrays (#2771)
|
|
29
|
+
const provides = params.provides ?? [];
|
|
30
|
+
const requires = params.requires ?? [];
|
|
31
|
+
const affects = params.affects ?? [];
|
|
32
|
+
const keyFiles = params.keyFiles ?? [];
|
|
33
|
+
const keyDecisions = params.keyDecisions ?? [];
|
|
34
|
+
const patternsEstablished = params.patternsEstablished ?? [];
|
|
35
|
+
const observabilitySurfaces = params.observabilitySurfaces ?? [];
|
|
36
|
+
const drillDownPaths = params.drillDownPaths ?? [];
|
|
37
|
+
const requirementsAdvanced = params.requirementsAdvanced ?? [];
|
|
38
|
+
const requirementsValidated = params.requirementsValidated ?? [];
|
|
39
|
+
const requirementsSurfaced = params.requirementsSurfaced ?? [];
|
|
40
|
+
const requirementsInvalidated = params.requirementsInvalidated ?? [];
|
|
41
|
+
const filesModified = params.filesModified ?? [];
|
|
42
|
+
const providesYaml = provides.length > 0
|
|
43
|
+
? provides.map(p => ` - ${p}`).join("\n")
|
|
30
44
|
: " - (none)";
|
|
31
|
-
const requiresYaml =
|
|
32
|
-
?
|
|
45
|
+
const requiresYaml = requires.length > 0
|
|
46
|
+
? requires.map(r => ` - slice: ${r.slice}\n provides: ${r.provides}`).join("\n")
|
|
33
47
|
: " []";
|
|
34
|
-
const affectsYaml =
|
|
35
|
-
?
|
|
48
|
+
const affectsYaml = affects.length > 0
|
|
49
|
+
? affects.map(a => ` - ${a}`).join("\n")
|
|
36
50
|
: " []";
|
|
37
|
-
const keyFilesYaml =
|
|
38
|
-
?
|
|
51
|
+
const keyFilesYaml = keyFiles.length > 0
|
|
52
|
+
? keyFiles.map(f => ` - ${f}`).join("\n")
|
|
39
53
|
: " - (none)";
|
|
40
|
-
const keyDecisionsYaml =
|
|
41
|
-
?
|
|
54
|
+
const keyDecisionsYaml = keyDecisions.length > 0
|
|
55
|
+
? keyDecisions.map(d => ` - ${d}`).join("\n")
|
|
42
56
|
: " - (none)";
|
|
43
|
-
const patternsYaml =
|
|
44
|
-
?
|
|
57
|
+
const patternsYaml = patternsEstablished.length > 0
|
|
58
|
+
? patternsEstablished.map(p => ` - ${p}`).join("\n")
|
|
45
59
|
: " - (none)";
|
|
46
|
-
const observabilityYaml =
|
|
47
|
-
?
|
|
60
|
+
const observabilityYaml = observabilitySurfaces.length > 0
|
|
61
|
+
? observabilitySurfaces.map(o => ` - ${o}`).join("\n")
|
|
48
62
|
: " - none";
|
|
49
|
-
const drillDownYaml =
|
|
50
|
-
?
|
|
63
|
+
const drillDownYaml = drillDownPaths.length > 0
|
|
64
|
+
? drillDownPaths.map(d => ` - ${d}`).join("\n")
|
|
51
65
|
: " []";
|
|
52
66
|
// Requirements sections
|
|
53
|
-
const reqAdvanced =
|
|
54
|
-
?
|
|
67
|
+
const reqAdvanced = requirementsAdvanced.length > 0
|
|
68
|
+
? requirementsAdvanced.map(r => `- ${r.id} — ${r.how}`).join("\n")
|
|
55
69
|
: "None.";
|
|
56
|
-
const reqValidated =
|
|
57
|
-
?
|
|
70
|
+
const reqValidated = requirementsValidated.length > 0
|
|
71
|
+
? requirementsValidated.map(r => `- ${r.id} — ${r.proof}`).join("\n")
|
|
58
72
|
: "None.";
|
|
59
|
-
const reqSurfaced =
|
|
60
|
-
?
|
|
73
|
+
const reqSurfaced = requirementsSurfaced.length > 0
|
|
74
|
+
? requirementsSurfaced.map(r => `- ${r}`).join("\n")
|
|
61
75
|
: "None.";
|
|
62
|
-
const reqInvalidated =
|
|
63
|
-
?
|
|
76
|
+
const reqInvalidated = requirementsInvalidated.length > 0
|
|
77
|
+
? requirementsInvalidated.map(r => `- ${r.id} — ${r.what}`).join("\n")
|
|
64
78
|
: "None.";
|
|
65
79
|
// Files modified
|
|
66
|
-
const filesMod =
|
|
67
|
-
?
|
|
80
|
+
const filesMod = filesModified.length > 0
|
|
81
|
+
? filesModified.map(f => `- \`${f.path}\` — ${f.description}`).join("\n")
|
|
68
82
|
: "None.";
|
|
69
83
|
return `---
|
|
70
84
|
id: ${params.sliceId}
|
|
@@ -35,11 +35,11 @@ function paramsToTaskRow(params, completedAt) {
|
|
|
35
35
|
verification_result: params.verification,
|
|
36
36
|
duration: "",
|
|
37
37
|
completed_at: completedAt,
|
|
38
|
-
blocker_discovered: params.blockerDiscovered,
|
|
39
|
-
deviations: params.deviations,
|
|
40
|
-
known_issues: params.knownIssues,
|
|
41
|
-
key_files: params.keyFiles,
|
|
42
|
-
key_decisions: params.keyDecisions,
|
|
38
|
+
blocker_discovered: params.blockerDiscovered ?? false,
|
|
39
|
+
deviations: params.deviations ?? "",
|
|
40
|
+
known_issues: params.knownIssues ?? "",
|
|
41
|
+
key_files: params.keyFiles ?? [],
|
|
42
|
+
key_decisions: params.keyDecisions ?? [],
|
|
43
43
|
full_summary_md: "",
|
|
44
44
|
description: "",
|
|
45
45
|
estimate: "",
|
|
@@ -113,13 +113,13 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
113
113
|
narrative: params.narrative,
|
|
114
114
|
verificationResult: params.verification,
|
|
115
115
|
duration: "",
|
|
116
|
-
blockerDiscovered: params.blockerDiscovered,
|
|
117
|
-
deviations: params.deviations,
|
|
118
|
-
knownIssues: params.knownIssues,
|
|
119
|
-
keyFiles: params.keyFiles,
|
|
120
|
-
keyDecisions: params.keyDecisions,
|
|
116
|
+
blockerDiscovered: params.blockerDiscovered ?? false,
|
|
117
|
+
deviations: params.deviations ?? "None.",
|
|
118
|
+
knownIssues: params.knownIssues ?? "None.",
|
|
119
|
+
keyFiles: params.keyFiles ?? [],
|
|
120
|
+
keyDecisions: params.keyDecisions ?? [],
|
|
121
121
|
});
|
|
122
|
-
for (const evidence of params.verificationEvidence) {
|
|
122
|
+
for (const evidence of (params.verificationEvidence ?? [])) {
|
|
123
123
|
insertVerificationEvidence({
|
|
124
124
|
taskId: params.taskId,
|
|
125
125
|
sliceId: params.sliceId,
|
|
@@ -139,7 +139,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
139
139
|
// verifyExpectedArtifact() stay consistent (both say "not done").
|
|
140
140
|
// Render summary markdown via the single source of truth (#2720)
|
|
141
141
|
const taskRow = paramsToTaskRow(params, completedAt);
|
|
142
|
-
const summaryMd = renderSummaryContent(taskRow, params.sliceId, params.milestoneId, params.verificationEvidence);
|
|
142
|
+
const summaryMd = renderSummaryContent(taskRow, params.sliceId, params.milestoneId, params.verificationEvidence ?? []);
|
|
143
143
|
// Resolve and write summary to disk
|
|
144
144
|
let summaryPath;
|
|
145
145
|
const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { clearParseCache } from "../files.js";
|
|
2
2
|
import { isClosedStatus } from "../status-guards.js";
|
|
3
3
|
import { isNonEmptyString, validateStringArray } from "../validation.js";
|
|
4
|
-
import { transaction, getMilestone, getMilestoneSlices, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
|
|
4
|
+
import { transaction, getMilestone, getMilestoneSlices, getSlice, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
|
|
5
5
|
import { invalidateStateCache } from "../state.js";
|
|
6
6
|
import { renderRoadmapFromDb } from "../markdown-renderer.js";
|
|
7
7
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
@@ -106,25 +106,20 @@ function validateParams(params) {
|
|
|
106
106
|
throw new Error("title is required");
|
|
107
107
|
if (!isNonEmptyString(params?.vision))
|
|
108
108
|
throw new Error("vision is required");
|
|
109
|
-
if (!isNonEmptyString(params?.verificationContract))
|
|
110
|
-
throw new Error("verificationContract is required");
|
|
111
|
-
if (!isNonEmptyString(params?.verificationIntegration))
|
|
112
|
-
throw new Error("verificationIntegration is required");
|
|
113
|
-
if (!isNonEmptyString(params?.verificationOperational))
|
|
114
|
-
throw new Error("verificationOperational is required");
|
|
115
|
-
if (!isNonEmptyString(params?.verificationUat))
|
|
116
|
-
throw new Error("verificationUat is required");
|
|
117
|
-
if (!isNonEmptyString(params?.requirementCoverage))
|
|
118
|
-
throw new Error("requirementCoverage is required");
|
|
119
|
-
if (!isNonEmptyString(params?.boundaryMapMarkdown))
|
|
120
|
-
throw new Error("boundaryMapMarkdown is required");
|
|
121
109
|
return {
|
|
122
110
|
...params,
|
|
123
111
|
dependsOn: params.dependsOn ? validateStringArray(params.dependsOn, "dependsOn") : [],
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
112
|
+
// Apply defaults for optional enrichment fields (#2771)
|
|
113
|
+
successCriteria: params.successCriteria ? validateStringArray(params.successCriteria, "successCriteria") : [],
|
|
114
|
+
keyRisks: params.keyRisks ? validateRiskEntries(params.keyRisks) : [],
|
|
115
|
+
proofStrategy: params.proofStrategy ? validateProofStrategy(params.proofStrategy) : [],
|
|
116
|
+
verificationContract: params.verificationContract ?? "Not provided.",
|
|
117
|
+
verificationIntegration: params.verificationIntegration ?? "Not provided.",
|
|
118
|
+
verificationOperational: params.verificationOperational ?? "Not provided.",
|
|
119
|
+
verificationUat: params.verificationUat ?? "Not provided.",
|
|
120
|
+
definitionOfDone: params.definitionOfDone ? validateStringArray(params.definitionOfDone, "definitionOfDone") : [],
|
|
121
|
+
requirementCoverage: params.requirementCoverage ?? "Not provided.",
|
|
122
|
+
boundaryMapMarkdown: params.boundaryMapMarkdown ?? "Not provided.",
|
|
128
123
|
slices: validateSlices(params.slices),
|
|
129
124
|
};
|
|
130
125
|
}
|
|
@@ -147,15 +142,19 @@ export async function handlePlanMilestone(rawParams, basePath) {
|
|
|
147
142
|
guardError = `cannot re-plan milestone ${params.milestoneId}: it is already complete`;
|
|
148
143
|
return;
|
|
149
144
|
}
|
|
150
|
-
// Guard: refuse to re-plan a milestone that
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
145
|
+
// Guard: refuse to re-plan a milestone that would drop completed slices (#2960).
|
|
146
|
+
// Allow re-planning when all completed slices are still present in the
|
|
147
|
+
// incoming plan — their status is preserved below (#2558). Block only when
|
|
148
|
+
// the new plan omits a completed slice, which could shadow completed work.
|
|
154
149
|
const existingSlices = getMilestoneSlices(params.milestoneId);
|
|
155
150
|
const completedSlices = existingSlices.filter(s => isClosedStatus(s.status));
|
|
156
151
|
if (completedSlices.length > 0) {
|
|
157
|
-
|
|
158
|
-
|
|
152
|
+
const incomingSliceIds = new Set(params.slices.map(s => s.sliceId));
|
|
153
|
+
const droppedCompleted = completedSlices.filter(s => !incomingSliceIds.has(s.id));
|
|
154
|
+
if (droppedCompleted.length > 0) {
|
|
155
|
+
guardError = `cannot re-plan milestone ${params.milestoneId}: ${droppedCompleted.length} completed slice(s) would be dropped (${droppedCompleted.map(s => s.id).join(", ")}). Use gsd_reassess_roadmap to modify the roadmap.`;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
159
158
|
}
|
|
160
159
|
// Validate depends_on: all dependencies must exist and be complete
|
|
161
160
|
if (params.dependsOn && params.dependsOn.length > 0) {
|
|
@@ -178,6 +177,8 @@ export async function handlePlanMilestone(rawParams, basePath) {
|
|
|
178
177
|
depends_on: params.dependsOn ?? [],
|
|
179
178
|
});
|
|
180
179
|
upsertMilestonePlanning(params.milestoneId, {
|
|
180
|
+
title: params.title,
|
|
181
|
+
status: params.status ?? "active",
|
|
181
182
|
vision: params.vision,
|
|
182
183
|
successCriteria: params.successCriteria,
|
|
183
184
|
keyRisks: params.keyRisks,
|
|
@@ -189,13 +190,20 @@ export async function handlePlanMilestone(rawParams, basePath) {
|
|
|
189
190
|
definitionOfDone: params.definitionOfDone,
|
|
190
191
|
requirementCoverage: params.requirementCoverage,
|
|
191
192
|
boundaryMapMarkdown: params.boundaryMapMarkdown,
|
|
192
|
-
}
|
|
193
|
+
});
|
|
193
194
|
for (const slice of params.slices) {
|
|
195
|
+
// Preserve completed/done status on re-plan (#2558).
|
|
196
|
+
// Without this, a re-plan after milestone transition would reset
|
|
197
|
+
// already-completed slices back to "pending".
|
|
198
|
+
const existing = getSlice(params.milestoneId, slice.sliceId);
|
|
199
|
+
const status = existing && (existing.status === "complete" || existing.status === "done")
|
|
200
|
+
? existing.status
|
|
201
|
+
: "pending";
|
|
194
202
|
insertSlice({
|
|
195
203
|
id: slice.sliceId,
|
|
196
204
|
milestoneId: params.milestoneId,
|
|
197
205
|
title: slice.title,
|
|
198
|
-
status
|
|
206
|
+
status,
|
|
199
207
|
risk: slice.risk,
|
|
200
208
|
depends: slice.depends,
|
|
201
209
|
demo: slice.demo,
|
|
@@ -72,16 +72,13 @@ function validateParams(params) {
|
|
|
72
72
|
throw new Error("sliceId is required");
|
|
73
73
|
if (!isNonEmptyString(params?.goal))
|
|
74
74
|
throw new Error("goal is required");
|
|
75
|
-
if (!isNonEmptyString(params?.successCriteria))
|
|
76
|
-
throw new Error("successCriteria is required");
|
|
77
|
-
if (!isNonEmptyString(params?.proofLevel))
|
|
78
|
-
throw new Error("proofLevel is required");
|
|
79
|
-
if (!isNonEmptyString(params?.integrationClosure))
|
|
80
|
-
throw new Error("integrationClosure is required");
|
|
81
|
-
if (!isNonEmptyString(params?.observabilityImpact))
|
|
82
|
-
throw new Error("observabilityImpact is required");
|
|
83
75
|
return {
|
|
84
76
|
...params,
|
|
77
|
+
// Apply defaults for optional enrichment fields (#2771)
|
|
78
|
+
successCriteria: params.successCriteria ?? "Not provided.",
|
|
79
|
+
proofLevel: params.proofLevel ?? "Not provided.",
|
|
80
|
+
integrationClosure: params.integrationClosure ?? "Not provided.",
|
|
81
|
+
observabilityImpact: params.observabilityImpact ?? "Not provided.",
|
|
85
82
|
tasks: validateTasks(params.tasks),
|
|
86
83
|
};
|
|
87
84
|
}
|
|
@@ -7,6 +7,20 @@ import { join } from "node:path";
|
|
|
7
7
|
import { mkdirSync, existsSync } from "node:fs";
|
|
8
8
|
import { logWarning } from "./workflow-logger.js";
|
|
9
9
|
import { deriveState } from "./state.js";
|
|
10
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Strip a leading ID prefix (e.g. "M001: " or "S04: ") from a title
|
|
13
|
+
* to prevent double-prefixing when the renderer adds its own prefix.
|
|
14
|
+
* Handles repeated prefixes (e.g. "M001: M001: M001: Title" → "Title").
|
|
15
|
+
*/
|
|
16
|
+
export function stripIdPrefix(title, id) {
|
|
17
|
+
const prefix = `${id}: `;
|
|
18
|
+
let result = title;
|
|
19
|
+
while (result.startsWith(prefix)) {
|
|
20
|
+
result = result.slice(prefix.length);
|
|
21
|
+
}
|
|
22
|
+
return result.trim() || title;
|
|
23
|
+
}
|
|
10
24
|
// ─── PLAN.md Projection ──────────────────────────────────────────────────
|
|
11
25
|
/**
|
|
12
26
|
* Render PLAN.md content from a slice row and its task rows.
|
|
@@ -14,7 +28,8 @@ import { deriveState } from "./state.js";
|
|
|
14
28
|
*/
|
|
15
29
|
export function renderPlanContent(sliceRow, taskRows) {
|
|
16
30
|
const lines = [];
|
|
17
|
-
|
|
31
|
+
const displayTitle = stripIdPrefix(sliceRow.title, sliceRow.id);
|
|
32
|
+
lines.push(`# ${sliceRow.id}: ${displayTitle}`);
|
|
18
33
|
lines.push("");
|
|
19
34
|
// #2945: never use full_summary_md/full_uat_md as display fallbacks —
|
|
20
35
|
// they contain multi-line rendered markdown that corrupts single-line fields.
|
|
@@ -71,7 +86,8 @@ export function renderPlanProjection(basePath, milestoneId, sliceId) {
|
|
|
71
86
|
*/
|
|
72
87
|
export function renderRoadmapContent(milestoneRow, sliceRows) {
|
|
73
88
|
const lines = [];
|
|
74
|
-
|
|
89
|
+
const displayTitle = stripIdPrefix(milestoneRow.title, milestoneRow.id);
|
|
90
|
+
lines.push(`# ${milestoneRow.id}: ${displayTitle}`);
|
|
75
91
|
lines.push("");
|
|
76
92
|
lines.push("## Vision");
|
|
77
93
|
lines.push(milestoneRow.vision || milestoneRow.title || "TBD");
|
|
@@ -218,14 +234,14 @@ export function renderStateContent(state) {
|
|
|
218
234
|
const lines = [];
|
|
219
235
|
lines.push("# GSD State", "");
|
|
220
236
|
const activeSlice = state.activeSlice
|
|
221
|
-
? `${state.activeSlice.id}: ${state.activeSlice.title}`
|
|
237
|
+
? `${state.activeSlice.id}: ${stripIdPrefix(state.activeSlice.title, state.activeSlice.id)}`
|
|
222
238
|
: "None";
|
|
223
239
|
if (state.phase === 'complete' && state.lastCompletedMilestone) {
|
|
224
240
|
lines.push(`**Last Completed Milestone:** ${state.lastCompletedMilestone.id}: ${state.lastCompletedMilestone.title}`);
|
|
225
241
|
}
|
|
226
242
|
else {
|
|
227
243
|
const activeMilestone = state.activeMilestone
|
|
228
|
-
? `${state.activeMilestone.id}: ${state.activeMilestone.title}`
|
|
244
|
+
? `${state.activeMilestone.id}: ${stripIdPrefix(state.activeMilestone.title, state.activeMilestone.id)}`
|
|
229
245
|
: "None";
|
|
230
246
|
lines.push(`**Active Milestone:** ${activeMilestone}`);
|
|
231
247
|
}
|
|
@@ -238,7 +254,7 @@ export function renderStateContent(state) {
|
|
|
238
254
|
lines.push("## Milestone Registry");
|
|
239
255
|
for (const entry of state.registry) {
|
|
240
256
|
const glyph = entry.status === "complete" ? "\u2705" : entry.status === "active" ? "\uD83D\uDD04" : entry.status === "parked" ? "\u23F8\uFE0F" : "\u2B1C";
|
|
241
|
-
lines.push(`- ${glyph} **${entry.id}:** ${entry.title}`);
|
|
257
|
+
lines.push(`- ${glyph} **${entry.id}:** ${stripIdPrefix(entry.title, entry.id)}`);
|
|
242
258
|
}
|
|
243
259
|
lines.push("");
|
|
244
260
|
lines.push("## Recent Decisions");
|