@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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Regression tests for source-write worktree safety phase behavior.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
import { AutoSession } from "../auto/session.ts";
|
|
12
|
+
import { validateSourceWriteWorktreeSafety } from "../auto/worktree-safety-phase.ts";
|
|
13
|
+
import { closeDatabase, insertMilestone, openDatabase } from "../gsd-db.ts";
|
|
14
|
+
import { registerAutoWorker } from "../db/auto-workers.ts";
|
|
15
|
+
import { claimMilestoneLease, getMilestoneLease, releaseMilestoneLease } from "../db/milestone-leases.ts";
|
|
16
|
+
|
|
17
|
+
function runGit(args: string[], cwd: string): void {
|
|
18
|
+
execFileSync("git", args, {
|
|
19
|
+
cwd,
|
|
20
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test("source-write safety reclaims a released milestone lease for resumed branch sessions", async () => {
|
|
25
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-wt-safety-reclaim-"));
|
|
26
|
+
const previousCwd = process.cwd();
|
|
27
|
+
const notifications: string[] = [];
|
|
28
|
+
const stopReasons: string[] = [];
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
|
|
32
|
+
runGit(["init", "-b", "auto/M001"], projectRoot);
|
|
33
|
+
runGit(["config", "user.email", "test@example.com"], projectRoot);
|
|
34
|
+
runGit(["config", "user.name", "Test User"], projectRoot);
|
|
35
|
+
process.chdir(projectRoot);
|
|
36
|
+
|
|
37
|
+
openDatabase(join(projectRoot, ".gsd", "gsd.db"));
|
|
38
|
+
insertMilestone({ id: "M001", title: "Milestone 1", status: "active" });
|
|
39
|
+
|
|
40
|
+
const priorWorkerId = registerAutoWorker({ projectRootRealpath: projectRoot });
|
|
41
|
+
const priorLease = claimMilestoneLease(priorWorkerId, "M001");
|
|
42
|
+
assert.equal(priorLease.ok, true);
|
|
43
|
+
if (!priorLease.ok) return;
|
|
44
|
+
assert.equal(releaseMilestoneLease(priorWorkerId, "M001", priorLease.token), true);
|
|
45
|
+
|
|
46
|
+
const resumedWorkerId = registerAutoWorker({ projectRootRealpath: projectRoot });
|
|
47
|
+
const session = new AutoSession();
|
|
48
|
+
session.basePath = projectRoot;
|
|
49
|
+
session.originalBasePath = projectRoot;
|
|
50
|
+
session.currentMilestoneId = null;
|
|
51
|
+
session.workerId = resumedWorkerId;
|
|
52
|
+
session.milestoneLeaseToken = null;
|
|
53
|
+
|
|
54
|
+
const result = await validateSourceWriteWorktreeSafety(
|
|
55
|
+
{
|
|
56
|
+
ctx: {
|
|
57
|
+
ui: {
|
|
58
|
+
notify(message: string) {
|
|
59
|
+
notifications.push(message);
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
pi: {},
|
|
64
|
+
s: session,
|
|
65
|
+
deps: {
|
|
66
|
+
getIsolationMode: () => "branch",
|
|
67
|
+
autoWorktreeBranch: () => "auto/M001",
|
|
68
|
+
stopAuto: async (_ctx: unknown, _pi: unknown, reason?: string) => {
|
|
69
|
+
if (reason) stopReasons.push(reason);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
prefs: undefined,
|
|
73
|
+
iteration: 1,
|
|
74
|
+
flowId: "flow-1",
|
|
75
|
+
nextSeq: () => 1,
|
|
76
|
+
} as any,
|
|
77
|
+
"execute-task",
|
|
78
|
+
"M001/S01/T01",
|
|
79
|
+
"M001",
|
|
80
|
+
"resume-pre-dispatch",
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
assert.equal(result, null);
|
|
84
|
+
assert.equal(session.currentMilestoneId, "M001");
|
|
85
|
+
assert.equal(session.milestoneLeaseToken, priorLease.token + 1);
|
|
86
|
+
const lease = getMilestoneLease("M001");
|
|
87
|
+
assert.equal(lease?.worker_id, resumedWorkerId);
|
|
88
|
+
assert.equal(lease?.status, "held");
|
|
89
|
+
assert.equal(stopReasons.length, 0);
|
|
90
|
+
assert.equal(notifications.length, 0);
|
|
91
|
+
} finally {
|
|
92
|
+
try {
|
|
93
|
+
closeDatabase();
|
|
94
|
+
} catch {
|
|
95
|
+
/* noop */
|
|
96
|
+
}
|
|
97
|
+
process.chdir(previousCwd);
|
|
98
|
+
rmSync(projectRoot, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
@@ -176,6 +176,54 @@ describe("Worktree Safety module", () => {
|
|
|
176
176
|
assert.equal(result.branch, "milestone/M001");
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
+
test("rejects non-project root for source-writing Unit when isolation mode is branch", () => {
|
|
180
|
+
const safety = createWorktreeSafetyModule({
|
|
181
|
+
existsSync: () => true,
|
|
182
|
+
lstatSync: () => ({ isFile: () => true }),
|
|
183
|
+
listRegisteredWorktrees: () => [{ path: unitRoot, branch: "milestone/M001" }],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const result = safety.validateUnitRoot({
|
|
187
|
+
unitType: "execute-task",
|
|
188
|
+
unitId: "M001/S01/T01",
|
|
189
|
+
writeScope: "source-writing",
|
|
190
|
+
projectRoot,
|
|
191
|
+
unitRoot,
|
|
192
|
+
milestoneId: "M001",
|
|
193
|
+
isolationMode: "branch",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
assert.equal(result.ok, false);
|
|
197
|
+
assert.equal(result.kind, "invalid-root");
|
|
198
|
+
assert.equal(result.details?.expectedRoot, projectRoot);
|
|
199
|
+
assert.equal(result.details?.unitRoot, unitRoot);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("rejects branch mismatch for source-writing Unit when isolation mode is branch", () => {
|
|
203
|
+
const safety = createWorktreeSafetyModule({
|
|
204
|
+
existsSync: () => true,
|
|
205
|
+
lstatSync: () => ({ isFile: () => false }),
|
|
206
|
+
listRegisteredWorktrees: () => [{ path: projectRoot, branch: "main" }],
|
|
207
|
+
getCurrentBranch: () => "main",
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const result = safety.validateUnitRoot({
|
|
211
|
+
unitType: "execute-task",
|
|
212
|
+
unitId: "M001/S01/T01",
|
|
213
|
+
writeScope: "source-writing",
|
|
214
|
+
projectRoot,
|
|
215
|
+
unitRoot: projectRoot,
|
|
216
|
+
milestoneId: "M001",
|
|
217
|
+
isolationMode: "branch",
|
|
218
|
+
expectedBranch: "milestone/M001",
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
assert.equal(result.ok, false);
|
|
222
|
+
assert.equal(result.kind, "branch-mismatch");
|
|
223
|
+
assert.equal(result.details?.branch, "main");
|
|
224
|
+
assert.equal(result.details?.expectedBranch, "milestone/M001");
|
|
225
|
+
});
|
|
226
|
+
|
|
179
227
|
test("rejects non-project root for source-writing Unit when isolation mode is none", () => {
|
|
180
228
|
const safety = createWorktreeSafetyModule({
|
|
181
229
|
existsSync: () => true,
|
|
@@ -420,6 +468,30 @@ describe("Worktree Safety module", () => {
|
|
|
420
468
|
assert.equal(result.kind, "empty-worktree-with-project-content");
|
|
421
469
|
});
|
|
422
470
|
|
|
471
|
+
test("rejects a source-writing Unit when the milestone lease is required but not held", () => {
|
|
472
|
+
const safety = createWorktreeSafetyModule({
|
|
473
|
+
existsSync: () => true,
|
|
474
|
+
lstatSync: () => ({ isFile: () => false }),
|
|
475
|
+
listRegisteredWorktrees: () => [{ path: projectRoot, branch: "main" }],
|
|
476
|
+
getCurrentBranch: () => "main",
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const result = safety.validateUnitRoot({
|
|
480
|
+
unitType: "execute-task",
|
|
481
|
+
unitId: "M001/S01/T01",
|
|
482
|
+
writeScope: "source-writing",
|
|
483
|
+
projectRoot,
|
|
484
|
+
unitRoot: projectRoot,
|
|
485
|
+
milestoneId: "M001",
|
|
486
|
+
isolationMode: "none",
|
|
487
|
+
lease: { required: true, held: false, owner: "worker-1" },
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
assert.equal(result.ok, false);
|
|
491
|
+
assert.equal(result.kind, "lease-lost");
|
|
492
|
+
assert.equal(result.details?.owner, "worker-1");
|
|
493
|
+
});
|
|
494
|
+
|
|
423
495
|
test("default adapter proves registered worktree and current branch", (t) => {
|
|
424
496
|
const base = makeBaseRepo();
|
|
425
497
|
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
@@ -1,8 +1,14 @@
|
|
|
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
3
|
|
|
4
|
+
import { testMcpServerConnection } from "../mcp-client/manager.js";
|
|
4
5
|
import { mcpToolMatchesBaseName } from "./mcp-tool-name.js";
|
|
5
6
|
import { getRequiredWorkflowToolsForUnit } from "./unit-tool-contracts.js";
|
|
7
|
+
import {
|
|
8
|
+
recordWorkflowMcpProbe,
|
|
9
|
+
WORKFLOW_MCP_PROBE_TIMEOUT_MS,
|
|
10
|
+
} from "./workflow-mcp-readiness-cache.js";
|
|
11
|
+
import { resolveWorkflowMcpProjectRoot } from "./workflow-mcp.js";
|
|
6
12
|
import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
|
|
7
13
|
|
|
8
14
|
/**
|
|
@@ -36,10 +42,112 @@ export interface LiveToolSurfaceObservation {
|
|
|
36
42
|
* workflow server is part of this session (native tool path), when the Unit
|
|
37
43
|
* requires no workflow tools, or when the surface is ready.
|
|
38
44
|
*/
|
|
45
|
+
export interface WorkflowMcpToolProbeResult {
|
|
46
|
+
ok: boolean;
|
|
47
|
+
tools: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type WorkflowMcpToolProbe = (
|
|
51
|
+
serverName: string,
|
|
52
|
+
projectRoot: string,
|
|
53
|
+
) => Promise<WorkflowMcpToolProbeResult>;
|
|
54
|
+
|
|
55
|
+
export const DEFAULT_WORKFLOW_MCP_PREFLIGHT_TIMEOUT_MS = 30_000;
|
|
56
|
+
export const DEFAULT_WORKFLOW_MCP_PREFLIGHT_POLL_MS = 200;
|
|
57
|
+
const RUN_UAT_PREFLIGHT_TIMEOUT_MS = 90_000;
|
|
58
|
+
|
|
59
|
+
function resolveWorkflowMcpPreflightTimeoutMs(unitType: string | undefined): number {
|
|
60
|
+
if (unitType === "run-uat") return RUN_UAT_PREFLIGHT_TIMEOUT_MS;
|
|
61
|
+
return DEFAULT_WORKFLOW_MCP_PREFLIGHT_TIMEOUT_MS;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function probeCoversRequiredWorkflowTools(
|
|
65
|
+
tools: readonly string[],
|
|
66
|
+
required: readonly string[],
|
|
67
|
+
): boolean {
|
|
68
|
+
return required.every((tool) =>
|
|
69
|
+
tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function awaitWorkflowMcpToolRegistration(input: {
|
|
74
|
+
unitType: string | undefined;
|
|
75
|
+
workflowServerName: string | undefined;
|
|
76
|
+
projectRoot: string;
|
|
77
|
+
timeoutMs?: number;
|
|
78
|
+
pollMs?: number;
|
|
79
|
+
probe?: WorkflowMcpToolProbe;
|
|
80
|
+
signal?: AbortSignal;
|
|
81
|
+
}): Promise<string | null> {
|
|
82
|
+
const { unitType, workflowServerName, projectRoot } = input;
|
|
83
|
+
if (!unitType || !workflowServerName) return null;
|
|
84
|
+
|
|
85
|
+
const required = getRequiredWorkflowToolsForUnit(unitType).filter(isWorkflowToolSurfaceName);
|
|
86
|
+
if (required.length === 0) return null;
|
|
87
|
+
|
|
88
|
+
const probe = input.probe ?? (async (serverName, root) => {
|
|
89
|
+
const result = await testMcpServerConnection(serverName, {
|
|
90
|
+
projectDir: resolveWorkflowMcpProjectRoot(root),
|
|
91
|
+
timeoutMs: WORKFLOW_MCP_PROBE_TIMEOUT_MS,
|
|
92
|
+
});
|
|
93
|
+
return { ok: result.ok, tools: result.tools };
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const deadline = Date.now() + (input.timeoutMs ?? resolveWorkflowMcpPreflightTimeoutMs(unitType));
|
|
97
|
+
const pollMs = input.pollMs ?? DEFAULT_WORKFLOW_MCP_PREFLIGHT_POLL_MS;
|
|
98
|
+
|
|
99
|
+
while (Date.now() < deadline) {
|
|
100
|
+
throwIfAborted(input.signal);
|
|
101
|
+
const result = await probe(workflowServerName, projectRoot);
|
|
102
|
+
throwIfAborted(input.signal);
|
|
103
|
+
if (result.ok && probeCoversRequiredWorkflowTools(result.tools, required)) {
|
|
104
|
+
recordWorkflowMcpProbe(projectRoot, workflowServerName, result.tools);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
await sleep(pollMs, input.signal);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" did not register required tools before session start: ${required.join(", ")}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function makeAbortError(): Error {
|
|
114
|
+
const error = new Error("AbortError: The operation was aborted");
|
|
115
|
+
error.name = "AbortError";
|
|
116
|
+
return error;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
120
|
+
if (signal?.aborted) throw makeAbortError();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function sleep(ms: number, signal: AbortSignal | undefined): Promise<void> {
|
|
124
|
+
throwIfAborted(signal);
|
|
125
|
+
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const timeout = setTimeout(() => {
|
|
128
|
+
signal?.removeEventListener("abort", onAbort);
|
|
129
|
+
resolve();
|
|
130
|
+
}, ms);
|
|
131
|
+
const onAbort = (): void => {
|
|
132
|
+
clearTimeout(timeout);
|
|
133
|
+
reject(makeAbortError());
|
|
134
|
+
};
|
|
135
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Brief pause after a successful preflight before SDK query (race with MCP attach). */
|
|
140
|
+
export const POST_PREFLIGHT_SDK_SETTLE_MS = 750;
|
|
141
|
+
|
|
142
|
+
export const POST_PREFLIGHT_READINESS_RETRY_DELAYS_MS = [
|
|
143
|
+
1_000, 2_000, 3_000, 5_000, 8_000, 10_000, 15_000, 15_000, 15_000, 15_000,
|
|
144
|
+
] as const;
|
|
145
|
+
|
|
39
146
|
export function getToolSurfaceReadinessError(input: {
|
|
40
147
|
unitType: string | undefined;
|
|
41
148
|
workflowServerName: string | undefined;
|
|
42
149
|
observation: LiveToolSurfaceObservation;
|
|
150
|
+
projectRoot?: string | undefined;
|
|
43
151
|
}): string | null {
|
|
44
152
|
const { unitType, workflowServerName, observation } = input;
|
|
45
153
|
if (!unitType || !workflowServerName) return null;
|
|
@@ -48,29 +156,28 @@ export function getToolSurfaceReadinessError(input: {
|
|
|
48
156
|
if (required.length === 0) return null;
|
|
49
157
|
|
|
50
158
|
const server = observation.mcpServers.find((entry) => entry.name === workflowServerName);
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// usually well before the Unit's first workflow tool call. Aborting on
|
|
58
|
-
// "pending" would fail the common healthy session, so it passes through;
|
|
59
|
-
// a genuine miss after pass-through still surfaces in-session as
|
|
60
|
-
// "No such tool available" and classifies tool-unavailable → bounded retry.
|
|
61
|
-
// Only statuses that cannot self-heal abort here.
|
|
62
|
-
if (server.status !== "connected" && !TERMINAL_MCP_SERVER_STATUSES.has(server.status)) {
|
|
63
|
-
return null;
|
|
159
|
+
if (server && TERMINAL_MCP_SERVER_STATUSES.has(server.status)) {
|
|
160
|
+
const missing = required.filter(
|
|
161
|
+
(tool) => !observation.tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)),
|
|
162
|
+
);
|
|
163
|
+
const tools = missing.length > 0 ? missing : required;
|
|
164
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" status is "${server.status}" (terminal) — cannot register: ${tools.join(", ")}`;
|
|
64
165
|
}
|
|
65
166
|
|
|
66
167
|
const missing = required.filter(
|
|
67
168
|
(tool) => !observation.tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)),
|
|
68
169
|
);
|
|
69
|
-
if (missing.length === 0)
|
|
170
|
+
if (missing.length === 0) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!server) {
|
|
175
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" is absent from the init surface (not yet connected): ${missing.join(", ")}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (server.status !== "connected") {
|
|
179
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" status is "${server.status}" (not yet connected): ${missing.join(", ")}`;
|
|
180
|
+
}
|
|
70
181
|
|
|
71
|
-
|
|
72
|
-
server.status === "connected"
|
|
73
|
-
? `MCP server "${workflowServerName}" is connected but has not registered`
|
|
74
|
-
: `MCP server "${workflowServerName}" status is "${server.status}" and it has not registered`;
|
|
75
|
-
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: ${serverDetail}: ${missing.join(", ")}`;
|
|
182
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" is connected but has not registered: ${missing.join(", ")}`;
|
|
76
183
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* back committed DB state.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
13
14
|
import { join } from "node:path";
|
|
14
15
|
|
|
15
16
|
import type { CompleteTaskParams, EscalationArtifact } from "../types.js";
|
|
@@ -86,6 +87,49 @@ function taskSummaryPath(
|
|
|
86
87
|
);
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
async function repairMissingTaskSummaryProjection(
|
|
91
|
+
artifactBasePath: string,
|
|
92
|
+
taskRow: TaskRow,
|
|
93
|
+
): Promise<{ summaryPath: string; stale: boolean }> {
|
|
94
|
+
const summaryPath = taskSummaryPath(
|
|
95
|
+
artifactBasePath,
|
|
96
|
+
taskRow.milestone_id,
|
|
97
|
+
taskRow.slice_id,
|
|
98
|
+
taskRow.id,
|
|
99
|
+
);
|
|
100
|
+
const summaryMd = renderSummaryContent(taskRow, taskRow.slice_id, taskRow.milestone_id, []);
|
|
101
|
+
let stale = false;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await saveFile(summaryPath, summaryMd);
|
|
105
|
+
await renderPlanCheckboxes(artifactBasePath, taskRow.milestone_id, taskRow.slice_id);
|
|
106
|
+
} catch (renderErr) {
|
|
107
|
+
stale = true;
|
|
108
|
+
logWarning(
|
|
109
|
+
"projection",
|
|
110
|
+
`complete_task missing-summary repair failed for ${taskRow.milestone_id}/${taskRow.slice_id}/${taskRow.id}`,
|
|
111
|
+
{ error: (renderErr as Error).message },
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
invalidateStateCache();
|
|
116
|
+
clearPathCache();
|
|
117
|
+
clearParseCache();
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
await flushWorkflowProjections(artifactBasePath, { milestoneId: taskRow.milestone_id });
|
|
121
|
+
} catch (projErr) {
|
|
122
|
+
logWarning("tool", `complete-task repair projection warning: ${(projErr as Error).message}`);
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
writeManifest(artifactBasePath);
|
|
126
|
+
} catch (mfErr) {
|
|
127
|
+
logWarning("tool", `complete-task repair manifest warning: ${(mfErr as Error).message}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { summaryPath, stale };
|
|
131
|
+
}
|
|
132
|
+
|
|
89
133
|
/**
|
|
90
134
|
* Map an execute-task-owned gate id to the CompleteTaskParams field whose
|
|
91
135
|
* presence drives `pass` vs. `omitted`. Keep in lockstep with the gates
|
|
@@ -208,6 +252,7 @@ export async function handleCompleteTask(
|
|
|
208
252
|
const completedAt = new Date().toISOString();
|
|
209
253
|
let guardError: string | null = null;
|
|
210
254
|
let summaryMd = "";
|
|
255
|
+
let repairTaskSummaryRow: TaskRow | null = null;
|
|
211
256
|
|
|
212
257
|
// ── ADR-011 Phase 2: validate escalation payload BEFORE any side effects ─
|
|
213
258
|
// Building the artifact runs the full shape validation (2-4 options, unique
|
|
@@ -272,6 +317,17 @@ export async function handleCompleteTask(
|
|
|
272
317
|
guardError = "__stale_duplicate__";
|
|
273
318
|
return;
|
|
274
319
|
}
|
|
320
|
+
const existingSummaryPath = taskSummaryPath(
|
|
321
|
+
artifactBasePath,
|
|
322
|
+
params.milestoneId,
|
|
323
|
+
params.sliceId,
|
|
324
|
+
params.taskId,
|
|
325
|
+
);
|
|
326
|
+
if (existingTask.full_summary_md.trim() && !existsSync(existingSummaryPath)) {
|
|
327
|
+
repairTaskSummaryRow = existingTask;
|
|
328
|
+
guardError = "__repair_missing_summary__";
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
275
331
|
guardError = `task ${params.taskId} is already complete — use gsd_task_reopen first if you need to redo it`;
|
|
276
332
|
return;
|
|
277
333
|
}
|
|
@@ -337,6 +393,18 @@ export async function handleCompleteTask(
|
|
|
337
393
|
};
|
|
338
394
|
}
|
|
339
395
|
|
|
396
|
+
if (guardError === "__repair_missing_summary__" && repairTaskSummaryRow) {
|
|
397
|
+
const repair = await repairMissingTaskSummaryProjection(artifactBasePath, repairTaskSummaryRow);
|
|
398
|
+
return {
|
|
399
|
+
taskId: params.taskId,
|
|
400
|
+
sliceId: params.sliceId,
|
|
401
|
+
milestoneId: params.milestoneId,
|
|
402
|
+
summaryPath: repair.summaryPath,
|
|
403
|
+
duplicate: true,
|
|
404
|
+
...(repair.stale ? { stale: true } : {}),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
340
408
|
if (guardError) {
|
|
341
409
|
return { error: guardError };
|
|
342
410
|
}
|
|
@@ -453,6 +521,25 @@ export async function handleCompleteTask(
|
|
|
453
521
|
}
|
|
454
522
|
}
|
|
455
523
|
} else if (params.escalation && !escalationWriteEnabled) {
|
|
524
|
+
if (params.escalation.continueWithDefault === false) {
|
|
525
|
+
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.`;
|
|
526
|
+
logWarning("tool", msg);
|
|
527
|
+
try {
|
|
528
|
+
deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
|
|
529
|
+
updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
|
|
530
|
+
invalidateStateCache();
|
|
531
|
+
logWarning(
|
|
532
|
+
"tool",
|
|
533
|
+
`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.`,
|
|
534
|
+
);
|
|
535
|
+
} catch (rollbackErr) {
|
|
536
|
+
logWarning(
|
|
537
|
+
"tool",
|
|
538
|
+
`complete-task rollback failed after disabled hard-blocker escalation for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${(rollbackErr as Error).message}`,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
return { error: msg };
|
|
542
|
+
}
|
|
456
543
|
logWarning(
|
|
457
544
|
"tool",
|
|
458
545
|
`complete-task received escalation payload but phases.mid_execution_escalation is not enabled; ignoring (${params.milestoneId}/${params.sliceId}/${params.taskId})`,
|
|
@@ -187,7 +187,7 @@ const TOOL_SURFACE_GUIDANCE_BY_UNIT: Record<string, string> = {
|
|
|
187
187
|
"run-uat":
|
|
188
188
|
"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.",
|
|
189
189
|
"complete-slice":
|
|
190
|
-
"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.",
|
|
190
|
+
"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.",
|
|
191
191
|
"gate-evaluate":
|
|
192
192
|
"Dispatch only **tester** subagents via `subagent`. Persist each gate with `gsd_save_gate_result`. Do not use `ToolSearch` — it is not available.",
|
|
193
193
|
"reactive-execute":
|
|
@@ -220,15 +220,19 @@ export const UNIT_REGISTRY = {
|
|
|
220
220
|
phaseChain: ["completion"],
|
|
221
221
|
toolContract: {
|
|
222
222
|
allowedGsdTools: [
|
|
223
|
+
"gsd_exec",
|
|
223
224
|
"gsd_slice_complete",
|
|
224
225
|
"gsd_task_reopen",
|
|
225
226
|
"gsd_replan_slice",
|
|
226
227
|
"gsd_decision_save",
|
|
228
|
+
"gsd_capture_thought",
|
|
227
229
|
"gsd_requirement_update",
|
|
228
230
|
"gsd_summary_save",
|
|
229
231
|
"subagent",
|
|
230
232
|
],
|
|
231
233
|
requiredWorkflowTools: [
|
|
234
|
+
"gsd_exec",
|
|
235
|
+
"gsd_capture_thought",
|
|
232
236
|
"gsd_slice_complete",
|
|
233
237
|
"gsd_task_reopen",
|
|
234
238
|
"gsd_replan_slice",
|
|
@@ -254,8 +258,21 @@ export const UNIT_REGISTRY = {
|
|
|
254
258
|
scopeClass: "execute-task",
|
|
255
259
|
phaseChain: ["execution"],
|
|
256
260
|
toolContract: {
|
|
257
|
-
allowedGsdTools: [
|
|
258
|
-
|
|
261
|
+
allowedGsdTools: [
|
|
262
|
+
"gsd_task_complete",
|
|
263
|
+
"gsd_exec",
|
|
264
|
+
"gsd_exec_search",
|
|
265
|
+
"gsd_resume",
|
|
266
|
+
"gsd_capture_thought",
|
|
267
|
+
"gsd_decision_save",
|
|
268
|
+
],
|
|
269
|
+
requiredWorkflowTools: [
|
|
270
|
+
"gsd_task_complete",
|
|
271
|
+
"gsd_exec",
|
|
272
|
+
"gsd_exec_search",
|
|
273
|
+
"gsd_resume",
|
|
274
|
+
"gsd_capture_thought",
|
|
275
|
+
],
|
|
259
276
|
},
|
|
260
277
|
},
|
|
261
278
|
"execute-task-simple": {
|
|
@@ -263,8 +280,21 @@ export const UNIT_REGISTRY = {
|
|
|
263
280
|
scopeClass: "execute-task",
|
|
264
281
|
phaseChain: ["execution_simple", "execution"],
|
|
265
282
|
toolContract: {
|
|
266
|
-
allowedGsdTools: [
|
|
267
|
-
|
|
283
|
+
allowedGsdTools: [
|
|
284
|
+
"gsd_task_complete",
|
|
285
|
+
"gsd_exec",
|
|
286
|
+
"gsd_exec_search",
|
|
287
|
+
"gsd_resume",
|
|
288
|
+
"gsd_capture_thought",
|
|
289
|
+
"gsd_decision_save",
|
|
290
|
+
],
|
|
291
|
+
requiredWorkflowTools: [
|
|
292
|
+
"gsd_task_complete",
|
|
293
|
+
"gsd_exec",
|
|
294
|
+
"gsd_exec_search",
|
|
295
|
+
"gsd_resume",
|
|
296
|
+
"gsd_capture_thought",
|
|
297
|
+
],
|
|
268
298
|
},
|
|
269
299
|
},
|
|
270
300
|
"reactive-execute": {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type EnsureProjectWorkflowMcpConfigResult,
|
|
5
5
|
ensureProjectWorkflowMcpConfig,
|
|
6
6
|
} from "./mcp-project-config.js";
|
|
7
|
+
import { warmWorkflowMcpProbeInBackground } from "./workflow-mcp-readiness-cache.js";
|
|
7
8
|
import { usesWorkflowMcpTransport } from "./workflow-mcp.js";
|
|
8
9
|
|
|
9
10
|
interface WorkflowMcpAutoPrepContext {
|
|
@@ -75,6 +76,7 @@ export function prepareWorkflowMcpForProject(
|
|
|
75
76
|
if (result.status !== "unchanged") {
|
|
76
77
|
prepCtx.ui?.notify?.(`GSD MCP Server Prepared at ${result.configPath}`, "info");
|
|
77
78
|
}
|
|
79
|
+
warmWorkflowMcpProbeInBackground(projectRoot);
|
|
78
80
|
return result;
|
|
79
81
|
} catch (err) {
|
|
80
82
|
prepCtx.ui?.notify?.(
|