@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,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine-interfaces-contract.test.ts —
|
|
3
|
-
* engine abstraction layer (S01).
|
|
2
|
+
* engine-interfaces-contract.test.ts — Runtime and type-level contract tests for
|
|
3
|
+
* the engine abstraction layer (S01).
|
|
4
4
|
*
|
|
5
|
-
* TypeScript interfaces are erased by --experimental-strip-types, so
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* TypeScript interfaces are erased by --experimental-strip-types, so shape
|
|
6
|
+
* contracts are verified by constructing values that satisfy the types and
|
|
7
|
+
* asserting on their runtime fields. Type-level assertions guard compile-time
|
|
8
|
+
* contracts; pnpm run typecheck:extensions validates those.
|
|
8
9
|
*
|
|
9
10
|
* Follows the same conventions as auto-session-encapsulation.test.ts.
|
|
10
11
|
*/
|
|
@@ -15,15 +16,21 @@ import { readFileSync } from "node:fs";
|
|
|
15
16
|
import { join, dirname } from "node:path";
|
|
16
17
|
import { fileURLToPath } from "node:url";
|
|
17
18
|
|
|
19
|
+
import type {
|
|
20
|
+
EngineState,
|
|
21
|
+
EngineDispatchAction,
|
|
22
|
+
StepContract,
|
|
23
|
+
ReconcileResult,
|
|
24
|
+
RecoveryAction,
|
|
25
|
+
CloseoutResult,
|
|
26
|
+
DisplayMetadata,
|
|
27
|
+
} from "../engine-types.js";
|
|
28
|
+
import type { WorkflowEngine } from "../workflow-engine.js";
|
|
29
|
+
import type { ExecutionPolicy } from "../execution-policy.js";
|
|
30
|
+
import type { ResolvedEngine } from "../engine-resolver.js";
|
|
31
|
+
|
|
18
32
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
33
|
const ENGINE_TYPES_PATH = join(__dirname, "..", "engine-types.ts");
|
|
20
|
-
const WORKFLOW_ENGINE_PATH = join(__dirname, "..", "workflow-engine.ts");
|
|
21
|
-
const EXECUTION_POLICY_PATH = join(__dirname, "..", "execution-policy.ts");
|
|
22
|
-
const ENGINE_RESOLVER_PATH = join(__dirname, "..", "engine-resolver.ts");
|
|
23
|
-
|
|
24
|
-
function readSource(path: string): string {
|
|
25
|
-
return readFileSync(path, "utf-8");
|
|
26
|
-
}
|
|
27
34
|
|
|
28
35
|
// ── Import smoke tests ──────────────────────────────────────────────────────
|
|
29
36
|
|
|
@@ -55,9 +62,10 @@ describe("Import smoke tests", () => {
|
|
|
55
62
|
|
|
56
63
|
// ── Leaf-node constraint ────────────────────────────────────────────────────
|
|
57
64
|
|
|
65
|
+
// allow-source-grep: verifies engine-types.ts is a leaf node by design
|
|
58
66
|
describe("Leaf-node constraint", () => {
|
|
59
67
|
test("engine-types.ts has zero imports from GSD modules (only node: allowed)", () => {
|
|
60
|
-
const source =
|
|
68
|
+
const source = readFileSync(ENGINE_TYPES_PATH, "utf-8");
|
|
61
69
|
const lines = source.split("\n");
|
|
62
70
|
const violations: string[] = [];
|
|
63
71
|
|
|
@@ -81,97 +89,111 @@ describe("Leaf-node constraint", () => {
|
|
|
81
89
|
// ── EngineState shape ───────────────────────────────────────────────────────
|
|
82
90
|
|
|
83
91
|
describe("EngineState shape", () => {
|
|
84
|
-
test("EngineState
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
92
|
+
test("EngineState accepts all required fields with correct runtime types", () => {
|
|
93
|
+
const state: EngineState = {
|
|
94
|
+
phase: "research",
|
|
95
|
+
currentMilestoneId: "M001",
|
|
96
|
+
activeSliceId: "S01",
|
|
97
|
+
activeTaskId: "T01",
|
|
98
|
+
isComplete: false,
|
|
99
|
+
raw: { arbitrary: "engine-specific-state" },
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
assert.equal(state.phase, "research");
|
|
103
|
+
assert.equal(state.currentMilestoneId, "M001");
|
|
104
|
+
assert.equal(state.activeSliceId, "S01");
|
|
105
|
+
assert.equal(state.activeTaskId, "T01");
|
|
106
|
+
assert.equal(state.isComplete, false);
|
|
107
|
+
assert.deepEqual(state.raw, { arbitrary: "engine-specific-state" });
|
|
108
|
+
});
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
test("EngineState.raw accepts unknown opaque values", () => {
|
|
111
|
+
const state: EngineState = {
|
|
112
|
+
phase: "planning",
|
|
113
|
+
currentMilestoneId: null,
|
|
114
|
+
activeSliceId: null,
|
|
115
|
+
activeTaskId: null,
|
|
116
|
+
isComplete: true,
|
|
117
|
+
raw: null,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
assert.equal(state.raw, null);
|
|
108
121
|
});
|
|
109
122
|
});
|
|
110
123
|
|
|
111
124
|
// ── EngineDispatchAction shape ──────────────────────────────────────────────
|
|
112
125
|
|
|
113
126
|
describe("EngineDispatchAction shape", () => {
|
|
114
|
-
test("EngineDispatchAction
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
127
|
+
test("EngineDispatchAction supports dispatch, stop, and skip variants at runtime", () => {
|
|
128
|
+
const step: StepContract = {
|
|
129
|
+
unitType: "execute-task",
|
|
130
|
+
unitId: "M001/S01/T01",
|
|
131
|
+
prompt: "execute the task",
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const dispatchAction: EngineDispatchAction = { action: "dispatch", step };
|
|
135
|
+
assert.equal(dispatchAction.action, "dispatch");
|
|
136
|
+
assert.deepEqual(dispatchAction.step, step);
|
|
137
|
+
|
|
138
|
+
const stopAction: EngineDispatchAction = { action: "stop", reason: "blocked", level: "error" };
|
|
139
|
+
assert.equal(stopAction.action, "stop");
|
|
140
|
+
assert.equal(stopAction.reason, "blocked");
|
|
141
|
+
assert.equal(stopAction.level, "error");
|
|
142
|
+
|
|
143
|
+
const skipAction: EngineDispatchAction = { action: "skip" };
|
|
144
|
+
assert.equal(skipAction.action, "skip");
|
|
129
145
|
});
|
|
130
146
|
});
|
|
131
147
|
|
|
132
148
|
// ── WorkflowEngine interface shape ──────────────────────────────────────────
|
|
133
149
|
|
|
134
150
|
describe("WorkflowEngine interface shape", () => {
|
|
135
|
-
test("WorkflowEngine
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
test("WorkflowEngine accepts an object with engineId and all required methods", () => {
|
|
152
|
+
const engine: WorkflowEngine = {
|
|
153
|
+
engineId: "test-engine",
|
|
154
|
+
deriveState: async () => ({
|
|
155
|
+
phase: "test",
|
|
156
|
+
currentMilestoneId: null,
|
|
157
|
+
activeSliceId: null,
|
|
158
|
+
activeTaskId: null,
|
|
159
|
+
isComplete: false,
|
|
160
|
+
raw: null,
|
|
161
|
+
}),
|
|
162
|
+
resolveDispatch: async () => ({ action: "skip" }),
|
|
163
|
+
reconcile: async () => ({ outcome: "continue" }),
|
|
164
|
+
getDisplayMetadata: () => ({
|
|
165
|
+
engineLabel: "Test Engine",
|
|
166
|
+
currentPhase: "test",
|
|
167
|
+
progressSummary: "testing",
|
|
168
|
+
stepCount: null,
|
|
169
|
+
}),
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
assert.equal(engine.engineId, "test-engine");
|
|
173
|
+
assert.equal(typeof engine.deriveState, "function");
|
|
174
|
+
assert.equal(typeof engine.resolveDispatch, "function");
|
|
175
|
+
assert.equal(typeof engine.reconcile, "function");
|
|
176
|
+
assert.equal(typeof engine.getDisplayMetadata, "function");
|
|
152
177
|
});
|
|
153
178
|
});
|
|
154
179
|
|
|
155
180
|
// ── ExecutionPolicy interface shape ─────────────────────────────────────────
|
|
156
181
|
|
|
157
182
|
describe("ExecutionPolicy interface shape", () => {
|
|
158
|
-
test("ExecutionPolicy
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`ExecutionPolicy must contain method: ${method}`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
183
|
+
test("ExecutionPolicy accepts an object with all required methods", () => {
|
|
184
|
+
const policy: ExecutionPolicy = {
|
|
185
|
+
prepareWorkspace: async () => {},
|
|
186
|
+
selectModel: async () => null,
|
|
187
|
+
verify: async () => "continue",
|
|
188
|
+
recover: async () => ({ outcome: "retry" } as RecoveryAction),
|
|
189
|
+
closeout: async () => ({ committed: true, artifacts: [] } as CloseoutResult),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
assert.equal(typeof policy.prepareWorkspace, "function");
|
|
193
|
+
assert.equal(typeof policy.selectModel, "function");
|
|
194
|
+
assert.equal(typeof policy.verify, "function");
|
|
195
|
+
assert.equal(typeof policy.recover, "function");
|
|
196
|
+
assert.equal(typeof policy.closeout, "function");
|
|
175
197
|
});
|
|
176
198
|
});
|
|
177
199
|
|
|
@@ -220,12 +242,16 @@ describe("Resolver stub behavior", () => {
|
|
|
220
242
|
);
|
|
221
243
|
});
|
|
222
244
|
|
|
223
|
-
test("ResolvedEngine type is exported
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
245
|
+
test("ResolvedEngine type is exported and has the expected shape", () => {
|
|
246
|
+
// Type-level assertion: ResolvedEngine must be { engine: WorkflowEngine; policy: ExecutionPolicy }.
|
|
247
|
+
// If the export or shape changes, the typecheck step fails.
|
|
248
|
+
type _AssertResolvedEngine = ResolvedEngine extends { engine: WorkflowEngine; policy: ExecutionPolicy }
|
|
249
|
+
? true
|
|
250
|
+
: false;
|
|
251
|
+
const _assertResolvedEngine: true = {} as _AssertResolvedEngine;
|
|
252
|
+
|
|
253
|
+
// Runtime sanity check that the import path resolves.
|
|
254
|
+
assert.ok(_assertResolvedEngine === undefined || true);
|
|
229
255
|
});
|
|
230
256
|
});
|
|
231
257
|
|
|
@@ -11,7 +11,10 @@ import * as path from 'node:path';
|
|
|
11
11
|
import * as os from 'node:os';
|
|
12
12
|
import * as fs from 'node:fs';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
14
16
|
import { closeDatabase, isDbAvailable, getDecisionById, SCHEMA_VERSION, _getAdapter } from '../gsd-db.ts';
|
|
17
|
+
import { formatWorkflowDatabaseOpenFailure } from '../bootstrap/dynamic-tools.ts';
|
|
15
18
|
|
|
16
19
|
const _require = createRequire(import.meta.url);
|
|
17
20
|
|
|
@@ -288,6 +291,116 @@ function createLegacyV15Db(dbPath: string): void {
|
|
|
288
291
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
292
|
|
|
290
293
|
describe('ensure-db-open', () => {
|
|
294
|
+
test('formatWorkflowDatabaseOpenFailure: open failure without provider gives actionable SQLite guidance', () => {
|
|
295
|
+
const message = formatWorkflowDatabaseOpenFailure(
|
|
296
|
+
{
|
|
297
|
+
ok: false,
|
|
298
|
+
reason: 'open-failed',
|
|
299
|
+
location: {
|
|
300
|
+
projectRoot: '/tmp/example',
|
|
301
|
+
projectGsd: '/tmp/example/.gsd',
|
|
302
|
+
projectDb: '/tmp/example/.gsd/gsd.db',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
available: false,
|
|
307
|
+
provider: null,
|
|
308
|
+
attempted: true,
|
|
309
|
+
lastError: null,
|
|
310
|
+
lastPhase: null,
|
|
311
|
+
},
|
|
312
|
+
'22.15.0',
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
assert.match(message, /\/tmp\/example\/\.gsd\/gsd\.db/);
|
|
316
|
+
assert.match(message, /No SQLite provider available/);
|
|
317
|
+
assert.match(message, /node:sqlite/);
|
|
318
|
+
assert.match(message, /better-sqlite3/);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test('formatWorkflowDatabaseOpenFailure: old Node includes upgrade guidance', () => {
|
|
322
|
+
const message = formatWorkflowDatabaseOpenFailure(
|
|
323
|
+
{
|
|
324
|
+
ok: false,
|
|
325
|
+
reason: 'open-failed',
|
|
326
|
+
location: {
|
|
327
|
+
projectRoot: '/tmp/example',
|
|
328
|
+
projectGsd: '/tmp/example/.gsd',
|
|
329
|
+
projectDb: '/tmp/example/.gsd/gsd.db',
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
available: false,
|
|
334
|
+
provider: null,
|
|
335
|
+
attempted: true,
|
|
336
|
+
lastError: null,
|
|
337
|
+
lastPhase: null,
|
|
338
|
+
},
|
|
339
|
+
'20.11.1',
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
assert.match(message, />= 22\.0\.0/);
|
|
343
|
+
assert.match(message, /current: v20\.11\.1/);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test('ensureDbOpen: source-mode runtime without node:sqlite records actionable guidance', () => {
|
|
347
|
+
const loaderPath = fileURLToPath(new URL('./resolve-ts.mjs', import.meta.url));
|
|
348
|
+
const runningFromDistTest = fileURLToPath(import.meta.url).includes(`${path.sep}dist-test${path.sep}`);
|
|
349
|
+
const dynamicToolsImportUrl = pathToFileURL(
|
|
350
|
+
path.resolve(
|
|
351
|
+
runningFromDistTest
|
|
352
|
+
? 'dist-test/src/resources/extensions/gsd/bootstrap/dynamic-tools.js'
|
|
353
|
+
: 'src/resources/extensions/gsd/bootstrap/dynamic-tools.ts',
|
|
354
|
+
),
|
|
355
|
+
).href;
|
|
356
|
+
const loggerImportUrl = pathToFileURL(
|
|
357
|
+
path.resolve(
|
|
358
|
+
runningFromDistTest
|
|
359
|
+
? 'dist-test/src/resources/extensions/gsd/workflow-logger.ts'
|
|
360
|
+
: 'src/resources/extensions/gsd/workflow-logger.ts',
|
|
361
|
+
),
|
|
362
|
+
).href;
|
|
363
|
+
const script = `
|
|
364
|
+
const fs = require('node:fs');
|
|
365
|
+
const os = require('node:os');
|
|
366
|
+
const path = require('node:path');
|
|
367
|
+
(async () => {
|
|
368
|
+
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-missing-sqlite-'));
|
|
369
|
+
try {
|
|
370
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
371
|
+
const dynamicTools = await import(${JSON.stringify(dynamicToolsImportUrl)});
|
|
372
|
+
const logger = await import(${JSON.stringify(loggerImportUrl)});
|
|
373
|
+
await dynamicTools.ensureDbOpen(base);
|
|
374
|
+
const messages = logger.peekLogs().map((entry) => entry.message);
|
|
375
|
+
console.log(JSON.stringify(messages));
|
|
376
|
+
} finally {
|
|
377
|
+
fs.rmSync(base, { recursive: true, force: true });
|
|
378
|
+
}
|
|
379
|
+
})().catch((error) => {
|
|
380
|
+
console.error(error.stack || error.message || String(error));
|
|
381
|
+
process.exit(1);
|
|
382
|
+
});
|
|
383
|
+
`;
|
|
384
|
+
const result = spawnSync(
|
|
385
|
+
process.execPath,
|
|
386
|
+
[
|
|
387
|
+
'--no-experimental-sqlite',
|
|
388
|
+
'--import',
|
|
389
|
+
loaderPath,
|
|
390
|
+
'--experimental-strip-types',
|
|
391
|
+
'-e',
|
|
392
|
+
script,
|
|
393
|
+
],
|
|
394
|
+
{ cwd: process.cwd(), encoding: 'utf-8' },
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
assert.equal(result.status, 0, result.stderr);
|
|
398
|
+
const messages = JSON.parse(result.stdout.trim()) as string[];
|
|
399
|
+
assert.ok(messages.some((message) => /No SQLite provider available/.test(message)), result.stdout);
|
|
400
|
+
assert.ok(messages.some((message) => /node:sqlite/.test(message)), result.stdout);
|
|
401
|
+
assert.ok(messages.some((message) => /better-sqlite3/.test(message)), result.stdout);
|
|
402
|
+
});
|
|
403
|
+
|
|
291
404
|
test('ensureDbOpen: creates empty DB without importing Markdown', async () => {
|
|
292
405
|
const tmpDir = makeTmpDir();
|
|
293
406
|
const gsdDir = path.join(tmpDir, '.gsd');
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
_dispatchWorkflowForTest,
|
|
12
12
|
resolveGuidedDispatchProjectRoot,
|
|
13
13
|
} from "../guided-flow.ts";
|
|
14
|
+
import { getRequiredWorkflowToolsForUnit } from "../unit-tool-contracts.ts";
|
|
14
15
|
|
|
15
16
|
test("guided dispatch falls back to cwd only when no project root is supplied", () => {
|
|
16
17
|
const cwd = process.cwd();
|
|
@@ -115,6 +116,7 @@ test("guided dispatch accepts workflow MCP tools absent from parent active tool
|
|
|
115
116
|
getProviderAuthMode: () => "externalCli",
|
|
116
117
|
},
|
|
117
118
|
ui: {
|
|
119
|
+
setStatus: () => {},
|
|
118
120
|
notify: (message: string) => {
|
|
119
121
|
notifications.push(message);
|
|
120
122
|
},
|
|
@@ -130,8 +132,22 @@ test("guided dispatch accepts workflow MCP tools absent from parent active tool
|
|
|
130
132
|
"write",
|
|
131
133
|
];
|
|
132
134
|
|
|
135
|
+
// The workflow MCP server registers its tools out of band, so the unit's
|
|
136
|
+
// required workflow tools show up in the registered tool snapshot
|
|
137
|
+
// (getAllTools) under the MCP server prefix without ever entering the parent
|
|
138
|
+
// session's active tool surface (getActiveTools). This is exactly the shape
|
|
139
|
+
// the readiness gate must accept. Derive the surface from the unit contract
|
|
140
|
+
// so this test stays correct if discuss-milestone's required tools change.
|
|
141
|
+
const registeredTools = [
|
|
142
|
+
...activeTools,
|
|
143
|
+
...getRequiredWorkflowToolsForUnit("discuss-milestone").map(
|
|
144
|
+
(tool) => `mcp__gsd-workflow__${tool}`,
|
|
145
|
+
),
|
|
146
|
+
];
|
|
147
|
+
|
|
133
148
|
const pi = {
|
|
134
149
|
getActiveTools: () => [...activeTools],
|
|
150
|
+
getAllTools: () => registeredTools.map((name) => ({ name })),
|
|
135
151
|
setActiveTools: (tools: string[]) => {
|
|
136
152
|
activeTools = [...tools];
|
|
137
153
|
},
|
|
@@ -134,6 +134,21 @@ describe("auto-worktree lifecycle", () => {
|
|
|
134
134
|
teardownAutoWorktree(tempDir, "M003");
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
+
test("isInAutoWorktree returns false when ambient cwd was deleted", (t) => {
|
|
138
|
+
const cwd = t.mock.method(process, "cwd", () => {
|
|
139
|
+
const err = new Error("process.cwd failed") as NodeJS.ErrnoException;
|
|
140
|
+
err.code = "ENOENT";
|
|
141
|
+
err.syscall = "uv_cwd";
|
|
142
|
+
throw err;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
assert.equal(isInAutoWorktree("/repo"), false);
|
|
147
|
+
} finally {
|
|
148
|
+
cwd.mock.restore();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
137
152
|
test("symlink-resolved auto worktree is detected after module state reset", () => {
|
|
138
153
|
tempDir = createTempRepo();
|
|
139
154
|
const savedGsdHome = process.env.GSD_HOME;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Verify the non-blocking runEnvironmentChecksAsync is behaviourally
|
|
3
|
+
// identical to the synchronous runEnvironmentChecks (the health-widget render
|
|
4
|
+
// path was moved onto the async variant for performance), and that the single-
|
|
5
|
+
// scan checkPortConflicts still detects a real in-use port.
|
|
6
|
+
|
|
7
|
+
import test, { type TestContext } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
10
|
+
import { join, dirname } from "node:path";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { createServer } from "node:net";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
runEnvironmentChecks,
|
|
16
|
+
runEnvironmentChecksAsync,
|
|
17
|
+
type EnvironmentCheckResult,
|
|
18
|
+
} from "../../doctor-environment.ts";
|
|
19
|
+
|
|
20
|
+
function makeProject(t: TestContext, files: Record<string, string>): string {
|
|
21
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-env-async-"));
|
|
22
|
+
t.after(() => {
|
|
23
|
+
try {
|
|
24
|
+
rmSync(dir, { recursive: true, force: true });
|
|
25
|
+
} catch {
|
|
26
|
+
/* ignore */
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
for (const [name, content] of Object.entries(files)) {
|
|
30
|
+
const filePath = join(dir, name);
|
|
31
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
32
|
+
writeFileSync(filePath, content);
|
|
33
|
+
}
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Stable, order-independent signature of a check-result set for comparison.
|
|
38
|
+
function normalize(results: EnvironmentCheckResult[]): string[] {
|
|
39
|
+
return results.map((r) => `${r.name}|${r.status}|${r.message}|${r.detail ?? ""}`).sort();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test("runEnvironmentChecksAsync returns the same results as runEnvironmentChecks", async (t) => {
|
|
43
|
+
const dir = makeProject(t, {
|
|
44
|
+
"package.json": JSON.stringify({
|
|
45
|
+
name: "fixture",
|
|
46
|
+
engines: { node: ">=18" },
|
|
47
|
+
scripts: { dev: "vite --port 4321", build: "tsc" },
|
|
48
|
+
devDependencies: { typescript: "^5.0.0" },
|
|
49
|
+
}),
|
|
50
|
+
".env.example": "API_KEY=\n",
|
|
51
|
+
Dockerfile: "FROM node:20\n",
|
|
52
|
+
});
|
|
53
|
+
mkdirSync(join(dir, "node_modules"), { recursive: true });
|
|
54
|
+
|
|
55
|
+
const sync = runEnvironmentChecks(dir);
|
|
56
|
+
const asyncResults = await runEnvironmentChecksAsync(dir);
|
|
57
|
+
|
|
58
|
+
assert.deepEqual(
|
|
59
|
+
normalize(asyncResults),
|
|
60
|
+
normalize(sync),
|
|
61
|
+
"async checks must produce the identical result set as the sync checks",
|
|
62
|
+
);
|
|
63
|
+
// Sanity: the fixture is rich enough that the suite actually produced checks.
|
|
64
|
+
assert.ok(sync.length > 0, "expected the fixture to yield environment checks");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("runEnvironmentChecksAsync matches sync on a bare directory (no package.json)", async (t) => {
|
|
68
|
+
const dir = makeProject(t, {});
|
|
69
|
+
const sync = runEnvironmentChecks(dir);
|
|
70
|
+
const asyncResults = await runEnvironmentChecksAsync(dir);
|
|
71
|
+
assert.deepEqual(normalize(asyncResults), normalize(sync));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test(
|
|
75
|
+
"single-scan port check detects a real in-use port, identically sync and async",
|
|
76
|
+
{ skip: process.platform === "win32" ? "lsof-based port check is macOS/Linux only" : false },
|
|
77
|
+
async (t) => {
|
|
78
|
+
// Bind a real listener, then reference its port from package.json scripts so
|
|
79
|
+
// collectPortsToCheck picks it up. The server stays up across both check runs.
|
|
80
|
+
const server = createServer();
|
|
81
|
+
const port = await new Promise<number>((resolve, reject) => {
|
|
82
|
+
server.once("error", reject);
|
|
83
|
+
server.listen(0, "127.0.0.1", () => {
|
|
84
|
+
const addr = server.address();
|
|
85
|
+
resolve(typeof addr === "object" && addr ? addr.port : 0);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
t.after(() => new Promise<void>((resolve) => server.close(() => resolve())));
|
|
89
|
+
assert.ok(port >= 1024 && port <= 65535, "ephemeral port should be in the checked range");
|
|
90
|
+
|
|
91
|
+
const dir = makeProject(t, {
|
|
92
|
+
"package.json": JSON.stringify({ name: "fixture", scripts: { dev: `serve --port ${port}` } }),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const syncConflicts = runEnvironmentChecks(dir).filter((r) => r.name === "port_conflict");
|
|
96
|
+
const asyncConflicts = (await runEnvironmentChecksAsync(dir)).filter((r) => r.name === "port_conflict");
|
|
97
|
+
|
|
98
|
+
// Equivalence holds regardless of whether lsof is present on the runner.
|
|
99
|
+
assert.deepEqual(normalize(asyncConflicts), normalize(syncConflicts), "sync and async must agree on port conflicts");
|
|
100
|
+
// On macOS/Linux lsof is standard, so the listener must be reported and named.
|
|
101
|
+
assert.equal(syncConflicts.length, 1, "expected exactly one port conflict for the in-use port");
|
|
102
|
+
assert.match(syncConflicts[0]!.message, new RegExp(`\\b${port}\\b`), "conflict message must name the in-use port");
|
|
103
|
+
},
|
|
104
|
+
);
|
|
@@ -317,6 +317,24 @@ test('(k2) run-uat prompt references gsd_uat_result_save, not direct write', ()
|
|
|
317
317
|
);
|
|
318
318
|
});
|
|
319
319
|
|
|
320
|
+
test('(k3) run-uat prompt warns that .gsd glob misses can be symlink traversal artifacts', async () => {
|
|
321
|
+
const base = createFixtureBase();
|
|
322
|
+
try {
|
|
323
|
+
const uatRel = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
|
|
324
|
+
const uatContent = makeUatContent('runtime-executable');
|
|
325
|
+
writeSliceFile(base, 'M001', 'S01', 'UAT', uatContent);
|
|
326
|
+
|
|
327
|
+
const prompt = await buildRunUatPrompt('M001', 'S01', uatRel, uatContent, base);
|
|
328
|
+
|
|
329
|
+
assert.match(prompt, /\.gsd\/\*\*/);
|
|
330
|
+
assert.match(prompt, /symlink-backed/i);
|
|
331
|
+
assert.match(prompt, /do not infer that the GSD harness is missing/i);
|
|
332
|
+
assert.match(prompt, /use the preloaded UAT context/i);
|
|
333
|
+
} finally {
|
|
334
|
+
cleanup(base);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
320
338
|
test('(l) dispatch preconditions via resolveSliceFile', () => {
|
|
321
339
|
const base = createFixtureBase();
|
|
322
340
|
const uatContent = makeUatContent('artifact-driven');
|