@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.75048e7
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/cli.js +3 -2
- package/dist/help-text.js +10 -6
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
- package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
- package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
- package/dist/resources/extensions/browser-tools/index.js +57 -9
- package/dist/resources/extensions/browser-tools/package.json +5 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +63 -22
- package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
- package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
- package/dist/resources/extensions/gsd/auto.js +9 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -14
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +28 -13
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
- package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
- package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
- package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
- package/dist/resources/extensions/gsd/escalation.js +4 -4
- package/dist/resources/extensions/gsd/forensics.js +74 -2
- package/dist/resources/extensions/gsd/gsd-db.js +5 -2
- package/dist/resources/extensions/gsd/guided-flow.js +118 -175
- package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
- package/dist/resources/extensions/gsd/memory-store.js +4 -1
- package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
- package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -21
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
- package/dist/resources/extensions/gsd/rule-registry.js +428 -52
- package/dist/resources/extensions/gsd/state.js +2 -2
- package/dist/resources/extensions/gsd/templates/plan.md +3 -1
- package/dist/resources/extensions/gsd/tool-contract.js +5 -0
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +30 -7
- package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +132 -18
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +169 -0
- package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
- package/dist/resources/extensions/gsd/verification-gate.js +72 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -75
- package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
- package/dist/rtk.d.ts +7 -1
- package/dist/rtk.js +27 -11
- package/dist/update-check.d.ts +15 -1
- package/dist/update-check.js +87 -12
- package/dist/update-cmd.d.ts +1 -0
- package/dist/update-cmd.js +53 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- 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/api/update/route.js +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 +8 -8
- 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/package.json +4 -2
- 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/dist/agent-session.d.ts +9 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +32 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/index.d.ts +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +1 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
- package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
- package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
- package/packages/gsd-agent-core/package.json +6 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -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 +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +23 -9
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +2 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +42 -3
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +6 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/api-registry.d.ts +2 -0
- package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
- package/packages/pi-ai/dist/api-registry.js +23 -0
- package/packages/pi-ai/dist/api-registry.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +74 -23
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +82 -31
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/stream.js +6 -6
- package/packages/pi-ai/dist/stream.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
- package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
- package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
- package/src/resources/extensions/browser-tools/index.ts +60 -9
- package/src/resources/extensions/browser-tools/package.json +5 -1
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +97 -15
- package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
- package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
- package/src/resources/extensions/gsd/auto.ts +12 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -14
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +32 -13
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
- package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
- package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
- package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
- package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
- package/src/resources/extensions/gsd/escalation.ts +4 -4
- package/src/resources/extensions/gsd/forensics.ts +99 -5
- package/src/resources/extensions/gsd/gsd-db.ts +5 -2
- package/src/resources/extensions/gsd/guided-flow.ts +214 -216
- package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
- package/src/resources/extensions/gsd/memory-store.ts +4 -1
- package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
- package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +25 -21
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
- package/src/resources/extensions/gsd/rule-registry.ts +558 -58
- package/src/resources/extensions/gsd/rule-types.ts +2 -0
- package/src/resources/extensions/gsd/state.ts +2 -2
- package/src/resources/extensions/gsd/templates/plan.md +3 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
- package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +72 -10
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +73 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +410 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tool-contract.ts +6 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +63 -7
- package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +163 -20
- package/src/resources/extensions/gsd/types.ts +69 -5
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +186 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
- package/src/resources/extensions/gsd/verification-gate.ts +87 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -75
- package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
- /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_ssgManifest.js +0 -0
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import { extractUatType } from '../../files.ts';
|
|
12
12
|
import { resolveSliceFile } from '../../paths.ts';
|
|
13
13
|
import { buildRunUatPrompt, checkNeedsRunUat } from '../../auto-prompts.ts';
|
|
14
|
+
import { buildRunUatResultPresentation, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from '../../tool-presentation-plan.ts';
|
|
14
15
|
|
|
15
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
17
|
const worktreePromptsDir = join(__dirname, '../..', 'prompts');
|
|
@@ -20,6 +21,8 @@ function loadPromptFromWorktree(name: string, vars: Record<string, string> = {})
|
|
|
20
21
|
let content = readFileSync(path, 'utf-8');
|
|
21
22
|
const effectiveVars = {
|
|
22
23
|
skillActivation: 'If no installed skill clearly matches this unit, skip explicit skill activation and continue with the required workflow.',
|
|
24
|
+
canonicalPresentation: JSON.stringify(buildRunUatResultPresentation(), null, 2),
|
|
25
|
+
toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
23
26
|
...vars,
|
|
24
27
|
};
|
|
25
28
|
for (const [key, value] of Object.entries(effectiveVars)) {
|
|
@@ -72,6 +75,38 @@ function makeBrowserObservableUatContent(mode = 'artifact-driven'): string {
|
|
|
72
75
|
].join('\n');
|
|
73
76
|
}
|
|
74
77
|
|
|
78
|
+
function makeDeferredBrowserUatContent(): string {
|
|
79
|
+
return [
|
|
80
|
+
'# UAT File',
|
|
81
|
+
'',
|
|
82
|
+
'## UAT Type',
|
|
83
|
+
'',
|
|
84
|
+
'- UAT mode: artifact-driven',
|
|
85
|
+
'- Why this mode is sufficient: Node interaction tests exercise the real app.js render/event/localStorage loop through a DOM harness. Live browser, keyboard, responsive, and visual-polish UAT remain intentionally deferred to S02.',
|
|
86
|
+
'',
|
|
87
|
+
'## Smoke Test',
|
|
88
|
+
'',
|
|
89
|
+
'Run `node --test tests/s01-static-interactions.test.js` and confirm all tests pass.',
|
|
90
|
+
'',
|
|
91
|
+
'## Test Cases',
|
|
92
|
+
'',
|
|
93
|
+
'1. Click the todo row edit control in the DOM harness.',
|
|
94
|
+
'2. Save changed text and reload/recreate the app from persisted localStorage.',
|
|
95
|
+
'3. Expected: the stored record shape remains unchanged.',
|
|
96
|
+
'',
|
|
97
|
+
'## Not Proven By This UAT',
|
|
98
|
+
'',
|
|
99
|
+
'- Final visual polish of edit controls.',
|
|
100
|
+
'- Keyboard usability through a real browser.',
|
|
101
|
+
'- Browser console and local network cleanliness.',
|
|
102
|
+
'',
|
|
103
|
+
'## Notes for Tester',
|
|
104
|
+
'',
|
|
105
|
+
'S02 should capture browser evidence for the full loop rather than changing this persisted model.',
|
|
106
|
+
'',
|
|
107
|
+
].join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
75
110
|
describe('run-uat', () => {
|
|
76
111
|
test('(a) artifact-driven', () => {
|
|
77
112
|
assert.deepStrictEqual(
|
|
@@ -232,8 +267,8 @@ test('(k) run-uat prompt template', () => {
|
|
|
232
267
|
`prompt contains detected dynamic uatType value "${uatType}" after substitution`,
|
|
233
268
|
);
|
|
234
269
|
assert.ok(
|
|
235
|
-
promptResult?.includes(`uatType: ${uatType}`) ?? false,
|
|
236
|
-
`prompt contains dynamic uatType
|
|
270
|
+
promptResult?.includes(`uatType: "${uatType}"`) ?? false,
|
|
271
|
+
`prompt contains dynamic uatType field "${uatType}" after substitution`,
|
|
237
272
|
);
|
|
238
273
|
assert.ok(
|
|
239
274
|
!/\{\{[^}]+\}\}/.test(promptResult ?? ''),
|
|
@@ -249,7 +284,7 @@ test('(k) run-uat prompt template', () => {
|
|
|
249
284
|
);
|
|
250
285
|
});
|
|
251
286
|
|
|
252
|
-
test('(k2) run-uat prompt references
|
|
287
|
+
test('(k2) run-uat prompt references gsd_uat_result_save, not direct write', () => {
|
|
253
288
|
const promptResult = loadPromptFromWorktree('run-uat', {
|
|
254
289
|
workingDirectory: '/tmp/test-project',
|
|
255
290
|
milestoneId: 'M001',
|
|
@@ -261,17 +296,25 @@ test('(k2) run-uat prompt references gsd_summary_save, not direct write', () =>
|
|
|
261
296
|
});
|
|
262
297
|
|
|
263
298
|
assert.ok(
|
|
264
|
-
promptResult.includes('
|
|
265
|
-
'run-uat prompt should reference
|
|
299
|
+
promptResult.includes('gsd_uat_result_save'),
|
|
300
|
+
'run-uat prompt should reference gsd_uat_result_save tool',
|
|
301
|
+
);
|
|
302
|
+
assert.ok(
|
|
303
|
+
promptResult.includes('presentedTools') && promptResult.includes('blockedTools'),
|
|
304
|
+
'run-uat prompt should specify the tool presentation contract',
|
|
266
305
|
);
|
|
267
306
|
assert.ok(
|
|
268
|
-
promptResult.includes('
|
|
269
|
-
'run-uat prompt should
|
|
307
|
+
!promptResult.includes('Call `gsd_summary_save`'),
|
|
308
|
+
'run-uat prompt should not instruct direct summary-save UAT persistence',
|
|
270
309
|
);
|
|
271
310
|
assert.ok(
|
|
272
311
|
!promptResult.includes('MUST write'),
|
|
273
312
|
'run-uat prompt should not instruct direct file write in footer',
|
|
274
313
|
);
|
|
314
|
+
assert.ok(
|
|
315
|
+
!promptResult.includes('Call `gsd_summary_save` with `artifact_type: "ASSESSMENT"`'),
|
|
316
|
+
'run-uat prompt should not instruct the legacy summary-save UAT path',
|
|
317
|
+
);
|
|
275
318
|
});
|
|
276
319
|
|
|
277
320
|
test('(l) dispatch preconditions via resolveSliceFile', () => {
|
|
@@ -482,8 +525,8 @@ test('(n) stale replay guard', async () => {
|
|
|
482
525
|
});
|
|
483
526
|
|
|
484
527
|
test('(q) verdict in ASSESSMENT file skips UAT dispatch (file-based path)', async () => {
|
|
485
|
-
// Regression test for #2644: run-uat
|
|
486
|
-
// S{sid}-ASSESSMENT.md
|
|
528
|
+
// Regression test for #2644: run-uat writes the verdict to
|
|
529
|
+
// S{sid}-ASSESSMENT.md through the structured UAT save path,
|
|
487
530
|
// but checkNeedsRunUat only checked S{sid}-UAT.md — causing a stuck loop.
|
|
488
531
|
const base = createFixtureBase();
|
|
489
532
|
try {
|
|
@@ -679,11 +722,30 @@ test('(u) run-uat prompt promotes artifact-driven browser specs to browser-execu
|
|
|
679
722
|
const prompt = await buildRunUatPrompt('M001', 'S01', uatRel, uatContent, base);
|
|
680
723
|
|
|
681
724
|
assert.match(prompt, /\*\*Detected UAT mode:\*\*\s*`browser-executable`/);
|
|
682
|
-
assert.match(prompt, /uatType: browser-executable/);
|
|
725
|
+
assert.match(prompt, /uatType: "browser-executable"/);
|
|
683
726
|
assert.match(prompt, /use gsd-browser tools/i);
|
|
727
|
+
assert.match(prompt, /"browser_navigate"/);
|
|
728
|
+
assert.match(prompt, /"browser_assert"/);
|
|
684
729
|
} finally {
|
|
685
730
|
cleanup(base);
|
|
686
731
|
}
|
|
687
732
|
});
|
|
688
733
|
|
|
734
|
+
test('(v) run-uat prompt keeps deferred browser work artifact-driven', async () => {
|
|
735
|
+
const base = createFixtureBase();
|
|
736
|
+
try {
|
|
737
|
+
const uatRel = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
|
|
738
|
+
const uatContent = makeDeferredBrowserUatContent();
|
|
739
|
+
writeSliceFile(base, 'M001', 'S01', 'UAT', uatContent);
|
|
740
|
+
|
|
741
|
+
const prompt = await buildRunUatPrompt('M001', 'S01', uatRel, uatContent, base);
|
|
742
|
+
|
|
743
|
+
assert.match(prompt, /\*\*Detected UAT mode:\*\*\s*`artifact-driven`/);
|
|
744
|
+
assert.match(prompt, /uatType: "artifact-driven"/);
|
|
745
|
+
assert.doesNotMatch(prompt, /uatType: "browser-executable"/);
|
|
746
|
+
assert.doesNotMatch(prompt, /"browser_navigate"/);
|
|
747
|
+
} finally {
|
|
748
|
+
cleanup(base);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
689
751
|
});
|
|
@@ -5,6 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
buildProjectBrowserMcpServerConfig,
|
|
8
9
|
ensureClaudeCodeMcpJsonServerEnabled,
|
|
9
10
|
ensureProjectWorkflowMcpConfig,
|
|
10
11
|
GSD_BROWSER_MCP_SERVER_NAME,
|
|
@@ -160,6 +161,37 @@ test("ensureProjectWorkflowMcpConfig can disable the default browser MCP server"
|
|
|
160
161
|
}
|
|
161
162
|
});
|
|
162
163
|
|
|
164
|
+
test("buildProjectBrowserMcpServerConfig prefers newer gsd-browser on PATH", () => {
|
|
165
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-browser-"));
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const config = buildProjectBrowserMcpServerConfig(projectRoot, {
|
|
169
|
+
GSD_BROWSER_PATH_VERSION: "99.0.0",
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
assert.equal(config?.command, "gsd-browser");
|
|
173
|
+
assert.equal(config?.args?.[0], "mcp");
|
|
174
|
+
} finally {
|
|
175
|
+
rmSync(projectRoot, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("buildProjectBrowserMcpServerConfig keeps bundled browser when PATH version is older", () => {
|
|
180
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-browser-"));
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const config = buildProjectBrowserMcpServerConfig(projectRoot, {
|
|
184
|
+
GSD_BROWSER_PATH_VERSION: "0.0.1",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
assert.equal(config?.command, process.execPath);
|
|
188
|
+
assert.match(config?.args?.[0] ?? "", /@opengsd[\/\\]gsd-browser[\/\\]bin[\/\\]gsd-browser/);
|
|
189
|
+
assert.equal(config?.args?.[1], "mcp");
|
|
190
|
+
} finally {
|
|
191
|
+
rmSync(projectRoot, { recursive: true, force: true });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
163
195
|
test("ensureProjectWorkflowMcpConfig is idempotent when config is already current", () => {
|
|
164
196
|
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-init-"));
|
|
165
197
|
mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
|
|
@@ -341,6 +341,8 @@ describe("formatMcpInitResult", () => {
|
|
|
341
341
|
assert.match(result, /created project mcp config/i);
|
|
342
342
|
assert.match(result, /\/tmp\/project\/\.mcp\.json/);
|
|
343
343
|
assert.match(result, /mcp-capable clients/i);
|
|
344
|
+
assert.match(result, /workflow and gsd-browser MCP servers/i);
|
|
345
|
+
assert.match(result, /Pi Providers use the managed gsd-browser engine/i);
|
|
344
346
|
assert.doesNotMatch(result, /claude code/i);
|
|
345
347
|
});
|
|
346
348
|
|
|
@@ -11,6 +11,16 @@ import {
|
|
|
11
11
|
import { createMemoryRelation, listRelationsFor } from '../memory-relations.ts';
|
|
12
12
|
import { saveEmbedding, getEmbeddingForMemory } from '../memory-embeddings.ts';
|
|
13
13
|
|
|
14
|
+
function markProcessedUnits(count = 21): void {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
for (let i = 0; i < count; i++) {
|
|
17
|
+
markUnitProcessed(`unit/${i}`, `file-${i}`);
|
|
18
|
+
_getAdapter()!
|
|
19
|
+
.prepare('UPDATE memory_processed_units SET processed_at = :ts WHERE unit_key = :key')
|
|
20
|
+
.run({ ':ts': new Date(now + i * 1000).toISOString(), ':key': `unit/${i}` });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
15
25
|
// enforceMemoryCap — cascade cleanup of embeddings and relations
|
|
16
26
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -71,14 +81,7 @@ test('memory-decay: returns decayed memory IDs', () => {
|
|
|
71
81
|
openDatabase(':memory:');
|
|
72
82
|
|
|
73
83
|
// Insert processed units — decayStaleMemories needs at least N rows.
|
|
74
|
-
|
|
75
|
-
for (let i = 0; i < 21; i++) {
|
|
76
|
-
markUnitProcessed(`unit/${i}`, `file-${i}`);
|
|
77
|
-
// small spacing to create deterministic ordering
|
|
78
|
-
const row = _getAdapter()!
|
|
79
|
-
.prepare('UPDATE memory_processed_units SET processed_at = :ts WHERE unit_key = :key');
|
|
80
|
-
row.run({ ':ts': new Date(now + i * 1000).toISOString(), ':key': `unit/${i}` });
|
|
81
|
-
}
|
|
84
|
+
markProcessedUnits();
|
|
82
85
|
|
|
83
86
|
// Create memory with updated_at in the distant past
|
|
84
87
|
createMemory({ category: 'pattern', content: 'stale entry', confidence: 0.9 });
|
|
@@ -98,6 +101,34 @@ test('memory-decay: returns decayed memory IDs', () => {
|
|
|
98
101
|
closeDatabase();
|
|
99
102
|
});
|
|
100
103
|
|
|
104
|
+
test('memory-decay: skips stale decision-sourced memories', () => {
|
|
105
|
+
openDatabase(':memory:');
|
|
106
|
+
markProcessedUnits();
|
|
107
|
+
|
|
108
|
+
createMemory({ category: 'pattern', content: 'stale working note', confidence: 0.9 });
|
|
109
|
+
createMemory({
|
|
110
|
+
category: 'architecture',
|
|
111
|
+
content: 'decision-backed architecture memory',
|
|
112
|
+
confidence: 0.85,
|
|
113
|
+
structuredFields: { sourceDecisionId: 'D001' },
|
|
114
|
+
});
|
|
115
|
+
_getAdapter()!
|
|
116
|
+
.prepare("UPDATE memories SET updated_at = '2000-01-01T00:00:00Z' WHERE id IN ('MEM001', 'MEM002')")
|
|
117
|
+
.run({});
|
|
118
|
+
|
|
119
|
+
const decayed = decayStaleMemories(20);
|
|
120
|
+
assert.ok(decayed.includes('MEM001'));
|
|
121
|
+
assert.ok(!decayed.includes('MEM002'));
|
|
122
|
+
|
|
123
|
+
const rows = _getAdapter()!
|
|
124
|
+
.prepare("SELECT id, confidence FROM memories WHERE id IN ('MEM001', 'MEM002') ORDER BY id")
|
|
125
|
+
.all() as Array<{ id: string; confidence: number }>;
|
|
126
|
+
assert.ok(rows.find((row) => row.id === 'MEM001')!.confidence < 0.9);
|
|
127
|
+
assert.equal(rows.find((row) => row.id === 'MEM002')!.confidence, 0.85);
|
|
128
|
+
|
|
129
|
+
closeDatabase();
|
|
130
|
+
});
|
|
131
|
+
|
|
101
132
|
test('memory-decay: returns empty when there are fewer processed units than the threshold', () => {
|
|
102
133
|
openDatabase(':memory:');
|
|
103
134
|
createMemory({ category: 'pattern', content: 'fresh' });
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Regression tests for DB-backed closeout consistency before merge.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { existsSync, mkdtempSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
|
|
11
|
+
import { mergeMilestoneToMain } from "../auto-worktree.ts";
|
|
12
|
+
import { closeDatabase, insertMilestone, openDatabase } from "../gsd-db.ts";
|
|
13
|
+
|
|
14
|
+
function git(args: string[], cwd: string): string {
|
|
15
|
+
return execFileSync("git", args, {
|
|
16
|
+
cwd,
|
|
17
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18
|
+
encoding: "utf-8",
|
|
19
|
+
}).trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createRepo(): string {
|
|
23
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "merge-closeout-gate-")));
|
|
24
|
+
git(["init"], dir);
|
|
25
|
+
git(["config", "user.email", "test@test.com"], dir);
|
|
26
|
+
git(["config", "user.name", "Test"], dir);
|
|
27
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
28
|
+
writeFileSync(join(dir, "README.md"), "# test\n");
|
|
29
|
+
git(["add", "."], dir);
|
|
30
|
+
git(["commit", "-m", "init"], dir);
|
|
31
|
+
git(["branch", "-M", "main"], dir);
|
|
32
|
+
|
|
33
|
+
git(["checkout", "-b", "milestone/M001"], dir);
|
|
34
|
+
writeFileSync(join(dir, "feature.ts"), "export const feature = true;\n");
|
|
35
|
+
git(["add", "feature.ts"], dir);
|
|
36
|
+
git(["commit", "-m", "feat: milestone work"], dir);
|
|
37
|
+
git(["checkout", "main"], dir);
|
|
38
|
+
return dir;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test("mergeMilestoneToMain blocks when project DB closeout is still open", () => {
|
|
42
|
+
const savedCwd = process.cwd();
|
|
43
|
+
const repo = createRepo();
|
|
44
|
+
try {
|
|
45
|
+
assert.equal(openDatabase(join(repo, ".gsd", "gsd.db")), true);
|
|
46
|
+
insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
|
|
47
|
+
|
|
48
|
+
const mainHeadBefore = git(["rev-parse", "main"], repo);
|
|
49
|
+
process.chdir(repo);
|
|
50
|
+
|
|
51
|
+
assert.throws(
|
|
52
|
+
() => mergeMilestoneToMain(repo, "M001", "# M001\n- [x] **S01: Done**\n"),
|
|
53
|
+
/closeout-consistency-blocked/,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
assert.equal(git(["rev-parse", "main"], repo), mainHeadBefore);
|
|
57
|
+
assert.equal(git(["branch", "--show-current"], repo), "main");
|
|
58
|
+
} finally {
|
|
59
|
+
closeDatabase();
|
|
60
|
+
process.chdir(savedCwd);
|
|
61
|
+
if (existsSync(repo)) rmSync(repo, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -15,7 +15,7 @@ import { delimiter, join } from "node:path";
|
|
|
15
15
|
import { execFileSync } from "node:child_process";
|
|
16
16
|
|
|
17
17
|
import { mergeMilestoneToMain } from "../auto-worktree.ts";
|
|
18
|
-
import { closeDatabase, openDatabase } from "../gsd-db.ts";
|
|
18
|
+
import { closeDatabase, insertAssessment, insertMilestone, insertSlice, openDatabase } from "../gsd-db.ts";
|
|
19
19
|
import { GIT_NO_PROMPT_ENV } from "../git-constants.js";
|
|
20
20
|
import { _clearGsdRootCache } from "../paths.ts";
|
|
21
21
|
import { _resetServiceCache } from "../worktree.ts";
|
|
@@ -145,6 +145,15 @@ test("mergeMilestoneToMain keeps the Windows DB cycle closed through squash merg
|
|
|
145
145
|
|
|
146
146
|
withPlatform("win32", () => {
|
|
147
147
|
assert.equal(openDatabase(join(repo, ".gsd", "gsd.db")), true);
|
|
148
|
+
insertMilestone({ id: "M001", title: "Windows DB cycle", status: "complete" });
|
|
149
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Done Slice", status: "complete" });
|
|
150
|
+
insertAssessment({
|
|
151
|
+
path: "milestones/M001/M001-VALIDATION.md",
|
|
152
|
+
milestoneId: "M001",
|
|
153
|
+
status: "pass",
|
|
154
|
+
scope: "milestone-validation",
|
|
155
|
+
fullContent: "verdict: pass",
|
|
156
|
+
});
|
|
148
157
|
assert.equal(existsSync(join(repo, ".gsd", "gsd.db-shm")), true);
|
|
149
158
|
|
|
150
159
|
process.env.PATH = `${bin}${delimiter}${originalPath}`;
|
|
@@ -6,7 +6,7 @@ import assert from "node:assert/strict";
|
|
|
6
6
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
|
-
import { openDatabase, insertMilestone, closeDatabase } from "../gsd-db.js";
|
|
9
|
+
import { openDatabase, insertAssessment, insertMilestone, insertSlice, closeDatabase } from "../gsd-db.js";
|
|
10
10
|
import {
|
|
11
11
|
isMilestoneCloseoutSettled,
|
|
12
12
|
evaluateCompleteMilestoneDispatch,
|
|
@@ -39,6 +39,14 @@ test("isMilestoneCloseoutSettled requires DB closed and summary artifact", async
|
|
|
39
39
|
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
40
40
|
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
41
41
|
insertMilestone({ id: "M001", title: "Done", status: "complete" });
|
|
42
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Done Slice", status: "complete" });
|
|
43
|
+
insertAssessment({
|
|
44
|
+
path: "milestones/M001/M001-VALIDATION.md",
|
|
45
|
+
milestoneId: "M001",
|
|
46
|
+
status: "pass",
|
|
47
|
+
scope: "milestone-validation",
|
|
48
|
+
fullContent: "verdict: pass",
|
|
49
|
+
});
|
|
42
50
|
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
43
51
|
mkdirSync(milestoneDir, { recursive: true });
|
|
44
52
|
writeFileSync(join(milestoneDir, "M001-SUMMARY.md"), "# Milestone Summary\n");
|
|
@@ -13,10 +13,10 @@ test("dispatchNewMilestoneDiscuss uses discuss.md only on greenfield projects",
|
|
|
13
13
|
|
|
14
14
|
assert.match(fnBody, /findMilestoneIds\(basePath\)\.length === 0/);
|
|
15
15
|
assert.match(fnBody, /prepareAndBuildDiscussPrompt/);
|
|
16
|
-
assert.match(fnBody, /
|
|
16
|
+
assert.match(fnBody, /buildDiscussMilestonePrompt/);
|
|
17
17
|
assert.match(
|
|
18
18
|
fnBody,
|
|
19
|
-
/if \(isGreenfield\)[\s\S]*prepareAndBuildDiscussPrompt[\s\S]*
|
|
19
|
+
/if \(isGreenfield\)[\s\S]*prepareAndBuildDiscussPrompt[\s\S]*buildDiscussMilestonePrompt/,
|
|
20
20
|
"greenfield branch must precede guided-discuss-milestone branch",
|
|
21
21
|
);
|
|
22
22
|
});
|
|
@@ -24,7 +24,7 @@ test("dispatchNewMilestoneDiscuss uses discuss.md only on greenfield projects",
|
|
|
24
24
|
test("dispatchNewMilestoneDiscuss uses milestone-specific preparation guidance", () => {
|
|
25
25
|
const source = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
|
|
26
26
|
const fnBody = extractSourceRegion(source, "async function dispatchNewMilestoneDiscuss(");
|
|
27
|
-
assert.match(fnBody, /buildDiscussPreparationContext\(ctx, basePath, "milestone"\)/);
|
|
27
|
+
assert.match(fnBody, /buildDiscussPreparationContext\(ctx, basePath, "milestone", true\)/);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
test("launchNextMilestoneDiscuss routes through dispatchNewMilestoneDiscuss for normal path", () => {
|
|
@@ -83,6 +83,15 @@ test("plan-slice prompt: compact planning gates survive template substitution",
|
|
|
83
83
|
assert.ok(!result.includes("{{"));
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
test("plan-slice prompt: absence checks use negated quiet searches", () => {
|
|
87
|
+
const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
|
|
88
|
+
assert.ok(result.includes("For absence checks"));
|
|
89
|
+
assert.ok(result.includes("`! grep -q 'pattern' file`"));
|
|
90
|
+
assert.ok(result.includes("`! rg -q 'pattern' file`"));
|
|
91
|
+
assert.ok(result.includes("do not use `grep -c` or `rg -c`"));
|
|
92
|
+
assert.ok(result.includes("count commands exit 1 when they find zero matches"));
|
|
93
|
+
});
|
|
94
|
+
|
|
86
95
|
test("plan-slice prompt: footer references gsd_plan_slice tool, not direct write", () => {
|
|
87
96
|
const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
|
|
88
97
|
assert.ok(
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resetHookState,
|
|
12
12
|
isRetryPending,
|
|
13
13
|
consumeRetryTrigger,
|
|
14
|
+
consumeGateBlock,
|
|
14
15
|
resolveHookArtifactPath,
|
|
15
16
|
runPreDispatchHooks,
|
|
16
17
|
persistHookState,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
formatHookStatus,
|
|
21
22
|
triggerHookManually,
|
|
22
23
|
} from "../post-unit-hooks.ts";
|
|
24
|
+
import { invalidateAllCaches } from "../cache.ts";
|
|
23
25
|
|
|
24
26
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
25
27
|
|
|
@@ -29,6 +31,11 @@ function createFixtureBase(): string {
|
|
|
29
31
|
return base;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
function writeHookPreferences(base: string, hookYaml: string): void {
|
|
35
|
+
writeFileSync(join(base, ".gsd", "PREFERENCES.md"), `---\npost_unit_hooks:\n${hookYaml}\n---\n`, "utf-8");
|
|
36
|
+
invalidateAllCaches();
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
33
40
|
// Phase 1: Post-Unit Hook Tests
|
|
34
41
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -104,6 +111,156 @@ test('consumeRetryTrigger clears state', () => {
|
|
|
104
111
|
assert.ok(!isRetryPending(), "no retry initially");
|
|
105
112
|
});
|
|
106
113
|
|
|
114
|
+
test('Advisory hook keeps artifact idempotency without verdict frontmatter', () => {
|
|
115
|
+
resetHookState();
|
|
116
|
+
const base = createFixtureBase();
|
|
117
|
+
try {
|
|
118
|
+
writeHookPreferences(base, ` - name: docs-hint
|
|
119
|
+
after:
|
|
120
|
+
- execute-task
|
|
121
|
+
prompt: Review docs
|
|
122
|
+
artifact: DOCS-HINT.md
|
|
123
|
+
`);
|
|
124
|
+
writeFileSync(resolveHookArtifactPath(base, "M001/S01/T01", "DOCS-HINT.md"), "plain advisory note", "utf-8");
|
|
125
|
+
|
|
126
|
+
const result = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
127
|
+
assert.deepStrictEqual(result, null, "existing advisory artifact remains idempotent");
|
|
128
|
+
assert.deepStrictEqual(consumeGateBlock(), null, "advisory hook does not create gate block");
|
|
129
|
+
} finally {
|
|
130
|
+
resetHookState();
|
|
131
|
+
invalidateAllCaches();
|
|
132
|
+
rmSync(base, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('Blocking hook skips only after passing frontmatter verdict', () => {
|
|
137
|
+
resetHookState();
|
|
138
|
+
const base = createFixtureBase();
|
|
139
|
+
try {
|
|
140
|
+
writeHookPreferences(base, ` - name: security-review
|
|
141
|
+
after:
|
|
142
|
+
- execute-task
|
|
143
|
+
prompt: Review security
|
|
144
|
+
artifact: SECURITY-REVIEW.md
|
|
145
|
+
criticality: blocking
|
|
146
|
+
`);
|
|
147
|
+
writeFileSync(
|
|
148
|
+
resolveHookArtifactPath(base, "M001/S01/T01", "SECURITY-REVIEW.md"),
|
|
149
|
+
"---\nverdict: pass\n---\n\nNo blocking findings.\n",
|
|
150
|
+
"utf-8",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const result = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
154
|
+
assert.deepStrictEqual(result, null, "passing gate artifact is idempotent");
|
|
155
|
+
assert.deepStrictEqual(consumeGateBlock(), null, "passing gate does not block");
|
|
156
|
+
} finally {
|
|
157
|
+
resetHookState();
|
|
158
|
+
invalidateAllCaches();
|
|
159
|
+
rmSync(base, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('Blocking hook reruns invalid artifact once then blocks at cycle budget', () => {
|
|
164
|
+
resetHookState();
|
|
165
|
+
const base = createFixtureBase();
|
|
166
|
+
try {
|
|
167
|
+
writeHookPreferences(base, ` - name: security-review
|
|
168
|
+
after:
|
|
169
|
+
- execute-task
|
|
170
|
+
prompt: Review security
|
|
171
|
+
artifact: SECURITY-REVIEW.md
|
|
172
|
+
criticality: blocking
|
|
173
|
+
`);
|
|
174
|
+
writeFileSync(resolveHookArtifactPath(base, "M001/S01/T01", "SECURITY-REVIEW.md"), "partial output", "utf-8");
|
|
175
|
+
|
|
176
|
+
const dispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
177
|
+
assert.ok(dispatch, "invalid gate artifact dispatches the blocking hook");
|
|
178
|
+
assert.equal(dispatch.unitType, "hook/security-review");
|
|
179
|
+
|
|
180
|
+
const afterHook = checkPostUnitHooks("hook/security-review", "M001/S01/T01", base);
|
|
181
|
+
assert.deepStrictEqual(afterHook, null, "no further hook dispatch after max_cycles=1");
|
|
182
|
+
const block = consumeGateBlock();
|
|
183
|
+
assert.ok(block, "gate block is recorded");
|
|
184
|
+
assert.equal(block.hookName, "security-review");
|
|
185
|
+
assert.match(block.reason, /missing frontmatter verdict/);
|
|
186
|
+
} finally {
|
|
187
|
+
resetHookState();
|
|
188
|
+
invalidateAllCaches();
|
|
189
|
+
rmSync(base, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('Blocking hook restored from disk does not trust artifact without clean hook completion', () => {
|
|
194
|
+
resetHookState();
|
|
195
|
+
const base = createFixtureBase();
|
|
196
|
+
try {
|
|
197
|
+
writeHookPreferences(base, ` - name: security-review
|
|
198
|
+
after:
|
|
199
|
+
- execute-task
|
|
200
|
+
prompt: Review security
|
|
201
|
+
artifact: SECURITY-REVIEW.md
|
|
202
|
+
criticality: blocking
|
|
203
|
+
max_cycles: 2
|
|
204
|
+
`);
|
|
205
|
+
const firstDispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
206
|
+
assert.ok(firstDispatch, "gate dispatches first cycle");
|
|
207
|
+
persistHookState(base);
|
|
208
|
+
|
|
209
|
+
writeFileSync(
|
|
210
|
+
resolveHookArtifactPath(base, "M001/S01/T01", "SECURITY-REVIEW.md"),
|
|
211
|
+
"---\noutcome:\n verdict: pass\n---\n",
|
|
212
|
+
"utf-8",
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
resetHookState();
|
|
216
|
+
restoreHookState(base);
|
|
217
|
+
|
|
218
|
+
const resumed = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
219
|
+
assert.ok(resumed, "persisted active gate reruns when clean hook completion was not observed");
|
|
220
|
+
assert.equal(resumed.unitType, "hook/security-review");
|
|
221
|
+
} finally {
|
|
222
|
+
resetHookState();
|
|
223
|
+
invalidateAllCaches();
|
|
224
|
+
rmSync(base, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('Blocking hook needs-rework verdict requests trigger unit retry', () => {
|
|
229
|
+
resetHookState();
|
|
230
|
+
const base = createFixtureBase();
|
|
231
|
+
try {
|
|
232
|
+
writeHookPreferences(base, ` - name: review-arbiter
|
|
233
|
+
after:
|
|
234
|
+
- execute-task
|
|
235
|
+
prompt: Review task
|
|
236
|
+
artifact: REVIEW-DEBATE.md
|
|
237
|
+
criticality: blocking
|
|
238
|
+
max_cycles: 2
|
|
239
|
+
on_block:
|
|
240
|
+
action: retry-unit
|
|
241
|
+
`);
|
|
242
|
+
const dispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
243
|
+
assert.ok(dispatch, "gate dispatches");
|
|
244
|
+
writeFileSync(
|
|
245
|
+
resolveHookArtifactPath(base, "M001/S01/T01", "REVIEW-DEBATE.md"),
|
|
246
|
+
"---\nverdict: needs-rework\n---\n\nRework required.\n",
|
|
247
|
+
"utf-8",
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const afterHook = checkPostUnitHooks("hook/review-arbiter", "M001/S01/T01", base);
|
|
251
|
+
assert.deepStrictEqual(afterHook, null, "needs-rework routes via retry signal");
|
|
252
|
+
assert.ok(isRetryPending(), "retry is pending");
|
|
253
|
+
assert.deepStrictEqual(consumeRetryTrigger(), {
|
|
254
|
+
unitType: "execute-task",
|
|
255
|
+
unitId: "M001/S01/T01",
|
|
256
|
+
});
|
|
257
|
+
} finally {
|
|
258
|
+
resetHookState();
|
|
259
|
+
invalidateAllCaches();
|
|
260
|
+
rmSync(base, { recursive: true, force: true });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
107
264
|
// ─── Variable substitution in prompts ──────────────────────────────────────
|
|
108
265
|
test('Variable substitution', () => {
|
|
109
266
|
const base = "/project";
|