@opengsd/gsd-pi 1.2.0-dev.9ad8ae33 → 1.2.0-dev.a6376d75
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/loader.js +6 -4
- package/dist/register-agent-bundles.d.ts +11 -2
- package/dist/register-agent-bundles.js +18 -4
- package/dist/resources/.managed-resources-content-hash +1 -1
- 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/dispatch-history.js +21 -6
- 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 +4 -1
- package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
- package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +17 -2372
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -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-worktree.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +15 -1
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
- 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/db/queries.js +30 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
- package/dist/resources/extensions/gsd/guided-flow.js +88 -2
- package/dist/resources/extensions/gsd/health-widget.js +87 -28
- package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
- package/dist/resources/extensions/gsd/notifications.js +12 -7
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -0
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/dist/resources/extensions/gsd/skill-activation.js +3 -6
- package/dist/resources/extensions/gsd/state.js +6 -2
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
- package/dist/resources/extensions/gsd/tools/complete-task.js +62 -0
- 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/workflow-mcp-auto-prep.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
- package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
- package/dist/resources/extensions/mcp-client/manager.js +6 -1
- package/dist/runtime-checks.d.ts +10 -0
- package/dist/runtime-checks.js +27 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/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 +2 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/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 +12 -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 -58
- package/packages/mcp-server/dist/cli.js.map +1 -1
- 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 +452 -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/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.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +62 -43
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +5 -5
- 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/package.json +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/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/dist/index.d.ts +1 -1
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +1 -1
- 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/tui.d.ts +8 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +63 -18
- 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/extensions/ask-user-questions.ts +7 -2
- 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/dispatch-history.ts +22 -6
- 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 +4 -1
- package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
- package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
- package/src/resources/extensions/gsd/auto/phases.ts +58 -3061
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -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-worktree.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +20 -1
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
- 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/db/queries.ts +29 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
- package/src/resources/extensions/gsd/guided-flow.ts +128 -2
- package/src/resources/extensions/gsd/health-widget.ts +91 -27
- package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
- package/src/resources/extensions/gsd/notifications.ts +13 -6
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -0
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/src/resources/extensions/gsd/skill-activation.ts +3 -6
- package/src/resources/extensions/gsd/state.ts +7 -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-orchestrator.test.ts +76 -12
- 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-unit-closeout.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -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/guided-dispatch-root.test.ts +16 -0
- 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 +18 -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-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-skill-prompt-integration.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -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 +1 -1
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
- 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/runtime-invariant-modules.test.ts +7 -1
- 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/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/uok-plan-v2-wiring.test.ts +1 -1
- 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-templates.test.ts +92 -0
- 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/tool-surface-readiness.ts +126 -19
- package/src/resources/extensions/gsd/tools/complete-task.ts +87 -0
- 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/workflow-mcp-auto-prep.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
- package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
- package/src/resources/extensions/mcp-client/manager.ts +7 -1
- /package/dist/web/standalone/.next/static/{FBNo5cT_chy7YNoAQsU3o → xyMkEaICFHJoa98VgJyzY}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{FBNo5cT_chy7YNoAQsU3o → xyMkEaICFHJoa98VgJyzY}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: Always-on ambient health signal rendered below the editor.
|
|
3
3
|
import { runProviderChecks, summariseProviderIssues } from "./doctor-providers.js";
|
|
4
|
-
import { runEnvironmentChecks } from "./doctor-environment.js";
|
|
4
|
+
import { runEnvironmentChecks, runEnvironmentChecksAsync } from "./doctor-environment.js";
|
|
5
5
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
6
6
|
import { nativeIsRepo, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeCommitSubject } from "./native-git-bridge.js";
|
|
7
7
|
import { loadLedgerFromDisk, getProjectTotals } from "./metrics.js";
|
|
@@ -9,7 +9,28 @@ import { projectRoot } from "./commands/context.js";
|
|
|
9
9
|
import { buildHealthLines, detectHealthWidgetProjectState, } from "./health-widget-core.js";
|
|
10
10
|
export const HEALTH_WIDGET_ACTIVE_HINTS = " /gsd auto to run · /gsd status to inspect · /gsd report for snapshots · /gsd notifications for history · /gsd help";
|
|
11
11
|
// ── Data loader ────────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
12
|
+
// Last-commit lookup is subprocess-backed (native-git-bridge → git spawns),
|
|
13
|
+
// so it is treated like the other expensive checks: skipped on first paint,
|
|
14
|
+
// run only by the background refresh.
|
|
15
|
+
function loadLastCommitInfo(basePath) {
|
|
16
|
+
try {
|
|
17
|
+
if (nativeIsRepo(basePath)) {
|
|
18
|
+
const branch = nativeGetCurrentBranch(basePath);
|
|
19
|
+
const epoch = nativeLastCommitEpoch(basePath, branch || "HEAD");
|
|
20
|
+
if (epoch > 0) {
|
|
21
|
+
return { epoch, message: nativeCommitSubject(basePath, branch || "HEAD") || null };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch { /* non-fatal */ }
|
|
26
|
+
return { epoch: null, message: null };
|
|
27
|
+
}
|
|
28
|
+
function loadHealthWidgetData(basePath, options) {
|
|
29
|
+
// `includeChecks` gates the expensive subprocess-backed checks (provider +
|
|
30
|
+
// environment doctor: `lsof`, `docker`, `node --version`, ...). The initial
|
|
31
|
+
// synchronous render passes `false` so first paint is never blocked on them;
|
|
32
|
+
// the async refresh (off the first-paint path) runs the full suite.
|
|
33
|
+
const includeChecks = options?.includeChecks ?? true;
|
|
13
34
|
let budgetCeiling;
|
|
14
35
|
let budgetSpent = 0;
|
|
15
36
|
let providerIssue = null;
|
|
@@ -28,13 +49,57 @@ function loadHealthWidgetData(basePath) {
|
|
|
28
49
|
}
|
|
29
50
|
}
|
|
30
51
|
catch { /* non-fatal */ }
|
|
52
|
+
if (includeChecks) {
|
|
53
|
+
try {
|
|
54
|
+
const providerResults = runProviderChecks();
|
|
55
|
+
providerIssue = summariseProviderIssues(providerResults);
|
|
56
|
+
}
|
|
57
|
+
catch { /* non-fatal */ }
|
|
58
|
+
try {
|
|
59
|
+
const envResults = runEnvironmentChecks(basePath);
|
|
60
|
+
for (const r of envResults) {
|
|
61
|
+
if (r.status === "error")
|
|
62
|
+
environmentErrorCount++;
|
|
63
|
+
else if (r.status === "warning")
|
|
64
|
+
environmentWarningCount++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch { /* non-fatal */ }
|
|
68
|
+
}
|
|
69
|
+
// ── Last commit info ── (git spawns — gated like the other expensive checks)
|
|
70
|
+
if (includeChecks) {
|
|
71
|
+
const commit = loadLastCommitInfo(basePath);
|
|
72
|
+
lastCommitEpoch = commit.epoch;
|
|
73
|
+
lastCommitMessage = commit.message;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
projectState,
|
|
77
|
+
budgetCeiling,
|
|
78
|
+
budgetSpent,
|
|
79
|
+
providerIssue,
|
|
80
|
+
environmentErrorCount,
|
|
81
|
+
environmentWarningCount,
|
|
82
|
+
lastCommitEpoch,
|
|
83
|
+
lastCommitMessage,
|
|
84
|
+
lastRefreshed: Date.now(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Non-blocking variant used by the widget's background refresh: the cheap fields
|
|
88
|
+
// come from the synchronous snapshot, then provider + environment checks are
|
|
89
|
+
// layered in off the event-loop critical path (env checks run concurrently via
|
|
90
|
+
// runEnvironmentChecksAsync). Keeps the always-on widget from stalling the UI on
|
|
91
|
+
// its initial enrichment or its 60s refresh.
|
|
92
|
+
async function loadHealthWidgetDataAsync(basePath) {
|
|
93
|
+
const data = loadHealthWidgetData(basePath, { includeChecks: false });
|
|
94
|
+
let providerIssue = data.providerIssue;
|
|
95
|
+
let environmentErrorCount = 0;
|
|
96
|
+
let environmentWarningCount = 0;
|
|
31
97
|
try {
|
|
32
|
-
|
|
33
|
-
providerIssue = summariseProviderIssues(providerResults);
|
|
98
|
+
providerIssue = summariseProviderIssues(runProviderChecks());
|
|
34
99
|
}
|
|
35
100
|
catch { /* non-fatal */ }
|
|
36
101
|
try {
|
|
37
|
-
const envResults =
|
|
102
|
+
const envResults = await runEnvironmentChecksAsync(basePath);
|
|
38
103
|
for (const r of envResults) {
|
|
39
104
|
if (r.status === "error")
|
|
40
105
|
environmentErrorCount++;
|
|
@@ -43,27 +108,14 @@ function loadHealthWidgetData(basePath) {
|
|
|
43
108
|
}
|
|
44
109
|
}
|
|
45
110
|
catch { /* non-fatal */ }
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
if (nativeIsRepo(basePath)) {
|
|
49
|
-
const branch = nativeGetCurrentBranch(basePath);
|
|
50
|
-
const epoch = nativeLastCommitEpoch(basePath, branch || "HEAD");
|
|
51
|
-
if (epoch > 0) {
|
|
52
|
-
lastCommitEpoch = epoch;
|
|
53
|
-
lastCommitMessage = nativeCommitSubject(basePath, branch || "HEAD") || null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch { /* non-fatal */ }
|
|
111
|
+
const commit = loadLastCommitInfo(basePath);
|
|
58
112
|
return {
|
|
59
|
-
|
|
60
|
-
budgetCeiling,
|
|
61
|
-
budgetSpent,
|
|
113
|
+
...data,
|
|
62
114
|
providerIssue,
|
|
63
115
|
environmentErrorCount,
|
|
64
116
|
environmentWarningCount,
|
|
65
|
-
lastCommitEpoch,
|
|
66
|
-
lastCommitMessage,
|
|
117
|
+
lastCommitEpoch: commit.epoch,
|
|
118
|
+
lastCommitMessage: commit.message,
|
|
67
119
|
lastRefreshed: Date.now(),
|
|
68
120
|
};
|
|
69
121
|
}
|
|
@@ -77,8 +129,13 @@ export function initHealthWidget(ctx) {
|
|
|
77
129
|
if (!ctx.hasUI)
|
|
78
130
|
return;
|
|
79
131
|
const basePath = projectRoot();
|
|
80
|
-
// String-array fallback — used in RPC mode (factory is a no-op there)
|
|
81
|
-
|
|
132
|
+
// String-array fallback — used in RPC mode (factory is a no-op there).
|
|
133
|
+
// Skip the expensive provider/environment doctor checks here: this runs
|
|
134
|
+
// synchronously on the interactive-startup path, where running them would
|
|
135
|
+
// block first paint by ~0.9s (lsof/docker probes, otherwise run again
|
|
136
|
+
// immediately by the factory below). The factory's async refresh fills in
|
|
137
|
+
// real health once the screen is up.
|
|
138
|
+
const initialData = loadHealthWidgetData(basePath, { includeChecks: false });
|
|
82
139
|
ctx.ui.setWidget("gsd-health", buildHealthLines(initialData), { placement: "belowEditor" });
|
|
83
140
|
// Factory-based widget for TUI mode — replaces the string-array above
|
|
84
141
|
ctx.ui.setWidget("gsd-health", (_tui, _theme) => {
|
|
@@ -91,7 +148,7 @@ export function initHealthWidget(ctx) {
|
|
|
91
148
|
return;
|
|
92
149
|
refreshInFlight = true;
|
|
93
150
|
try {
|
|
94
|
-
data =
|
|
151
|
+
data = await loadHealthWidgetDataAsync(basePath);
|
|
95
152
|
cachedLines = undefined;
|
|
96
153
|
if (!isDisposed)
|
|
97
154
|
_tui.requestRender();
|
|
@@ -101,9 +158,11 @@ export function initHealthWidget(ctx) {
|
|
|
101
158
|
refreshInFlight = false;
|
|
102
159
|
}
|
|
103
160
|
};
|
|
104
|
-
// Fire first enrichment
|
|
105
|
-
//
|
|
106
|
-
|
|
161
|
+
// Fire the first full enrichment off the first-paint path. setTimeout(0)
|
|
162
|
+
// yields to the initial render + input loop, so the expensive doctor checks
|
|
163
|
+
// (provider + environment) never delay the moment the user sees the UI.
|
|
164
|
+
// requestRender() inside refresh repaints the widget once data is ready.
|
|
165
|
+
setTimeout(() => { void refresh(); }, 0);
|
|
107
166
|
const refreshTimer = setInterval(() => {
|
|
108
167
|
void refresh();
|
|
109
168
|
}, REFRESH_INTERVAL_MS);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// mcp-bridge.ts — stable runtime seam for MCP server consumption (phase 1).
|
|
2
|
+
export { loadWriteGateSnapshot, shouldBlockPendingGateInSnapshot, shouldBlockQueueExecutionInSnapshot, } from "./bootstrap/write-gate.js";
|
|
3
|
+
export { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
4
|
+
export { _getAdapter, checkpointDatabase, closeDatabase, getAllMilestones, getDb, getGateResults, getMilestoneSlices, getPendingGates, getSliceTasks, insertDecision, insertMilestone, insertSlice, openDatabase, upsertMilestonePlanning, } from "./gsd-db.js";
|
|
5
|
+
export { invalidateStateCache, isReusableGhostMilestone } from "./state.js";
|
|
6
|
+
export { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
7
|
+
export { saveDecisionToDb, saveRequirementToDb, updateRequirementInDb, } from "./db-writer.js";
|
|
8
|
+
export { rebuildState } from "./doctor.js";
|
|
9
|
+
export { queryJournal } from "./journal.js";
|
|
10
|
+
export { claimReservedId, findMilestoneIds, getReservedMilestoneIds, milestoneIdSort, nextMilestoneId, } from "./milestone-ids.js";
|
|
@@ -43,8 +43,8 @@ export function evaluateAllCompleteSettlement(input) {
|
|
|
43
43
|
reason: "merge-pending",
|
|
44
44
|
action: "pause",
|
|
45
45
|
message: `Milestone ${milestoneId} is complete, but its worktree branch has not been merged to main. ` +
|
|
46
|
-
`Retry with \`/gsd dispatch complete-milestone ${milestoneId}\`
|
|
47
|
-
nextAction: `Retry \`/gsd dispatch complete-milestone ${milestoneId}
|
|
46
|
+
`Retry with \`/gsd dispatch complete-milestone ${milestoneId}\` to finish the system-owned merge.`,
|
|
47
|
+
nextAction: `Retry \`/gsd dispatch complete-milestone ${milestoneId}\`.`,
|
|
48
48
|
milestoneId,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
@@ -2,31 +2,36 @@
|
|
|
2
2
|
// Cross-platform desktop notifications for auto-mode events.
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
5
|
-
import { sendRemoteNotification } from "../remote-questions/notify.js";
|
|
5
|
+
import { sendRemoteNotification as _sendRemoteNotification } from "../remote-questions/notify.js";
|
|
6
|
+
/** Swappable dispatcher for remote notifications — exported so tests can mock it. */
|
|
7
|
+
export const remoteNotificationDispatcher = {
|
|
8
|
+
send: _sendRemoteNotification,
|
|
9
|
+
};
|
|
6
10
|
/**
|
|
7
11
|
* Send a native desktop notification. Non-blocking, non-fatal.
|
|
8
12
|
* macOS: osascript, Linux: notify-send, Windows: skipped.
|
|
9
13
|
*/
|
|
10
|
-
export function sendDesktopNotification(title, message, level = "info", kind = "complete", projectName) {
|
|
14
|
+
export function sendDesktopNotification(title, message, level = "info", kind = "complete", projectName, deps = {}) {
|
|
11
15
|
// When a projectName is provided and the title is the default "GSD",
|
|
12
16
|
// replace it with a project-qualified title for multi-project clarity.
|
|
13
17
|
if (projectName && title === "GSD") {
|
|
14
18
|
title = formatNotificationTitle(projectName);
|
|
15
19
|
}
|
|
16
|
-
const
|
|
20
|
+
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
21
|
+
const notifications = deps.notifications ?? loadedPreferences?.notifications;
|
|
17
22
|
// Remote notifications fire independently of desktop preferences.
|
|
18
23
|
// sendRemoteNotification handles "not configured" gracefully (early return).
|
|
19
|
-
void
|
|
20
|
-
if (!shouldSendDesktopNotification(kind,
|
|
24
|
+
void remoteNotificationDispatcher.send(title, message).catch(() => { });
|
|
25
|
+
if (!shouldSendDesktopNotification(kind, notifications))
|
|
21
26
|
return;
|
|
22
27
|
// cmux delivery and desktop delivery are independent — if cmux import or
|
|
23
28
|
// delivery fails, we must still attempt the native desktop notification.
|
|
24
29
|
const runCmux = async () => {
|
|
25
30
|
try {
|
|
26
31
|
const { CmuxClient, emitOsc777Notification, resolveCmuxConfig } = await import("../cmux/index.js");
|
|
27
|
-
const cmux = resolveCmuxConfig(
|
|
32
|
+
const cmux = resolveCmuxConfig(loadedPreferences);
|
|
28
33
|
if (cmux.notifications) {
|
|
29
|
-
const delivered = CmuxClient.fromPreferences(
|
|
34
|
+
const delivered = CmuxClient.fromPreferences(loadedPreferences).notify(title, message);
|
|
30
35
|
if (delivered)
|
|
31
36
|
return true;
|
|
32
37
|
emitOsc777Notification(title, message);
|
|
@@ -38,7 +38,7 @@ Use `subagent` only when useful: reviewer, security, or tester. Apply findings b
|
|
|
38
38
|
10. Prepare `gsd_slice_complete` content with camelCase fields `milestoneId`, `sliceId`, `sliceTitle`, `oneLiner`, `narrative`, `verification`, and `uatContent`.
|
|
39
39
|
11. Draft concrete UAT with preconditions, steps, expected outcomes, edge cases, and UAT Type. Declare the type as a bullet under a `## UAT Type` heading, exactly like `- UAT mode: browser-executable`.
|
|
40
40
|
**Web apps:** when inlined Web App UAT guidance is present, declare `browser-executable` or `runtime-executable` (not `artifact-driven`) for localhost/browser/screenshot steps; include dev-server preconditions and name Playwright specs when they exist.
|
|
41
|
-
12. Review the inlined task-summary excerpts for DECISIONS.md/KNOWLEDGE.md-worthy decisions and gotchas. Read full `*-SUMMARY.md` only if needed. Capture with `capture_thought`; do not append knowledge files.
|
|
41
|
+
12. Review the inlined task-summary excerpts for DECISIONS.md/KNOWLEDGE.md-worthy decisions and gotchas. Read full `*-SUMMARY.md` only if needed. Capture with `gsd_capture_thought` (MCP-scoped `mcp__...__gsd_capture_thought`), not bare `capture_thought`; do not append knowledge files.
|
|
42
42
|
13. When verification passes, call `gsd_slice_complete`. The DB-backed tool is the canonical write path. Do **not** manually write `{{sliceSummaryPath}}`. Do **not** manually write `{{sliceUatPath}}`. Do not edit roadmap checkboxes.
|
|
43
43
|
14. Do not run git commands.
|
|
44
44
|
15. If the current project state needs refresh, call `gsd_summary_save` with `artifact_type: "PROJECT"` and the full updated project markdown as `content`; omit `milestone_id`. Do not write or edit `.gsd/PROJECT.md` directly.
|
|
@@ -52,6 +52,7 @@ You execute. The inlined task plan is authoritative. Verify referenced files and
|
|
|
52
52
|
|
|
53
53
|
- If task sections exist for Failure Modes (Q5), Load Profile (Q6), Negative Tests (Q7), or Observability Impact, implement and verify them.
|
|
54
54
|
- Verify must-haves with concrete commands or observable behavior.
|
|
55
|
+
- Run verification commands through `gsd_exec` / Context Mode evidence when workflow MCP tools are presented. Use `gsd_exec_search` before rerunning noisy checks, and `gsd_resume` after compaction or resume. Do not call direct `bash` for final verification evidence in this unit.
|
|
55
56
|
- Run slice-level verification from the slice plan. Final tasks need all checks passing; intermediate tasks should record partial passes.
|
|
56
57
|
- Populate `## Verification Evidence` with `formatEvidenceTable` rows: command, exit code, verdict, duration. If no checks were found, say so.
|
|
57
58
|
- For UI/browser/DOM/user-visible web changes, exercise the real flow and record explicit checks.
|
|
@@ -64,7 +65,7 @@ Keep about **{{verificationBudget}}** for verification and summary. If context i
|
|
|
64
65
|
|
|
65
66
|
- If the plan is fundamentally invalid, set `blocker_discovered: true` in the summary and explain.
|
|
66
67
|
- For downstream-impacting ambiguity that cannot be resolved from code, plans, or decisions, include an `escalation` object with question, options, recommendation, rationale, and `continueWithDefault`.
|
|
67
|
-
- Capture meaningful architecture/pattern/observability decisions with `
|
|
68
|
+
- Capture meaningful architecture/pattern/observability decisions with `gsd_capture_thought` (or MCP-scoped `mcp__...__gsd_capture_thought`) when workflow MCP tools are presented; capture non-obvious gotchas or conventions only when they save future investigation.
|
|
68
69
|
- Use the inlined Task Summary template below. Read `{{taskSummaryTemplatePath}}` only if the inlined template is absent or visibly truncated.
|
|
69
70
|
- Call `gsd_task_complete` with camelCase fields `milestoneId`, `sliceId`, `taskId`, `oneLiner`, `narrative`, `verification`, and `verificationEvidence`. Include `blockerDiscovered: true` when a stale-path safety failure or other plan-invalidating blocker prevents execution.
|
|
70
71
|
- The DB-backed tool is the canonical write path. Do **not** manually write `{{taskSummaryPath}}` or edit PLAN.md checkboxes; the tool renders the summary and updates state.
|
|
@@ -10,6 +10,8 @@ If any inlined plan, summary, verification command, or prior artifact names an a
|
|
|
10
10
|
|
|
11
11
|
All relevant context has been preloaded below. Start working immediately without re-reading these files.
|
|
12
12
|
|
|
13
|
+
If a `.gsd/**` or `.gsd/**/*` glob returns no matches, treat that as a possible symlink-backed traversal limitation; do not infer that the GSD harness is missing. Use the preloaded UAT context and direct `.gsd/...` file paths named in this prompt instead.
|
|
14
|
+
|
|
13
15
|
{{inlinedContext}}
|
|
14
16
|
|
|
15
17
|
{{skillActivation}}
|
|
@@ -25,4 +25,5 @@ Follow the workflow defined below. Execute each phase in order, completing one b
|
|
|
25
25
|
3. **Atomic commits.** Commit working code after each meaningful change. Use conventional commit format: `<type>(<scope>): <description>`.
|
|
26
26
|
4. **Verify before shipping.** Run the project's test suite and build before marking the workflow complete.
|
|
27
27
|
5. **Decision gates, not ceremony.** After each phase, summarize what changed. For low/medium complexity, ask for confirmation only when the next phase depends on a real user choice or external approval. For high complexity, confirm before proceeding to each new phase.
|
|
28
|
-
6. **
|
|
28
|
+
6. **Persist workflow state.** If the artifact directory contains `STATE.json`, update it after each phase: mark the finished phase `completed`, mark the next phase `active`, set `currentPhase` to the active phase index, and refresh `updatedAt`. When every phase is completed, set `completedAt` to the completion timestamp.
|
|
29
|
+
7. **Stay focused.** This is a {{complexity}}-complexity workflow. Match your ceremony level to the task — don't over-engineer or under-deliver.
|
|
@@ -108,11 +108,8 @@ function formatSkillActivationBlock(skillNames) {
|
|
|
108
108
|
const safe = skillNames.filter(name => SAFE_SKILL_NAME.test(name));
|
|
109
109
|
if (safe.length === 0)
|
|
110
110
|
return "";
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// parameter name, causing tool validation failures — see #2224.
|
|
114
|
-
const calls = safe.map(name => `Call Skill({ skill: '${name}' })`).join('. ');
|
|
115
|
-
return `<skill_activation>${calls}.</skill_activation>`;
|
|
111
|
+
const reads = safe.map(name => `Read the installed '${name}' skill file from <available_skills>`).join(". ");
|
|
112
|
+
return `<skill_activation>${reads}.</skill_activation>`;
|
|
116
113
|
}
|
|
117
114
|
/**
|
|
118
115
|
* Manifest-driven recommendations block — informational only, does NOT
|
|
@@ -132,7 +129,7 @@ function formatSkillRecommendationsBlock(unitType, skillNames) {
|
|
|
132
129
|
const safe = skillNames.filter(name => SAFE_SKILL_NAME.test(name));
|
|
133
130
|
if (safe.length === 0)
|
|
134
131
|
return "";
|
|
135
|
-
return `<skill_recommendations unit="${unitType}">For this unit type, also consider
|
|
132
|
+
return `<skill_recommendations unit="${unitType}">For this unit type, also consider reading these installed skill files from <available_skills>: ${safe.join(", ")}. These are recommendations, not requirements.</skill_recommendations>`;
|
|
136
133
|
}
|
|
137
134
|
export function buildSkillActivationBlock(params) {
|
|
138
135
|
const prefs = params.preferences ?? loadEffectiveGSDPreferences(params.base)?.preferences;
|
|
@@ -23,7 +23,7 @@ import { logWarning } from './workflow-logger.js';
|
|
|
23
23
|
import { extractVerdict } from './verdict-parser.js';
|
|
24
24
|
import { detectPendingEscalation } from './escalation.js';
|
|
25
25
|
import { isTerminalMilestoneSummaryContent } from './milestone-summary-classifier.js';
|
|
26
|
-
import { isDbAvailable, getAllMilestones, getMilestone,
|
|
26
|
+
import { isDbAvailable, getAllMilestones, getMilestone, getSlicesByMilestoneIds, getSliceTasks, getReplanHistory, getSlice, getRequirementCounts, getLatestAssessmentByScope, getPendingGateCountForTurn, } from './gsd-db.js';
|
|
27
27
|
import { wasWorkflowDatabaseOpenAttempted } from './db-workspace.js';
|
|
28
28
|
import { formatCompletePhaseNextAction, countUnmappedActiveRequirements } from './requirements-backlog.js';
|
|
29
29
|
import { classifyMilestoneReadiness, readinessNeedsDiscussion, } from './milestone-readiness.js';
|
|
@@ -366,12 +366,16 @@ async function buildRegistryAndFindActive(basePath, milestones, completeMileston
|
|
|
366
366
|
let activeMilestoneFound = false;
|
|
367
367
|
let activeMilestoneHasDraft = false;
|
|
368
368
|
let firstDeferredQueuedShell = null;
|
|
369
|
+
const activeMilestoneIds = milestones
|
|
370
|
+
.filter((m) => !parkedMilestoneIds.has(m.id))
|
|
371
|
+
.map((m) => m.id);
|
|
372
|
+
const slicesByMilestone = getSlicesByMilestoneIds(activeMilestoneIds);
|
|
369
373
|
for (const m of milestones) {
|
|
370
374
|
if (parkedMilestoneIds.has(m.id)) {
|
|
371
375
|
registry.push({ id: m.id, title: stripMilestonePrefix(m.title) || m.id, status: 'parked' });
|
|
372
376
|
continue;
|
|
373
377
|
}
|
|
374
|
-
const slices =
|
|
378
|
+
const slices = slicesByMilestone.get(m.id) ?? [];
|
|
375
379
|
// DB-authoritative completeness (#4179): only trust completeMilestoneIds,
|
|
376
380
|
// which is itself derived from DB status. SUMMARY-file presence alone must
|
|
377
381
|
// not imply completion.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: Tool Contract module's runtime face — verify the live SDK tool surface covers a Unit's required workflow tools.
|
|
3
|
+
import { testMcpServerConnection } from "../mcp-client/manager.js";
|
|
3
4
|
import { mcpToolMatchesBaseName } from "./mcp-tool-name.js";
|
|
4
5
|
import { getRequiredWorkflowToolsForUnit } from "./unit-tool-contracts.js";
|
|
6
|
+
import { recordWorkflowMcpProbe, WORKFLOW_MCP_PROBE_TIMEOUT_MS, } from "./workflow-mcp-readiness-cache.js";
|
|
7
|
+
import { resolveWorkflowMcpProjectRoot } from "./workflow-mcp.js";
|
|
5
8
|
import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
|
|
6
9
|
/**
|
|
7
10
|
* Stable phrase recognized as transient by auto-tool-tracking's
|
|
@@ -11,20 +14,73 @@ import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
|
|
|
11
14
|
export const TOOL_SURFACE_NOT_READY = "workflow tool surface not ready";
|
|
12
15
|
/** MCP server statuses that will not self-heal within the session. */
|
|
13
16
|
const TERMINAL_MCP_SERVER_STATUSES = new Set(["failed", "needs-auth", "disabled"]);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
export const DEFAULT_WORKFLOW_MCP_PREFLIGHT_TIMEOUT_MS = 30_000;
|
|
18
|
+
export const DEFAULT_WORKFLOW_MCP_PREFLIGHT_POLL_MS = 200;
|
|
19
|
+
const RUN_UAT_PREFLIGHT_TIMEOUT_MS = 90_000;
|
|
20
|
+
function resolveWorkflowMcpPreflightTimeoutMs(unitType) {
|
|
21
|
+
if (unitType === "run-uat")
|
|
22
|
+
return RUN_UAT_PREFLIGHT_TIMEOUT_MS;
|
|
23
|
+
return DEFAULT_WORKFLOW_MCP_PREFLIGHT_TIMEOUT_MS;
|
|
24
|
+
}
|
|
25
|
+
export function probeCoversRequiredWorkflowTools(tools, required) {
|
|
26
|
+
return required.every((tool) => tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)));
|
|
27
|
+
}
|
|
28
|
+
export async function awaitWorkflowMcpToolRegistration(input) {
|
|
29
|
+
const { unitType, workflowServerName, projectRoot } = input;
|
|
30
|
+
if (!unitType || !workflowServerName)
|
|
31
|
+
return null;
|
|
32
|
+
const required = getRequiredWorkflowToolsForUnit(unitType).filter(isWorkflowToolSurfaceName);
|
|
33
|
+
if (required.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
const probe = input.probe ?? (async (serverName, root) => {
|
|
36
|
+
const result = await testMcpServerConnection(serverName, {
|
|
37
|
+
projectDir: resolveWorkflowMcpProjectRoot(root),
|
|
38
|
+
timeoutMs: WORKFLOW_MCP_PROBE_TIMEOUT_MS,
|
|
39
|
+
});
|
|
40
|
+
return { ok: result.ok, tools: result.tools };
|
|
41
|
+
});
|
|
42
|
+
const deadline = Date.now() + (input.timeoutMs ?? resolveWorkflowMcpPreflightTimeoutMs(unitType));
|
|
43
|
+
const pollMs = input.pollMs ?? DEFAULT_WORKFLOW_MCP_PREFLIGHT_POLL_MS;
|
|
44
|
+
while (Date.now() < deadline) {
|
|
45
|
+
throwIfAborted(input.signal);
|
|
46
|
+
const result = await probe(workflowServerName, projectRoot);
|
|
47
|
+
throwIfAborted(input.signal);
|
|
48
|
+
if (result.ok && probeCoversRequiredWorkflowTools(result.tools, required)) {
|
|
49
|
+
recordWorkflowMcpProbe(projectRoot, workflowServerName, result.tools);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
await sleep(pollMs, input.signal);
|
|
53
|
+
}
|
|
54
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" did not register required tools before session start: ${required.join(", ")}`;
|
|
55
|
+
}
|
|
56
|
+
function makeAbortError() {
|
|
57
|
+
const error = new Error("AbortError: The operation was aborted");
|
|
58
|
+
error.name = "AbortError";
|
|
59
|
+
return error;
|
|
60
|
+
}
|
|
61
|
+
function throwIfAborted(signal) {
|
|
62
|
+
if (signal?.aborted)
|
|
63
|
+
throw makeAbortError();
|
|
64
|
+
}
|
|
65
|
+
function sleep(ms, signal) {
|
|
66
|
+
throwIfAborted(signal);
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const timeout = setTimeout(() => {
|
|
69
|
+
signal?.removeEventListener("abort", onAbort);
|
|
70
|
+
resolve();
|
|
71
|
+
}, ms);
|
|
72
|
+
const onAbort = () => {
|
|
73
|
+
clearTimeout(timeout);
|
|
74
|
+
reject(makeAbortError());
|
|
75
|
+
};
|
|
76
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Brief pause after a successful preflight before SDK query (race with MCP attach). */
|
|
80
|
+
export const POST_PREFLIGHT_SDK_SETTLE_MS = 750;
|
|
81
|
+
export const POST_PREFLIGHT_READINESS_RETRY_DELAYS_MS = [
|
|
82
|
+
1_000, 2_000, 3_000, 5_000, 8_000, 10_000, 15_000, 15_000, 15_000, 15_000,
|
|
83
|
+
];
|
|
28
84
|
export function getToolSurfaceReadinessError(input) {
|
|
29
85
|
const { unitType, workflowServerName, observation } = input;
|
|
30
86
|
if (!unitType || !workflowServerName)
|
|
@@ -33,24 +89,20 @@ export function getToolSurfaceReadinessError(input) {
|
|
|
33
89
|
if (required.length === 0)
|
|
34
90
|
return null;
|
|
35
91
|
const server = observation.mcpServers.find((entry) => entry.name === workflowServerName);
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// server reports "pending" there routinely, then registers within seconds,
|
|
41
|
-
// usually well before the Unit's first workflow tool call. Aborting on
|
|
42
|
-
// "pending" would fail the common healthy session, so it passes through;
|
|
43
|
-
// a genuine miss after pass-through still surfaces in-session as
|
|
44
|
-
// "No such tool available" and classifies tool-unavailable → bounded retry.
|
|
45
|
-
// Only statuses that cannot self-heal abort here.
|
|
46
|
-
if (server.status !== "connected" && !TERMINAL_MCP_SERVER_STATUSES.has(server.status)) {
|
|
47
|
-
return null;
|
|
92
|
+
if (server && TERMINAL_MCP_SERVER_STATUSES.has(server.status)) {
|
|
93
|
+
const missing = required.filter((tool) => !observation.tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)));
|
|
94
|
+
const tools = missing.length > 0 ? missing : required;
|
|
95
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" status is "${server.status}" (terminal) — cannot register: ${tools.join(", ")}`;
|
|
48
96
|
}
|
|
49
97
|
const missing = required.filter((tool) => !observation.tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)));
|
|
50
|
-
if (missing.length === 0)
|
|
98
|
+
if (missing.length === 0) {
|
|
51
99
|
return null;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
:
|
|
55
|
-
|
|
100
|
+
}
|
|
101
|
+
if (!server) {
|
|
102
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" is absent from the init surface (not yet connected): ${missing.join(", ")}`;
|
|
103
|
+
}
|
|
104
|
+
if (server.status !== "connected") {
|
|
105
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" status is "${server.status}" (not yet connected): ${missing.join(", ")}`;
|
|
106
|
+
}
|
|
107
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" is connected but has not registered: ${missing.join(", ")}`;
|
|
56
108
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Projection write failures are reported as stale projections and do not roll
|
|
9
9
|
* back committed DB state.
|
|
10
10
|
*/
|
|
11
|
+
import { existsSync } from "node:fs";
|
|
11
12
|
import { join } from "node:path";
|
|
12
13
|
import { isClosedStatus } from "../status-guards.js";
|
|
13
14
|
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, deleteVerificationEvidence, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
|
|
@@ -29,6 +30,35 @@ import { buildEscalationArtifact, writeEscalationArtifact } from "../escalation.
|
|
|
29
30
|
function taskSummaryPath(basePath, milestoneId, sliceId, taskId) {
|
|
30
31
|
return join(gsdProjectionRoot(basePath), "milestones", milestoneId, "slices", sliceId, "tasks", `${taskId}-SUMMARY.md`);
|
|
31
32
|
}
|
|
33
|
+
async function repairMissingTaskSummaryProjection(artifactBasePath, taskRow) {
|
|
34
|
+
const summaryPath = taskSummaryPath(artifactBasePath, taskRow.milestone_id, taskRow.slice_id, taskRow.id);
|
|
35
|
+
const summaryMd = renderSummaryContent(taskRow, taskRow.slice_id, taskRow.milestone_id, []);
|
|
36
|
+
let stale = false;
|
|
37
|
+
try {
|
|
38
|
+
await saveFile(summaryPath, summaryMd);
|
|
39
|
+
await renderPlanCheckboxes(artifactBasePath, taskRow.milestone_id, taskRow.slice_id);
|
|
40
|
+
}
|
|
41
|
+
catch (renderErr) {
|
|
42
|
+
stale = true;
|
|
43
|
+
logWarning("projection", `complete_task missing-summary repair failed for ${taskRow.milestone_id}/${taskRow.slice_id}/${taskRow.id}`, { error: renderErr.message });
|
|
44
|
+
}
|
|
45
|
+
invalidateStateCache();
|
|
46
|
+
clearPathCache();
|
|
47
|
+
clearParseCache();
|
|
48
|
+
try {
|
|
49
|
+
await flushWorkflowProjections(artifactBasePath, { milestoneId: taskRow.milestone_id });
|
|
50
|
+
}
|
|
51
|
+
catch (projErr) {
|
|
52
|
+
logWarning("tool", `complete-task repair projection warning: ${projErr.message}`);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
writeManifest(artifactBasePath);
|
|
56
|
+
}
|
|
57
|
+
catch (mfErr) {
|
|
58
|
+
logWarning("tool", `complete-task repair manifest warning: ${mfErr.message}`);
|
|
59
|
+
}
|
|
60
|
+
return { summaryPath, stale };
|
|
61
|
+
}
|
|
32
62
|
/**
|
|
33
63
|
* Map an execute-task-owned gate id to the CompleteTaskParams field whose
|
|
34
64
|
* presence drives `pass` vs. `omitted`. Keep in lockstep with the gates
|
|
@@ -136,6 +166,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
136
166
|
const completedAt = new Date().toISOString();
|
|
137
167
|
let guardError = null;
|
|
138
168
|
let summaryMd = "";
|
|
169
|
+
let repairTaskSummaryRow = null;
|
|
139
170
|
// ── ADR-011 Phase 2: validate escalation payload BEFORE any side effects ─
|
|
140
171
|
// Building the artifact runs the full shape validation (2-4 options, unique
|
|
141
172
|
// ids, recommendation references a real id). If the payload is malformed
|
|
@@ -197,6 +228,12 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
197
228
|
guardError = "__stale_duplicate__";
|
|
198
229
|
return;
|
|
199
230
|
}
|
|
231
|
+
const existingSummaryPath = taskSummaryPath(artifactBasePath, params.milestoneId, params.sliceId, params.taskId);
|
|
232
|
+
if (existingTask.full_summary_md.trim() && !existsSync(existingSummaryPath)) {
|
|
233
|
+
repairTaskSummaryRow = existingTask;
|
|
234
|
+
guardError = "__repair_missing_summary__";
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
200
237
|
guardError = `task ${params.taskId} is already complete — use gsd_task_reopen first if you need to redo it`;
|
|
201
238
|
return;
|
|
202
239
|
}
|
|
@@ -252,6 +289,17 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
252
289
|
stale: true,
|
|
253
290
|
};
|
|
254
291
|
}
|
|
292
|
+
if (guardError === "__repair_missing_summary__" && repairTaskSummaryRow) {
|
|
293
|
+
const repair = await repairMissingTaskSummaryProjection(artifactBasePath, repairTaskSummaryRow);
|
|
294
|
+
return {
|
|
295
|
+
taskId: params.taskId,
|
|
296
|
+
sliceId: params.sliceId,
|
|
297
|
+
milestoneId: params.milestoneId,
|
|
298
|
+
summaryPath: repair.summaryPath,
|
|
299
|
+
duplicate: true,
|
|
300
|
+
...(repair.stale ? { stale: true } : {}),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
255
303
|
if (guardError) {
|
|
256
304
|
return { error: guardError };
|
|
257
305
|
}
|
|
@@ -349,6 +397,20 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
349
397
|
}
|
|
350
398
|
}
|
|
351
399
|
else if (params.escalation && !escalationWriteEnabled) {
|
|
400
|
+
if (params.escalation.continueWithDefault === false) {
|
|
401
|
+
const msg = `complete-task received a hard-blocker escalation (continueWithDefault=false) but phases.mid_execution_escalation is disabled for ${params.milestoneId}/${params.sliceId}/${params.taskId}; reverting to pending instead of silently advancing.`;
|
|
402
|
+
logWarning("tool", msg);
|
|
403
|
+
try {
|
|
404
|
+
deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
|
|
405
|
+
updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
|
|
406
|
+
invalidateStateCache();
|
|
407
|
+
logWarning("tool", `complete-task rolled back DB completion for ${params.milestoneId}/${params.sliceId}/${params.taskId} because hard-blocker escalation handling is disabled; SUMMARY.md left on disk for retry.`);
|
|
408
|
+
}
|
|
409
|
+
catch (rollbackErr) {
|
|
410
|
+
logWarning("tool", `complete-task rollback failed after disabled hard-blocker escalation for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${rollbackErr.message}`);
|
|
411
|
+
}
|
|
412
|
+
return { error: msg };
|
|
413
|
+
}
|
|
352
414
|
logWarning("tool", `complete-task received escalation payload but phases.mid_execution_escalation is not enabled; ignoring (${params.milestoneId}/${params.sliceId}/${params.taskId})`);
|
|
353
415
|
}
|
|
354
416
|
// Invalidate all caches
|
|
@@ -125,7 +125,7 @@ export function composeContextModeInstructions(unitType, opts) {
|
|
|
125
125
|
}
|
|
126
126
|
const TOOL_SURFACE_GUIDANCE_BY_UNIT = {
|
|
127
127
|
"run-uat": "Do not call `gsd_exec`, `Bash`, `Write`, or `Edit` — they are unavailable in this unit. Run every automated check through `gsd_uat_exec` with the appropriate `intent`. For browser UAT modes, use `browser_*` tools when presented; if browser automation fails, record the failure honestly and use `gsd_uat_exec` for the best objective substitute.",
|
|
128
|
-
"complete-slice": "Run slice-level verification through `gsd_exec` (or MCP-scoped `mcp__…__gsd_exec`), not direct `bash`. Do not call `gsd_uat_result_save` — run-uat owns persisted UAT assessment. On verification failure, do not edit user source files in this unit.",
|
|
128
|
+
"complete-slice": "Run slice-level verification through `gsd_exec` (or MCP-scoped `mcp__…__gsd_exec`), not direct `bash`. Capture learnings through `gsd_capture_thought` (or MCP-scoped `mcp__…__gsd_capture_thought`), not bare `capture_thought`, when workflow MCP tools are presented. Do not call `gsd_uat_result_save` — run-uat owns persisted UAT assessment. On verification failure, do not edit user source files in this unit.",
|
|
129
129
|
"gate-evaluate": "Dispatch only **tester** subagents via `subagent`. Persist each gate with `gsd_save_gate_result`. Do not use `ToolSearch` — it is not available.",
|
|
130
130
|
"reactive-execute": "Dispatch only **worker** subagents via `subagent`. Do not call `gsd_task_complete` from this parent batch — each worker owns its task completion. If a failed task left no summary, call `gsd_summary_save` with `blocker_discovered: true`.",
|
|
131
131
|
"execute-task": "Complete only this task via `gsd_task_complete`. Do not call `gsd_slice_complete`, `gsd_validate_milestone`, or `gsd_complete_milestone` — the orchestrator owns phase transitions.",
|