gsd-pi 2.80.0-dev.2be7caf18 → 2.80.0-dev.3065f7bfa
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/README.md +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +178 -108
- package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +20 -30
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
- package/dist/resources/extensions/gsd/auto-prompts.js +90 -15
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
- package/dist/resources/extensions/gsd/auto-start.js +197 -6
- package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +79 -24
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +42 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +238 -28
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
- package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
- package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
- package/dist/resources/extensions/gsd/git-service.js +36 -4
- package/dist/resources/extensions/gsd/gsd-db.js +46 -13
- package/dist/resources/extensions/gsd/guided-flow.js +66 -32
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
- package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
- 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 +15 -15
- 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 +15 -15
- 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 +3 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +9 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +2 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
- package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
- package/packages/pi-agent-core/dist/token-audit.js +221 -0
- package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
- package/packages/pi-agent-core/dist/types.d.ts +9 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-agent-core/src/agent.ts +8 -0
- package/packages/pi-agent-core/src/index.ts +2 -0
- package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
- package/packages/pi-agent-core/src/token-audit.ts +287 -0
- package/packages/pi-agent-core/src/types.ts +14 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +41 -7
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +3 -6
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +3 -3
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +32 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +45 -7
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +3 -3
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +5 -5
- package/packages/pi-coding-agent/src/core/extensions/types.ts +35 -1
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
- package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
- package/src/resources/extensions/gsd/auto/phases.ts +251 -172
- package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +25 -30
- package/src/resources/extensions/gsd/auto/session.ts +8 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +95 -14
- package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
- package/src/resources/extensions/gsd/auto-start.ts +230 -9
- package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
- package/src/resources/extensions/gsd/auto.ts +87 -19
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +49 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +283 -28
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
- package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
- package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
- package/src/resources/extensions/gsd/git-service.ts +46 -8
- package/src/resources/extensions/gsd/gsd-db.ts +50 -13
- package/src/resources/extensions/gsd/guided-flow.ts +84 -39
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
- package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +417 -10
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
- package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
- package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +11 -0
- package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts
|
|
2
|
+
// Regression: parallel-orchestrator's `_createMilestoneWorktree` must
|
|
3
|
+
// fast-forward a reused milestone branch onto integration before creating
|
|
4
|
+
// the worktree, matching the behavior added to the auto-mode path in
|
|
5
|
+
// commit 8996cb68e (#5549 post-merge audit, R3).
|
|
6
|
+
|
|
7
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
import { _createMilestoneWorktree } from "../parallel-orchestrator.js";
|
|
15
|
+
|
|
16
|
+
const NO_PROMPT_ENV = {
|
|
17
|
+
...process.env,
|
|
18
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
19
|
+
GIT_AUTHOR_NAME: "test",
|
|
20
|
+
GIT_AUTHOR_EMAIL: "test@example.com",
|
|
21
|
+
GIT_COMMITTER_NAME: "test",
|
|
22
|
+
GIT_COMMITTER_EMAIL: "test@example.com",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function git(cwd: string, ...args: string[]): string {
|
|
26
|
+
return execFileSync("git", args, {
|
|
27
|
+
cwd,
|
|
28
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
env: NO_PROMPT_ENV,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function rev(cwd: string, ref: string): string {
|
|
35
|
+
return git(cwd, "rev-parse", ref).trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("_createMilestoneWorktree fast-forwards reused milestone branches (#5549 R3)", () => {
|
|
39
|
+
let repo: string;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
repo = mkdtempSync(join(tmpdir(), "parallel-orch-ff-"));
|
|
43
|
+
git(repo, "init", "-q", "-b", "main");
|
|
44
|
+
git(repo, "config", "user.email", "test@example.com");
|
|
45
|
+
git(repo, "config", "user.name", "test");
|
|
46
|
+
writeFileSync(join(repo, "seed.txt"), "seed\n");
|
|
47
|
+
git(repo, "add", "seed.txt");
|
|
48
|
+
git(repo, "commit", "-q", "-m", "initial");
|
|
49
|
+
// Minimal .gsd/ structure so syncGsdStateToWorktree doesn't crash on a
|
|
50
|
+
// bare repo. We don't care if it copies anything — only that the FF ran.
|
|
51
|
+
mkdirSync(join(repo, ".gsd"), { recursive: true });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
rmSync(repo, { recursive: true, force: true });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("reused milestone branch behind main is fast-forwarded before worktree creation", () => {
|
|
59
|
+
// Worker N drained M001 in a previous run, leaving milestone/M001 forked
|
|
60
|
+
// from old main. Worker N+1 picks up M002 — but the milestone/M002 branch
|
|
61
|
+
// was created from old main in a sibling run, so it's now N commits behind.
|
|
62
|
+
git(repo, "branch", "milestone/M002");
|
|
63
|
+
const m002Initial = rev(repo, "milestone/M002");
|
|
64
|
+
|
|
65
|
+
writeFileSync(join(repo, "seed.txt"), "main moved forward\n");
|
|
66
|
+
git(repo, "add", "seed.txt");
|
|
67
|
+
git(repo, "commit", "-q", "-m", "main advanced");
|
|
68
|
+
const mainTip = rev(repo, "main");
|
|
69
|
+
|
|
70
|
+
assert.notEqual(m002Initial, mainTip, "main must be ahead before the test");
|
|
71
|
+
|
|
72
|
+
// _createMilestoneWorktree may throw inside createWorktree/syncGsdStateToWorktree
|
|
73
|
+
// (e.g. if the worktree-manager has stricter requirements than this minimal
|
|
74
|
+
// repo provides). The fast-forward runs BEFORE those calls, so the branch
|
|
75
|
+
// ref should have moved regardless. Catch and assert on observable state.
|
|
76
|
+
try {
|
|
77
|
+
_createMilestoneWorktree(repo, "M002");
|
|
78
|
+
} catch {
|
|
79
|
+
// Fine — we only care that FF executed.
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
assert.equal(
|
|
83
|
+
rev(repo, "milestone/M002"),
|
|
84
|
+
mainTip,
|
|
85
|
+
"milestone/M002 must be fast-forwarded to main's tip before worktree is built",
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("diverged milestone branch is NOT touched (would lose work)", () => {
|
|
90
|
+
git(repo, "checkout", "-q", "-b", "milestone/M002");
|
|
91
|
+
writeFileSync(join(repo, "wip.txt"), "milestone-only work\n");
|
|
92
|
+
git(repo, "add", "wip.txt");
|
|
93
|
+
git(repo, "commit", "-q", "-m", "M002 work");
|
|
94
|
+
const m002Tip = rev(repo, "milestone/M002");
|
|
95
|
+
|
|
96
|
+
git(repo, "checkout", "-q", "main");
|
|
97
|
+
writeFileSync(join(repo, "seed.txt"), "main moved forward\n");
|
|
98
|
+
git(repo, "add", "seed.txt");
|
|
99
|
+
git(repo, "commit", "-q", "-m", "main advanced");
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
_createMilestoneWorktree(repo, "M002");
|
|
103
|
+
} catch {
|
|
104
|
+
// ignored
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
assert.equal(
|
|
108
|
+
rev(repo, "milestone/M002"),
|
|
109
|
+
m002Tip,
|
|
110
|
+
"diverged milestone branch must NOT be touched — would lose committed work",
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Unit tests for pre-execution validation checks.
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* pre-execution-checks.test.ts — Unit tests for pre-execution validation checks.
|
|
3
6
|
*
|
|
@@ -1542,6 +1545,27 @@ describe("checkFilePathConsistency directory inputs (#4446)", () => {
|
|
|
1542
1545
|
assert.equal(results[0].blocking, true);
|
|
1543
1546
|
});
|
|
1544
1547
|
|
|
1548
|
+
test("runtime directory annotation is skipped as a pre-execution file dependency", (t) => {
|
|
1549
|
+
const tempDir = join(tmpdir(), `pre-exec-dir-runtime-${Date.now()}`);
|
|
1550
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1551
|
+
t.after(() => rmSync(tempDir, { recursive: true, force: true }));
|
|
1552
|
+
|
|
1553
|
+
const tasks = [
|
|
1554
|
+
createTask({
|
|
1555
|
+
id: "T02",
|
|
1556
|
+
inputs: ["entries/ directory (runtime)"],
|
|
1557
|
+
expected_output: ["src/commands/delete.ts", "src/index.ts"],
|
|
1558
|
+
}),
|
|
1559
|
+
];
|
|
1560
|
+
|
|
1561
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
1562
|
+
assert.deepEqual(
|
|
1563
|
+
results,
|
|
1564
|
+
[],
|
|
1565
|
+
"Runtime-only directory inputs are created during command execution, not required before the task starts",
|
|
1566
|
+
);
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1545
1569
|
test("tilde-prefixed input is matched against $HOME, not the project basePath", (t) => {
|
|
1546
1570
|
const fakeHome = join(tmpdir(), `pre-exec-tilde-home-${Date.now()}`);
|
|
1547
1571
|
const projectDir = join(tmpdir(), `pre-exec-tilde-proj-${Date.now()}`);
|
|
@@ -1597,6 +1621,20 @@ describe("checkTaskOrdering directory inputs (#4446)", () => {
|
|
|
1597
1621
|
"Directory reference should not be treated as reading a file created later",
|
|
1598
1622
|
);
|
|
1599
1623
|
});
|
|
1624
|
+
|
|
1625
|
+
test("runtime directory annotation does not produce an ordering violation", () => {
|
|
1626
|
+
const tasks = [
|
|
1627
|
+
createTask({
|
|
1628
|
+
id: "T02",
|
|
1629
|
+
sequence: 0,
|
|
1630
|
+
inputs: ["entries/ directory (runtime)"],
|
|
1631
|
+
expected_output: [],
|
|
1632
|
+
}),
|
|
1633
|
+
];
|
|
1634
|
+
|
|
1635
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1636
|
+
assert.deepEqual(results, []);
|
|
1637
|
+
});
|
|
1600
1638
|
});
|
|
1601
1639
|
|
|
1602
1640
|
// ─── Regression Tests: checkTaskOrdering false positive for pre-execution refs (#4071) ──
|
|
@@ -1781,7 +1819,7 @@ describe("checkFilePathConsistency completed-task output exemption (#4071)", ()
|
|
|
1781
1819
|
);
|
|
1782
1820
|
});
|
|
1783
1821
|
|
|
1784
|
-
test("pending task at higher index
|
|
1822
|
+
test("pending task at higher index does NOT cause a duplicate consistency error (ordering check handles it)", (t) => {
|
|
1785
1823
|
const tempDir = join(tmpdir(), `pre-exec-fc-pending-${Date.now()}`);
|
|
1786
1824
|
mkdirSync(tempDir, { recursive: true });
|
|
1787
1825
|
t.after(() => rmSync(tempDir, { recursive: true, force: true }));
|
|
@@ -1803,14 +1841,16 @@ describe("checkFilePathConsistency completed-task output exemption (#4071)", ()
|
|
|
1803
1841
|
}),
|
|
1804
1842
|
];
|
|
1805
1843
|
|
|
1844
|
+
// checkFilePathConsistency suppresses the error here because checkTaskOrdering
|
|
1845
|
+
// will fire a more precise "sequence violation" error for the same file.
|
|
1846
|
+
// The combined output of runPreExecutionChecks still flags the issue — just
|
|
1847
|
+
// once, via the ordering check, instead of twice.
|
|
1806
1848
|
const results = checkFilePathConsistency(tasks, tempDir);
|
|
1807
1849
|
assert.equal(
|
|
1808
1850
|
results.length,
|
|
1809
|
-
|
|
1810
|
-
"
|
|
1851
|
+
0,
|
|
1852
|
+
"consistency check must not duplicate what the ordering check already reports",
|
|
1811
1853
|
);
|
|
1812
|
-
assert.equal(results[0].blocking, true);
|
|
1813
|
-
assert.equal(results[0].target, "artifacts/output.json");
|
|
1814
1854
|
});
|
|
1815
1855
|
});
|
|
1816
1856
|
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Verifies low-risk auto-prompt duplication cuts render through prompt builders.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import type { TestContext } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
|
|
11
|
+
import { invalidateAllCaches } from "../cache.ts";
|
|
12
|
+
import {
|
|
13
|
+
closeDatabase,
|
|
14
|
+
insertMilestone,
|
|
15
|
+
insertSlice,
|
|
16
|
+
insertTask,
|
|
17
|
+
openDatabase,
|
|
18
|
+
upsertMilestonePlanning,
|
|
19
|
+
} from "../gsd-db.ts";
|
|
20
|
+
|
|
21
|
+
type AutoPromptBuilders = typeof import("../auto-prompts.ts");
|
|
22
|
+
|
|
23
|
+
function makeBase(prefix: string): string {
|
|
24
|
+
const base = mkdtempSync(join(tmpdir(), prefix));
|
|
25
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
|
|
26
|
+
return base;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cleanup(base: string): void {
|
|
30
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
31
|
+
invalidateAllCaches();
|
|
32
|
+
rmSync(base, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function loadAutoPromptBuilders(t: TestContext): Promise<AutoPromptBuilders> {
|
|
36
|
+
const previousGsdHome = process.env.GSD_HOME;
|
|
37
|
+
const isolatedHome = mkdtempSync(join(tmpdir(), "gsd-prompt-loader-home-"));
|
|
38
|
+
process.env.GSD_HOME = isolatedHome;
|
|
39
|
+
t.after(() => {
|
|
40
|
+
if (previousGsdHome === undefined) delete process.env.GSD_HOME;
|
|
41
|
+
else process.env.GSD_HOME = previousGsdHome;
|
|
42
|
+
rmSync(isolatedHome, { recursive: true, force: true });
|
|
43
|
+
});
|
|
44
|
+
return import(`../auto-prompts.ts?promptDupCuts=${Date.now()}-${Math.random()}`) as Promise<AutoPromptBuilders>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function seedDb(base: string, taskStatus = "complete"): void {
|
|
48
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
49
|
+
insertMilestone({ id: "M001", title: "Prompt Cuts", status: "active", depends_on: [] });
|
|
50
|
+
upsertMilestonePlanning("M001", {
|
|
51
|
+
title: "Prompt Cuts",
|
|
52
|
+
status: "active",
|
|
53
|
+
vision: "Reduce duplicate prompt reads.",
|
|
54
|
+
successCriteria: ["Prompt builders render compact context."],
|
|
55
|
+
keyRisks: [],
|
|
56
|
+
proofStrategy: [],
|
|
57
|
+
verificationContract: "",
|
|
58
|
+
verificationIntegration: "",
|
|
59
|
+
verificationOperational: "",
|
|
60
|
+
verificationUat: "",
|
|
61
|
+
definitionOfDone: [],
|
|
62
|
+
requirementCoverage: "",
|
|
63
|
+
boundaryMapMarkdown: "",
|
|
64
|
+
});
|
|
65
|
+
insertSlice({
|
|
66
|
+
id: "S01",
|
|
67
|
+
milestoneId: "M001",
|
|
68
|
+
title: "Prompt Slice",
|
|
69
|
+
status: "active",
|
|
70
|
+
risk: "low",
|
|
71
|
+
depends: [],
|
|
72
|
+
demo: "",
|
|
73
|
+
sequence: 1,
|
|
74
|
+
});
|
|
75
|
+
insertTask({
|
|
76
|
+
id: "T01",
|
|
77
|
+
sliceId: "S01",
|
|
78
|
+
milestoneId: "M001",
|
|
79
|
+
title: "Task one",
|
|
80
|
+
status: taskStatus,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function writeRoadmapAndPlan(base: string): void {
|
|
85
|
+
writeFileSync(
|
|
86
|
+
join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
|
|
87
|
+
[
|
|
88
|
+
"# M001 Roadmap",
|
|
89
|
+
"## Slices",
|
|
90
|
+
"- [ ] **S01: Prompt Slice** `risk:low` `depends:[]`",
|
|
91
|
+
].join("\n"),
|
|
92
|
+
);
|
|
93
|
+
writeFileSync(
|
|
94
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md"),
|
|
95
|
+
[
|
|
96
|
+
"# S01 Plan",
|
|
97
|
+
"",
|
|
98
|
+
"**Goal:** Reduce duplicate prompt reads.",
|
|
99
|
+
"",
|
|
100
|
+
"## Tasks",
|
|
101
|
+
"- [x] **T01: Task one** `est:15m`",
|
|
102
|
+
].join("\n"),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function writeTaskSummary(base: string, options?: { blocker?: boolean; repeatedNarrative?: string }): void {
|
|
107
|
+
const narrative = options?.repeatedNarrative ?? "This full implementation narrative should stay out of closer prompts.";
|
|
108
|
+
writeFileSync(
|
|
109
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-SUMMARY.md"),
|
|
110
|
+
[
|
|
111
|
+
"---",
|
|
112
|
+
"id: T01",
|
|
113
|
+
"parent: S01",
|
|
114
|
+
"milestone: M001",
|
|
115
|
+
"provides:",
|
|
116
|
+
" - prompt context reduction",
|
|
117
|
+
"key_files:",
|
|
118
|
+
" - src/resources/extensions/gsd/auto-prompts.ts",
|
|
119
|
+
"key_decisions:",
|
|
120
|
+
" - use compact excerpts before full reads",
|
|
121
|
+
"patterns_established:",
|
|
122
|
+
" - excerpt-first complete-slice context",
|
|
123
|
+
"observability_surfaces: []",
|
|
124
|
+
"duration: 15m",
|
|
125
|
+
"verification_result: passed",
|
|
126
|
+
"completed_at: 2026-05-06T12:00:00Z",
|
|
127
|
+
`blocker_discovered: ${options?.blocker ? "true" : "false"}`,
|
|
128
|
+
"---",
|
|
129
|
+
"",
|
|
130
|
+
"# T01: Task one",
|
|
131
|
+
"**One-line result.**",
|
|
132
|
+
"",
|
|
133
|
+
"## What Happened",
|
|
134
|
+
narrative,
|
|
135
|
+
"",
|
|
136
|
+
"## Verification",
|
|
137
|
+
"node:test passed.",
|
|
138
|
+
"",
|
|
139
|
+
"## Diagnostics",
|
|
140
|
+
"Prompt size stayed bounded.",
|
|
141
|
+
].join("\n"),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
test("execute-task rendering makes memory_query and template disk reads fallback-only", async (t) => {
|
|
146
|
+
const base = makeBase("gsd-execute-dup-cuts-");
|
|
147
|
+
t.after(() => cleanup(base));
|
|
148
|
+
invalidateAllCaches();
|
|
149
|
+
|
|
150
|
+
seedDb(base, "pending");
|
|
151
|
+
writeRoadmapAndPlan(base);
|
|
152
|
+
writeFileSync(
|
|
153
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-PLAN.md"),
|
|
154
|
+
"# T01 Plan\n\nDo the prompt edit.\n",
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const { buildExecuteTaskPrompt } = await loadAutoPromptBuilders(t);
|
|
158
|
+
const prompt = await buildExecuteTaskPrompt("M001", "S01", "Prompt Slice", "T01", "Task one", base);
|
|
159
|
+
|
|
160
|
+
assert.match(prompt, /Call `memory_query`.*only when no injected memory block exists or the inlined memory\/context is insufficient/s);
|
|
161
|
+
assert.doesNotMatch(prompt, /Call `memory_query` with 2-4 keywords from the task title and touched files unless this is purely mechanical/);
|
|
162
|
+
assert.match(prompt, /Use the inlined Task Summary template below/);
|
|
163
|
+
assert.match(prompt, /Read `.*task-summary\.md` only if the inlined template is absent or visibly truncated/);
|
|
164
|
+
assert.doesNotMatch(prompt, /Read the template at `.*task-summary\.md`/);
|
|
165
|
+
assert.match(prompt, /### Output Template: Task Summary/);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("complete-slice renders task summary excerpts without full summary bodies", async (t) => {
|
|
169
|
+
const base = makeBase("gsd-complete-slice-excerpts-");
|
|
170
|
+
t.after(() => cleanup(base));
|
|
171
|
+
invalidateAllCaches();
|
|
172
|
+
|
|
173
|
+
seedDb(base);
|
|
174
|
+
writeRoadmapAndPlan(base);
|
|
175
|
+
const repeatedNarrative = "FULL_TASK_BODY_SHOULD_NOT_RENDER ".repeat(40);
|
|
176
|
+
writeTaskSummary(base, { repeatedNarrative });
|
|
177
|
+
|
|
178
|
+
const { buildCompleteSlicePrompt } = await loadAutoPromptBuilders(t);
|
|
179
|
+
const prompt = await buildCompleteSlicePrompt("M001", "Prompt Cuts", "S01", "Prompt Slice", base);
|
|
180
|
+
|
|
181
|
+
assert.match(prompt, /### Task Summary: T01 \(excerpt\)/);
|
|
182
|
+
assert.match(prompt, /On-demand.*read `\.gsd\/milestones\/M001\/slices\/S01\/tasks\/T01-SUMMARY\.md` only when this excerpt is absent\/truncated/s);
|
|
183
|
+
assert.doesNotMatch(prompt, /FULL_TASK_BODY_SHOULD_NOT_RENDER/);
|
|
184
|
+
assert.match(prompt, /Review the inlined task-summary excerpts/);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("complete-slice caps malformed task summaries instead of inlining full bodies", async (t) => {
|
|
188
|
+
const base = makeBase("gsd-complete-slice-malformed-excerpts-");
|
|
189
|
+
t.after(() => cleanup(base));
|
|
190
|
+
invalidateAllCaches();
|
|
191
|
+
|
|
192
|
+
seedDb(base);
|
|
193
|
+
writeRoadmapAndPlan(base);
|
|
194
|
+
writeFileSync(
|
|
195
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-SUMMARY.md"),
|
|
196
|
+
[
|
|
197
|
+
"# Legacy summary without frontmatter id",
|
|
198
|
+
"LEGACY_FULL_BODY_SHOULD_BE_CAPPED ".repeat(200),
|
|
199
|
+
].join("\n"),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const { buildCompleteSlicePrompt } = await loadAutoPromptBuilders(t);
|
|
203
|
+
const prompt = await buildCompleteSlicePrompt("M001", "Prompt Cuts", "S01", "Prompt Slice", base);
|
|
204
|
+
|
|
205
|
+
assert.match(prompt, /Truncated malformed summary/);
|
|
206
|
+
assert.ok(prompt.length < 20_000);
|
|
207
|
+
assert.ok((prompt.match(/LEGACY_FULL_BODY_SHOULD_BE_CAPPED/g) ?? []).length < 60);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("replan-slice renders blocker summary excerpt and tells the agent to read full only on demand", async (t) => {
|
|
211
|
+
const base = makeBase("gsd-replan-excerpts-");
|
|
212
|
+
t.after(() => cleanup(base));
|
|
213
|
+
invalidateAllCaches();
|
|
214
|
+
|
|
215
|
+
seedDb(base);
|
|
216
|
+
writeRoadmapAndPlan(base);
|
|
217
|
+
writeTaskSummary(base, {
|
|
218
|
+
blocker: true,
|
|
219
|
+
repeatedNarrative: "FULL_BLOCKER_BODY_SHOULD_NOT_RENDER ".repeat(40),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const { buildReplanSlicePrompt } = await loadAutoPromptBuilders(t);
|
|
223
|
+
const prompt = await buildReplanSlicePrompt("M001", "Prompt Cuts", "S01", "Prompt Slice", base);
|
|
224
|
+
|
|
225
|
+
assert.match(prompt, /### Blocker Task Summary: T01 \(excerpt\)/);
|
|
226
|
+
assert.match(prompt, /Use the inlined blocker summary excerpt first/);
|
|
227
|
+
assert.match(prompt, /Read the full blocker task summary only if the excerpt is absent, marked truncated, or lacks the specific blocker evidence needed to replan/);
|
|
228
|
+
assert.doesNotMatch(prompt, /FULL_BLOCKER_BODY_SHOULD_NOT_RENDER/);
|
|
229
|
+
assert.doesNotMatch(prompt, /Read the blocker task summary carefully/);
|
|
230
|
+
});
|
|
@@ -65,6 +65,25 @@ describe('prompt step ordering (#3696)', () => {
|
|
|
65
65
|
assert.ok(learningsIdx < completeMilestoneIdx, 'learnings extraction must happen before gsd_complete_milestone');
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
test('complete-milestone duplicate guard checks milestone status before durable writes', () => {
|
|
69
|
+
const guardMatch = completeMilestoneMd.match(/^\d+\.\s.*gsd_milestone_status/m);
|
|
70
|
+
const reqUpdateMatch = completeMilestoneMd.match(/^\d+\.\s.*gsd_requirement_update/m);
|
|
71
|
+
assert.ok(guardMatch, 'complete-milestone must start with a gsd_milestone_status duplicate guard');
|
|
72
|
+
assert.ok(reqUpdateMatch, 'gsd_requirement_update should appear in a numbered step');
|
|
73
|
+
|
|
74
|
+
const guardIdx = completeMilestoneMd.indexOf(guardMatch![0]);
|
|
75
|
+
const reqUpdateIdx = completeMilestoneMd.indexOf(reqUpdateMatch![0]);
|
|
76
|
+
assert.ok(
|
|
77
|
+
guardIdx < reqUpdateIdx,
|
|
78
|
+
'duplicate guard must run before requirement/project/learnings writes',
|
|
79
|
+
);
|
|
80
|
+
assert.match(
|
|
81
|
+
completeMilestoneMd,
|
|
82
|
+
/status(?:`|\*\*)?\s+(?:is\s+)?(?:`complete`|"complete")/i,
|
|
83
|
+
'duplicate guard must tell the agent to stop when status is complete',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
68
87
|
test('complete-slice.md uses gsd_requirement_update', () => {
|
|
69
88
|
assert.match(completeSliceMd, /gsd_requirement_update/,
|
|
70
89
|
'complete-slice.md should reference gsd_requirement_update');
|
|
@@ -28,8 +28,8 @@ describe('query-tools ensureDbOpen usage (#3672)', () => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
test('calls ensureDbOpen() before DB queries', () => {
|
|
31
|
-
assert.match(source, /await ensureDbOpen\(
|
|
32
|
-
'query-tools should call await ensureDbOpen()');
|
|
31
|
+
assert.match(source, /await ensureDbOpen\([^)]*\)/,
|
|
32
|
+
'query-tools should call await ensureDbOpen(...)');
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
test('no longer imports isDbAvailable in the execute path', () => {
|
|
@@ -41,7 +41,7 @@ describe('query-tools ensureDbOpen usage (#3672)', () => {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
test('uses dbAvailable result from ensureDbOpen', () => {
|
|
44
|
-
assert.match(source, /dbAvailable\s*=\s*await ensureDbOpen\(
|
|
44
|
+
assert.match(source, /dbAvailable\s*=\s*await ensureDbOpen\([^)]*\)/,
|
|
45
45
|
'should store ensureDbOpen result in dbAvailable');
|
|
46
46
|
});
|
|
47
47
|
});
|
|
@@ -24,7 +24,7 @@ describe('restore tools after discuss flow scoping (#3628)', () => {
|
|
|
24
24
|
it('savedTools is declared before the discuss scoping block', () => {
|
|
25
25
|
// savedTools must be declared before the discuss-* check
|
|
26
26
|
const savedToolsDecl = src.indexOf('let savedTools')
|
|
27
|
-
const discussCheck = src.indexOf('if (unitType?.startsWith("discuss-")
|
|
27
|
+
const discussCheck = src.indexOf('if (unitType?.startsWith("discuss-")')
|
|
28
28
|
assert.ok(savedToolsDecl !== -1, 'savedTools variable must be declared')
|
|
29
29
|
assert.ok(discussCheck !== -1, 'discuss-* type check must exist')
|
|
30
30
|
assert.ok(
|
|
@@ -33,40 +33,61 @@ describe('restore tools after discuss flow scoping (#3628)', () => {
|
|
|
33
33
|
)
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
it('savedTools captures current tools
|
|
37
|
-
const discussCheck = src.indexOf('if (unitType?.startsWith("discuss-")
|
|
36
|
+
it('savedTools captures current tools before scoping can mutate active state', () => {
|
|
37
|
+
const discussCheck = src.indexOf('if (unitType?.startsWith("discuss-")')
|
|
38
38
|
assert.ok(discussCheck !== -1)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
const
|
|
40
|
+
const currentToolsDecl = src.indexOf('const currentTools = pi.getActiveTools()')
|
|
41
|
+
const savedToolsAssign = src.indexOf('savedTools = {', currentToolsDecl)
|
|
42
|
+
const firstMutation = src.indexOf('pi.setActiveTools(scopedTools)')
|
|
42
43
|
assert.ok(
|
|
43
|
-
|
|
44
|
-
'
|
|
44
|
+
currentToolsDecl !== -1 && savedToolsAssign !== -1 && firstMutation !== -1,
|
|
45
|
+
'guided-flow.ts must capture current tools, save them, and then scope active tools',
|
|
46
|
+
)
|
|
47
|
+
assert.ok(
|
|
48
|
+
currentToolsDecl < savedToolsAssign && savedToolsAssign < firstMutation,
|
|
49
|
+
'savedTools must capture currentTools before any discuss scoping mutation',
|
|
50
|
+
)
|
|
51
|
+
assert.ok(
|
|
52
|
+
src.slice(savedToolsAssign, firstMutation).includes('tools: currentTools'),
|
|
53
|
+
'savedTools must include currentTools before the first scoping mutation',
|
|
45
54
|
)
|
|
46
55
|
})
|
|
47
56
|
|
|
57
|
+
it('scoping and workflow read happen inside the restore try block', () => {
|
|
58
|
+
const savedToolsDecl = src.indexOf('let savedTools')
|
|
59
|
+
const tryIdx = src.indexOf('try {', savedToolsDecl)
|
|
60
|
+
const firstMutation = src.indexOf('pi.setActiveTools(scopedTools)')
|
|
61
|
+
const workflowRead = src.indexOf('readFileSync(workflowPath')
|
|
62
|
+
const finallyIdx = src.indexOf('} finally {', tryIdx)
|
|
63
|
+
|
|
64
|
+
assert.ok(savedToolsDecl !== -1, 'savedTools variable must be declared')
|
|
65
|
+
assert.ok(tryIdx !== -1, 'restore try block must exist')
|
|
66
|
+
assert.ok(firstMutation !== -1, 'discuss scoping mutation must exist')
|
|
67
|
+
assert.ok(workflowRead !== -1, 'workflow file read must exist')
|
|
68
|
+
assert.ok(finallyIdx !== -1, 'restore finally block must exist')
|
|
69
|
+
assert.ok(tryIdx < firstMutation && firstMutation < finallyIdx, 'scoping mutation must be inside try/finally')
|
|
70
|
+
assert.ok(tryIdx < workflowRead && workflowRead < finallyIdx, 'workflow file read must be inside try/finally')
|
|
71
|
+
})
|
|
72
|
+
|
|
48
73
|
it('savedTools is restored after sendMessage', () => {
|
|
49
74
|
// #4573: guided-flow.ts now contains multiple `triggerTurn: true` calls
|
|
50
75
|
// (ready-phrase and empty-turn recovery paths). The discuss-flow scoping
|
|
51
|
-
// sendMessage is the one that follows `
|
|
76
|
+
// sendMessage is the one that follows `tools: currentTools`, so
|
|
52
77
|
// anchor the search there rather than at the first `triggerTurn: true`.
|
|
53
|
-
const savedToolsAssign = src.indexOf('
|
|
54
|
-
assert.ok(savedToolsAssign !== -1, 'savedTools
|
|
78
|
+
const savedToolsAssign = src.indexOf('tools: currentTools')
|
|
79
|
+
assert.ok(savedToolsAssign !== -1, 'savedTools must capture currentTools')
|
|
55
80
|
|
|
56
81
|
const sendMsg = src.indexOf('triggerTurn: true', savedToolsAssign)
|
|
57
82
|
assert.ok(sendMsg !== -1, 'discuss-flow sendMessage with triggerTurn must exist after savedTools capture')
|
|
58
83
|
|
|
59
|
-
// After sendMessage, savedTools should be restored via
|
|
84
|
+
// After sendMessage, savedTools should be restored via the shared helper.
|
|
60
85
|
// Use fromIdx to anchor at the discuss-flow sendMessage, not the first
|
|
61
86
|
// triggerTurn: true occurrence in the file.
|
|
62
87
|
const afterSend = extractSourceRegion(src, 'triggerTurn: true', { fromIdx: savedToolsAssign })
|
|
63
88
|
assert.ok(
|
|
64
|
-
afterSend.includes('
|
|
65
|
-
'savedTools
|
|
66
|
-
)
|
|
67
|
-
assert.ok(
|
|
68
|
-
afterSend.includes('setActiveTools(savedTools)'),
|
|
69
|
-
'setActiveTools(savedTools) must be called to restore the full tool set',
|
|
89
|
+
afterSend.includes('restoreGsdWorkflowTools(pi, savedTools)'),
|
|
90
|
+
'restoreGsdWorkflowTools(pi, savedTools) must restore the full scoped state',
|
|
70
91
|
)
|
|
71
92
|
})
|
|
72
93
|
})
|