gsd-pi 2.49.0-dev.de3d9f6 → 2.50.0-dev.9476db8
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/headless-ui.js +12 -2
- package/dist/headless.js +29 -13
- package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
- package/dist/resources/extensions/gsd/auto/phases.js +11 -11
- package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
- package/dist/resources/extensions/gsd/auto-dispatch.js +33 -21
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -24
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
- package/dist/resources/extensions/gsd/auto-start.js +4 -31
- package/dist/resources/extensions/gsd/auto-timers.js +2 -2
- package/dist/resources/extensions/gsd/auto-verification.js +4 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +257 -113
- package/dist/resources/extensions/gsd/auto.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
- package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
- package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
- package/dist/resources/extensions/gsd/doctor.js +9 -1
- package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
- package/dist/resources/extensions/gsd/git-service.js +9 -10
- package/dist/resources/extensions/gsd/gsd-db.js +124 -1
- package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
- package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
- package/dist/resources/extensions/gsd/preferences-types.js +2 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +29 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
- package/dist/resources/extensions/gsd/session-forensics.js +6 -11
- package/dist/resources/extensions/gsd/session-lock.js +67 -56
- package/dist/resources/extensions/gsd/state.js +34 -7
- package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/dist/resources/extensions/gsd/templates/plan.md +16 -0
- package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
- package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
- package/dist/resources/extensions/gsd/worktree.js +3 -2
- package/dist/resources/extensions/remote-questions/config.js +3 -5
- package/dist/resources/extensions/search-the-web/native-search.js +8 -3
- package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
- package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -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 +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/chunks/229.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.7c75ac378de0f2b5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +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/src/agent-loop.ts +4 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
- package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -32
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
- package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
- package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +10 -11
- package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto/types.ts +13 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +39 -21
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -28
- package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
- package/src/resources/extensions/gsd/auto-start.ts +7 -27
- package/src/resources/extensions/gsd/auto-timers.ts +2 -2
- package/src/resources/extensions/gsd/auto-verification.ts +4 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +305 -108
- package/src/resources/extensions/gsd/auto.ts +11 -10
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
- package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
- package/src/resources/extensions/gsd/doctor.ts +9 -1
- package/src/resources/extensions/gsd/extension-manifest.json +1 -1
- package/src/resources/extensions/gsd/git-service.ts +7 -15
- package/src/resources/extensions/gsd/gsd-db.ts +150 -2
- package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
- package/src/resources/extensions/gsd/preferences-types.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +28 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
- package/src/resources/extensions/gsd/session-forensics.ts +6 -11
- package/src/resources/extensions/gsd/session-lock.ts +92 -64
- package/src/resources/extensions/gsd/state.ts +38 -5
- package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/src/resources/extensions/gsd/templates/plan.md +16 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +9 -12
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/quality-gates.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
- package/src/resources/extensions/gsd/types.ts +30 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
- package/src/resources/extensions/gsd/verification-gate.ts +0 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
- package/src/resources/extensions/gsd/worktree.ts +3 -2
- package/src/resources/extensions/remote-questions/config.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +8 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
- package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
- package/dist/resources/extensions/gsd/resource-version.js +0 -97
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
- package/src/resources/extensions/gsd/resource-version.ts +0 -101
- /package/dist/web/standalone/.next/static/{ceckLbAMjhzHaQ3RPtJnT → MkE9kzqUGny3-cSE0GNnm}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ceckLbAMjhzHaQ3RPtJnT → MkE9kzqUGny3-cSE0GNnm}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { extractSection } from "../files.ts";
|
|
5
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const templatesDir = join(__dirname, "..", "templates");
|
|
9
|
+
const promptsDir = join(__dirname, "..", "prompts");
|
|
10
|
+
|
|
11
|
+
const { assertTrue, report } = createTestContext();
|
|
12
|
+
|
|
13
|
+
function loadTemplate(name: string): string {
|
|
14
|
+
return readFileSync(join(templatesDir, `${name}.md`), "utf-8");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function loadPrompt(name: string): string {
|
|
18
|
+
return readFileSync(join(promptsDir, `${name}.md`), "utf-8");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
|
+
// Level 1: Templates contain quality gate headings
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
24
|
+
|
|
25
|
+
console.log("\n=== Level 1: Templates contain quality gate headings ===");
|
|
26
|
+
{
|
|
27
|
+
const plan = loadTemplate("plan");
|
|
28
|
+
assertTrue(plan.includes("## Threat Surface"), "plan.md contains ## Threat Surface");
|
|
29
|
+
assertTrue(plan.includes("## Requirement Impact"), "plan.md contains ## Requirement Impact");
|
|
30
|
+
|
|
31
|
+
const taskPlan = loadTemplate("task-plan");
|
|
32
|
+
assertTrue(taskPlan.includes("## Failure Modes"), "task-plan.md contains ## Failure Modes");
|
|
33
|
+
assertTrue(taskPlan.includes("## Load Profile"), "task-plan.md contains ## Load Profile");
|
|
34
|
+
assertTrue(taskPlan.includes("## Negative Tests"), "task-plan.md contains ## Negative Tests");
|
|
35
|
+
|
|
36
|
+
const sliceSummary = loadTemplate("slice-summary");
|
|
37
|
+
assertTrue(sliceSummary.includes("## Operational Readiness"), "slice-summary.md contains ## Operational Readiness");
|
|
38
|
+
|
|
39
|
+
const roadmap = loadTemplate("roadmap");
|
|
40
|
+
assertTrue(roadmap.includes("## Horizontal Checklist"), "roadmap.md contains ## Horizontal Checklist");
|
|
41
|
+
|
|
42
|
+
const milestoneSummary = loadTemplate("milestone-summary");
|
|
43
|
+
assertTrue(milestoneSummary.includes("## Decision Re-evaluation"), "milestone-summary.md contains ## Decision Re-evaluation");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
47
|
+
// Level 2: Prompts reference quality gates
|
|
48
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
49
|
+
|
|
50
|
+
console.log("\n=== Level 2: Prompts reference quality gates ===");
|
|
51
|
+
{
|
|
52
|
+
const planSlice = loadPrompt("plan-slice");
|
|
53
|
+
assertTrue(planSlice.includes("Threat Surface"), "plan-slice.md mentions Threat Surface");
|
|
54
|
+
assertTrue(planSlice.includes("Requirement Impact"), "plan-slice.md mentions Requirement Impact");
|
|
55
|
+
assertTrue(planSlice.toLowerCase().includes("quality gate"), "plan-slice.md mentions quality gate");
|
|
56
|
+
|
|
57
|
+
const guidedPlanSlice = loadPrompt("guided-plan-slice");
|
|
58
|
+
assertTrue(
|
|
59
|
+
guidedPlanSlice.includes("Threat Surface") || guidedPlanSlice.includes("Q3"),
|
|
60
|
+
"guided-plan-slice.md mentions Threat Surface or Q3"
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const executeTask = loadPrompt("execute-task");
|
|
64
|
+
assertTrue(executeTask.includes("Failure Modes"), "execute-task.md mentions Failure Modes");
|
|
65
|
+
assertTrue(executeTask.includes("Load Profile"), "execute-task.md mentions Load Profile");
|
|
66
|
+
assertTrue(executeTask.includes("Negative Tests"), "execute-task.md mentions Negative Tests");
|
|
67
|
+
|
|
68
|
+
const guidedExecuteTask = loadPrompt("guided-execute-task");
|
|
69
|
+
assertTrue(
|
|
70
|
+
guidedExecuteTask.includes("Failure Modes") || guidedExecuteTask.includes("Q5"),
|
|
71
|
+
"guided-execute-task.md mentions Failure Modes or Q5"
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const completeSlice = loadPrompt("complete-slice");
|
|
75
|
+
assertTrue(completeSlice.includes("Operational Readiness"), "complete-slice.md mentions Operational Readiness");
|
|
76
|
+
|
|
77
|
+
const guidedCompleteSlice = loadPrompt("guided-complete-slice");
|
|
78
|
+
assertTrue(
|
|
79
|
+
guidedCompleteSlice.includes("Operational Readiness") || guidedCompleteSlice.includes("Q8"),
|
|
80
|
+
"guided-complete-slice.md mentions Operational Readiness or Q8"
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const completeMilestone = loadPrompt("complete-milestone");
|
|
84
|
+
assertTrue(completeMilestone.includes("Horizontal Checklist"), "complete-milestone.md mentions Horizontal Checklist");
|
|
85
|
+
assertTrue(completeMilestone.includes("Decision Re-evaluation"), "complete-milestone.md mentions Decision Re-evaluation");
|
|
86
|
+
|
|
87
|
+
const planMilestone = loadPrompt("plan-milestone");
|
|
88
|
+
assertTrue(planMilestone.toLowerCase().includes("horizontal checklist"), "plan-milestone.md mentions horizontal checklist");
|
|
89
|
+
|
|
90
|
+
const guidedPlanMilestone = loadPrompt("guided-plan-milestone");
|
|
91
|
+
assertTrue(guidedPlanMilestone.includes("Horizontal Checklist"), "guided-plan-milestone.md mentions Horizontal Checklist");
|
|
92
|
+
|
|
93
|
+
const reassess = loadPrompt("reassess-roadmap");
|
|
94
|
+
assertTrue(reassess.includes("Threat Surface"), "reassess-roadmap.md mentions Threat Surface");
|
|
95
|
+
assertTrue(reassess.includes("Operational Readiness"), "reassess-roadmap.md mentions Operational Readiness");
|
|
96
|
+
assertTrue(reassess.includes("Horizontal Checklist"), "reassess-roadmap.md mentions Horizontal Checklist");
|
|
97
|
+
|
|
98
|
+
const replan = loadPrompt("replan-slice");
|
|
99
|
+
assertTrue(replan.includes("Threat Surface"), "replan-slice.md mentions Threat Surface");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
103
|
+
// Level 3: Parser backward compatibility — extractSection handles new headings
|
|
104
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
105
|
+
|
|
106
|
+
console.log("\n=== Level 3: extractSection backward compatibility ===");
|
|
107
|
+
{
|
|
108
|
+
// Old-style slice plan (no quality gate sections)
|
|
109
|
+
const oldPlan = `# S01: Auth Flow
|
|
110
|
+
|
|
111
|
+
**Goal:** Build login
|
|
112
|
+
**Demo:** User can log in
|
|
113
|
+
|
|
114
|
+
## Must-Haves
|
|
115
|
+
|
|
116
|
+
- Login form works
|
|
117
|
+
- Session persists
|
|
118
|
+
|
|
119
|
+
## Proof Level
|
|
120
|
+
|
|
121
|
+
- This slice proves: integration
|
|
122
|
+
|
|
123
|
+
## Tasks
|
|
124
|
+
|
|
125
|
+
- [ ] **T01: Build login** \`est:1h\`
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
// New-style slice plan (with quality gate sections)
|
|
129
|
+
const newPlan = `# S01: Auth Flow
|
|
130
|
+
|
|
131
|
+
**Goal:** Build login
|
|
132
|
+
**Demo:** User can log in
|
|
133
|
+
|
|
134
|
+
## Must-Haves
|
|
135
|
+
|
|
136
|
+
- Login form works
|
|
137
|
+
- Session persists
|
|
138
|
+
|
|
139
|
+
## Threat Surface
|
|
140
|
+
|
|
141
|
+
- **Abuse**: Credential stuffing, brute force login attempts
|
|
142
|
+
- **Data exposure**: Session tokens in cookies, password in request body
|
|
143
|
+
- **Input trust**: Username/password from form input reaching DB query
|
|
144
|
+
|
|
145
|
+
## Requirement Impact
|
|
146
|
+
|
|
147
|
+
- **Requirements touched**: R001, R003
|
|
148
|
+
- **Re-verify**: Login flow, session management
|
|
149
|
+
- **Decisions revisited**: D002
|
|
150
|
+
|
|
151
|
+
## Proof Level
|
|
152
|
+
|
|
153
|
+
- This slice proves: integration
|
|
154
|
+
|
|
155
|
+
## Tasks
|
|
156
|
+
|
|
157
|
+
- [ ] **T01: Build login** \`est:1h\`
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
// Old plan: quality gate sections return null (not found)
|
|
161
|
+
assertTrue(
|
|
162
|
+
extractSection(oldPlan, "Threat Surface") === null,
|
|
163
|
+
"extractSection returns null for Threat Surface on old plan"
|
|
164
|
+
);
|
|
165
|
+
assertTrue(
|
|
166
|
+
extractSection(oldPlan, "Requirement Impact") === null,
|
|
167
|
+
"extractSection returns null for Requirement Impact on old plan"
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Old plan: core sections still parse correctly
|
|
171
|
+
const oldMustHaves = extractSection(oldPlan, "Must-Haves");
|
|
172
|
+
assertTrue(
|
|
173
|
+
oldMustHaves !== null && oldMustHaves.includes("Login form works"),
|
|
174
|
+
"extractSection still parses Must-Haves on old plan"
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// New plan: quality gate sections are extracted
|
|
178
|
+
const threatSurface = extractSection(newPlan, "Threat Surface");
|
|
179
|
+
assertTrue(
|
|
180
|
+
threatSurface !== null && threatSurface.includes("Credential stuffing"),
|
|
181
|
+
"extractSection extracts Threat Surface content from new plan"
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const reqImpact = extractSection(newPlan, "Requirement Impact");
|
|
185
|
+
assertTrue(
|
|
186
|
+
reqImpact !== null && reqImpact.includes("R001"),
|
|
187
|
+
"extractSection extracts Requirement Impact content from new plan"
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// New plan: core sections still parse correctly
|
|
191
|
+
const newMustHaves = extractSection(newPlan, "Must-Haves");
|
|
192
|
+
assertTrue(
|
|
193
|
+
newMustHaves !== null && newMustHaves.includes("Login form works"),
|
|
194
|
+
"extractSection still parses Must-Haves on new plan"
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Task plan: Failure Modes
|
|
198
|
+
const oldTaskPlan = `# T01: Build Login
|
|
199
|
+
|
|
200
|
+
## Description
|
|
201
|
+
|
|
202
|
+
Build the login endpoint.
|
|
203
|
+
|
|
204
|
+
## Steps
|
|
205
|
+
|
|
206
|
+
1. Create route
|
|
207
|
+
`;
|
|
208
|
+
|
|
209
|
+
const newTaskPlan = `# T01: Build Login
|
|
210
|
+
|
|
211
|
+
## Description
|
|
212
|
+
|
|
213
|
+
Build the login endpoint.
|
|
214
|
+
|
|
215
|
+
## Failure Modes
|
|
216
|
+
|
|
217
|
+
| Dependency | On error | On timeout | On malformed response |
|
|
218
|
+
|------------|----------|-----------|----------------------|
|
|
219
|
+
| Auth DB | Return 500 | 3s timeout, retry once | Reject, log warning |
|
|
220
|
+
|
|
221
|
+
## Steps
|
|
222
|
+
|
|
223
|
+
1. Create route
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
assertTrue(
|
|
227
|
+
extractSection(oldTaskPlan, "Failure Modes") === null,
|
|
228
|
+
"extractSection returns null for Failure Modes on old task plan"
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const failureModes = extractSection(newTaskPlan, "Failure Modes");
|
|
232
|
+
assertTrue(
|
|
233
|
+
failureModes !== null && failureModes.includes("Auth DB"),
|
|
234
|
+
"extractSection extracts Failure Modes content from new task plan"
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Slice summary: Operational Readiness
|
|
238
|
+
const oldSummary = `# S01: Auth Flow
|
|
239
|
+
|
|
240
|
+
**Built login with session management**
|
|
241
|
+
|
|
242
|
+
## Verification
|
|
243
|
+
|
|
244
|
+
All tests pass.
|
|
245
|
+
|
|
246
|
+
## Deviations
|
|
247
|
+
|
|
248
|
+
None.
|
|
249
|
+
`;
|
|
250
|
+
|
|
251
|
+
const newSummary = `# S01: Auth Flow
|
|
252
|
+
|
|
253
|
+
**Built login with session management**
|
|
254
|
+
|
|
255
|
+
## Verification
|
|
256
|
+
|
|
257
|
+
All tests pass.
|
|
258
|
+
|
|
259
|
+
## Operational Readiness
|
|
260
|
+
|
|
261
|
+
- **Health signal**: /health endpoint returns 200 with session count
|
|
262
|
+
- **Failure signal**: Auth error rate > 5% triggers alert
|
|
263
|
+
- **Recovery**: Stateless — restart clears nothing
|
|
264
|
+
- **Monitoring gaps**: None
|
|
265
|
+
|
|
266
|
+
## Deviations
|
|
267
|
+
|
|
268
|
+
None.
|
|
269
|
+
`;
|
|
270
|
+
|
|
271
|
+
assertTrue(
|
|
272
|
+
extractSection(oldSummary, "Operational Readiness") === null,
|
|
273
|
+
"extractSection returns null for Operational Readiness on old summary"
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const opReadiness = extractSection(newSummary, "Operational Readiness");
|
|
277
|
+
assertTrue(
|
|
278
|
+
opReadiness !== null && opReadiness.includes("/health endpoint"),
|
|
279
|
+
"extractSection extracts Operational Readiness content from new summary"
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
284
|
+
// Level 4: Template section ordering is correct
|
|
285
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
286
|
+
|
|
287
|
+
console.log("\n=== Level 4: Template section ordering ===");
|
|
288
|
+
{
|
|
289
|
+
const plan = loadTemplate("plan");
|
|
290
|
+
const mustHavesIdx = plan.indexOf("## Must-Haves");
|
|
291
|
+
const threatIdx = plan.indexOf("## Threat Surface");
|
|
292
|
+
const proofIdx = plan.indexOf("## Proof Level");
|
|
293
|
+
assertTrue(
|
|
294
|
+
mustHavesIdx < threatIdx && threatIdx < proofIdx,
|
|
295
|
+
"plan.md: Threat Surface is between Must-Haves and Proof Level"
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const reqImpactIdx = plan.indexOf("## Requirement Impact");
|
|
299
|
+
assertTrue(
|
|
300
|
+
threatIdx < reqImpactIdx && reqImpactIdx < proofIdx,
|
|
301
|
+
"plan.md: Requirement Impact is between Threat Surface and Proof Level"
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
const taskPlan = loadTemplate("task-plan");
|
|
305
|
+
const descIdx = taskPlan.indexOf("## Description");
|
|
306
|
+
const failIdx = taskPlan.indexOf("## Failure Modes");
|
|
307
|
+
const stepsIdx = taskPlan.indexOf("## Steps");
|
|
308
|
+
assertTrue(
|
|
309
|
+
descIdx < failIdx && failIdx < stepsIdx,
|
|
310
|
+
"task-plan.md: Failure Modes is between Description and Steps"
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const loadIdx = taskPlan.indexOf("## Load Profile");
|
|
314
|
+
const negIdx = taskPlan.indexOf("## Negative Tests");
|
|
315
|
+
assertTrue(
|
|
316
|
+
failIdx < loadIdx && loadIdx < negIdx && negIdx < stepsIdx,
|
|
317
|
+
"task-plan.md: Failure Modes < Load Profile < Negative Tests < Steps"
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const sliceSummary = loadTemplate("slice-summary");
|
|
321
|
+
const reqInvalidIdx = sliceSummary.indexOf("## Requirements Invalidated");
|
|
322
|
+
const opIdx = sliceSummary.indexOf("## Operational Readiness");
|
|
323
|
+
const devIdx = sliceSummary.indexOf("## Deviations");
|
|
324
|
+
assertTrue(
|
|
325
|
+
reqInvalidIdx < opIdx && opIdx < devIdx,
|
|
326
|
+
"slice-summary.md: Operational Readiness is between Requirements Invalidated and Deviations"
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const roadmap = loadTemplate("roadmap");
|
|
330
|
+
const horizIdx = roadmap.indexOf("## Horizontal Checklist");
|
|
331
|
+
const boundaryIdx = roadmap.indexOf("## Boundary Map");
|
|
332
|
+
assertTrue(
|
|
333
|
+
horizIdx > 0 && horizIdx < boundaryIdx,
|
|
334
|
+
"roadmap.md: Horizontal Checklist is before Boundary Map"
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
const milestoneSummary = loadTemplate("milestone-summary");
|
|
338
|
+
const reqChangesIdx = milestoneSummary.indexOf("## Requirement Changes");
|
|
339
|
+
const decRevalIdx = milestoneSummary.indexOf("## Decision Re-evaluation");
|
|
340
|
+
const fwdIntelIdx = milestoneSummary.indexOf("## Forward Intelligence");
|
|
341
|
+
assertTrue(
|
|
342
|
+
reqChangesIdx < decRevalIdx && decRevalIdx < fwdIntelIdx,
|
|
343
|
+
"milestone-summary.md: Decision Re-evaluation is between Requirement Changes and Forward Intelligence"
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
report();
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2379: /gsd queue fails with 429 rate limit on projects
|
|
3
|
+
* with many completed milestones.
|
|
4
|
+
*
|
|
5
|
+
* The bug: buildExistingMilestonesContext iterates over ALL milestones
|
|
6
|
+
* (including completed ones) and calls loadFile for CONTEXT, SUMMARY,
|
|
7
|
+
* CONTEXT-DRAFT, and ROADMAP files on each — causing excessive I/O that
|
|
8
|
+
* triggers rate limits on large projects.
|
|
9
|
+
*
|
|
10
|
+
* The fix: completed milestones should emit a short summary line without
|
|
11
|
+
* loading their heavy artifact files (CONTEXT.md, SUMMARY.md, etc.).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
|
|
18
|
+
import { buildExistingMilestonesContext } from "../guided-flow-queue.ts";
|
|
19
|
+
import type { GSDState, MilestoneRegistryEntry } from "../types.ts";
|
|
20
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
21
|
+
|
|
22
|
+
const { assertTrue, assertEq, report } = createTestContext();
|
|
23
|
+
|
|
24
|
+
// ─── Fixture: project with many completed milestones ─────────────────────
|
|
25
|
+
|
|
26
|
+
const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-perf-"));
|
|
27
|
+
const gsd = join(tmpBase, ".gsd");
|
|
28
|
+
mkdirSync(join(gsd, "milestones"), { recursive: true });
|
|
29
|
+
|
|
30
|
+
const COMPLETED_COUNT = 25;
|
|
31
|
+
const ACTIVE_COUNT = 1;
|
|
32
|
+
const PENDING_COUNT = 2;
|
|
33
|
+
|
|
34
|
+
const allMilestoneIds: string[] = [];
|
|
35
|
+
const registry: MilestoneRegistryEntry[] = [];
|
|
36
|
+
|
|
37
|
+
// Create 25 completed milestones with CONTEXT.md and SUMMARY.md files
|
|
38
|
+
for (let i = 1; i <= COMPLETED_COUNT; i++) {
|
|
39
|
+
const mid = `M${String(i).padStart(3, "0")}`;
|
|
40
|
+
allMilestoneIds.push(mid);
|
|
41
|
+
registry.push({ id: mid, title: `Completed milestone ${i}`, status: "complete" });
|
|
42
|
+
mkdirSync(join(gsd, "milestones", mid), { recursive: true });
|
|
43
|
+
writeFileSync(
|
|
44
|
+
join(gsd, "milestones", mid, `${mid}-CONTEXT.md`),
|
|
45
|
+
`# ${mid}: Completed milestone ${i}\n\nThis is a large context document for ${mid}.\n${"Lorem ipsum dolor sit amet. ".repeat(50)}\n`,
|
|
46
|
+
);
|
|
47
|
+
writeFileSync(
|
|
48
|
+
join(gsd, "milestones", mid, `${mid}-SUMMARY.md`),
|
|
49
|
+
`# ${mid} Summary\n\nDelivered feature ${i} successfully.\n`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create 1 active milestone
|
|
54
|
+
{
|
|
55
|
+
const mid = `M${String(COMPLETED_COUNT + 1).padStart(3, "0")}`;
|
|
56
|
+
allMilestoneIds.push(mid);
|
|
57
|
+
registry.push({ id: mid, title: "Active milestone", status: "active" });
|
|
58
|
+
mkdirSync(join(gsd, "milestones", mid), { recursive: true });
|
|
59
|
+
writeFileSync(
|
|
60
|
+
join(gsd, "milestones", mid, `${mid}-CONTEXT.md`),
|
|
61
|
+
`# ${mid}: Active milestone\n\nCurrently in progress.\n`,
|
|
62
|
+
);
|
|
63
|
+
writeFileSync(
|
|
64
|
+
join(gsd, "milestones", mid, `${mid}-ROADMAP.md`),
|
|
65
|
+
`# ${mid} Roadmap\n\nSlices planned.\n`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Create 2 pending milestones
|
|
70
|
+
for (let i = 0; i < PENDING_COUNT; i++) {
|
|
71
|
+
const mid = `M${String(COMPLETED_COUNT + ACTIVE_COUNT + 1 + i).padStart(3, "0")}`;
|
|
72
|
+
allMilestoneIds.push(mid);
|
|
73
|
+
registry.push({ id: mid, title: `Pending milestone ${i + 1}`, status: "pending" });
|
|
74
|
+
mkdirSync(join(gsd, "milestones", mid), { recursive: true });
|
|
75
|
+
writeFileSync(
|
|
76
|
+
join(gsd, "milestones", mid, `${mid}-CONTEXT.md`),
|
|
77
|
+
`# ${mid}: Pending milestone ${i + 1}\n\nQueued work.\n`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const state: GSDState = {
|
|
82
|
+
activeMilestone: { id: `M${String(COMPLETED_COUNT + 1).padStart(3, "0")}`, title: "Active milestone" },
|
|
83
|
+
activeSlice: null,
|
|
84
|
+
activeTask: null,
|
|
85
|
+
phase: "executing",
|
|
86
|
+
recentDecisions: [],
|
|
87
|
+
blockers: [],
|
|
88
|
+
nextAction: "",
|
|
89
|
+
registry,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// ─── Test: completed milestones should NOT have their files loaded ────────
|
|
93
|
+
|
|
94
|
+
console.log("\n=== Queue completed milestone performance (#2379) ===");
|
|
95
|
+
|
|
96
|
+
const context = await buildExistingMilestonesContext(tmpBase, allMilestoneIds, state);
|
|
97
|
+
|
|
98
|
+
// Active and pending milestones SHOULD have full context loaded
|
|
99
|
+
const activeMid = `M${String(COMPLETED_COUNT + 1).padStart(3, "0")}`;
|
|
100
|
+
assertTrue(
|
|
101
|
+
context.includes("Currently in progress"),
|
|
102
|
+
"Active milestone context content should be loaded",
|
|
103
|
+
);
|
|
104
|
+
assertTrue(
|
|
105
|
+
context.includes("Slices planned"),
|
|
106
|
+
"Active milestone roadmap should be loaded",
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < PENDING_COUNT; i++) {
|
|
110
|
+
const mid = `M${String(COMPLETED_COUNT + ACTIVE_COUNT + 1 + i).padStart(3, "0")}`;
|
|
111
|
+
assertTrue(
|
|
112
|
+
context.includes(`Pending milestone ${i + 1}`),
|
|
113
|
+
`Pending milestone ${mid} context should be loaded`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Completed milestones should NOT have their CONTEXT.md body or SUMMARY.md
|
|
118
|
+
// content loaded — only a status line
|
|
119
|
+
for (let i = 1; i <= COMPLETED_COUNT; i++) {
|
|
120
|
+
const mid = `M${String(i).padStart(3, "0")}`;
|
|
121
|
+
|
|
122
|
+
// Should still mention the milestone ID and status
|
|
123
|
+
assertTrue(
|
|
124
|
+
context.includes(mid),
|
|
125
|
+
`Completed milestone ${mid} should still be referenced`,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Should NOT contain the heavy context body text
|
|
129
|
+
assertTrue(
|
|
130
|
+
!context.includes(`This is a large context document for ${mid}`),
|
|
131
|
+
`Completed milestone ${mid} should NOT have its full CONTEXT.md body loaded`,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Should NOT contain the summary body
|
|
135
|
+
assertTrue(
|
|
136
|
+
!context.includes(`Delivered feature ${i} successfully`),
|
|
137
|
+
`Completed milestone ${mid} should NOT have its SUMMARY.md body loaded`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ─── Test: the overall context should be reasonable in size ──────────────
|
|
142
|
+
|
|
143
|
+
// With 25 completed milestones NOT loading files, the context should be
|
|
144
|
+
// significantly smaller than if all files were loaded
|
|
145
|
+
const contextLines = context.split("\n").length;
|
|
146
|
+
assertTrue(
|
|
147
|
+
contextLines < 200,
|
|
148
|
+
`Context should be concise (got ${contextLines} lines); completed milestones should not inflate it`,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// ─── Cleanup ──────────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
rmSync(tmpBase, { recursive: true, force: true });
|
|
154
|
+
|
|
155
|
+
report();
|
|
@@ -503,7 +503,8 @@ console.log('\n=== doctor: no blocker → no blocker_discovered_no_replan issue
|
|
|
503
503
|
// Artifact Resolution: resolveExpectedArtifactPath for replan-slice (#858)
|
|
504
504
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
505
505
|
|
|
506
|
-
import { resolveExpectedArtifactPath
|
|
506
|
+
import { resolveExpectedArtifactPath } from '../auto-artifact-paths.ts';
|
|
507
|
+
import { verifyExpectedArtifact } from '../auto-recovery.ts';
|
|
507
508
|
|
|
508
509
|
|
|
509
510
|
describe('replan-slice', () => {
|
|
@@ -184,6 +184,38 @@ test('subdirectory of parent repo gets unique identity after git init (#1639)',
|
|
|
184
184
|
rmSync(parentRepo, { recursive: true, force: true });
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
+
test('ensureGsdSymlink from subdirectory does not create .gsd in subdir when git-root .gsd exists (#2380)', () => {
|
|
188
|
+
const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-subdir-symlink-")));
|
|
189
|
+
run("git init -b main", repo);
|
|
190
|
+
run('git config user.name "Pi Test"', repo);
|
|
191
|
+
run('git config user.email "pi@example.com"', repo);
|
|
192
|
+
run('git remote add origin git@github.com:example/subdir-test.git', repo);
|
|
193
|
+
writeFileSync(join(repo, "README.md"), "# Subdir Test\n", "utf-8");
|
|
194
|
+
run("git add README.md", repo);
|
|
195
|
+
run('git commit -m "init"', repo);
|
|
196
|
+
|
|
197
|
+
// Set up .gsd symlink at the git root (normal project initialisation)
|
|
198
|
+
ensureGsdSymlink(repo);
|
|
199
|
+
assert.ok(existsSync(join(repo, ".gsd")), "root .gsd exists after ensureGsdSymlink");
|
|
200
|
+
assert.ok(lstatSync(join(repo, ".gsd")).isSymbolicLink(), "root .gsd is a symlink");
|
|
201
|
+
|
|
202
|
+
// Create a subdirectory and call ensureGsdSymlink from there
|
|
203
|
+
const subdir = join(repo, "src", "lib");
|
|
204
|
+
mkdirSync(subdir, { recursive: true });
|
|
205
|
+
ensureGsdSymlink(subdir);
|
|
206
|
+
|
|
207
|
+
// ensureGsdSymlink should NOT create a .gsd in the subdirectory
|
|
208
|
+
// because the git root already has a valid .gsd symlink.
|
|
209
|
+
assert.ok(!existsSync(join(subdir, ".gsd")), "no .gsd created in subdirectory when git-root .gsd exists (#2380)");
|
|
210
|
+
assert.ok(!existsSync(join(repo, "src", ".gsd")), "no .gsd created in intermediate directory");
|
|
211
|
+
|
|
212
|
+
// The root .gsd should still be intact
|
|
213
|
+
assert.ok(existsSync(join(repo, ".gsd")), "root .gsd still exists");
|
|
214
|
+
assert.ok(lstatSync(join(repo, ".gsd")).isSymbolicLink(), "root .gsd is still a symlink");
|
|
215
|
+
|
|
216
|
+
rmSync(repo, { recursive: true, force: true });
|
|
217
|
+
});
|
|
218
|
+
|
|
187
219
|
test('validateProjectId rejects invalid values', () => {
|
|
188
220
|
for (const invalid of ["has spaces", "path/traversal", "dot..dot", "back\\slash"]) {
|
|
189
221
|
assert.ok(!validateProjectId(invalid), `validateProjectId rejects invalid value: "${invalid}"`);
|
|
@@ -236,6 +236,32 @@ test("parseRoadmapSlices: ## Slices with valid checkboxes does NOT invoke prose
|
|
|
236
236
|
assert.equal(slices[0]?.done, true);
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
+
// ── Regression test for #1940 ───────────────────────────────────────────────
|
|
240
|
+
// '## Slice Roadmap' header is not recognized by extractSlicesSection, causing
|
|
241
|
+
// checkbox-format slices to be missed and all slices reported as incomplete.
|
|
242
|
+
|
|
243
|
+
test("parseRoadmapSlices: ## Slice Roadmap heading recognized (#1940)", () => {
|
|
244
|
+
const roadmapContent = [
|
|
245
|
+
"# M002: Current Milestone", "",
|
|
246
|
+
"**Vision:** Ship it.", "",
|
|
247
|
+
"## Slice Roadmap", "",
|
|
248
|
+
"- [x] **S01: Foundation** `risk:low` `depends:[]`",
|
|
249
|
+
" > After this: base layer works.",
|
|
250
|
+
"- [x] **S02: Core Logic** `risk:medium` `depends:[S01]`",
|
|
251
|
+
"- [ ] **S03: Polish** `risk:low` `depends:[S01,S02]`", "",
|
|
252
|
+
"## Boundary Map",
|
|
253
|
+
].join("\n");
|
|
254
|
+
const slices = parseRoadmapSlices(roadmapContent);
|
|
255
|
+
assert.equal(slices.length, 3, "should parse 3 slices under '## Slice Roadmap'");
|
|
256
|
+
assert.equal(slices[0]?.id, "S01");
|
|
257
|
+
assert.equal(slices[0]?.done, true, "S01 should be marked done");
|
|
258
|
+
assert.equal(slices[1]?.id, "S02");
|
|
259
|
+
assert.equal(slices[1]?.done, true, "S02 should be marked done");
|
|
260
|
+
assert.equal(slices[2]?.id, "S03");
|
|
261
|
+
assert.equal(slices[2]?.done, false, "S03 should be pending");
|
|
262
|
+
assert.deepEqual(slices[2]?.depends, ["S01", "S02"]);
|
|
263
|
+
});
|
|
264
|
+
|
|
239
265
|
test("parseRoadmapSlices: ## Slices with only non-matching lines returns prose fallback results", () => {
|
|
240
266
|
const weirdContent = `# M020: Odd
|
|
241
267
|
|
|
@@ -171,7 +171,7 @@ test('(k) run-uat prompt template', () => {
|
|
|
171
171
|
const milestoneId = 'M001';
|
|
172
172
|
const sliceId = 'S01';
|
|
173
173
|
const uatPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
|
|
174
|
-
const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT
|
|
174
|
+
const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
|
|
175
175
|
const uatType = 'live-runtime';
|
|
176
176
|
const inlinedContext = '<!-- no context -->';
|
|
177
177
|
let promptResult: string | undefined;
|
|
@@ -234,7 +234,7 @@ test('(k2) run-uat prompt references gsd_summary_save, not direct write', () =>
|
|
|
234
234
|
milestoneId: 'M001',
|
|
235
235
|
sliceId: 'S01',
|
|
236
236
|
uatPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
237
|
-
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT
|
|
237
|
+
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
238
238
|
uatType: 'artifact-driven',
|
|
239
239
|
inlinedContext: '<!-- no context -->',
|
|
240
240
|
});
|
|
@@ -265,14 +265,13 @@ test('(l) dispatch preconditions via resolveSliceFile', () => {
|
|
|
265
265
|
'resolveSliceFile(..., "UAT") returns non-null when UAT file exists (dispatch trigger state)',
|
|
266
266
|
);
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
'
|
|
268
|
+
// UAT spec without a verdict line means UAT has not been run yet
|
|
269
|
+
const rawContent = readFileSync(uatFilePath!, 'utf-8');
|
|
270
|
+
assert.ok(
|
|
271
|
+
!/verdict:\s*[\w-]+/i.test(rawContent),
|
|
272
|
+
'UAT file without verdict indicates UAT has not been run (dispatch trigger state)',
|
|
273
273
|
);
|
|
274
274
|
|
|
275
|
-
const rawContent = readFileSync(uatFilePath!, 'utf-8');
|
|
276
275
|
assert.deepStrictEqual(
|
|
277
276
|
extractUatType(rawContent),
|
|
278
277
|
'artifact-driven',
|
|
@@ -286,13 +285,18 @@ test('(l) dispatch preconditions via resolveSliceFile', () => {
|
|
|
286
285
|
test('test block at line 307', () => {
|
|
287
286
|
const base = createFixtureBase();
|
|
288
287
|
try {
|
|
289
|
-
|
|
290
|
-
writeSliceFile(base, 'M001', 'S01', 'UAT
|
|
288
|
+
// Write UAT file with a verdict — simulates completed UAT
|
|
289
|
+
writeSliceFile(base, 'M001', 'S01', 'UAT', '# UAT Result\n\nverdict: PASS\n');
|
|
291
290
|
|
|
292
|
-
const
|
|
291
|
+
const uatFilePath = resolveSliceFile(base, 'M001', 'S01', 'UAT');
|
|
292
|
+
assert.ok(
|
|
293
|
+
uatFilePath !== null,
|
|
294
|
+
'resolveSliceFile(..., "UAT") returns non-null when UAT file exists',
|
|
295
|
+
);
|
|
296
|
+
const content = readFileSync(uatFilePath!, 'utf-8');
|
|
293
297
|
assert.ok(
|
|
294
|
-
|
|
295
|
-
'
|
|
298
|
+
/verdict:\s*[\w-]+/i.test(content),
|
|
299
|
+
'UAT file with verdict indicates UAT has been completed (idempotent skip state)',
|
|
296
300
|
);
|
|
297
301
|
} finally {
|
|
298
302
|
cleanup(base);
|
|
@@ -390,7 +394,7 @@ test('(p) run-uat prompt allows PASS when human-only checks remain as NEEDS-HUMA
|
|
|
390
394
|
milestoneId: 'M001',
|
|
391
395
|
sliceId: 'S01',
|
|
392
396
|
uatPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
393
|
-
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT
|
|
397
|
+
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
394
398
|
uatType: 'mixed',
|
|
395
399
|
inlinedContext: '<!-- no context -->',
|
|
396
400
|
});
|
|
@@ -432,7 +436,7 @@ test('(n) stale replay guard', async () => {
|
|
|
432
436
|
);
|
|
433
437
|
|
|
434
438
|
writeSliceFile(base, 'M001', 'S01', 'UAT', makeUatContent('artifact-driven'));
|
|
435
|
-
writeSliceFile(base, 'M001', 'S01', 'UAT
|
|
439
|
+
writeSliceFile(base, 'M001', 'S01', 'UAT', '---\nverdict: FAIL\n---\n');
|
|
436
440
|
|
|
437
441
|
const state = {
|
|
438
442
|
activeMilestone: { id: 'M001', title: 'Test roadmap' },
|
|
@@ -449,7 +453,7 @@ test('(n) stale replay guard', async () => {
|
|
|
449
453
|
assert.deepStrictEqual(
|
|
450
454
|
result,
|
|
451
455
|
null,
|
|
452
|
-
'existing UAT
|
|
456
|
+
'existing UAT with FAIL verdict does not re-dispatch; verdict gate owns blocking',
|
|
453
457
|
);
|
|
454
458
|
} finally {
|
|
455
459
|
cleanup(base);
|