@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.9bb7453
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/claude-code-cli/stream-adapter.js +167 -16
- package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
- package/dist/resources/extensions/gsd/auto/phases.js +4 -3
- package/dist/resources/extensions/gsd/auto-dashboard.js +92 -17
- package/dist/resources/extensions/gsd/auto-dispatch.js +44 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +134 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +68 -22
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +94 -15
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
- package/dist/resources/extensions/gsd/auto.js +31 -6
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +83 -4
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +39 -14
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
- package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +9 -5
- package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
- package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +109 -60
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
- package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.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/error-classifier.js +2 -1
- package/dist/resources/extensions/gsd/escalation.js +4 -4
- package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
- package/dist/resources/extensions/gsd/forensics.js +74 -2
- package/dist/resources/extensions/gsd/gsd-db.js +42 -6
- package/dist/resources/extensions/gsd/guided-flow.js +30 -69
- package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +76 -84
- package/dist/resources/extensions/gsd/memory-store.js +4 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
- 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 +48 -24
- package/dist/resources/extensions/gsd/prompts/system.md +3 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/dist/resources/extensions/gsd/rule-registry.js +428 -52
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
- package/dist/resources/extensions/gsd/skill-activation.js +20 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
- package/dist/resources/extensions/gsd/state.js +17 -14
- package/dist/resources/extensions/gsd/templates/plan.md +3 -1
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
- 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/exec-tool.js +109 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +403 -3
- package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -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-auto-prep.js +3 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
- package/dist/resources/extensions/mcp-client/manager.js +31 -1
- 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 +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/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 +6 -6
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +5 -3
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- 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/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/controllers/chat-controller.d.ts +1 -0
- 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 +92 -31
- 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/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- 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 +1 -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 +5 -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/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.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +84 -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 +38 -0
- 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/types.d.ts +3 -0
- 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/image-models.generated.d.ts +15 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +15 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +406 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +484 -116
- 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/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/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/package.json +7 -7
- package/packages/pi-tui/dist/terminal.d.ts +1 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +8 -4
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- 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/claude-code-cli/stream-adapter.ts +196 -16
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
- package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
- package/src/resources/extensions/gsd/auto/phases.ts +5 -3
- package/src/resources/extensions/gsd/auto-dashboard.ts +98 -18
- package/src/resources/extensions/gsd/auto-dispatch.ts +53 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +166 -9
- package/src/resources/extensions/gsd/auto-prompts.ts +102 -15
- package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-start.ts +112 -17
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
- package/src/resources/extensions/gsd/auto.ts +47 -5
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +90 -4
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +60 -19
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
- package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +9 -5
- package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
- package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +136 -58
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
- package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +3 -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/error-classifier.ts +2 -1
- package/src/resources/extensions/gsd/escalation.ts +4 -4
- package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
- package/src/resources/extensions/gsd/forensics.ts +99 -5
- package/src/resources/extensions/gsd/gsd-db.ts +46 -8
- package/src/resources/extensions/gsd/guided-flow.ts +91 -83
- package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +105 -88
- package/src/resources/extensions/gsd/memory-store.ts +4 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
- package/src/resources/extensions/gsd/preferences-types.ts +1 -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 +48 -24
- package/src/resources/extensions/gsd/prompts/system.md +3 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- 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/safety/destructive-guard.ts +3 -0
- package/src/resources/extensions/gsd/skill-activation.ts +20 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
- package/src/resources/extensions/gsd/state.ts +18 -14
- package/src/resources/extensions/gsd/templates/plan.md +3 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +156 -4
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
- package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
- 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/derive-state-db.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
- package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
- 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/exec-sandbox.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
- 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/gsd-rebuild.test.ts +199 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
- 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.test.ts +12 -9
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
- package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
- 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 +53 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
- 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/validation-block-guard.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +213 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
- 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/exec-tool.ts +130 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +489 -3
- package/src/resources/extensions/gsd/types.ts +69 -5
- package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -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-auto-prep.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
- package/src/resources/extensions/mcp-client/manager.ts +33 -1
- package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
|
@@ -10,6 +10,9 @@ import { resolvePostUnitHooks, resolvePreDispatchHooks } from "./preferences.js"
|
|
|
10
10
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { parseUnitId } from "./unit-id.js";
|
|
13
|
+
import { queryJournal } from "./journal.js";
|
|
14
|
+
import { readUnitRuntimeRecord } from "./unit-runtime.js";
|
|
15
|
+
import { extractFrontmatterVerdict } from "./verdict-parser.js";
|
|
13
16
|
// ─── Artifact Path Resolution ──────────────────────────────────────────────
|
|
14
17
|
export function resolveHookArtifactPath(basePath, unitId, artifactName) {
|
|
15
18
|
const { milestone, slice, task } = parseUnitId(unitId);
|
|
@@ -38,6 +41,28 @@ export function convertDispatchRules(rules) {
|
|
|
38
41
|
}
|
|
39
42
|
// ─── RuleRegistry ─────────────────────────────────────────────────────────
|
|
40
43
|
const HOOK_STATE_FILE = "hook-state.json";
|
|
44
|
+
const FAILED_HOOK_RUNTIME_PHASES = new Set([
|
|
45
|
+
"timeout",
|
|
46
|
+
"finalize-timeout",
|
|
47
|
+
"crashed",
|
|
48
|
+
"paused",
|
|
49
|
+
]);
|
|
50
|
+
const HOOK_OUTCOME_VERDICTS = new Set([
|
|
51
|
+
"pass",
|
|
52
|
+
"advisory",
|
|
53
|
+
"needs-rework",
|
|
54
|
+
"needs-remediation",
|
|
55
|
+
"needs-attention",
|
|
56
|
+
]);
|
|
57
|
+
function isBlockingHook(config) {
|
|
58
|
+
return config?.criticality === "blocking";
|
|
59
|
+
}
|
|
60
|
+
function hookMaxCycles(config) {
|
|
61
|
+
return config.max_cycles ?? 1;
|
|
62
|
+
}
|
|
63
|
+
function hookCycleKey(config, trigger) {
|
|
64
|
+
return `${config.name}/${trigger.triggerUnitType}/${trigger.triggerUnitId}`;
|
|
65
|
+
}
|
|
41
66
|
export class RuleRegistry {
|
|
42
67
|
/** Static dispatch rules provided at construction time. */
|
|
43
68
|
dispatchRules;
|
|
@@ -47,6 +72,8 @@ export class RuleRegistry {
|
|
|
47
72
|
cycleCounts = new Map();
|
|
48
73
|
retryPending = false;
|
|
49
74
|
retryTrigger = null;
|
|
75
|
+
hookFailure = null;
|
|
76
|
+
gateBlockPending = null;
|
|
50
77
|
constructor(dispatchRules) {
|
|
51
78
|
this.dispatchRules = dispatchRules;
|
|
52
79
|
}
|
|
@@ -71,6 +98,7 @@ export class RuleRegistry {
|
|
|
71
98
|
artifact: hook.artifact,
|
|
72
99
|
retry_on: hook.retry_on,
|
|
73
100
|
max_cycles: hook.max_cycles,
|
|
101
|
+
criticality: hook.criticality,
|
|
74
102
|
},
|
|
75
103
|
});
|
|
76
104
|
}
|
|
@@ -117,7 +145,9 @@ export class RuleRegistry {
|
|
|
117
145
|
evaluatePostUnit(completedUnitType, completedUnitId, basePath) {
|
|
118
146
|
// If we just completed a hook unit, handle its result
|
|
119
147
|
if (this.activeHook) {
|
|
120
|
-
|
|
148
|
+
const observedCleanExecution = completedUnitType === `hook/${this.activeHook.hookName}` &&
|
|
149
|
+
completedUnitId === this.activeHook.triggerUnitId;
|
|
150
|
+
return this._handleHookCompletion(basePath, observedCleanExecution);
|
|
121
151
|
}
|
|
122
152
|
// Don't trigger hooks for other hook units (prevent hook-on-hook chains)
|
|
123
153
|
// Don't trigger hooks for triage units or quick-task units
|
|
@@ -141,75 +171,371 @@ export class RuleRegistry {
|
|
|
141
171
|
_dequeueNextHook(basePath) {
|
|
142
172
|
while (this.hookQueue.length > 0) {
|
|
143
173
|
const entry = this.hookQueue.shift();
|
|
144
|
-
const { config, triggerUnitType, triggerUnitId } = entry;
|
|
145
|
-
//
|
|
146
|
-
|
|
174
|
+
const { config, triggerUnitType, triggerUnitId, forceRun } = entry;
|
|
175
|
+
// Advisory hooks preserve existing idempotency: any configured artifact
|
|
176
|
+
// means the hook already ran. Blocking gates must verify outcome first.
|
|
177
|
+
if (config.artifact && !forceRun) {
|
|
147
178
|
const artifactPath = resolveHookArtifactPath(basePath, triggerUnitId, config.artifact);
|
|
148
|
-
if (existsSync(artifactPath))
|
|
149
|
-
|
|
179
|
+
if (existsSync(artifactPath)) {
|
|
180
|
+
const completion = this._assessConfiguredHookCompletion(basePath, config.name, triggerUnitId);
|
|
181
|
+
if (completion.outcome === "failed") {
|
|
182
|
+
return this._handleFailedHookCompletion(basePath, {
|
|
183
|
+
hookName: config.name,
|
|
184
|
+
triggerUnitType,
|
|
185
|
+
triggerUnitId,
|
|
186
|
+
cycle: this.cycleCounts.get(hookCycleKey(config, { triggerUnitType, triggerUnitId })) ?? 0,
|
|
187
|
+
pendingRetry: false,
|
|
188
|
+
}, config, completion.reason);
|
|
189
|
+
}
|
|
190
|
+
if (!isBlockingHook(config))
|
|
191
|
+
continue;
|
|
192
|
+
const decision = this._handleExistingBlockingArtifact(config, { triggerUnitType, triggerUnitId }, basePath);
|
|
193
|
+
if (decision === "skip")
|
|
194
|
+
continue;
|
|
195
|
+
return decision;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const dispatch = this._startHook(config, triggerUnitType, triggerUnitId);
|
|
199
|
+
if (dispatch)
|
|
200
|
+
return dispatch;
|
|
201
|
+
if (isBlockingHook(config)) {
|
|
202
|
+
const cycleKey = hookCycleKey(config, { triggerUnitType, triggerUnitId });
|
|
203
|
+
const maxCycles = hookMaxCycles(config);
|
|
204
|
+
const currentCycle = this.cycleCounts.get(cycleKey) ?? 0;
|
|
205
|
+
if (currentCycle >= maxCycles) {
|
|
206
|
+
this._setGateBlock(config, { triggerUnitType, triggerUnitId }, {
|
|
207
|
+
action: "pause",
|
|
208
|
+
reason: `gate cycle budget exhausted before ${config.name} produced a passing outcome`,
|
|
209
|
+
cycle: currentCycle,
|
|
210
|
+
maxCycles,
|
|
211
|
+
});
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
150
214
|
}
|
|
151
|
-
// Check cycle limit
|
|
152
|
-
const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
|
|
153
|
-
const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
|
|
154
|
-
const maxCycles = config.max_cycles ?? 1;
|
|
155
|
-
if (currentCycle > maxCycles)
|
|
156
|
-
continue;
|
|
157
|
-
this.cycleCounts.set(cycleKey, currentCycle);
|
|
158
|
-
this.activeHook = {
|
|
159
|
-
hookName: config.name,
|
|
160
|
-
triggerUnitType,
|
|
161
|
-
triggerUnitId,
|
|
162
|
-
cycle: currentCycle,
|
|
163
|
-
pendingRetry: false,
|
|
164
|
-
};
|
|
165
|
-
// Build prompt with variable substitution
|
|
166
|
-
const { milestone: mid, slice: sid, task: tid } = parseUnitId(triggerUnitId);
|
|
167
|
-
let prompt = config.prompt
|
|
168
|
-
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
169
|
-
.replace(/\{sliceId\}/g, sid ?? "")
|
|
170
|
-
.replace(/\{taskId\}/g, tid ?? "");
|
|
171
|
-
// Inject browser safety instruction
|
|
172
|
-
prompt += "\n\n**Browser tool safety:** Do NOT use `browser_wait_for` with `condition: \"network_idle\"` — it hangs indefinitely when dev servers keep persistent connections (Vite HMR, WebSocket). Use `selector_visible`, `text_visible`, or `delay` instead.";
|
|
173
|
-
return {
|
|
174
|
-
hookName: config.name,
|
|
175
|
-
prompt,
|
|
176
|
-
model: config.model,
|
|
177
|
-
unitType: `hook/${config.name}`,
|
|
178
|
-
unitId: triggerUnitId,
|
|
179
|
-
};
|
|
180
215
|
}
|
|
181
216
|
// No more hooks — clear active state
|
|
182
217
|
this.activeHook = null;
|
|
183
218
|
return null;
|
|
184
219
|
}
|
|
185
|
-
_handleHookCompletion(basePath) {
|
|
220
|
+
_handleHookCompletion(basePath, observedCleanExecution) {
|
|
186
221
|
const hook = this.activeHook;
|
|
187
222
|
const hooks = resolvePostUnitHooks(basePath);
|
|
188
223
|
const config = hooks.find(h => h.name === hook.hookName);
|
|
224
|
+
if (!config) {
|
|
225
|
+
this.activeHook = null;
|
|
226
|
+
return this._dequeueNextHook(basePath);
|
|
227
|
+
}
|
|
228
|
+
const completion = this._assessHookCompletion(basePath, hook);
|
|
229
|
+
if (completion.outcome === "failed") {
|
|
230
|
+
return this._handleFailedHookCompletion(basePath, hook, config, completion.reason);
|
|
231
|
+
}
|
|
189
232
|
// Check if retry was requested via retry_on artifact
|
|
190
|
-
if (config
|
|
233
|
+
if (config.retry_on) {
|
|
191
234
|
const retryArtifactPath = resolveHookArtifactPath(basePath, hook.triggerUnitId, config.retry_on);
|
|
192
235
|
if (existsSync(retryArtifactPath)) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
236
|
+
if (this._requestTriggerRetry(config, hook, config.retry_on)) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
if (isBlockingHook(config)) {
|
|
240
|
+
this._setGateBlock(config, hook, {
|
|
241
|
+
action: "pause",
|
|
242
|
+
reason: `gate cycle budget exhausted after ${config.retry_on} requested rework`,
|
|
243
|
+
retryArtifact: config.retry_on,
|
|
244
|
+
});
|
|
197
245
|
this.activeHook = null;
|
|
198
246
|
this.hookQueue = [];
|
|
199
|
-
this.retryPending = true;
|
|
200
|
-
this.retryTrigger = {
|
|
201
|
-
unitType: hook.triggerUnitType,
|
|
202
|
-
unitId: hook.triggerUnitId,
|
|
203
|
-
retryArtifact: config.retry_on,
|
|
204
|
-
};
|
|
205
247
|
return null;
|
|
206
248
|
}
|
|
207
249
|
}
|
|
208
250
|
}
|
|
251
|
+
if (isBlockingHook(config)) {
|
|
252
|
+
return this._handleBlockingGateCompletion(config, hook, basePath, observedCleanExecution);
|
|
253
|
+
}
|
|
209
254
|
// Hook completed normally — try next hook in queue
|
|
210
255
|
this.activeHook = null;
|
|
211
256
|
return this._dequeueNextHook(basePath);
|
|
212
257
|
}
|
|
258
|
+
_startHook(config, triggerUnitType, triggerUnitId) {
|
|
259
|
+
const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
|
|
260
|
+
const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
|
|
261
|
+
const maxCycles = config.max_cycles ?? 1;
|
|
262
|
+
if (currentCycle > maxCycles)
|
|
263
|
+
return null;
|
|
264
|
+
this.cycleCounts.set(cycleKey, currentCycle);
|
|
265
|
+
this.activeHook = {
|
|
266
|
+
hookName: config.name,
|
|
267
|
+
triggerUnitType,
|
|
268
|
+
triggerUnitId,
|
|
269
|
+
cycle: currentCycle,
|
|
270
|
+
pendingRetry: false,
|
|
271
|
+
};
|
|
272
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(triggerUnitId);
|
|
273
|
+
let prompt = config.prompt
|
|
274
|
+
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
275
|
+
.replace(/\{sliceId\}/g, sid ?? "")
|
|
276
|
+
.replace(/\{taskId\}/g, tid ?? "");
|
|
277
|
+
prompt += "\n\n**Browser tool safety:** Do NOT use `browser_wait_for` with `condition: \"network_idle\"` — it hangs indefinitely when dev servers keep persistent connections (Vite HMR, WebSocket). Use `selector_visible`, `text_visible`, or `delay` instead.";
|
|
278
|
+
return {
|
|
279
|
+
hookName: config.name,
|
|
280
|
+
prompt,
|
|
281
|
+
model: config.model,
|
|
282
|
+
unitType: `hook/${config.name}`,
|
|
283
|
+
unitId: triggerUnitId,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
_assessHookCompletion(basePath, hook) {
|
|
287
|
+
return this._assessConfiguredHookCompletion(basePath, hook.hookName, hook.triggerUnitId);
|
|
288
|
+
}
|
|
289
|
+
_assessConfiguredHookCompletion(basePath, hookName, unitId) {
|
|
290
|
+
const unitType = `hook/${hookName}`;
|
|
291
|
+
const latestUnitEnd = this._latestHookUnitEnd(basePath, unitType, unitId);
|
|
292
|
+
if (latestUnitEnd) {
|
|
293
|
+
const data = latestUnitEnd.data ?? {};
|
|
294
|
+
const status = data.status;
|
|
295
|
+
const artifactVerified = data.artifactVerified;
|
|
296
|
+
if (status === "completed" && artifactVerified !== false) {
|
|
297
|
+
return { outcome: "success" };
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
outcome: "failed",
|
|
301
|
+
reason: this._formatHookFailureReason(status, artifactVerified, data.errorContext),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const runtime = readUnitRuntimeRecord(basePath, unitType, unitId);
|
|
305
|
+
if (runtime && FAILED_HOOK_RUNTIME_PHASES.has(runtime.phase)) {
|
|
306
|
+
return { outcome: "failed", reason: `runtime phase ${runtime.phase}` };
|
|
307
|
+
}
|
|
308
|
+
return { outcome: "unknown" };
|
|
309
|
+
}
|
|
310
|
+
_latestHookUnitEnd(basePath, unitType, unitId) {
|
|
311
|
+
const unitEnds = queryJournal(basePath, { eventType: "unit-end", unitId })
|
|
312
|
+
.filter(entry => entry.data?.unitType === unitType);
|
|
313
|
+
return unitEnds[unitEnds.length - 1] ?? null;
|
|
314
|
+
}
|
|
315
|
+
_formatHookFailureReason(status, artifactVerified, errorContext) {
|
|
316
|
+
const parts = [`status ${typeof status === "string" ? status : "unknown"}`];
|
|
317
|
+
if (artifactVerified === false) {
|
|
318
|
+
parts.push("artifact not verified");
|
|
319
|
+
}
|
|
320
|
+
if (typeof errorContext === "object" && errorContext !== null && "message" in errorContext) {
|
|
321
|
+
const message = errorContext.message;
|
|
322
|
+
if (typeof message === "string" && message.length > 0) {
|
|
323
|
+
parts.push(message);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return parts.join("; ");
|
|
327
|
+
}
|
|
328
|
+
_handleFailedHookCompletion(basePath, hook, config, reason) {
|
|
329
|
+
if (config) {
|
|
330
|
+
const retry = this._startHook(config, hook.triggerUnitType, hook.triggerUnitId);
|
|
331
|
+
if (retry)
|
|
332
|
+
return retry;
|
|
333
|
+
}
|
|
334
|
+
this.hookFailure = {
|
|
335
|
+
hookName: hook.hookName,
|
|
336
|
+
unitType: `hook/${hook.hookName}`,
|
|
337
|
+
unitId: hook.triggerUnitId,
|
|
338
|
+
reason,
|
|
339
|
+
};
|
|
340
|
+
this.activeHook = null;
|
|
341
|
+
this.hookQueue = [];
|
|
342
|
+
this.persistState(basePath);
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
_handleExistingBlockingArtifact(config, trigger, basePath) {
|
|
346
|
+
const outcome = this._readGateOutcome(config, trigger, basePath);
|
|
347
|
+
switch (outcome.verdict) {
|
|
348
|
+
case "pass":
|
|
349
|
+
case "advisory":
|
|
350
|
+
return "skip";
|
|
351
|
+
case "needs-rework":
|
|
352
|
+
return this._routeNeedsRework(config, trigger, outcome);
|
|
353
|
+
case "needs-remediation":
|
|
354
|
+
case "needs-attention":
|
|
355
|
+
this._pauseForGate(config, trigger, outcome, `gate reported ${outcome.verdict}`);
|
|
356
|
+
return null;
|
|
357
|
+
case "failed":
|
|
358
|
+
case undefined:
|
|
359
|
+
return this._rerunGateOrBlock(config, trigger, basePath, {
|
|
360
|
+
reason: outcome.reason ?? `gate artifact reported verdict=${outcome.verdict}`,
|
|
361
|
+
outcome,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return this._rerunGateOrBlock(config, trigger, basePath, {
|
|
365
|
+
reason: `gate artifact reported unsupported verdict=${String(outcome.verdict)}`,
|
|
366
|
+
outcome,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
_handleBlockingGateCompletion(config, hook, basePath, observedCleanExecution) {
|
|
370
|
+
if (!observedCleanExecution) {
|
|
371
|
+
return this._rerunGateOrBlock(config, hook, basePath, {
|
|
372
|
+
reason: `hook/${config.name} did not complete cleanly before the trigger unit resumed`,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const outcome = this._readGateOutcome(config, hook, basePath);
|
|
376
|
+
switch (outcome.verdict) {
|
|
377
|
+
case "pass":
|
|
378
|
+
case "advisory":
|
|
379
|
+
this.activeHook = null;
|
|
380
|
+
return this._dequeueNextHook(basePath);
|
|
381
|
+
case "needs-rework":
|
|
382
|
+
return this._routeNeedsRework(config, hook, outcome);
|
|
383
|
+
case "needs-remediation":
|
|
384
|
+
case "needs-attention":
|
|
385
|
+
return this._pauseForGate(config, hook, outcome, `gate reported ${outcome.verdict}`);
|
|
386
|
+
case "failed":
|
|
387
|
+
case undefined:
|
|
388
|
+
return this._rerunGateOrBlock(config, hook, basePath, {
|
|
389
|
+
reason: outcome.reason ?? `gate artifact reported verdict=${outcome.verdict}`,
|
|
390
|
+
outcome,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return this._rerunGateOrBlock(config, hook, basePath, {
|
|
394
|
+
reason: `gate artifact reported unsupported verdict=${String(outcome.verdict)}`,
|
|
395
|
+
outcome,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
_routeNeedsRework(config, trigger, outcome) {
|
|
399
|
+
const action = config.on_block?.action ?? "retry-unit";
|
|
400
|
+
if (action === "retry-task" || action === "retry-unit") {
|
|
401
|
+
if (this._requestTriggerRetry(config, trigger, config.on_block?.artifact)) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
this._setGateBlock(config, trigger, {
|
|
405
|
+
action: "pause",
|
|
406
|
+
reason: "gate cycle budget exhausted after needs-rework",
|
|
407
|
+
outcome,
|
|
408
|
+
retryArtifact: config.on_block?.artifact,
|
|
409
|
+
});
|
|
410
|
+
this.activeHook = null;
|
|
411
|
+
this.hookQueue = [];
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
return this._pauseForGate(config, trigger, outcome, `gate reported needs-rework; configured on_block action is ${action}`);
|
|
415
|
+
}
|
|
416
|
+
_pauseForGate(config, trigger, outcome, reason) {
|
|
417
|
+
this._setGateBlock(config, trigger, {
|
|
418
|
+
action: config.on_block?.action ?? "pause",
|
|
419
|
+
reason,
|
|
420
|
+
outcome,
|
|
421
|
+
retryArtifact: config.on_block?.artifact,
|
|
422
|
+
});
|
|
423
|
+
this.activeHook = null;
|
|
424
|
+
this.hookQueue = [];
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
_requestTriggerRetry(config, hook, retryArtifact) {
|
|
428
|
+
const cycleKey = hookCycleKey(config, hook);
|
|
429
|
+
const currentCycle = this.cycleCounts.get(cycleKey) ?? 1;
|
|
430
|
+
const maxCycles = hookMaxCycles(config);
|
|
431
|
+
if (currentCycle >= maxCycles)
|
|
432
|
+
return false;
|
|
433
|
+
this.activeHook = null;
|
|
434
|
+
this.hookQueue = [];
|
|
435
|
+
this.retryPending = true;
|
|
436
|
+
this.retryTrigger = {
|
|
437
|
+
unitType: hook.triggerUnitType,
|
|
438
|
+
unitId: hook.triggerUnitId,
|
|
439
|
+
};
|
|
440
|
+
if (retryArtifact !== undefined) {
|
|
441
|
+
this.retryTrigger.retryArtifact = retryArtifact;
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
_rerunGateOrBlock(config, trigger, basePath, opts) {
|
|
446
|
+
const cycleKey = hookCycleKey(config, trigger);
|
|
447
|
+
const currentCycle = this.cycleCounts.get(cycleKey) ?? 0;
|
|
448
|
+
const maxCycles = hookMaxCycles(config);
|
|
449
|
+
if (currentCycle < maxCycles) {
|
|
450
|
+
this.activeHook = null;
|
|
451
|
+
this.hookQueue.unshift({
|
|
452
|
+
config,
|
|
453
|
+
triggerUnitType: trigger.triggerUnitType,
|
|
454
|
+
triggerUnitId: trigger.triggerUnitId,
|
|
455
|
+
forceRun: true,
|
|
456
|
+
});
|
|
457
|
+
return this._dequeueNextHook(basePath);
|
|
458
|
+
}
|
|
459
|
+
this._setGateBlock(config, trigger, {
|
|
460
|
+
action: "pause",
|
|
461
|
+
reason: `${opts.reason}; gate cycle budget exhausted`,
|
|
462
|
+
outcome: opts.outcome,
|
|
463
|
+
cycle: currentCycle,
|
|
464
|
+
maxCycles,
|
|
465
|
+
});
|
|
466
|
+
this.activeHook = null;
|
|
467
|
+
this.hookQueue = [];
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
_readGateOutcome(config, trigger, basePath) {
|
|
471
|
+
if (!config.artifact) {
|
|
472
|
+
return { reason: "blocking gate has no configured artifact" };
|
|
473
|
+
}
|
|
474
|
+
const artifactPath = resolveHookArtifactPath(basePath, trigger.triggerUnitId, config.artifact);
|
|
475
|
+
if (!existsSync(artifactPath)) {
|
|
476
|
+
return {
|
|
477
|
+
artifact: config.artifact,
|
|
478
|
+
artifactPath,
|
|
479
|
+
reason: `missing required gate artifact ${config.artifact}`,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
let content = "";
|
|
483
|
+
try {
|
|
484
|
+
content = readFileSync(artifactPath, "utf-8");
|
|
485
|
+
}
|
|
486
|
+
catch (e) {
|
|
487
|
+
return {
|
|
488
|
+
artifact: config.artifact,
|
|
489
|
+
artifactPath,
|
|
490
|
+
reason: `could not read gate artifact ${config.artifact}: ${e.message}`,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const rawVerdict = extractFrontmatterVerdict(content);
|
|
494
|
+
if (!rawVerdict) {
|
|
495
|
+
return {
|
|
496
|
+
artifact: config.artifact,
|
|
497
|
+
artifactPath,
|
|
498
|
+
reason: `gate artifact ${config.artifact} is missing frontmatter verdict`,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
if (rawVerdict === "failed") {
|
|
502
|
+
return {
|
|
503
|
+
artifact: config.artifact,
|
|
504
|
+
artifactPath,
|
|
505
|
+
verdict: "failed",
|
|
506
|
+
reason: `gate artifact ${config.artifact} reported verdict=failed`,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
if (!HOOK_OUTCOME_VERDICTS.has(rawVerdict)) {
|
|
510
|
+
return {
|
|
511
|
+
artifact: config.artifact,
|
|
512
|
+
artifactPath,
|
|
513
|
+
reason: `gate artifact ${config.artifact} has unsupported verdict=${rawVerdict}`,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
artifact: config.artifact,
|
|
518
|
+
artifactPath,
|
|
519
|
+
verdict: rawVerdict,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
_setGateBlock(config, trigger, opts) {
|
|
523
|
+
const cycleKey = hookCycleKey(config, trigger);
|
|
524
|
+
const cycle = opts.cycle ?? this.cycleCounts.get(cycleKey) ?? 0;
|
|
525
|
+
this.gateBlockPending = {
|
|
526
|
+
hookName: config.name,
|
|
527
|
+
triggerUnitType: trigger.triggerUnitType,
|
|
528
|
+
triggerUnitId: trigger.triggerUnitId,
|
|
529
|
+
artifact: opts.outcome?.artifact ?? config.artifact,
|
|
530
|
+
artifactPath: opts.outcome?.artifactPath,
|
|
531
|
+
verdict: opts.outcome?.verdict,
|
|
532
|
+
action: opts.action,
|
|
533
|
+
reason: opts.reason,
|
|
534
|
+
cycle,
|
|
535
|
+
maxCycles: opts.maxCycles ?? hookMaxCycles(config),
|
|
536
|
+
retryArtifact: opts.retryArtifact,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
213
539
|
// ── Pre-dispatch hook evaluation (sync, all-matching with compose) ──
|
|
214
540
|
/**
|
|
215
541
|
* Replicate exact semantics of runPreDispatchHooks from post-unit-hooks.ts:
|
|
@@ -275,6 +601,16 @@ export class RuleRegistry {
|
|
|
275
601
|
isRetryPending() {
|
|
276
602
|
return this.retryPending;
|
|
277
603
|
}
|
|
604
|
+
consumeHookFailure() {
|
|
605
|
+
if (!this.hookFailure)
|
|
606
|
+
return null;
|
|
607
|
+
const failure = { ...this.hookFailure };
|
|
608
|
+
this.hookFailure = null;
|
|
609
|
+
return failure;
|
|
610
|
+
}
|
|
611
|
+
isGateBlockPending() {
|
|
612
|
+
return this.gateBlockPending !== null;
|
|
613
|
+
}
|
|
278
614
|
/**
|
|
279
615
|
* Returns the trigger unit info for a pending retry, or null.
|
|
280
616
|
* Clears the retry state after reading.
|
|
@@ -287,22 +623,42 @@ export class RuleRegistry {
|
|
|
287
623
|
this.retryTrigger = null;
|
|
288
624
|
return trigger;
|
|
289
625
|
}
|
|
290
|
-
/**
|
|
626
|
+
/**
|
|
627
|
+
* Returns a pending post-unit gate block, or null.
|
|
628
|
+
* Clears the block state after reading.
|
|
629
|
+
*/
|
|
630
|
+
consumeGateBlock() {
|
|
631
|
+
if (!this.gateBlockPending)
|
|
632
|
+
return null;
|
|
633
|
+
const block = { ...this.gateBlockPending };
|
|
634
|
+
this.gateBlockPending = null;
|
|
635
|
+
return block;
|
|
636
|
+
}
|
|
637
|
+
/** Clear all mutable hook lifecycle state. */
|
|
291
638
|
resetState() {
|
|
292
639
|
this.activeHook = null;
|
|
293
640
|
this.hookQueue = [];
|
|
294
641
|
this.cycleCounts.clear();
|
|
295
642
|
this.retryPending = false;
|
|
296
643
|
this.retryTrigger = null;
|
|
644
|
+
this.hookFailure = null;
|
|
645
|
+
this.gateBlockPending = null;
|
|
297
646
|
}
|
|
298
647
|
// ── Persistence ─────────────────────────────────────────────────────
|
|
299
648
|
_hookStatePath(basePath) {
|
|
300
649
|
return join(basePath, ".gsd", HOOK_STATE_FILE);
|
|
301
650
|
}
|
|
302
|
-
/** Persist current hook
|
|
651
|
+
/** Persist current hook state to disk. */
|
|
303
652
|
persistState(basePath) {
|
|
304
653
|
const state = {
|
|
305
654
|
cycleCounts: Object.fromEntries(this.cycleCounts),
|
|
655
|
+
activeHook: this.activeHook ? { ...this.activeHook } : null,
|
|
656
|
+
hookQueue: this.hookQueue.map(entry => ({
|
|
657
|
+
hookName: entry.config.name,
|
|
658
|
+
triggerUnitType: entry.triggerUnitType,
|
|
659
|
+
triggerUnitId: entry.triggerUnitId,
|
|
660
|
+
forceRun: entry.forceRun,
|
|
661
|
+
})),
|
|
306
662
|
savedAt: new Date().toISOString(),
|
|
307
663
|
};
|
|
308
664
|
try {
|
|
@@ -315,7 +671,7 @@ export class RuleRegistry {
|
|
|
315
671
|
logWarning("registry", `failed to persist hook state: ${e.message}`);
|
|
316
672
|
}
|
|
317
673
|
}
|
|
318
|
-
/** Restore hook
|
|
674
|
+
/** Restore hook state from disk after a crash/restart. */
|
|
319
675
|
restoreState(basePath) {
|
|
320
676
|
try {
|
|
321
677
|
const filePath = this._hookStatePath(basePath);
|
|
@@ -331,6 +687,24 @@ export class RuleRegistry {
|
|
|
331
687
|
}
|
|
332
688
|
}
|
|
333
689
|
}
|
|
690
|
+
this.activeHook = state.activeHook && typeof state.activeHook === "object"
|
|
691
|
+
? { ...state.activeHook }
|
|
692
|
+
: null;
|
|
693
|
+
this.hookQueue = [];
|
|
694
|
+
if (Array.isArray(state.hookQueue)) {
|
|
695
|
+
const hooks = resolvePostUnitHooks(basePath);
|
|
696
|
+
for (const entry of state.hookQueue) {
|
|
697
|
+
const config = hooks.find(h => h.name === entry.hookName);
|
|
698
|
+
if (config) {
|
|
699
|
+
this.hookQueue.push({
|
|
700
|
+
config,
|
|
701
|
+
triggerUnitType: entry.triggerUnitType,
|
|
702
|
+
triggerUnitId: entry.triggerUnitId,
|
|
703
|
+
forceRun: entry.forceRun,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
334
708
|
}
|
|
335
709
|
catch (e) {
|
|
336
710
|
logWarning("registry", `failed to restore hook state: ${e.message}`);
|
|
@@ -341,7 +715,7 @@ export class RuleRegistry {
|
|
|
341
715
|
try {
|
|
342
716
|
const filePath = this._hookStatePath(basePath);
|
|
343
717
|
if (existsSync(filePath)) {
|
|
344
|
-
writeFileSync(filePath, JSON.stringify({ cycleCounts: {}, savedAt: new Date().toISOString() }, null, 2), "utf-8");
|
|
718
|
+
writeFileSync(filePath, JSON.stringify({ cycleCounts: {}, activeHook: null, hookQueue: [], savedAt: new Date().toISOString() }, null, 2), "utf-8");
|
|
345
719
|
}
|
|
346
720
|
}
|
|
347
721
|
catch (e) {
|
|
@@ -365,6 +739,7 @@ export class RuleRegistry {
|
|
|
365
739
|
type: "post",
|
|
366
740
|
enabled: hook.enabled !== false,
|
|
367
741
|
targets: hook.after,
|
|
742
|
+
criticality: hook.criticality ?? "advisory",
|
|
368
743
|
activeCycles,
|
|
369
744
|
});
|
|
370
745
|
}
|
|
@@ -436,9 +811,10 @@ export class RuleRegistry {
|
|
|
436
811
|
lines.push("Post-Unit Hooks (run after unit completes):");
|
|
437
812
|
for (const hook of postHooks) {
|
|
438
813
|
const status = hook.enabled ? "enabled" : "disabled";
|
|
814
|
+
const criticality = hook.criticality ?? "advisory";
|
|
439
815
|
const cycles = Object.keys(hook.activeCycles).length;
|
|
440
816
|
const cycleInfo = cycles > 0 ? ` (${cycles} active cycle${cycles === 1 ? "" : "s"})` : "";
|
|
441
|
-
lines.push(` ${hook.name} [${status}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
|
|
817
|
+
lines.push(` ${hook.name} [${status}, ${criticality}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
|
|
442
818
|
}
|
|
443
819
|
lines.push("");
|
|
444
820
|
}
|
|
@@ -16,6 +16,9 @@ const DESTRUCTIVE_PATTERNS = [
|
|
|
16
16
|
{ pattern: /\btruncate\s+table\b/i, label: "SQL truncate" },
|
|
17
17
|
{ pattern: /\bchmod\s+777\b/, label: "world-writable permissions" },
|
|
18
18
|
{ pattern: /\bcurl\s.*\|\s*(bash|sh|zsh)\b/, label: "pipe to shell" },
|
|
19
|
+
{ pattern: /\bterra(form|grunt)\s+(apply|destroy)/i, label: "IaC apply/destroy" },
|
|
20
|
+
{ pattern: /\baws\s+\w+\s+(delete|create|put|remove|terminate)\b/i, label: "AWS mutation" },
|
|
21
|
+
{ pattern: /\bkubectl\s+(delete|apply)\b/i, label: "kubectl mutation" },
|
|
19
22
|
];
|
|
20
23
|
/**
|
|
21
24
|
* Classify a bash command for destructive operations.
|
|
@@ -41,6 +41,16 @@ function tokenizeSkillContext(...parts) {
|
|
|
41
41
|
}
|
|
42
42
|
return tokens;
|
|
43
43
|
}
|
|
44
|
+
function tokenizeUnitType(unitType) {
|
|
45
|
+
const tokens = new Set();
|
|
46
|
+
const value = unitType?.trim().toLowerCase();
|
|
47
|
+
if (!value)
|
|
48
|
+
return tokens;
|
|
49
|
+
tokens.add(value);
|
|
50
|
+
tokens.add(value.replace(/[-_]+/g, " "));
|
|
51
|
+
tokens.add(value.replace(/[-_\s]+/g, ""));
|
|
52
|
+
return tokens;
|
|
53
|
+
}
|
|
44
54
|
function skillMatchesContext(skill, contextTokens) {
|
|
45
55
|
const haystacks = [
|
|
46
56
|
skill.name.toLowerCase(),
|
|
@@ -63,13 +73,20 @@ function ruleMatchesContext(when, contextTokens) {
|
|
|
63
73
|
const whenTokens = tokenizeSkillContext(when);
|
|
64
74
|
return [...whenTokens].some(token => contextTokens.has(token) || [...contextTokens].some(ctx => ctx.includes(token) || token.includes(ctx)));
|
|
65
75
|
}
|
|
66
|
-
function
|
|
76
|
+
function ruleMatchesUnitType(when, unitType) {
|
|
77
|
+
if (!unitType)
|
|
78
|
+
return false;
|
|
79
|
+
const whenTokens = tokenizeSkillContext(when);
|
|
80
|
+
const unitTokens = tokenizeUnitType(unitType);
|
|
81
|
+
return [...unitTokens].some(token => whenTokens.has(token));
|
|
82
|
+
}
|
|
83
|
+
function resolveSkillRuleMatches(prefs, contextTokens, base, unitType) {
|
|
67
84
|
if (!prefs?.skill_rules?.length)
|
|
68
85
|
return { include: [], avoid: [] };
|
|
69
86
|
const include = [];
|
|
70
87
|
const avoid = [];
|
|
71
88
|
for (const rule of prefs.skill_rules) {
|
|
72
|
-
if (!ruleMatchesContext(rule.when, contextTokens))
|
|
89
|
+
if (!ruleMatchesContext(rule.when, contextTokens) && !ruleMatchesUnitType(rule.when, unitType))
|
|
73
90
|
continue;
|
|
74
91
|
include.push(...resolvePreferenceSkillNames([...(rule.use ?? []), ...(rule.prefer ?? [])], base));
|
|
75
92
|
avoid.push(...resolvePreferenceSkillNames(rule.avoid ?? [], base));
|
|
@@ -141,7 +158,7 @@ export function buildSkillActivationBlock(params) {
|
|
|
141
158
|
for (const name of resolvePreferenceSkillNames(prefs?.always_use_skills ?? [], params.base)) {
|
|
142
159
|
matched.add(name);
|
|
143
160
|
}
|
|
144
|
-
const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base);
|
|
161
|
+
const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base, params.unitType);
|
|
145
162
|
for (const name of ruleMatches.include)
|
|
146
163
|
matched.add(name);
|
|
147
164
|
for (const name of ruleMatches.avoid)
|
|
@@ -313,7 +313,8 @@ export function repairArtifactDbDrift(record, ctx) {
|
|
|
313
313
|
throw new Error(`Artifact/DB status drift in ${record.milestoneId}` +
|
|
314
314
|
`${record.sliceId ? `/${record.sliceId}` : ""}` +
|
|
315
315
|
`${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
|
|
316
|
-
"Runtime will not silently import completion artifacts into DB state
|
|
316
|
+
"Runtime will not silently import completion artifacts into DB state. " +
|
|
317
|
+
"Run `/gsd rebuild markdown` after review to quarantine stale projections and re-render from the DB; use `/gsd recover --confirm` only when markdown should repopulate a lost or corrupt DB.");
|
|
317
318
|
}
|
|
318
319
|
export function describeArtifactDbDriftBlocker(record) {
|
|
319
320
|
if (record.kind === "disk-slice-id-divergence") {
|
|
@@ -329,7 +330,8 @@ export function describeArtifactDbDriftBlocker(record) {
|
|
|
329
330
|
return (`Artifact/DB status drift in ${record.milestoneId}` +
|
|
330
331
|
`${record.sliceId ? `/${record.sliceId}` : ""}` +
|
|
331
332
|
`${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
|
|
332
|
-
"Runtime will not silently import completion artifacts into DB state
|
|
333
|
+
"Runtime will not silently import completion artifacts into DB state. " +
|
|
334
|
+
"Run `/gsd rebuild markdown` after review to quarantine stale projections and re-render from the DB; use `/gsd recover --confirm` only when markdown should repopulate a lost or corrupt DB.");
|
|
333
335
|
}
|
|
334
336
|
export const diskSliceIdDivergenceHandler = {
|
|
335
337
|
kind: "disk-slice-id-divergence",
|