@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless-recover.js +56 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
- package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
- package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/dist/resources/extensions/browser-tools/index.js +68 -24
- package/dist/resources/extensions/browser-tools/state.js +12 -0
- package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
- package/dist/resources/extensions/browser-tools/utils.js +3 -3
- package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +87 -12
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
- package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-timers.js +24 -10
- package/dist/resources/extensions/gsd/auto.js +40 -15
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
- package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
- package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
- package/dist/resources/extensions/gsd/config-overlay.js +1 -0
- package/dist/resources/extensions/gsd/context-masker.js +129 -5
- package/dist/resources/extensions/gsd/db-writer.js +35 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
- package/dist/resources/extensions/gsd/gsd-db.js +480 -172
- package/dist/resources/extensions/gsd/guided-flow.js +4 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
- package/dist/resources/extensions/gsd/md-importer.js +38 -3
- package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
- package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
- package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
- package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
- package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
- package/dist/resources/extensions/gsd/preferences-models.js +111 -43
- package/dist/resources/extensions/gsd/preferences-types.js +13 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
- package/dist/resources/extensions/gsd/preferences.js +4 -1
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
- package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
- package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
- package/dist/resources/extensions/gsd/source-observations.js +306 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
- package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
- package/dist/resources/extensions/gsd/state.js +7 -3
- package/dist/resources/extensions/gsd/tool-contract.js +15 -1
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
- package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
- package/dist/resources/extensions/gsd/uat-policy.js +130 -0
- package/dist/resources/extensions/gsd/uat-run.js +414 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
- package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
- package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
- package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
- package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
- package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
- package/dist/resources/extensions/subagent/agents.js +1 -0
- package/dist/resources/extensions/subagent/index.js +27 -12
- package/dist/resources/extensions/subagent/launch.js +7 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
- 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 +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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +4 -4
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/dist/native.js +22 -0
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +30 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +174 -29
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +178 -54
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
- package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/utils.d.ts +11 -0
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +119 -6
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/package.json +2 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/themes.js +1 -1
- package/pkg/dist/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/scripts/install/handoff.js +16 -3
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
- package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
- package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/src/resources/extensions/browser-tools/index.ts +75 -27
- package/src/resources/extensions/browser-tools/state.ts +13 -0
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
- package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
- package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
- package/src/resources/extensions/browser-tools/utils.ts +3 -3
- package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +4 -2
- package/src/resources/extensions/gsd/auto/phases.ts +89 -15
- package/src/resources/extensions/gsd/auto/session.ts +24 -1
- package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
- package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
- package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
- package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
- package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-timers.ts +25 -9
- package/src/resources/extensions/gsd/auto.ts +41 -14
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
- package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
- package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
- package/src/resources/extensions/gsd/config-overlay.ts +1 -0
- package/src/resources/extensions/gsd/context-masker.ts +152 -5
- package/src/resources/extensions/gsd/db-writer.ts +38 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
- package/src/resources/extensions/gsd/gsd-db.ts +564 -186
- package/src/resources/extensions/gsd/guided-flow.ts +4 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
- package/src/resources/extensions/gsd/md-importer.ts +49 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
- package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
- package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
- package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
- package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
- package/src/resources/extensions/gsd/preferences-models.ts +113 -43
- package/src/resources/extensions/gsd/preferences-types.ts +47 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
- package/src/resources/extensions/gsd/preferences.ts +5 -0
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
- package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
- package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
- package/src/resources/extensions/gsd/source-observations.ts +402 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
- package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
- package/src/resources/extensions/gsd/state.ts +7 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
- package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
- package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
- package/src/resources/extensions/gsd/tool-contract.ts +29 -1
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
- package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/uat-policy.ts +191 -0
- package/src/resources/extensions/gsd/uat-run.ts +550 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
- package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
- package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
- package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
- package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
- package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
- package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
- package/src/resources/extensions/subagent/agents.ts +4 -0
- package/src/resources/extensions/subagent/index.ts +28 -3
- package/src/resources/extensions/subagent/launch.ts +8 -0
- package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
- /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
|
@@ -1158,7 +1158,10 @@ async function dispatchWorkflow(
|
|
|
1158
1158
|
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
1159
1159
|
: undefined,
|
|
1160
1160
|
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
1161
|
-
|
|
1161
|
+
// Guided flow starts the MCP workflow server as part of dispatch, so the
|
|
1162
|
+
// parent session's activeTools doesn't include MCP tools yet. The MCP
|
|
1163
|
+
// launch config check (detectWorkflowMcpLaunchConfig) is the right gate
|
|
1164
|
+
// here — not whether MCP tools are pre-registered in the parent session.
|
|
1162
1165
|
},
|
|
1163
1166
|
);
|
|
1164
1167
|
if (compatibilityError) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
13
13
|
import { logWarning } from "./workflow-logger.js";
|
|
14
14
|
import { isClosedStatus } from "./status-guards.js";
|
|
15
|
-
import { join, relative } from "node:path";
|
|
15
|
+
import { dirname, join, relative } from "node:path";
|
|
16
16
|
import { createRequire } from "node:module";
|
|
17
17
|
import {
|
|
18
18
|
getAllMilestones,
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
getSliceTasks,
|
|
22
22
|
getTask,
|
|
23
23
|
getSlice,
|
|
24
|
-
getArtifact,
|
|
25
24
|
insertArtifact,
|
|
26
25
|
getGateResults,
|
|
27
26
|
} from "./gsd-db.js";
|
|
@@ -109,22 +108,6 @@ function sanitizeInlineRoadmapText(value: string | null | undefined): string {
|
|
|
109
108
|
.trim();
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
/**
|
|
113
|
-
* Load artifact content from the DB. Markdown projections are not authoritative
|
|
114
|
-
* during runtime; when the artifact row is missing, callers regenerate from DB
|
|
115
|
-
* rows instead of patching disk fallback content and storing it back.
|
|
116
|
-
*/
|
|
117
|
-
function loadArtifactContent(
|
|
118
|
-
artifactPath: string,
|
|
119
|
-
): string | null {
|
|
120
|
-
const artifact = getArtifact(artifactPath);
|
|
121
|
-
if (artifact && artifact.full_content) {
|
|
122
|
-
return artifact.full_content;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
111
|
function resolveRoadmapProjectionPath(basePath: string, milestoneId: string): string {
|
|
129
112
|
const projectionMilestonesDir = join(gsdProjectionRoot(basePath), "milestones");
|
|
130
113
|
const milestoneDirName = resolveDir(projectionMilestonesDir, milestoneId) ?? milestoneId;
|
|
@@ -397,6 +380,7 @@ export async function renderPlanFromDb(
|
|
|
397
380
|
basePath: string,
|
|
398
381
|
milestoneId: string,
|
|
399
382
|
sliceId: string,
|
|
383
|
+
outputPath?: string,
|
|
400
384
|
): Promise<{ planPath: string; taskPlanPaths: string[]; content: string }> {
|
|
401
385
|
const slice = getSlice(milestoneId, sliceId);
|
|
402
386
|
if (!slice) {
|
|
@@ -408,9 +392,16 @@ export async function renderPlanFromDb(
|
|
|
408
392
|
throw new Error(`no tasks found for ${milestoneId}/${sliceId}`);
|
|
409
393
|
}
|
|
410
394
|
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
395
|
+
const defaultPlanPath = join(
|
|
396
|
+
gsdProjectionRoot(basePath),
|
|
397
|
+
"milestones",
|
|
398
|
+
milestoneId,
|
|
399
|
+
"slices",
|
|
400
|
+
sliceId,
|
|
401
|
+
`${sliceId}-PLAN.md`,
|
|
402
|
+
);
|
|
403
|
+
const absPath = outputPath ?? defaultPlanPath;
|
|
404
|
+
mkdirSync(dirname(absPath), { recursive: true });
|
|
414
405
|
const artifactPath = toArtifactPath(absPath, basePath);
|
|
415
406
|
const sliceGates = getGateResults(milestoneId, sliceId, "slice");
|
|
416
407
|
const content = renderSlicePlanMarkdown(slice, tasks, sliceGates);
|
|
@@ -510,8 +501,14 @@ export async function renderRoadmapCheckboxes(
|
|
|
510
501
|
/**
|
|
511
502
|
* Render plan checkbox states from DB.
|
|
512
503
|
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
504
|
+
* Compatibility wrapper for legacy callers that used to patch plan checkboxes
|
|
505
|
+
* in-place. Plans are now fully regenerated from DB rows (mirroring
|
|
506
|
+
* renderRoadmapCheckboxes) so the projection always reflects the complete
|
|
507
|
+
* current task set and statuses. The previous regex-patch approach reused the
|
|
508
|
+
* cached PLAN artifact as the render input, which silently dropped tasks added
|
|
509
|
+
* to the DB after the artifact was first written — producing a lossy
|
|
510
|
+
* projection (the 4S/0T-vs-5S/13T drift class). The artifacts table is an
|
|
511
|
+
* output sink, never a render input.
|
|
515
512
|
*
|
|
516
513
|
* @returns true if the plan was written, false on skip/error
|
|
517
514
|
*/
|
|
@@ -519,6 +516,7 @@ export async function renderPlanCheckboxes(
|
|
|
519
516
|
basePath: string,
|
|
520
517
|
milestoneId: string,
|
|
521
518
|
sliceId: string,
|
|
519
|
+
outputPath?: string,
|
|
522
520
|
): Promise<boolean> {
|
|
523
521
|
const tasks = getSliceTasks(milestoneId, sliceId);
|
|
524
522
|
if (tasks.length === 0) {
|
|
@@ -528,48 +526,7 @@ export async function renderPlanCheckboxes(
|
|
|
528
526
|
return false;
|
|
529
527
|
}
|
|
530
528
|
|
|
531
|
-
|
|
532
|
-
const artifactPath = absPath ? toArtifactPath(absPath, basePath) : null;
|
|
533
|
-
|
|
534
|
-
let content: string | null = null;
|
|
535
|
-
if (artifactPath) {
|
|
536
|
-
content = loadArtifactContent(artifactPath);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
if (!content) {
|
|
540
|
-
await renderPlanFromDb(basePath, milestoneId, sliceId);
|
|
541
|
-
return true;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Apply checkbox patches for each task
|
|
545
|
-
let updated = content;
|
|
546
|
-
for (const task of tasks) {
|
|
547
|
-
const isDone = isClosedStatus(task.status);
|
|
548
|
-
const tid = task.id;
|
|
549
|
-
|
|
550
|
-
if (isDone) {
|
|
551
|
-
// Set [x]
|
|
552
|
-
updated = updated.replace(
|
|
553
|
-
new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${tid}:`, "m"),
|
|
554
|
-
`$1[x] **${tid}:`,
|
|
555
|
-
);
|
|
556
|
-
} else {
|
|
557
|
-
// Set [ ]
|
|
558
|
-
updated = updated.replace(
|
|
559
|
-
new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${tid}:`, "mi"),
|
|
560
|
-
`$1[ ] **${tid}:`,
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if (!absPath) return false;
|
|
566
|
-
|
|
567
|
-
await writeAndStore(absPath, artifactPath!, updated, {
|
|
568
|
-
artifact_type: "PLAN",
|
|
569
|
-
milestone_id: milestoneId,
|
|
570
|
-
slice_id: sliceId,
|
|
571
|
-
});
|
|
572
|
-
|
|
529
|
+
await renderPlanFromDb(basePath, milestoneId, sliceId, outputPath);
|
|
573
530
|
return true;
|
|
574
531
|
}
|
|
575
532
|
|
|
@@ -747,6 +704,18 @@ export async function renderAllFromDb(basePath: string): Promise<RenderAllResult
|
|
|
747
704
|
}
|
|
748
705
|
}
|
|
749
706
|
|
|
707
|
+
// Re-project root DECISIONS.md from the authoritative decision records so a
|
|
708
|
+
// full DB → markdown re-projection (recover, rebuild) also heals decisions
|
|
709
|
+
// drift — e.g. a worktree merge that accepted one branch's DECISIONS.md while
|
|
710
|
+
// the DB holds the union of both branches' decisions.
|
|
711
|
+
try {
|
|
712
|
+
const { regenerateDecisionsMarkdown } = await import("./db-writer.js");
|
|
713
|
+
await regenerateDecisionsMarkdown(basePath);
|
|
714
|
+
result.rendered++;
|
|
715
|
+
} catch (err) {
|
|
716
|
+
result.errors.push(`decisions: ${(err as Error).message}`);
|
|
717
|
+
}
|
|
718
|
+
|
|
750
719
|
return result;
|
|
751
720
|
}
|
|
752
721
|
|
|
@@ -832,7 +801,16 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
|
|
|
832
801
|
for (const task of tasks) {
|
|
833
802
|
const isDoneInDb = isClosedStatus(task.status);
|
|
834
803
|
const planTask = parsed.tasks.find((t: { id: string }) => t.id === task.id);
|
|
835
|
-
if (!planTask)
|
|
804
|
+
if (!planTask) {
|
|
805
|
+
// DB has a task the plan markdown lacks: the projection is
|
|
806
|
+
// lossy (e.g. tasks added after the PLAN artifact was first
|
|
807
|
+
// written). Flag it so the plan is re-rendered from DB rows.
|
|
808
|
+
stale.push({
|
|
809
|
+
path: planPath,
|
|
810
|
+
reason: `${task.id} exists in DB but is missing in plan`,
|
|
811
|
+
});
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
836
814
|
|
|
837
815
|
if (isDoneInDb && !planTask.done) {
|
|
838
816
|
stale.push({
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Exports: parseDecisionsTable, parseRequirementsSections, migrateFromMarkdown
|
|
6
6
|
|
|
7
|
-
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
7
|
+
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
|
|
8
8
|
import { join, relative } from 'node:path';
|
|
9
9
|
import type { Decision, Requirement } from './types.js';
|
|
10
10
|
import {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
insertTask,
|
|
17
17
|
openDatabase,
|
|
18
18
|
transaction,
|
|
19
|
+
updateMilestoneStatus,
|
|
19
20
|
updateSliceStatus,
|
|
20
21
|
_getAdapter,
|
|
21
22
|
} from './gsd-db.js';
|
|
@@ -38,6 +39,27 @@ import { logWarning } from './workflow-logger.js';
|
|
|
38
39
|
|
|
39
40
|
const VALID_MADE_BY = new Set(['human', 'agent', 'collaborative']);
|
|
40
41
|
|
|
42
|
+
const IMPORT_COMPLETE_STATUSES = new Set(['complete', 'done']);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Completion timestamp for an entity imported as complete: the SUMMARY.md mtime
|
|
46
|
+
* when one exists (deterministic, matches the completion drift handler), else
|
|
47
|
+
* the import time. Crucially this never returns null, so a complete entity is
|
|
48
|
+
* never left with completed_at=null — which would otherwise be permanent,
|
|
49
|
+
* undetectable drift when no SUMMARY file is present.
|
|
50
|
+
*/
|
|
51
|
+
function importCompletionTimestamp(summaryPath: string | null): string {
|
|
52
|
+
if (summaryPath && existsSync(summaryPath)) {
|
|
53
|
+
try {
|
|
54
|
+
return statSync(summaryPath).mtime.toISOString();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Fall through to import time.
|
|
57
|
+
logWarning('projection', `summary mtime read failed for ${summaryPath}: ${(err as Error).message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return new Date().toISOString();
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
/**
|
|
42
64
|
* Parse a DECISIONS.md markdown table into Decision objects (without seq).
|
|
43
65
|
* Detects `(amends DXXX)` in the Decision column to build supersession info.
|
|
@@ -587,6 +609,14 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
587
609
|
boundaryMapMarkdown: boundaryMapSection,
|
|
588
610
|
},
|
|
589
611
|
});
|
|
612
|
+
// insertMilestone never sets completed_at; backfill it now for milestones
|
|
613
|
+
// imported as complete so the DB is internally coherent immediately after
|
|
614
|
+
// import (rather than relying on a later reconciliation pass that can't even
|
|
615
|
+
// see the drift when no SUMMARY file exists).
|
|
616
|
+
if (IMPORT_COMPLETE_STATUSES.has(milestoneStatus)) {
|
|
617
|
+
const summaryPath = resolveMilestoneFile(basePath, milestoneId, 'SUMMARY');
|
|
618
|
+
updateMilestoneStatus(milestoneId, milestoneStatus, importCompletionTimestamp(summaryPath));
|
|
619
|
+
}
|
|
590
620
|
counts.milestones++;
|
|
591
621
|
|
|
592
622
|
// Parse roadmap for slices
|
|
@@ -614,10 +644,22 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
614
644
|
depends: sliceEntry.depends,
|
|
615
645
|
demo: sliceEntry.demo,
|
|
616
646
|
sequence: si + 1, // Preserve roadmap parse order (#3356)
|
|
647
|
+
isSketch: sliceEntry.isSketch ?? false, // ADR-011: preserve the `[sketch]` flag on re-import
|
|
617
648
|
planning: {
|
|
618
649
|
goal: plan?.goal ?? '',
|
|
619
650
|
},
|
|
620
651
|
});
|
|
652
|
+
// insertSlice never sets completed_at; backfill it for slices imported as
|
|
653
|
+
// complete (same rationale as milestones above).
|
|
654
|
+
if (IMPORT_COMPLETE_STATUSES.has(sliceStatus)) {
|
|
655
|
+
const sliceSummary = resolveSliceFile(basePath, milestoneId, sliceEntry.id, 'SUMMARY');
|
|
656
|
+
updateSliceStatus(
|
|
657
|
+
milestoneId,
|
|
658
|
+
sliceEntry.id,
|
|
659
|
+
sliceStatus,
|
|
660
|
+
importCompletionTimestamp(sliceSummary),
|
|
661
|
+
);
|
|
662
|
+
}
|
|
621
663
|
counts.slices++;
|
|
622
664
|
|
|
623
665
|
// Insert tasks from parsed plan
|
|
@@ -674,7 +716,12 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
674
716
|
});
|
|
675
717
|
if (allTasksDone && hasSliceSummary) {
|
|
676
718
|
if (_getAdapter()) {
|
|
677
|
-
updateSliceStatus(
|
|
719
|
+
updateSliceStatus(
|
|
720
|
+
milestoneId,
|
|
721
|
+
sliceEntry.id,
|
|
722
|
+
'complete',
|
|
723
|
+
importCompletionTimestamp(sliceSummaryPath),
|
|
724
|
+
);
|
|
678
725
|
process.stderr.write(
|
|
679
726
|
`gsd-migrate: ${milestoneId}/${sliceEntry.id} all tasks + slice summary complete — upgrading slice to complete\n`,
|
|
680
727
|
);
|
|
@@ -23,7 +23,7 @@ export interface HierarchyCounts {
|
|
|
23
23
|
|
|
24
24
|
export interface MigrationAutoCheckResult {
|
|
25
25
|
action: "none" | "recovery-required";
|
|
26
|
-
reason: "no-markdown" | "in-sync" | "db-empty" | "count-mismatch";
|
|
26
|
+
reason: "no-markdown" | "in-sync" | "db-empty" | "count-mismatch" | "markdown-missing";
|
|
27
27
|
markdown: HierarchyCounts;
|
|
28
28
|
beforeDb: HierarchyCounts;
|
|
29
29
|
afterDb: HierarchyCounts;
|
|
@@ -31,71 +31,146 @@ export interface MigrationAutoCheckResult {
|
|
|
31
31
|
message?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
interface HierarchyScan {
|
|
35
|
+
counts: HierarchyCounts;
|
|
36
|
+
// Fully-qualified identities: milestone "M001", slice "M001/S01",
|
|
37
|
+
// task "M001/S01/T01". Used to detect drift the cardinalities miss (a
|
|
38
|
+
// deleted+added pair nets to the same counts but is real divergence).
|
|
39
|
+
milestones: Set<string>;
|
|
40
|
+
slices: Set<string>;
|
|
41
|
+
tasks: Set<string>;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
function zeroCounts(): HierarchyCounts {
|
|
35
45
|
return { milestones: 0, slices: 0, tasks: 0 };
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
function emptyScan(): HierarchyScan {
|
|
49
|
+
return { counts: zeroCounts(), milestones: new Set(), slices: new Set(), tasks: new Set() };
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
function sameCounts(a: HierarchyCounts, b: HierarchyCounts): boolean {
|
|
39
53
|
return a.milestones === b.milestones && a.slices === b.slices && a.tasks === b.tasks;
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
|
|
56
|
+
function setsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
|
|
57
|
+
if (a.size !== b.size) return false;
|
|
58
|
+
for (const value of a) if (!b.has(value)) return false;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function scanIdentitiesMatch(a: HierarchyScan, b: HierarchyScan): boolean {
|
|
63
|
+
return (
|
|
64
|
+
setsEqual(a.milestones, b.milestones) &&
|
|
65
|
+
setsEqual(a.slices, b.slices) &&
|
|
66
|
+
setsEqual(a.tasks, b.tasks)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** True if any element of `a` is absent from `b`. */
|
|
71
|
+
function hasExtra(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
|
|
72
|
+
for (const value of a) if (!b.has(value)) return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function scanHasExtraIdentities(a: HierarchyScan, b: HierarchyScan): boolean {
|
|
77
|
+
return (
|
|
78
|
+
hasExtra(a.milestones, b.milestones) ||
|
|
79
|
+
hasExtra(a.slices, b.slices) ||
|
|
80
|
+
hasExtra(a.tasks, b.tasks)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* True when the DB holds any milestone/slice/task identity the markdown lacks —
|
|
86
|
+
* i.e. a `/gsd recover --confirm` (markdown → DB) would DELETE authoritative DB
|
|
87
|
+
* rows. This is identity-based, so it catches equal-count divergence (e.g. DB
|
|
88
|
+
* slice `S99` vs markdown `S01`) that a cardinality-only check misses. Used by
|
|
89
|
+
* the recover data-loss guard.
|
|
90
|
+
*/
|
|
91
|
+
export function recoverWouldDeleteDbRows(basePath: string): boolean {
|
|
92
|
+
return scanHasExtraIdentities(scanDbHierarchy(), scanMarkdownHierarchy(basePath));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function scanMarkdownHierarchy(basePath: string): HierarchyScan {
|
|
43
96
|
const root = milestonesDir(basePath);
|
|
44
|
-
if (!existsSync(root)) return
|
|
97
|
+
if (!existsSync(root)) return emptyScan();
|
|
45
98
|
|
|
46
|
-
const
|
|
99
|
+
const scan = emptyScan();
|
|
47
100
|
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
48
101
|
if (!entry.isDirectory() || !/^M\d+/.test(entry.name)) continue;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
102
|
+
// Use the CANONICAL milestone id (e.g. "M001" or "M001-a1b2c3"), matching
|
|
103
|
+
// scanDbHierarchy's milestone.id — not the raw directory name, which may
|
|
104
|
+
// carry a legacy descriptor (e.g. "M001-some-descriptor"). The canonical id
|
|
105
|
+
// both keys the identity sets AND resolves files correctly: resolveFile's
|
|
106
|
+
// prefix/legacy-pattern matching handles the descriptor dir either way.
|
|
107
|
+
const milestoneId = entry.name.match(/^(M\d+(?:-[a-z0-9]{6})?)/)?.[1] ?? entry.name;
|
|
108
|
+
scan.counts.milestones++;
|
|
109
|
+
scan.milestones.add(milestoneId);
|
|
110
|
+
|
|
111
|
+
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
52
112
|
if (!roadmapPath || !existsSync(roadmapPath)) continue;
|
|
53
113
|
|
|
54
114
|
const roadmap = parseRoadmap(readFileSync(roadmapPath, "utf-8"));
|
|
55
|
-
counts.slices += roadmap.slices.length;
|
|
115
|
+
scan.counts.slices += roadmap.slices.length;
|
|
56
116
|
|
|
57
117
|
for (const slice of roadmap.slices) {
|
|
58
|
-
|
|
118
|
+
scan.slices.add(`${milestoneId}/${slice.id}`);
|
|
119
|
+
const planPath = resolveSliceFile(basePath, milestoneId, slice.id, "PLAN");
|
|
59
120
|
if (!planPath || !existsSync(planPath)) continue;
|
|
60
121
|
const plan = parsePlan(readFileSync(planPath, "utf-8"));
|
|
61
|
-
counts.tasks += plan.tasks.length;
|
|
122
|
+
scan.counts.tasks += plan.tasks.length;
|
|
123
|
+
for (const task of plan.tasks) {
|
|
124
|
+
scan.tasks.add(`${milestoneId}/${slice.id}/${task.id}`);
|
|
125
|
+
}
|
|
62
126
|
}
|
|
63
127
|
}
|
|
64
128
|
|
|
65
|
-
return
|
|
129
|
+
return scan;
|
|
66
130
|
}
|
|
67
131
|
|
|
68
|
-
export function
|
|
69
|
-
if (!isDbAvailable()) return
|
|
70
|
-
const
|
|
132
|
+
export function scanDbHierarchy(): HierarchyScan {
|
|
133
|
+
if (!isDbAvailable()) return emptyScan();
|
|
134
|
+
const scan = emptyScan();
|
|
71
135
|
const milestones = getAllMilestones();
|
|
72
|
-
counts.milestones = milestones.length;
|
|
136
|
+
scan.counts.milestones = milestones.length;
|
|
73
137
|
|
|
74
138
|
for (const milestone of milestones) {
|
|
139
|
+
scan.milestones.add(milestone.id);
|
|
75
140
|
const slices = getMilestoneSlices(milestone.id);
|
|
76
|
-
counts.slices += slices.length;
|
|
141
|
+
scan.counts.slices += slices.length;
|
|
77
142
|
for (const slice of slices) {
|
|
78
|
-
|
|
143
|
+
scan.slices.add(`${milestone.id}/${slice.id}`);
|
|
144
|
+
const tasks = getSliceTasks(milestone.id, slice.id);
|
|
145
|
+
scan.counts.tasks += tasks.length;
|
|
146
|
+
for (const task of tasks) {
|
|
147
|
+
scan.tasks.add(`${milestone.id}/${slice.id}/${task.id}`);
|
|
148
|
+
}
|
|
79
149
|
}
|
|
80
150
|
}
|
|
81
151
|
|
|
82
|
-
return
|
|
152
|
+
return scan;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function countMarkdownHierarchy(basePath: string): HierarchyCounts {
|
|
156
|
+
return scanMarkdownHierarchy(basePath).counts;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function countDbHierarchy(): HierarchyCounts {
|
|
160
|
+
return scanDbHierarchy().counts;
|
|
83
161
|
}
|
|
84
162
|
|
|
85
163
|
export async function checkMarkdownHierarchyAgainstDb(
|
|
86
164
|
basePath: string,
|
|
87
165
|
): Promise<MigrationAutoCheckResult> {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
166
|
+
const markdownScan = scanMarkdownHierarchy(basePath);
|
|
167
|
+
const markdown = markdownScan.counts;
|
|
168
|
+
|
|
169
|
+
// Always open the DB before deciding. An empty markdown tree does NOT imply
|
|
170
|
+
// an empty project — the DB may hold authoritative rows whose markdown was
|
|
171
|
+
// lost, which is itself recoverable drift. The previous early return here
|
|
172
|
+
// skipped the DB entirely and silently hid a populated-DB/empty-markdown
|
|
173
|
+
// project.
|
|
99
174
|
const opened = await ensureDbOpen(basePath);
|
|
100
175
|
if (!opened || !isDbAvailable()) {
|
|
101
176
|
throw new Error(`failed to open or create the GSD database at ${basePath}`);
|
|
@@ -106,12 +181,58 @@ export async function checkMarkdownHierarchyAgainstDb(
|
|
|
106
181
|
// warn from a stale long-lived SQLite handle.
|
|
107
182
|
refreshOpenDatabaseFromDisk();
|
|
108
183
|
|
|
109
|
-
const
|
|
110
|
-
|
|
184
|
+
const dbScan = scanDbHierarchy();
|
|
185
|
+
const beforeDb = dbScan.counts;
|
|
186
|
+
|
|
187
|
+
const markdownEmpty = sameCounts(markdown, zeroCounts());
|
|
188
|
+
const dbEmpty = sameCounts(beforeDb, zeroCounts());
|
|
189
|
+
|
|
190
|
+
// Genuinely empty project: nothing on disk, nothing in the DB.
|
|
191
|
+
if (markdownEmpty && dbEmpty) {
|
|
192
|
+
return { action: "none", reason: "no-markdown", markdown, beforeDb, afterDb: beforeDb };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// In sync only when both cardinalities AND identities agree. Identity
|
|
196
|
+
// comparison catches drift the counts miss (e.g. a slice deleted from the DB
|
|
197
|
+
// and a different one added nets to the same count but is real divergence,
|
|
198
|
+
// and a missing PLAN.md vs DB tasks shows up as a task-identity gap).
|
|
199
|
+
if (sameCounts(markdown, beforeDb) && scanIdentitiesMatch(markdownScan, dbScan)) {
|
|
111
200
|
return { action: "none", reason: "in-sync", markdown, beforeDb, afterDb: beforeDb };
|
|
112
201
|
}
|
|
113
202
|
|
|
114
|
-
|
|
203
|
+
// Choose the SAFE repair direction by IDENTITY, not cardinality. Recover
|
|
204
|
+
// imports markdown → DB and DELETES any DB row markdown lacks, so it must
|
|
205
|
+
// never be recommended when the DB holds identities the markdown is missing —
|
|
206
|
+
// including equal-count divergence (DB `S99` vs markdown `S01`), which a
|
|
207
|
+
// count-only check would wrongly route to recover. Whenever the DB holds rows
|
|
208
|
+
// markdown lacks, the correct repair is to re-project from the DB (rebuild).
|
|
209
|
+
const dbHasExtra = scanHasExtraIdentities(dbScan, markdownScan);
|
|
210
|
+
|
|
211
|
+
const countsLine =
|
|
212
|
+
`Markdown planning artifacts (${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T) ` +
|
|
213
|
+
`do not match the authoritative DB (${beforeDb.milestones}M/${beforeDb.slices}S/${beforeDb.tasks}T). `;
|
|
214
|
+
|
|
215
|
+
// The DB holds rows markdown lacks (richer, identity-diverged, or markdown
|
|
216
|
+
// entirely missing): re-project from the DB. Recover here would destroy data.
|
|
217
|
+
if (dbHasExtra) {
|
|
218
|
+
return {
|
|
219
|
+
action: "recovery-required",
|
|
220
|
+
reason: markdownEmpty ? "markdown-missing" : "count-mismatch",
|
|
221
|
+
markdown,
|
|
222
|
+
beforeDb,
|
|
223
|
+
afterDb: beforeDb,
|
|
224
|
+
recoveryCommand: "/gsd rebuild markdown",
|
|
225
|
+
message:
|
|
226
|
+
countsLine +
|
|
227
|
+
"The DB holds rows the markdown lacks, so the markdown projection is stale. " +
|
|
228
|
+
"Run `/gsd rebuild markdown` to re-project from the authoritative DB. " +
|
|
229
|
+
"Do NOT run `/gsd recover --confirm` here — it would delete the extra DB rows.",
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// DB is empty (or markdown is strictly richer): markdown is the surviving
|
|
234
|
+
// source to import.
|
|
235
|
+
const reason = dbEmpty ? "db-empty" : "count-mismatch";
|
|
115
236
|
return {
|
|
116
237
|
action: "recovery-required",
|
|
117
238
|
reason,
|
|
@@ -120,8 +241,7 @@ export async function checkMarkdownHierarchyAgainstDb(
|
|
|
120
241
|
afterDb: beforeDb,
|
|
121
242
|
recoveryCommand: "/gsd recover --confirm",
|
|
122
243
|
message:
|
|
123
|
-
|
|
124
|
-
`do not match the authoritative DB (${beforeDb.milestones}M/${beforeDb.slices}S/${beforeDb.tasks}T). ` +
|
|
244
|
+
countsLine +
|
|
125
245
|
"Runtime startup will not import markdown automatically; run `/gsd recover --confirm` if markdown should repopulate the database.",
|
|
126
246
|
};
|
|
127
247
|
}
|
|
@@ -60,11 +60,31 @@ export function parseRoadmap(content: string): Roadmap {
|
|
|
60
60
|
return cachedParse(content, 'roadmap', _parseRoadmapImpl);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* ADR-011: the roadmap renderer writes a `[sketch]` badge for sketch slices,
|
|
65
|
+
* but the native parser does not surface it. Re-scan the markdown and set
|
|
66
|
+
* isSketch on the matching slice so the flag survives a markdown → DB re-import
|
|
67
|
+
* (e.g. /gsd recover) instead of being silently dropped.
|
|
68
|
+
*/
|
|
69
|
+
function applySketchFlags(roadmap: Roadmap, content: string): void {
|
|
70
|
+
const sketchIds = new Set<string>();
|
|
71
|
+
for (const line of content.split("\n")) {
|
|
72
|
+
if (!/\[sketch\]/i.test(line)) continue;
|
|
73
|
+
const m = line.match(/\*\*([\w.]+):/);
|
|
74
|
+
if (m) sketchIds.add(m[1]!);
|
|
75
|
+
}
|
|
76
|
+
if (sketchIds.size === 0) return;
|
|
77
|
+
for (const slice of roadmap.slices) {
|
|
78
|
+
if (sketchIds.has(slice.id)) slice.isSketch = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
63
82
|
function _parseRoadmapImpl(content: string): Roadmap {
|
|
64
83
|
const stopTimer = debugTime("parse-roadmap");
|
|
65
84
|
// Try native parser first for better performance
|
|
66
85
|
const nativeResult = nativeParseRoadmap(content);
|
|
67
86
|
if (nativeResult) {
|
|
87
|
+
applySketchFlags(nativeResult, content);
|
|
68
88
|
stopTimer({ native: true, slices: nativeResult.slices.length, boundaryEntries: nativeResult.boundaryMap.length });
|
|
69
89
|
debugCount("parseRoadmapCalls");
|
|
70
90
|
return nativeResult;
|