@opengsd/gsd-pi 1.2.0-dev.fb12b103 → 1.2.0-dev.fbdca60b
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-model-override.d.ts +15 -0
- package/dist/cli-model-override.js +21 -0
- package/dist/cli.js +1 -18
- package/dist/headless-events.js +7 -5
- package/dist/loader.js +6 -4
- package/dist/mcp-server.js +2 -1
- package/dist/register-agent-bundles.d.ts +11 -2
- package/dist/register-agent-bundles.js +18 -4
- package/dist/resource-loader.d.ts +10 -5
- package/dist/resource-loader.js +121 -6
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +5 -4
- package/dist/resources/extensions/ask-user-questions.js +3 -2
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
- package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +17 -2
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +33 -13
- package/dist/resources/extensions/gsd/auto/dispatch-history.js +120 -0
- package/dist/resources/extensions/gsd/auto/dispatch-key.js +37 -0
- package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
- package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
- package/dist/resources/extensions/gsd/auto/loop.js +7 -1
- package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +167 -64
- package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
- package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -34
- package/dist/resources/extensions/gsd/auto-dispatch.js +39 -58
- package/dist/resources/extensions/gsd/auto-model-selection.js +11 -7
- package/dist/resources/extensions/gsd/auto-post-unit.js +30 -12
- package/dist/resources/extensions/gsd/auto-prompts.js +66 -9
- package/dist/resources/extensions/gsd/auto-start.js +26 -8
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +5 -4
- package/dist/resources/extensions/gsd/auto-verification.js +23 -30
- package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
- package/dist/resources/extensions/gsd/auto.js +52 -2
- package/dist/resources/extensions/gsd/blocked-models.js +28 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +26 -6
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +107 -45
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +302 -80
- package/dist/resources/extensions/gsd/closeout-wizard.js +92 -0
- package/dist/resources/extensions/gsd/commands/context.js +16 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +46 -3
- package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
- package/dist/resources/extensions/gsd/consent-question.js +353 -0
- package/dist/resources/extensions/gsd/consent-verdict.js +63 -0
- package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
- package/dist/resources/extensions/gsd/db/engine.js +24 -6
- package/dist/resources/extensions/gsd/db/queries.js +56 -0
- package/dist/resources/extensions/gsd/db-migration-backup.js +51 -8
- package/dist/resources/extensions/gsd/db-transaction.js +27 -23
- package/dist/resources/extensions/gsd/db-writer.js +8 -17
- package/dist/resources/extensions/gsd/dispatch-guard.js +10 -35
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
- package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
- package/dist/resources/extensions/gsd/doctor-git-checks.js +2 -18
- package/dist/resources/extensions/gsd/engine-hook-contract.js +70 -0
- package/dist/resources/extensions/gsd/files.js +33 -19
- package/dist/resources/extensions/gsd/gsd-command-home.js +22 -12
- package/dist/resources/extensions/gsd/gsd-db.js +17 -21
- package/dist/resources/extensions/gsd/guidance.js +60 -0
- package/dist/resources/extensions/gsd/guided-flow.js +93 -4
- package/dist/resources/extensions/gsd/health-widget.js +87 -28
- package/dist/resources/extensions/gsd/markdown-renderer.js +10 -0
- package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
- package/dist/resources/extensions/gsd/memory-relations.js +1 -1
- package/dist/resources/extensions/gsd/milestone-closeout.js +85 -24
- package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
- package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
- package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
- package/dist/resources/extensions/gsd/notifications.js +12 -7
- package/dist/resources/extensions/gsd/parsers-legacy.js +16 -4
- package/dist/resources/extensions/gsd/projection-flush.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
- 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/quick-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +8 -4
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/dist/resources/extensions/gsd/reactive-graph.js +8 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
- package/dist/resources/extensions/gsd/session-lock.js +1 -1
- package/dist/resources/extensions/gsd/skill-activation.js +3 -6
- package/dist/resources/extensions/gsd/state.js +11 -2
- package/dist/resources/extensions/gsd/tool-contract.js +14 -3
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +4 -4
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +22 -12
- package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +67 -2
- package/dist/resources/extensions/gsd/uat-policy.js +40 -15
- package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
- package/dist/resources/extensions/gsd/unit-registry.js +34 -4
- package/dist/resources/extensions/gsd/verdict-parser.js +1 -1
- package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
- package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
- package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
- package/dist/resources/extensions/gsd/workflow-events.js +6 -18
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +3 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
- package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
- package/dist/resources/extensions/gsd/worktree.js +8 -1
- package/dist/resources/extensions/mcp-client/manager.js +6 -1
- package/dist/resources/extensions/shared/gsd-browser-cli.js +21 -2
- package/dist/resources/shared/gsd-browser-path-sync.js +214 -0
- package/dist/resources/shared/package-manager-detection.js +1 -1
- package/dist/resources/skills/create-skill/SKILL.md +3 -0
- package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
- package/dist/runtime-checks.d.ts +10 -0
- package/dist/runtime-checks.js +27 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/update-check.d.ts +2 -0
- package/dist/update-check.js +24 -1
- package/dist/update-cmd.js +20 -3
- 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/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
- package/dist/web/standalone/.next/server/chunks/{5942.js → 1128.js} +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- 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/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/container.js +26 -18
- package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +47 -14
- package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
- package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
- package/dist/web/standalone/node_modules/postcss/lib/input.js +54 -29
- package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +47 -37
- package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +26 -9
- package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +57 -55
- package/dist/web/standalone/node_modules/postcss/lib/node.js +99 -31
- package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/parser.js +10 -9
- package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
- package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +30 -11
- package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
- package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
- package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
- package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +69 -28
- package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +6 -2
- package/dist/web/standalone/node_modules/postcss/package.json +48 -48
- package/package.json +3 -3
- 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/sdk.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.js +6 -4
- package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -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 +50 -6
- 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 +2 -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 +34 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +17 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +12 -3
- package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
- package/packages/mcp-server/dist/cli-runner.js +137 -0
- package/packages/mcp-server/dist/cli-runner.js.map +1 -0
- package/packages/mcp-server/dist/cli.js +2 -53
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/moonshot-tool-schema.d.ts +29 -0
- package/packages/mcp-server/dist/moonshot-tool-schema.d.ts.map +1 -0
- package/packages/mcp-server/dist/moonshot-tool-schema.js +50 -0
- package/packages/mcp-server/dist/moonshot-tool-schema.js.map +1 -0
- package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
- package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
- package/packages/mcp-server/dist/pid-registry.js +459 -0
- package/packages/mcp-server/dist/pid-registry.js.map +1 -0
- package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
- package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
- package/packages/mcp-server/dist/probe-mode.js +10 -0
- package/packages/mcp-server/dist/probe-mode.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +4 -0
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
- package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
- package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
- package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts +18 -18
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +161 -81
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +5 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +43 -2
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/README.md +1 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
- package/packages/pi-ai/dist/image-models.generated.js +6 -6
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/index.d.ts +2 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +2 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +419 -221
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +468 -269
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +12 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.d.ts +5 -0
- package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js +12 -3
- package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +7 -3
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts +9 -0
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.js +34 -0
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +6 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/package.json +3 -2
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/README.md +15 -0
- package/packages/pi-tui/dist/index.d.ts +2 -2
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +2 -2
- package/packages/pi-tui/dist/index.js.map +1 -1
- package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
- package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal-image.js +54 -2
- package/packages/pi-tui/dist/terminal-image.js.map +1 -1
- package/packages/pi-tui/dist/terminal.d.ts +12 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +70 -25
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +15 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +106 -21
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +110 -36
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +45 -17
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +5 -4
- package/src/resources/extensions/ask-user-questions.ts +7 -2
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +11 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
- package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +21 -3
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +32 -9
- package/src/resources/extensions/gsd/auto/dispatch-history.ts +168 -0
- package/src/resources/extensions/gsd/auto/dispatch-key.ts +39 -0
- package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
- package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
- package/src/resources/extensions/gsd/auto/loop.ts +7 -1
- package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +186 -66
- package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
- package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
- package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +18 -48
- package/src/resources/extensions/gsd/auto-dispatch.ts +37 -62
- package/src/resources/extensions/gsd/auto-model-selection.ts +16 -7
- package/src/resources/extensions/gsd/auto-post-unit.ts +33 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +78 -9
- package/src/resources/extensions/gsd/auto-start.ts +27 -11
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +4 -4
- package/src/resources/extensions/gsd/auto-verification.ts +26 -28
- package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
- package/src/resources/extensions/gsd/auto.ts +64 -2
- package/src/resources/extensions/gsd/blocked-models.ts +49 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -5
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +118 -50
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +350 -86
- package/src/resources/extensions/gsd/closeout-wizard.ts +102 -0
- package/src/resources/extensions/gsd/commands/context.ts +16 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +46 -3
- package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
- package/src/resources/extensions/gsd/consent-question.ts +431 -0
- package/src/resources/extensions/gsd/consent-verdict.ts +86 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
- package/src/resources/extensions/gsd/db/engine.ts +26 -6
- package/src/resources/extensions/gsd/db/queries.ts +66 -0
- package/src/resources/extensions/gsd/db-migration-backup.ts +56 -7
- package/src/resources/extensions/gsd/db-transaction.ts +37 -20
- package/src/resources/extensions/gsd/db-writer.ts +11 -19
- package/src/resources/extensions/gsd/dispatch-guard.ts +8 -31
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
- package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
- package/src/resources/extensions/gsd/doctor-git-checks.ts +2 -19
- package/src/resources/extensions/gsd/engine-hook-contract.ts +79 -0
- package/src/resources/extensions/gsd/files.ts +33 -12
- package/src/resources/extensions/gsd/gsd-command-home.ts +13 -3
- package/src/resources/extensions/gsd/gsd-db.ts +19 -22
- package/src/resources/extensions/gsd/guidance.ts +78 -0
- package/src/resources/extensions/gsd/guided-flow.ts +145 -24
- package/src/resources/extensions/gsd/health-widget.ts +91 -27
- package/src/resources/extensions/gsd/markdown-renderer.ts +11 -0
- package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
- package/src/resources/extensions/gsd/memory-relations.ts +1 -1
- package/src/resources/extensions/gsd/milestone-closeout.ts +109 -24
- package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
- package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
- package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
- package/src/resources/extensions/gsd/notifications.ts +13 -6
- package/src/resources/extensions/gsd/parsers-legacy.ts +16 -4
- package/src/resources/extensions/gsd/projection-flush.ts +20 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
- 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/quick-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +8 -4
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/src/resources/extensions/gsd/reactive-graph.ts +11 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
- package/src/resources/extensions/gsd/session-lock.ts +1 -1
- package/src/resources/extensions/gsd/skill-activation.ts +3 -6
- package/src/resources/extensions/gsd/state.ts +12 -1
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +75 -1
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +257 -18
- package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
- package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/blocked-models.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
- package/src/resources/extensions/gsd/tests/consent-question.test.ts +351 -0
- package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +68 -19
- package/src/resources/extensions/gsd/tests/db-transaction.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +12 -11
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +328 -0
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
- package/src/resources/extensions/gsd/tests/doctor-git-checks-terminal.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/engine-hook-contract.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/gsd-command-home.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/guidance.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +217 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
- package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +95 -4
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +143 -0
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +124 -6
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
- package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
- package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +273 -38
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +358 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -1
- package/src/resources/extensions/gsd/tool-contract.ts +38 -3
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +4 -4
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
- package/src/resources/extensions/gsd/tools/complete-slice.ts +22 -12
- package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +81 -2
- package/src/resources/extensions/gsd/uat-policy.ts +60 -15
- package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
- package/src/resources/extensions/gsd/unit-registry.ts +34 -4
- package/src/resources/extensions/gsd/verdict-parser.ts +1 -1
- package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
- package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
- package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
- package/src/resources/extensions/gsd/workflow-events.ts +12 -20
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +3 -8
- package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
- package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
- package/src/resources/extensions/gsd/worktree.ts +7 -1
- package/src/resources/extensions/mcp-client/manager.ts +7 -1
- package/src/resources/extensions/shared/gsd-browser-cli.ts +23 -2
- package/src/resources/shared/gsd-browser-path-sync.ts +273 -0
- package/src/resources/shared/package-manager-detection.ts +1 -1
- package/src/resources/skills/create-skill/SKILL.md +3 -0
- package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
- package/dist/resources/extensions/gsd/user-input-boundary.js +0 -218
- package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
- package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +0 -173
- package/src/resources/extensions/gsd/user-input-boundary.ts +0 -216
- package/src/resources/skills/gsd-browser/SKILL.md +0 -41
- /package/dist/web/standalone/.next/static/{mU4QIDlpVHDdjDpeEKh5W → 2T9IOdiiM3o3gZ4UbPi8E}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{mU4QIDlpVHDdjDpeEKh5W → 2T9IOdiiM3o3gZ4UbPi8E}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// gsd-pi — Dispatch History module tests (#482 / #442 deepening)
|
|
2
|
+
//
|
|
3
|
+
// Covers: canonical key building and legacy-key normalization, window
|
|
4
|
+
// record/evict semantics, ledger error attachment, cross-session rehydration
|
|
5
|
+
// from unit_dispatches (the #482 regression: a fresh history rehydrated from
|
|
6
|
+
// the DB must detect a re-dispatch loop that spans session restarts),
|
|
7
|
+
// retry-budget suppression, and clearOnRecovery.
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
openDatabase,
|
|
17
|
+
closeDatabase,
|
|
18
|
+
insertMilestone,
|
|
19
|
+
} from "../gsd-db.ts";
|
|
20
|
+
import { registerAutoWorker } from "../db/auto-workers.ts";
|
|
21
|
+
import { claimMilestoneLease } from "../db/milestone-leases.ts";
|
|
22
|
+
import { recordDispatchClaim, markFailed, markCanceled } from "../db/unit-dispatches.ts";
|
|
23
|
+
import {
|
|
24
|
+
buildDispatchKey,
|
|
25
|
+
createDispatchHistory,
|
|
26
|
+
lookupLatestLedgerError,
|
|
27
|
+
normalizeDispatchKey,
|
|
28
|
+
parseDispatchKey,
|
|
29
|
+
STUCK_WINDOW_SIZE,
|
|
30
|
+
} from "../auto/dispatch-history.ts";
|
|
31
|
+
|
|
32
|
+
function makeBase(): string {
|
|
33
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-dispatch-history-"));
|
|
34
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
35
|
+
return base;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function cleanup(base: string): void {
|
|
39
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
40
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface LedgerFixture {
|
|
44
|
+
base: string;
|
|
45
|
+
worker: string;
|
|
46
|
+
token: number;
|
|
47
|
+
claim(unitType: string, unitId: string): number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function makeLedgerFixture(t: { after(fn: () => void): void }): LedgerFixture {
|
|
51
|
+
const base = makeBase();
|
|
52
|
+
t.after(() => cleanup(base));
|
|
53
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
54
|
+
insertMilestone({ id: "M001", title: "T", status: "active" });
|
|
55
|
+
const worker = registerAutoWorker({ projectRootRealpath: base });
|
|
56
|
+
const lease = claimMilestoneLease(worker, "M001");
|
|
57
|
+
assert.equal(lease.ok, true);
|
|
58
|
+
if (!lease.ok) throw new Error("lease claim failed");
|
|
59
|
+
return {
|
|
60
|
+
base,
|
|
61
|
+
worker,
|
|
62
|
+
token: lease.token,
|
|
63
|
+
claim(unitType: string, unitId: string): number {
|
|
64
|
+
const claim = recordDispatchClaim({
|
|
65
|
+
traceId: `${unitType}-${unitId}-${Math.random()}`,
|
|
66
|
+
workerId: worker,
|
|
67
|
+
milestoneLeaseToken: lease.token,
|
|
68
|
+
milestoneId: "M001",
|
|
69
|
+
unitType,
|
|
70
|
+
unitId,
|
|
71
|
+
});
|
|
72
|
+
assert.equal(claim.ok, true);
|
|
73
|
+
if (!claim.ok) throw new Error("claim failed");
|
|
74
|
+
return claim.dispatchId;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function historyFor(scopeId: string | null) {
|
|
80
|
+
return createDispatchHistory({ resolveScopeId: () => scopeId });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─── Key format ──────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
test("buildDispatchKey produces the canonical unitType:unitId format", () => {
|
|
86
|
+
assert.equal(buildDispatchKey("execute-task", "M001/S01/T01"), "execute-task:M001/S01/T01");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("normalizeDispatchKey converts legacy slash keys and keeps canonical keys", () => {
|
|
90
|
+
assert.equal(normalizeDispatchKey("execute-task/M001/S01/T01"), "execute-task:M001/S01/T01");
|
|
91
|
+
assert.equal(normalizeDispatchKey("execute-task:M001/S01/T01"), "execute-task:M001/S01/T01");
|
|
92
|
+
assert.equal(normalizeDispatchKey("opaque"), "opaque");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("parseDispatchKey splits canonical and legacy keys at the unit type", () => {
|
|
96
|
+
assert.deepEqual(parseDispatchKey("complete-slice:M001/S01"), {
|
|
97
|
+
unitType: "complete-slice",
|
|
98
|
+
unitId: "M001/S01",
|
|
99
|
+
});
|
|
100
|
+
assert.deepEqual(parseDispatchKey("complete-slice/M001/S01"), {
|
|
101
|
+
unitType: "complete-slice",
|
|
102
|
+
unitId: "M001/S01",
|
|
103
|
+
});
|
|
104
|
+
assert.equal(parseDispatchKey("opaque"), null);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ─── Window record/evict ─────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
test("recordDispatch caps the window at the window size, evicting oldest-first", () => {
|
|
110
|
+
const history = historyFor(null);
|
|
111
|
+
for (let i = 0; i < STUCK_WINDOW_SIZE + 2; i++) {
|
|
112
|
+
history.recordDispatch("execute-task", `M001/S01/T${i}`);
|
|
113
|
+
}
|
|
114
|
+
const window = history.getRecentWindow();
|
|
115
|
+
assert.equal(window.length, STUCK_WINDOW_SIZE);
|
|
116
|
+
assert.equal(window[0].key, "execute-task:M001/S01/T2");
|
|
117
|
+
assert.equal(window[window.length - 1].key, `execute-task:M001/S01/T${STUCK_WINDOW_SIZE + 1}`);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("countMatching counts entries for the canonical key", () => {
|
|
121
|
+
const history = historyFor(null);
|
|
122
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
123
|
+
history.recordDispatch("complete-slice", "M001/S01");
|
|
124
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
125
|
+
assert.equal(history.countMatching("execute-task:M001/S01/T01"), 2);
|
|
126
|
+
assert.equal(history.countMatching("complete-slice:M001/S01"), 1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("clearOnRecovery empties the window", () => {
|
|
130
|
+
const history = historyFor(null);
|
|
131
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
132
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
133
|
+
history.clearOnRecovery();
|
|
134
|
+
assert.equal(history.getRecentWindow().length, 0);
|
|
135
|
+
assert.equal(history.detectStuck(), null);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ─── Ledger error attachment + detect-stuck delegation ──────────────────────
|
|
139
|
+
|
|
140
|
+
test("recordDispatch attaches the latest ledger error on repeats so repeat-error detection fires", (t) => {
|
|
141
|
+
const f = makeLedgerFixture(t);
|
|
142
|
+
const dispatchId = f.claim("execute-task", "M001/S01/T01");
|
|
143
|
+
markFailed(dispatchId, { errorSummary: "boom: deterministic failure" });
|
|
144
|
+
|
|
145
|
+
const history = historyFor(f.base);
|
|
146
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
147
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
148
|
+
history.recordDispatch("execute-task", "M001/S01/T01");
|
|
149
|
+
|
|
150
|
+
const window = history.getRecentWindow();
|
|
151
|
+
// First dispatch of a unit skips the ledger lookup (zero DB cost on the
|
|
152
|
+
// common path); repeats attach the latest error.
|
|
153
|
+
assert.equal(window[0].error, undefined);
|
|
154
|
+
assert.equal(window[1].error, "boom: deterministic failure");
|
|
155
|
+
assert.equal(window[2].error, "boom: deterministic failure");
|
|
156
|
+
const verdict = history.detectStuck();
|
|
157
|
+
assert.equal(verdict?.stuck, true);
|
|
158
|
+
assert.match(verdict?.reason ?? "", /Same error repeated/);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("lookupLatestLedgerError matches the bare unit id, not the compound key", (t) => {
|
|
162
|
+
const f = makeLedgerFixture(t);
|
|
163
|
+
const dispatchId = f.claim("execute-task", "M001/S01/T01");
|
|
164
|
+
markFailed(dispatchId, { errorSummary: "boom: deterministic failure" });
|
|
165
|
+
|
|
166
|
+
// The ledger keys rows by the bare unit id with the unit type in its own
|
|
167
|
+
// column. The shared lookup (also used by dispatch.ts's runDispatch path)
|
|
168
|
+
// must use the bare id; a compound `unitType/unitId` value misses entirely,
|
|
169
|
+
// which previously silently dropped repeat-error detection on that path.
|
|
170
|
+
assert.equal(
|
|
171
|
+
lookupLatestLedgerError("execute-task", "M001/S01/T01"),
|
|
172
|
+
"boom: deterministic failure",
|
|
173
|
+
);
|
|
174
|
+
assert.equal(
|
|
175
|
+
lookupLatestLedgerError("execute-task", "execute-task/M001/S01/T01"),
|
|
176
|
+
undefined,
|
|
177
|
+
"a compound key must not match the bare-id ledger row",
|
|
178
|
+
);
|
|
179
|
+
assert.equal(
|
|
180
|
+
lookupLatestLedgerError("plan-slice", "M001/S01/T01"),
|
|
181
|
+
undefined,
|
|
182
|
+
"a different unit type on the same id must not be attached",
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("recordDispatch never attaches another unit type's ledger error for the same unit id", (t) => {
|
|
187
|
+
const f = makeLedgerFixture(t);
|
|
188
|
+
const dispatchId = f.claim("plan-slice", "M001/S01");
|
|
189
|
+
markFailed(dispatchId, { errorSummary: "boom: plan failure" });
|
|
190
|
+
|
|
191
|
+
const history = historyFor(f.base);
|
|
192
|
+
history.recordDispatch("execute-task", "M001/S01");
|
|
193
|
+
history.recordDispatch("execute-task", "M001/S01");
|
|
194
|
+
|
|
195
|
+
assert.ok(history.getRecentWindow().every((entry) => entry.error === undefined));
|
|
196
|
+
assert.equal(history.detectStuck(), null);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("detectStuck fires on three consecutive same-key dispatches without errors", () => {
|
|
200
|
+
const history = historyFor(null);
|
|
201
|
+
for (let i = 0; i < 3; i++) history.recordDispatch("plan-slice", "M001/S01");
|
|
202
|
+
const verdict = history.detectStuck();
|
|
203
|
+
assert.equal(verdict?.stuck, true);
|
|
204
|
+
assert.match(verdict?.reason ?? "", /plan-slice:M001\/S01 derived 3 consecutive times/);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// ─── Retry-budget suppression ────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
test("a bare-id ledger row for a different unit type does not suppress the stuck verdict", (t) => {
|
|
210
|
+
const f = makeLedgerFixture(t);
|
|
211
|
+
// Retry backoff is open for plan-slice:M001/S01 only (bare-id ledger row).
|
|
212
|
+
const dispatchId = f.claim("plan-slice", "M001/S01");
|
|
213
|
+
markFailed(dispatchId, { errorSummary: "", retryAfterMs: 60_000 });
|
|
214
|
+
|
|
215
|
+
const history = historyFor(f.base);
|
|
216
|
+
for (let i = 0; i < 3; i++) history.recordDispatch("execute-task", "M001/S01");
|
|
217
|
+
const verdict = history.detectStuck();
|
|
218
|
+
assert.equal(verdict?.stuck, true, "another unit type's backoff must not suppress this unit");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("consecutive-repeat verdict is suppressed while the retry budget drains", (t) => {
|
|
222
|
+
const f = makeLedgerFixture(t);
|
|
223
|
+
// markCanceled leaves error_summary null so the repeat-error rule (which is
|
|
224
|
+
// never suppressed) cannot fire; the suppression target is rule 2/2b.
|
|
225
|
+
const first = f.claim("plan-slice", "M001/S01");
|
|
226
|
+
markCanceled(first, "retry");
|
|
227
|
+
const second = f.claim("plan-slice", "M001/S01");
|
|
228
|
+
markFailed(second, { errorSummary: "", retryAfterMs: 60_000 });
|
|
229
|
+
|
|
230
|
+
const history = historyFor(f.base);
|
|
231
|
+
for (let i = 0; i < 3; i++) history.recordDispatch("plan-slice", "M001/S01");
|
|
232
|
+
assert.equal(history.detectStuck(), null, "stuck verdict must be suppressed inside the retry backoff window");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("exhausted retry budget does not suppress the stuck verdict", (t) => {
|
|
236
|
+
const f = makeLedgerFixture(t);
|
|
237
|
+
const ids: number[] = [];
|
|
238
|
+
for (let i = 0; i < 3; i++) {
|
|
239
|
+
const id = f.claim("plan-slice", "M001/S01");
|
|
240
|
+
markCanceled(id, "retry");
|
|
241
|
+
ids.push(id);
|
|
242
|
+
}
|
|
243
|
+
const last = f.claim("plan-slice", "M001/S01");
|
|
244
|
+
// attempt_n defaults to 1 here; bump via a fresh claim is unnecessary —
|
|
245
|
+
// exhaust by omitting next_run_at instead (no scheduled retry → no backoff).
|
|
246
|
+
markFailed(last, { errorSummary: "" });
|
|
247
|
+
|
|
248
|
+
const history = historyFor(f.base);
|
|
249
|
+
for (let i = 0; i < 3; i++) history.recordDispatch("plan-slice", "M001/S01");
|
|
250
|
+
const verdict = history.detectStuck();
|
|
251
|
+
assert.equal(verdict?.stuck, true);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// ─── Rehydration (#482 regression) ───────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
test("rehydrate seeds the window from the ledger with normalized canonical keys", (t) => {
|
|
257
|
+
const f = makeLedgerFixture(t);
|
|
258
|
+
for (let i = 0; i < 2; i++) {
|
|
259
|
+
const id = f.claim("execute-task", "M001/S01/T01");
|
|
260
|
+
markFailed(id, { errorSummary: "" });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const history = historyFor(f.base);
|
|
264
|
+
const count = history.rehydrate();
|
|
265
|
+
assert.equal(count, 2);
|
|
266
|
+
assert.deepEqual(
|
|
267
|
+
history.getRecentWindow().map((e) => e.key),
|
|
268
|
+
["execute-task:M001/S01/T01", "execute-task:M001/S01/T01"],
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("#482 regression: a re-dispatch loop spanning a session restart is detected as stuck", (t) => {
|
|
273
|
+
const f = makeLedgerFixture(t);
|
|
274
|
+
// Session 1: the same unit was dispatched twice and never made progress.
|
|
275
|
+
for (let i = 0; i < 2; i++) {
|
|
276
|
+
const id = f.claim("execute-task", "M001/S01/T01");
|
|
277
|
+
markFailed(id, { errorSummary: "" });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Session 2: a brand-new history (fresh orchestrator) rehydrates from the
|
|
281
|
+
// ledger; the very next decision for the same unit trips the stuck verdict
|
|
282
|
+
// instead of silently re-dispatching forever.
|
|
283
|
+
const restarted = historyFor(f.base);
|
|
284
|
+
restarted.rehydrate();
|
|
285
|
+
restarted.recordDispatch("execute-task", "M001/S01/T01");
|
|
286
|
+
const verdict = restarted.detectStuck();
|
|
287
|
+
assert.equal(verdict?.stuck, true);
|
|
288
|
+
assert.match(verdict?.reason ?? "", /execute-task:M001\/S01\/T01 derived 3 consecutive times/);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("rehydrate mirrors recordDispatch error attachment so repeat-error detection fires after the next dispatch", (t) => {
|
|
292
|
+
const f = makeLedgerFixture(t);
|
|
293
|
+
// Session 1: two failed dispatches for the same unit, same error summary.
|
|
294
|
+
for (let i = 0; i < 2; i++) {
|
|
295
|
+
const id = f.claim("execute-task", "M001/S01/T01");
|
|
296
|
+
markFailed(id, { errorSummary: "boom: deterministic failure" });
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Session 2: fresh history rehydrates from the ledger. Just like the live
|
|
300
|
+
// recordDispatch path, the first occurrence of a unit skips the ledger
|
|
301
|
+
// lookup; only repeats carry the error. So rehydration alone does not trip
|
|
302
|
+
// Rule 1 — keeping the post-restart window no more aggressive than the live
|
|
303
|
+
// one (Rule 1 has no retry-budget suppression).
|
|
304
|
+
const restarted = historyFor(f.base);
|
|
305
|
+
const count = restarted.rehydrate();
|
|
306
|
+
assert.equal(count, 2);
|
|
307
|
+
const window = restarted.getRecentWindow();
|
|
308
|
+
assert.equal(window[0].error, undefined);
|
|
309
|
+
assert.equal(window[1].error, "boom: deterministic failure");
|
|
310
|
+
assert.equal(restarted.detectStuck(), null);
|
|
311
|
+
|
|
312
|
+
// The next dispatch of the same unit attaches the error again, giving two
|
|
313
|
+
// consecutive matching errors → Rule 1 fires, exactly as in the live path.
|
|
314
|
+
restarted.recordDispatch("execute-task", "M001/S01/T01");
|
|
315
|
+
const verdict = restarted.detectStuck();
|
|
316
|
+
assert.equal(verdict?.stuck, true);
|
|
317
|
+
assert.match(verdict?.reason ?? "", /Same error repeated/);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("rehydrate degrades to an empty window without a scope or ledger", () => {
|
|
321
|
+
const noScope = historyFor(null);
|
|
322
|
+
assert.equal(noScope.rehydrate(), 0);
|
|
323
|
+
assert.equal(noScope.getRecentWindow().length, 0);
|
|
324
|
+
|
|
325
|
+
const noDb = historyFor("/nonexistent/scope");
|
|
326
|
+
assert.equal(noDb.rehydrate(), 0);
|
|
327
|
+
assert.equal(noDb.getRecentWindow().length, 0);
|
|
328
|
+
});
|
|
@@ -7,6 +7,10 @@ const require = createRequire(import.meta.url);
|
|
|
7
7
|
const ROOT = new URL("../../../../../", import.meta.url);
|
|
8
8
|
|
|
9
9
|
export function resolve(specifier, context, nextResolve) {
|
|
10
|
+
if (specifier.startsWith('node:')) {
|
|
11
|
+
return { url: specifier, format: 'builtin', shortCircuit: true };
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
// 1. Redirect all workspace package bare imports to source.
|
|
11
15
|
// CI portability runs don't build any packages/ dist artifacts, so every
|
|
12
16
|
// @gsd/* specifier (including transitive ones pulled in by pi-coding-agent
|
|
@@ -100,6 +104,10 @@ export function resolve(specifier, context, nextResolve) {
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
export function load(url, context, nextLoad) {
|
|
107
|
+
if (url.startsWith('node:') || context.format === 'builtin') {
|
|
108
|
+
return { format: 'builtin', source: '', shortCircuit: true };
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
// jiti/CJS may still enter through stale packages/*/dist/index.js — redirect to src.
|
|
104
112
|
if (url.includes('/packages/pi-ai/dist/index.js')) {
|
|
105
113
|
url = url.replace('/dist/index.js', '/src/index.ts');
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Doctor git checks treat validation-pass closeout as terminal without SUMMARY.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
import { runGSDDoctor } from "../doctor.ts";
|
|
12
|
+
import { openDatabase, insertMilestone, insertSlice, insertAssessment, closeDatabase } from "../gsd-db.js";
|
|
13
|
+
import { createWorktree, worktreePath } from "../worktree-manager.ts";
|
|
14
|
+
|
|
15
|
+
function runGit(args: string[], cwd: string): string {
|
|
16
|
+
return execFileSync("git", args, {
|
|
17
|
+
cwd,
|
|
18
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
}).trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeRepo(): string {
|
|
24
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-terminal-"));
|
|
25
|
+
runGit(["init", "-b", "main"], base);
|
|
26
|
+
runGit(["config", "user.name", "Test User"], base);
|
|
27
|
+
runGit(["config", "user.email", "test@example.com"], base);
|
|
28
|
+
writeFileSync(join(base, "package.json"), "{\"scripts\":{}}\n", "utf-8");
|
|
29
|
+
runGit(["add", "."], base);
|
|
30
|
+
runGit(["commit", "-m", "chore: init"], base);
|
|
31
|
+
return base;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test.after(() => {
|
|
35
|
+
closeDatabase();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("doctor flags orphaned worktree for DB-complete milestone without SUMMARY", async (t) => {
|
|
39
|
+
const base = makeRepo();
|
|
40
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
41
|
+
|
|
42
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
43
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
44
|
+
insertMilestone({ id: "M008", title: "Done", status: "complete" });
|
|
45
|
+
insertSlice({ id: "S01", milestoneId: "M008", title: "Slice", status: "complete" });
|
|
46
|
+
insertAssessment({
|
|
47
|
+
path: "milestones/M008/M008-VALIDATION.md",
|
|
48
|
+
milestoneId: "M008",
|
|
49
|
+
status: "pass",
|
|
50
|
+
scope: "milestone-validation",
|
|
51
|
+
fullContent: "verdict: pass",
|
|
52
|
+
});
|
|
53
|
+
writeFileSync(
|
|
54
|
+
join(base, ".gsd", "PREFERENCES.md"),
|
|
55
|
+
"---\ngit:\n isolation: worktree\n---\n",
|
|
56
|
+
);
|
|
57
|
+
mkdirSync(join(base, ".gsd", "milestones", "M008"), { recursive: true });
|
|
58
|
+
writeFileSync(
|
|
59
|
+
join(base, ".gsd", "milestones", "M008", "M008-ROADMAP.md"),
|
|
60
|
+
"# M008 Roadmap\n\n- [x] **S01: Slice** `risk:low` `depends:[]`\n",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
createWorktree(base, "M008", { branch: "milestone/M008" });
|
|
64
|
+
const wtPath = worktreePath(base, "M008");
|
|
65
|
+
assert.ok(existsSync(wtPath), "worktree should exist for the test");
|
|
66
|
+
|
|
67
|
+
const report = await runGSDDoctor(base, { isolationMode: "worktree" });
|
|
68
|
+
|
|
69
|
+
assert.ok(
|
|
70
|
+
report.issues.some((issue) => issue.code === "orphaned_auto_worktree" && issue.unitId === "M008"),
|
|
71
|
+
"doctor should treat DB-complete milestone without SUMMARY as terminal for cleanup",
|
|
72
|
+
);
|
|
73
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Engine hook contract: which tool lifecycle hooks fire under which engine,
|
|
2
|
+
// and the consolidated tool-name normalizer seam.
|
|
3
|
+
//
|
|
4
|
+
// 1. Pins the contract arrays (engine-hook-contract.ts) so a change to the
|
|
5
|
+
// fire matrix is a deliberate, reviewed act.
|
|
6
|
+
// 2. Source-scans register-hooks.ts (in the style of
|
|
7
|
+
// single-writer-invariant.test.ts) to assert that the concerns documented
|
|
8
|
+
// as universally mirrored — evidence collection and write-gate (re-)arming —
|
|
9
|
+
// actually live inside a tool_execution_start handler, not only in the
|
|
10
|
+
// native-only tool_call handlers.
|
|
11
|
+
// 3. Pins normalizer parity: canonicalHeadlessToolName delegates to
|
|
12
|
+
// canonicalToolName (strip-only), and canonicalWorkflowToolName adds alias
|
|
13
|
+
// resolution on top of the same strip.
|
|
14
|
+
|
|
15
|
+
import test from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
UNIVERSAL_TOOL_HOOKS,
|
|
22
|
+
NATIVE_ONLY_TOOL_HOOKS,
|
|
23
|
+
canonicalToolName,
|
|
24
|
+
canonicalWorkflowToolName,
|
|
25
|
+
} from "../engine-hook-contract.js";
|
|
26
|
+
import { canonicalHeadlessToolName } from "../../../../headless-events.js";
|
|
27
|
+
import { WORKFLOW_TOOL_ALIAS_PAIRS } from "../workflow-tool-surface.js";
|
|
28
|
+
|
|
29
|
+
test("contract arrays pin the verified fire matrix", () => {
|
|
30
|
+
assert.deepEqual([...UNIVERSAL_TOOL_HOOKS], ["tool_execution_start", "tool_execution_end"]);
|
|
31
|
+
assert.deepEqual([...NATIVE_ONLY_TOOL_HOOKS], ["tool_call", "tool_result"]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Source scan: register-hooks.ts must mirror safety concerns universally
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
const registerHooksSource = readFileSync(
|
|
39
|
+
join(process.cwd(), "src/resources/extensions/gsd/bootstrap/register-hooks.ts"),
|
|
40
|
+
"utf8",
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Extract the body of every `pi.on("<eventName>", ...)` registration by
|
|
45
|
+
* scanning balanced parentheses from each registration site.
|
|
46
|
+
*/
|
|
47
|
+
function hookBodies(source: string, eventName: string): string[] {
|
|
48
|
+
const bodies: string[] = [];
|
|
49
|
+
const marker = `pi.on("${eventName}"`;
|
|
50
|
+
let from = 0;
|
|
51
|
+
for (;;) {
|
|
52
|
+
const start = source.indexOf(marker, from);
|
|
53
|
+
if (start < 0) break;
|
|
54
|
+
let depth = 0;
|
|
55
|
+
let end = -1;
|
|
56
|
+
// Scan from pi.on's own opening paren so the whole registration is balanced.
|
|
57
|
+
for (let i = start + "pi.on".length; i < source.length; i++) {
|
|
58
|
+
const ch = source[i];
|
|
59
|
+
if (ch === "(") depth++;
|
|
60
|
+
else if (ch === ")") {
|
|
61
|
+
depth--;
|
|
62
|
+
if (depth === 0) {
|
|
63
|
+
end = i;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
assert.ok(end > start, `unbalanced pi.on("${eventName}") registration`);
|
|
69
|
+
bodies.push(source.slice(start, end + 1));
|
|
70
|
+
from = end + 1;
|
|
71
|
+
}
|
|
72
|
+
return bodies;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
test("tool_execution_start handler mirrors evidence collection and write-gate arming", () => {
|
|
76
|
+
const bodies = hookBodies(registerHooksSource, "tool_execution_start");
|
|
77
|
+
assert.ok(bodies.length >= 1, "register-hooks.ts must register a tool_execution_start handler");
|
|
78
|
+
const merged = bodies.join("\n");
|
|
79
|
+
|
|
80
|
+
// Evidence collection: the safety harness records the call on the universal
|
|
81
|
+
// hook (deduped by toolCallId against the native-only tool_call recording).
|
|
82
|
+
assert.match(merged, /safetyRecordToolCall\(/);
|
|
83
|
+
assert.match(merged, /saveEvidenceToDisk\(/);
|
|
84
|
+
|
|
85
|
+
// Write-gate (re-)arming: external engines never reach the tool_call
|
|
86
|
+
// deferApprovalGate path, so the durable pending gate must be armed here.
|
|
87
|
+
assert.match(merged, /hostWriteGateAdapter\.setPending\(/);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("mirrored concerns also exist on the native-only tool_call side (dedup pairing)", () => {
|
|
91
|
+
const bodies = hookBodies(registerHooksSource, "tool_call");
|
|
92
|
+
assert.ok(bodies.length >= 1, "register-hooks.ts must register tool_call handlers");
|
|
93
|
+
const merged = bodies.join("\n");
|
|
94
|
+
assert.match(merged, /safetyRecordToolCall\(/);
|
|
95
|
+
assert.match(merged, /deferApprovalGate\(/);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("tool_execution_end handler mirrors error classification and evidence persistence", () => {
|
|
99
|
+
const bodies = hookBodies(registerHooksSource, "tool_execution_end");
|
|
100
|
+
assert.ok(bodies.length >= 1, "register-hooks.ts must register a tool_execution_end handler");
|
|
101
|
+
const merged = bodies.join("\n");
|
|
102
|
+
assert.match(merged, /recordToolInvocationError\(/);
|
|
103
|
+
assert.match(merged, /safetyRecordToolResult\(/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Normalizer parity
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
test("canonicalHeadlessToolName matches canonicalToolName on the edge-case table", () => {
|
|
111
|
+
const cases: Array<{ input: string; expected: string }> = [
|
|
112
|
+
{ input: "mcp__server__tool", expected: "tool" },
|
|
113
|
+
{ input: "mcp__gsd-workflow__gsd_status", expected: "gsd_status" },
|
|
114
|
+
{ input: "plain", expected: "plain" },
|
|
115
|
+
{ input: "ask_user_questions", expected: "ask_user_questions" },
|
|
116
|
+
// Nested underscores: only the first server/tool delimiter splits.
|
|
117
|
+
{ input: "mcp__s__a__b", expected: "a__b" },
|
|
118
|
+
{ input: "", expected: "" },
|
|
119
|
+
{ input: "mcp__", expected: "mcp__" },
|
|
120
|
+
// Malformed names stay unchanged (strict parser: empty server/tool → no strip).
|
|
121
|
+
{ input: "mcp____tool", expected: "mcp____tool" },
|
|
122
|
+
{ input: "mcp__server__", expected: "mcp__server__" },
|
|
123
|
+
];
|
|
124
|
+
for (const { input, expected } of cases) {
|
|
125
|
+
assert.equal(canonicalToolName(input), expected, `canonicalToolName(${JSON.stringify(input)})`);
|
|
126
|
+
assert.equal(
|
|
127
|
+
canonicalHeadlessToolName(input),
|
|
128
|
+
canonicalToolName(input),
|
|
129
|
+
`headless/strip parity for ${JSON.stringify(input)}`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
assert.equal(canonicalHeadlessToolName(undefined), "");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("canonicalWorkflowToolName = prefix strip + workflow alias resolution", () => {
|
|
136
|
+
// Non-alias names behave exactly like canonicalToolName.
|
|
137
|
+
for (const name of ["mcp__server__tool", "plain", "mcp__s__a__b"]) {
|
|
138
|
+
assert.equal(canonicalWorkflowToolName(name), canonicalToolName(name));
|
|
139
|
+
}
|
|
140
|
+
// Alias names additionally resolve to the canonical workflow tool.
|
|
141
|
+
assert.ok(WORKFLOW_TOOL_ALIAS_PAIRS.length > 0, "expected at least one workflow alias");
|
|
142
|
+
for (const { alias, canonical } of WORKFLOW_TOOL_ALIAS_PAIRS) {
|
|
143
|
+
assert.equal(canonicalWorkflowToolName(alias), canonical);
|
|
144
|
+
assert.equal(canonicalWorkflowToolName(`mcp__gsd-workflow__${alias}`), canonical);
|
|
145
|
+
// Plain strip must NOT resolve aliases — that distinction is the seam.
|
|
146
|
+
assert.equal(canonicalToolName(alias), alias);
|
|
147
|
+
}
|
|
148
|
+
});
|