gsd-pi 2.48.0-dev.ced2eca → 2.49.0-dev.9e177e9
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 +19 -19
- 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 +19 -19
- 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/{PTL5V00OW8q4-092tUQKx → vNN0h0emdEi8l_npi8poE}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{PTL5V00OW8q4-092tUQKx → vNN0h0emdEi8l_npi8poE}/_ssgManifest.js +0 -0
|
@@ -17,6 +17,30 @@ skills_used:
|
|
|
17
17
|
|
|
18
18
|
{{description}}
|
|
19
19
|
|
|
20
|
+
## Failure Modes
|
|
21
|
+
|
|
22
|
+
<!-- Q5: What breaks when dependencies fail? OMIT ENTIRELY for tasks with no external dependencies. -->
|
|
23
|
+
|
|
24
|
+
| Dependency | On error | On timeout | On malformed response |
|
|
25
|
+
|------------|----------|-----------|----------------------|
|
|
26
|
+
| {{dependency}} | {{errorStrategy}} | {{timeoutStrategy}} | {{malformedStrategy}} |
|
|
27
|
+
|
|
28
|
+
## Load Profile
|
|
29
|
+
|
|
30
|
+
<!-- Q6: What breaks at 10x load? OMIT ENTIRELY for tasks with no shared resources or scaling concerns. -->
|
|
31
|
+
|
|
32
|
+
- **Shared resources**: {{sharedResources — DB connections, caches, rate limiters, or none}}
|
|
33
|
+
- **Per-operation cost**: {{perOpCost — N API calls, M DB queries, K bytes, or trivial}}
|
|
34
|
+
- **10x breakpoint**: {{whatBreaksFirst — pool exhaustion, rate limit, memory, or N/A}}
|
|
35
|
+
|
|
36
|
+
## Negative Tests
|
|
37
|
+
|
|
38
|
+
<!-- Q7: What negative tests prove robustness? OMIT ENTIRELY for trivial tasks. -->
|
|
39
|
+
|
|
40
|
+
- **Malformed inputs**: {{malformedInputTests — empty string, null, oversized, wrong type}}
|
|
41
|
+
- **Error paths**: {{errorPathTests — network timeout, auth failure, 5xx, invalid JSON}}
|
|
42
|
+
- **Boundary conditions**: {{boundaryTests — empty list, max length, zero, off-by-one}}
|
|
43
|
+
|
|
20
44
|
## Steps
|
|
21
45
|
|
|
22
46
|
1. {{step}}
|
|
@@ -116,7 +116,7 @@ test("auto-timers.ts idle watchdog catch calls resolveAgentEndCancelled", () =>
|
|
|
116
116
|
// Check that resolveAgentEndCancelled is called near this catch
|
|
117
117
|
const catchRegion = source.slice(Math.max(0, idleCatchIdx - 200), idleCatchIdx + 200);
|
|
118
118
|
assert.ok(
|
|
119
|
-
catchRegion.includes("resolveAgentEndCancelled(
|
|
119
|
+
catchRegion.includes("resolveAgentEndCancelled("),
|
|
120
120
|
"idle watchdog catch block must call resolveAgentEndCancelled",
|
|
121
121
|
);
|
|
122
122
|
});
|
|
@@ -129,7 +129,7 @@ test("auto-timers.ts hard timeout catch calls resolveAgentEndCancelled", () => {
|
|
|
129
129
|
assert.ok(hardCatchIdx > -1, "hard timeout catch block must exist");
|
|
130
130
|
const catchRegion = source.slice(Math.max(0, hardCatchIdx - 200), hardCatchIdx + 200);
|
|
131
131
|
assert.ok(
|
|
132
|
-
catchRegion.includes("resolveAgentEndCancelled(
|
|
132
|
+
catchRegion.includes("resolveAgentEndCancelled("),
|
|
133
133
|
"hard timeout catch block must call resolveAgentEndCancelled",
|
|
134
134
|
);
|
|
135
135
|
});
|
|
@@ -1745,6 +1745,41 @@ test("resolveAgentEndCancelled prevents orphaned promise after abort path", asyn
|
|
|
1745
1745
|
assert.equal(result.status, "cancelled");
|
|
1746
1746
|
});
|
|
1747
1747
|
|
|
1748
|
+
test("resolveAgentEndCancelled with errorContext passes it through to resolved promise", async () => {
|
|
1749
|
+
_resetPendingResolve();
|
|
1750
|
+
|
|
1751
|
+
const { _setCurrentResolve } = await import("../auto/resolve.js");
|
|
1752
|
+
|
|
1753
|
+
const p = new Promise<UnitResult>((r) => {
|
|
1754
|
+
_setCurrentResolve(r);
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
resolveAgentEndCancelled({ message: "test timeout", category: "timeout", isTransient: true });
|
|
1758
|
+
|
|
1759
|
+
const resolved = await p;
|
|
1760
|
+
assert.equal(resolved.status, "cancelled");
|
|
1761
|
+
assert.ok(resolved.errorContext, "errorContext must be present");
|
|
1762
|
+
assert.equal(resolved.errorContext!.category, "timeout");
|
|
1763
|
+
assert.equal(resolved.errorContext!.message, "test timeout");
|
|
1764
|
+
assert.equal(resolved.errorContext!.isTransient, true);
|
|
1765
|
+
});
|
|
1766
|
+
|
|
1767
|
+
test("resolveAgentEndCancelled without args produces no errorContext field", async () => {
|
|
1768
|
+
_resetPendingResolve();
|
|
1769
|
+
|
|
1770
|
+
const { _setCurrentResolve } = await import("../auto/resolve.js");
|
|
1771
|
+
|
|
1772
|
+
const p = new Promise<UnitResult>((r) => {
|
|
1773
|
+
_setCurrentResolve(r);
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
resolveAgentEndCancelled();
|
|
1777
|
+
|
|
1778
|
+
const resolved = await p;
|
|
1779
|
+
assert.equal(resolved.status, "cancelled");
|
|
1780
|
+
assert.equal(resolved.errorContext, undefined, "errorContext must not be present when no args passed");
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1748
1783
|
// ─── #1571: artifact verification retry ──────────────────────────────────────
|
|
1749
1784
|
|
|
1750
1785
|
test("autoLoop re-iterates when postUnitPreVerification returns retry (#1571)", async () => {
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
verifyExpectedArtifact,
|
|
11
11
|
diagnoseExpectedArtifact,
|
|
12
12
|
buildLoopRemediationSteps,
|
|
13
|
-
selfHealRuntimeRecords,
|
|
14
13
|
hasImplementationArtifacts,
|
|
15
14
|
} from "../auto-recovery.ts";
|
|
16
15
|
import { parseRoadmap, parsePlan } from "../parsers-legacy.ts";
|
|
@@ -112,7 +111,7 @@ test("resolveExpectedArtifactPath returns correct path for all slice-level types
|
|
|
112
111
|
|
|
113
112
|
const uatResult = resolveExpectedArtifactPath("run-uat", "M001/S01", base);
|
|
114
113
|
assert.ok(uatResult);
|
|
115
|
-
assert.ok(uatResult!.includes("UAT
|
|
114
|
+
assert.ok(uatResult!.includes("UAT"));
|
|
116
115
|
});
|
|
117
116
|
|
|
118
117
|
// ─── diagnoseExpectedArtifact ─────────────────────────────────────────────
|
|
@@ -572,85 +571,6 @@ test("verifyExpectedArtifact plan-slice fails after deleting a rendered task pla
|
|
|
572
571
|
}
|
|
573
572
|
});
|
|
574
573
|
|
|
575
|
-
// ─── selfHealRuntimeRecords — worktree base path (#769) ──────────────────
|
|
576
|
-
|
|
577
|
-
test("selfHealRuntimeRecords clears stale dispatched records (#769)", async (t) => {
|
|
578
|
-
// selfHealRuntimeRecords now only clears stale dispatched records (>1h).
|
|
579
|
-
// No completedKeySet parameter — deriveState is sole authority.
|
|
580
|
-
const worktreeBase = makeTmpBase();
|
|
581
|
-
const mainBase = makeTmpBase();
|
|
582
|
-
t.after(() => {
|
|
583
|
-
cleanup(worktreeBase);
|
|
584
|
-
cleanup(mainBase);
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
|
|
588
|
-
|
|
589
|
-
// Write a stale runtime record in the worktree .gsd/runtime/units/
|
|
590
|
-
writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
|
|
591
|
-
phase: "dispatched",
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
// Verify the runtime record exists before heal
|
|
595
|
-
const before = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
|
|
596
|
-
assert.ok(before, "runtime record should exist before heal");
|
|
597
|
-
|
|
598
|
-
// Mock ExtensionContext with minimal notify
|
|
599
|
-
const notifications: string[] = [];
|
|
600
|
-
const mockCtx = {
|
|
601
|
-
ui: { notify: (msg: string) => { notifications.push(msg); } },
|
|
602
|
-
} as any;
|
|
603
|
-
|
|
604
|
-
// Call selfHeal with worktreeBase — should clear the stale record
|
|
605
|
-
await selfHealRuntimeRecords(worktreeBase, mockCtx);
|
|
606
|
-
|
|
607
|
-
// The stale record should be cleared
|
|
608
|
-
const after = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
|
|
609
|
-
assert.equal(after, null, "runtime record should be cleared after heal");
|
|
610
|
-
assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
|
|
611
|
-
|
|
612
|
-
// Write a stale record at mainBase
|
|
613
|
-
writeUnitRuntimeRecord(mainBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
|
|
614
|
-
phase: "dispatched",
|
|
615
|
-
});
|
|
616
|
-
await selfHealRuntimeRecords(mainBase, mockCtx);
|
|
617
|
-
|
|
618
|
-
// The record at mainBase should also be cleared by the stale timeout (>1h)
|
|
619
|
-
const afterMain = readUnitRuntimeRecord(mainBase, "run-uat", "M001/S01");
|
|
620
|
-
assert.equal(afterMain, null, "stale record at main base should be cleared by timeout");
|
|
621
|
-
});
|
|
622
|
-
|
|
623
|
-
// ─── #1625: selfHealRuntimeRecords on resume clears paused-session leftovers ──
|
|
624
|
-
|
|
625
|
-
test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async (t) => {
|
|
626
|
-
// When pauseAuto closes out a unit but clearUnitRuntimeRecord silently fails
|
|
627
|
-
// (e.g. permission error), selfHealRuntimeRecords on resume should still
|
|
628
|
-
// clean up stale dispatched records that are >1h old.
|
|
629
|
-
const base = makeTmpBase();
|
|
630
|
-
t.after(() => cleanup(base));
|
|
631
|
-
|
|
632
|
-
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
|
|
633
|
-
|
|
634
|
-
// Simulate a record left behind after a pause — aged >1h to be considered stale
|
|
635
|
-
writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
|
|
636
|
-
phase: "dispatched",
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
const before = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
|
|
640
|
-
assert.ok(before, "dispatched record should exist before resume heal");
|
|
641
|
-
assert.equal(before!.phase, "dispatched");
|
|
642
|
-
|
|
643
|
-
const notifications: string[] = [];
|
|
644
|
-
const mockCtx = {
|
|
645
|
-
ui: { notify: (msg: string) => { notifications.push(msg); } },
|
|
646
|
-
} as any;
|
|
647
|
-
|
|
648
|
-
await selfHealRuntimeRecords(base, mockCtx);
|
|
649
|
-
|
|
650
|
-
const after = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
|
|
651
|
-
assert.equal(after, null, "stale dispatched record should be cleared on resume (#1625)");
|
|
652
|
-
});
|
|
653
|
-
|
|
654
574
|
// ─── #793: invalidateAllCaches unblocks skip-loop ─────────────────────────
|
|
655
575
|
// When the skip-loop breaker fires, it must call invalidateAllCaches() (not
|
|
656
576
|
// just invalidateStateCache()) to clear path/parse caches that deriveState
|
|
@@ -125,9 +125,9 @@ console.log('\n=== complete-slice: schema v6 migration ===');
|
|
|
125
125
|
|
|
126
126
|
const adapter = _getAdapter()!;
|
|
127
127
|
|
|
128
|
-
// Verify schema version is current (
|
|
128
|
+
// Verify schema version is current (v12 after quality gates table)
|
|
129
129
|
const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
130
|
-
assertEq(versionRow?.['v'],
|
|
130
|
+
assertEq(versionRow?.['v'], 12, 'schema version should be 12');
|
|
131
131
|
|
|
132
132
|
// Verify slices table has full_summary_md and full_uat_md columns
|
|
133
133
|
const cols = adapter.prepare("PRAGMA table_info(slices)").all();
|
|
@@ -109,9 +109,9 @@ console.log('\n=== complete-task: schema v5 migration ===');
|
|
|
109
109
|
|
|
110
110
|
const adapter = _getAdapter()!;
|
|
111
111
|
|
|
112
|
-
// Verify schema version is current (
|
|
112
|
+
// Verify schema version is current (v12 after quality gates table)
|
|
113
113
|
const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
114
|
-
assertEq(versionRow?.['v'],
|
|
114
|
+
assertEq(versionRow?.['v'], 12, 'schema version should be 12');
|
|
115
115
|
|
|
116
116
|
// Verify all 4 new tables exist
|
|
117
117
|
const tables = adapter.prepare(
|
|
@@ -48,35 +48,32 @@ test("#2313: completed-units.json should not be blindly wiped to [] on milestone
|
|
|
48
48
|
// ─── Bug 2: metrics.json should be in the sync file lists ──────────────────
|
|
49
49
|
|
|
50
50
|
test("#2313: syncStateToProjectRoot should sync metrics.json", () => {
|
|
51
|
-
const syncSrcPath = join(import.meta.dirname, "..", "auto-worktree
|
|
51
|
+
const syncSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
52
52
|
const syncSrc = readFileSync(syncSrcPath, "utf-8");
|
|
53
53
|
|
|
54
54
|
// syncStateToProjectRoot should copy metrics.json from worktree to project root
|
|
55
55
|
assert.ok(
|
|
56
56
|
syncSrc.includes("metrics.json"),
|
|
57
|
-
"auto-worktree
|
|
57
|
+
"auto-worktree.ts should reference metrics.json for sync",
|
|
58
58
|
);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
test("#2313: syncWorktreeStateBack should include metrics.json in
|
|
61
|
+
test("#2313: syncWorktreeStateBack should include metrics.json in ROOT_STATE_FILES", () => {
|
|
62
62
|
const autoWorktreeSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
63
63
|
const autoWorktreeSrc = readFileSync(autoWorktreeSrcPath, "utf-8");
|
|
64
64
|
|
|
65
|
-
// Find the
|
|
66
|
-
const
|
|
67
|
-
assert.ok(
|
|
65
|
+
// Find the ROOT_STATE_FILES constant (single source of truth for both sync directions)
|
|
66
|
+
const constIdx = autoWorktreeSrc.indexOf("ROOT_STATE_FILES");
|
|
67
|
+
assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists");
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// Get the rootFiles array content
|
|
73
|
-
const arrayStart = autoWorktreeSrc.indexOf("[", rootFilesIdx);
|
|
69
|
+
// Get the array content
|
|
70
|
+
const arrayStart = autoWorktreeSrc.indexOf("[", constIdx);
|
|
74
71
|
const arrayEnd = autoWorktreeSrc.indexOf("]", arrayStart);
|
|
75
72
|
const rootFilesBlock = autoWorktreeSrc.slice(arrayStart, arrayEnd);
|
|
76
73
|
|
|
77
74
|
assert.ok(
|
|
78
75
|
rootFilesBlock.includes("metrics.json"),
|
|
79
|
-
"metrics.json should be in
|
|
76
|
+
"metrics.json should be in ROOT_STATE_FILES list",
|
|
80
77
|
);
|
|
81
78
|
});
|
|
82
79
|
|
|
@@ -15,7 +15,7 @@ import assert from 'node:assert/strict';
|
|
|
15
15
|
* - Report formatting
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
18
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, utimesSync } from "node:fs";
|
|
19
19
|
import { join, dirname } from "node:path";
|
|
20
20
|
import { tmpdir } from "node:os";
|
|
21
21
|
|
|
@@ -102,6 +102,120 @@ describe('doctor-environment', async () => {
|
|
|
102
102
|
assert.deepStrictEqual(depsCheck!.status, "ok", "existing node_modules is ok");
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
+
// ── Stale Dependencies: marker file check (#1974) ──────────────────
|
|
106
|
+
console.log("\n=== env: npm marker file newer than lockfile → ok (#1974) ===");
|
|
107
|
+
{
|
|
108
|
+
// Simulate the exact bug scenario:
|
|
109
|
+
// 1. node_modules dir mtime is old (no entries added/removed recently)
|
|
110
|
+
// 2. package-lock.json mtime is recent (npm rewrote it)
|
|
111
|
+
// 3. node_modules/.package-lock.json mtime is between dir and lockfile
|
|
112
|
+
// (npm wrote it during the same install that rewrote the lockfile)
|
|
113
|
+
//
|
|
114
|
+
// The bug: code compares lockfile mtime vs dir mtime → false positive warning
|
|
115
|
+
// The fix: compare lockfile mtime vs marker file mtime → correctly ok
|
|
116
|
+
const dir = createProjectDir({
|
|
117
|
+
"package.json": JSON.stringify({ name: "test" }),
|
|
118
|
+
});
|
|
119
|
+
mkdirSync(join(dir, "node_modules"), { recursive: true });
|
|
120
|
+
|
|
121
|
+
// Simulate the exact bug: npm install with "up to date" rewrites the
|
|
122
|
+
// lockfile and the marker, but no packages are added/removed so the
|
|
123
|
+
// directory mtime should be old. We write the marker first (which
|
|
124
|
+
// bumps dir mtime), then force the dir mtime back to the past.
|
|
125
|
+
//
|
|
126
|
+
// Timeline: dir(T-120s) < lockfile(T-5s) ≈ marker(T-5s)
|
|
127
|
+
// Bug: code compares lockfile vs dir → false positive stale warning
|
|
128
|
+
// Fix: code compares lockfile vs marker → correctly reports ok
|
|
129
|
+
const dirTime = new Date(Date.now() - 120_000);
|
|
130
|
+
const installTime = new Date(Date.now() - 5_000);
|
|
131
|
+
|
|
132
|
+
// Write marker file (this bumps dir mtime as a side effect)
|
|
133
|
+
writeFileSync(join(dir, "node_modules", ".package-lock.json"), "{}");
|
|
134
|
+
utimesSync(join(dir, "node_modules", ".package-lock.json"), installTime, installTime);
|
|
135
|
+
|
|
136
|
+
// Force dir mtime back to the past — simulates no top-level entries changed
|
|
137
|
+
utimesSync(join(dir, "node_modules"), dirTime, dirTime);
|
|
138
|
+
|
|
139
|
+
// Lockfile written at install time (same as marker, or slightly after)
|
|
140
|
+
writeFileSync(join(dir, "package-lock.json"), "{}");
|
|
141
|
+
utimesSync(join(dir, "package-lock.json"), installTime, installTime);
|
|
142
|
+
|
|
143
|
+
cleanups.push(dir);
|
|
144
|
+
const results = runEnvironmentChecks(dir);
|
|
145
|
+
const depsCheck = results.find(r => r.name === "dependencies");
|
|
146
|
+
assert.ok(depsCheck !== undefined, "dependencies check runs");
|
|
147
|
+
assert.equal(depsCheck!.status, "ok", "npm marker newer than lockfile → not stale");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log("\n=== env: yarn marker file newer than lockfile → ok (#1974) ===");
|
|
151
|
+
{
|
|
152
|
+
const dir = createProjectDir({
|
|
153
|
+
"package.json": JSON.stringify({ name: "test" }),
|
|
154
|
+
});
|
|
155
|
+
mkdirSync(join(dir, "node_modules"), { recursive: true });
|
|
156
|
+
|
|
157
|
+
const dirTime = new Date(Date.now() - 120_000);
|
|
158
|
+
const installTime = new Date(Date.now() - 5_000);
|
|
159
|
+
|
|
160
|
+
writeFileSync(join(dir, "node_modules", ".yarn-integrity"), "{}");
|
|
161
|
+
utimesSync(join(dir, "node_modules", ".yarn-integrity"), installTime, installTime);
|
|
162
|
+
utimesSync(join(dir, "node_modules"), dirTime, dirTime);
|
|
163
|
+
|
|
164
|
+
writeFileSync(join(dir, "yarn.lock"), "");
|
|
165
|
+
utimesSync(join(dir, "yarn.lock"), installTime, installTime);
|
|
166
|
+
|
|
167
|
+
cleanups.push(dir);
|
|
168
|
+
const results = runEnvironmentChecks(dir);
|
|
169
|
+
const depsCheck = results.find(r => r.name === "dependencies");
|
|
170
|
+
assert.ok(depsCheck !== undefined, "dependencies check runs");
|
|
171
|
+
assert.equal(depsCheck!.status, "ok", "yarn marker newer than lockfile → not stale");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log("\n=== env: pnpm marker file newer than lockfile → ok (#1974) ===");
|
|
175
|
+
{
|
|
176
|
+
const dir = createProjectDir({
|
|
177
|
+
"package.json": JSON.stringify({ name: "test" }),
|
|
178
|
+
});
|
|
179
|
+
mkdirSync(join(dir, "node_modules"), { recursive: true });
|
|
180
|
+
|
|
181
|
+
const dirTime = new Date(Date.now() - 120_000);
|
|
182
|
+
const installTime = new Date(Date.now() - 5_000);
|
|
183
|
+
|
|
184
|
+
writeFileSync(join(dir, "node_modules", ".modules.yaml"), "{}");
|
|
185
|
+
utimesSync(join(dir, "node_modules", ".modules.yaml"), installTime, installTime);
|
|
186
|
+
utimesSync(join(dir, "node_modules"), dirTime, dirTime);
|
|
187
|
+
|
|
188
|
+
writeFileSync(join(dir, "pnpm-lock.yaml"), "");
|
|
189
|
+
utimesSync(join(dir, "pnpm-lock.yaml"), installTime, installTime);
|
|
190
|
+
|
|
191
|
+
cleanups.push(dir);
|
|
192
|
+
const results = runEnvironmentChecks(dir);
|
|
193
|
+
const depsCheck = results.find(r => r.name === "dependencies");
|
|
194
|
+
assert.ok(depsCheck !== undefined, "dependencies check runs");
|
|
195
|
+
assert.equal(depsCheck!.status, "ok", "pnpm marker newer than lockfile → not stale");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log("\n=== env: no marker file falls back to dir mtime → stale warning (#1974) ===");
|
|
199
|
+
{
|
|
200
|
+
// No marker file exists, lockfile newer than dir → should still warn
|
|
201
|
+
const dir = createProjectDir({
|
|
202
|
+
"package.json": JSON.stringify({ name: "test" }),
|
|
203
|
+
});
|
|
204
|
+
mkdirSync(join(dir, "node_modules"), { recursive: true });
|
|
205
|
+
|
|
206
|
+
const past = new Date(Date.now() - 60_000);
|
|
207
|
+
utimesSync(join(dir, "node_modules"), past, past);
|
|
208
|
+
|
|
209
|
+
writeFileSync(join(dir, "package-lock.json"), "{}");
|
|
210
|
+
// No marker file written — fallback to dir mtime comparison
|
|
211
|
+
|
|
212
|
+
cleanups.push(dir);
|
|
213
|
+
const results = runEnvironmentChecks(dir);
|
|
214
|
+
const depsCheck = results.find(r => r.name === "dependencies");
|
|
215
|
+
assert.ok(depsCheck !== undefined, "dependencies check runs");
|
|
216
|
+
assert.equal(depsCheck!.status, "warning", "no marker + lockfile newer → stale warning");
|
|
217
|
+
}
|
|
218
|
+
|
|
105
219
|
// ── Env File Check ─────────────────────────────────────────────────
|
|
106
220
|
test('env: .env.example without .env detected', () => {
|
|
107
221
|
const dir = createProjectDir({
|
|
@@ -15,6 +15,7 @@ import { tmpdir } from "node:os";
|
|
|
15
15
|
import test from "node:test";
|
|
16
16
|
import assert from "node:assert/strict";
|
|
17
17
|
import { runGSDDoctor } from "../doctor.ts";
|
|
18
|
+
import { closeDatabase } from "../gsd-db.ts";
|
|
18
19
|
|
|
19
20
|
function makeTmp(name: string): string {
|
|
20
21
|
const dir = join(tmpdir(), `doctor-fixlevel-${name}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
@@ -112,6 +113,70 @@ test("fixLevel:all — no reconciliation issue codes are reported", async (t) =>
|
|
|
112
113
|
assert.ok(roadmapContent.includes("- [ ] **S01"), "roadmap should remain unchecked");
|
|
113
114
|
});
|
|
114
115
|
|
|
116
|
+
test("legacy roadmap fallback: future slices are treated as pending, active slice is not", async (t) => {
|
|
117
|
+
const tmp = makeTmp("legacy-pending-fallback");
|
|
118
|
+
t.after(() => {
|
|
119
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
120
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Force the legacy parser branch.
|
|
124
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
125
|
+
|
|
126
|
+
const gsd = join(tmp, ".gsd");
|
|
127
|
+
const m = join(gsd, "milestones", "M001");
|
|
128
|
+
const s01 = join(m, "slices", "S01", "tasks");
|
|
129
|
+
mkdirSync(s01, { recursive: true });
|
|
130
|
+
|
|
131
|
+
writeFileSync(join(m, "M001-ROADMAP.md"), `# M001: Test
|
|
132
|
+
|
|
133
|
+
## Slices
|
|
134
|
+
|
|
135
|
+
- [x] **S01: Done Slice** \`risk:low\` \`depends:[]\`
|
|
136
|
+
> Done
|
|
137
|
+
- [ ] **S02: Active Slice** \`risk:medium\` \`depends:[S01]\`
|
|
138
|
+
> In progress
|
|
139
|
+
- [ ] **S03: Future Slice** \`risk:low\` \`depends:[S02]\`
|
|
140
|
+
> Later
|
|
141
|
+
- [ ] **S04: Future Slice Two** \`risk:low\` \`depends:[S03]\`
|
|
142
|
+
> Later
|
|
143
|
+
`);
|
|
144
|
+
|
|
145
|
+
writeFileSync(join(m, "slices", "S01", "S01-PLAN.md"), `# S01: Done Slice
|
|
146
|
+
|
|
147
|
+
**Goal:** done
|
|
148
|
+
|
|
149
|
+
## Tasks
|
|
150
|
+
|
|
151
|
+
- [x] **T01: Done task** \`est:5m\`
|
|
152
|
+
`);
|
|
153
|
+
|
|
154
|
+
// Active slice exists in state/registry but has no directory yet — this should
|
|
155
|
+
// still be reported as a real error, while future untouched slices should be skipped.
|
|
156
|
+
const report = await runGSDDoctor(tmp, { scope: "M001" });
|
|
157
|
+
const missingSliceDirUnits = report.issues
|
|
158
|
+
.filter(i => i.code === "missing_slice_dir")
|
|
159
|
+
.map(i => i.unitId)
|
|
160
|
+
.sort();
|
|
161
|
+
|
|
162
|
+
assert.deepStrictEqual(
|
|
163
|
+
missingSliceDirUnits,
|
|
164
|
+
["M001/S02"],
|
|
165
|
+
"legacy fallback should only report the active slice, not future unstarted slices",
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const missingTasksDirUnits = report.issues
|
|
169
|
+
.filter(i => i.code === "missing_tasks_dir")
|
|
170
|
+
.map(i => i.unitId)
|
|
171
|
+
.sort();
|
|
172
|
+
|
|
173
|
+
assert.deepStrictEqual(
|
|
174
|
+
missingTasksDirUnits,
|
|
175
|
+
[],
|
|
176
|
+
"future slices without directories should be skipped before missing_tasks_dir checks",
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
115
180
|
test("fixLevel:all — delimiter_in_title still fixable", async (t) => {
|
|
116
181
|
const tmp = makeTmp("delimiter-fix");
|
|
117
182
|
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
@@ -141,7 +206,6 @@ test("fixLevel:all — delimiter_in_title still fixable", async (t) => {
|
|
|
141
206
|
|
|
142
207
|
const report = await runGSDDoctor(tmp, { fix: true });
|
|
143
208
|
|
|
144
|
-
const delimiterIssues = report.issues.filter(i => i.code === "delimiter_in_title");
|
|
145
209
|
// The milestone-level delimiter is auto-fixed, but the report may or may not include it
|
|
146
210
|
// depending on whether it was fixed successfully. Just verify it ran without crashing.
|
|
147
211
|
assert.ok(report.issues !== undefined, "doctor produces a report");
|
|
@@ -145,6 +145,56 @@ describe('doctor-git', async () => {
|
|
|
145
145
|
} else {
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// ─── Test 1b: Orphaned worktree fix when cwd is inside worktree (#1946) ──
|
|
149
|
+
// Reproduces the deadlock: if process.cwd() is inside the orphaned worktree,
|
|
150
|
+
// the doctor must chdir out before removing it — not skip the removal.
|
|
151
|
+
if (process.platform !== "win32") {
|
|
152
|
+
console.log("\n=== orphaned_auto_worktree (cwd inside worktree) ===");
|
|
153
|
+
{
|
|
154
|
+
const dir = createRepoWithCompletedMilestone();
|
|
155
|
+
cleanups.push(dir);
|
|
156
|
+
|
|
157
|
+
// Create worktree with milestone/M001 branch under .gsd/worktrees/
|
|
158
|
+
mkdirSync(join(dir, ".gsd", "worktrees"), { recursive: true });
|
|
159
|
+
run("git worktree add -b milestone/M001 .gsd/worktrees/M001", dir);
|
|
160
|
+
|
|
161
|
+
const wtPath = realpathSync(join(dir, ".gsd", "worktrees", "M001"));
|
|
162
|
+
|
|
163
|
+
// Simulate the deadlock: set cwd inside the orphaned worktree
|
|
164
|
+
const previousCwd = process.cwd();
|
|
165
|
+
process.chdir(wtPath);
|
|
166
|
+
try {
|
|
167
|
+
const fixed = await runGSDDoctor(dir, { fix: true, isolationMode: "worktree" });
|
|
168
|
+
|
|
169
|
+
// The fix must NOT skip removal — it should chdir out and remove
|
|
170
|
+
assert.ok(
|
|
171
|
+
!fixed.fixesApplied.some(f => f.includes("skipped removing worktree")),
|
|
172
|
+
"does NOT skip removal when cwd is inside worktree",
|
|
173
|
+
);
|
|
174
|
+
assert.ok(
|
|
175
|
+
fixed.fixesApplied.some(f => f.includes("removed orphaned worktree")),
|
|
176
|
+
"removes orphaned worktree even when cwd was inside it",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Verify worktree is gone
|
|
180
|
+
const wtList = run("git worktree list", dir);
|
|
181
|
+
assert.ok(!wtList.includes("milestone/M001"), "worktree removed after fix with cwd inside");
|
|
182
|
+
|
|
183
|
+
// Verify cwd was moved out (should be basePath, not still inside worktree)
|
|
184
|
+
const newCwd = process.cwd();
|
|
185
|
+
assert.ok(
|
|
186
|
+
!newCwd.startsWith(wtPath),
|
|
187
|
+
"cwd moved out of worktree after fix",
|
|
188
|
+
);
|
|
189
|
+
} finally {
|
|
190
|
+
// Restore cwd — the worktree dir may be gone, so chdir to previousCwd
|
|
191
|
+
try { process.chdir(previousCwd); } catch { process.chdir(dir); }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
console.log("\n=== orphaned_auto_worktree (cwd inside worktree — skipped on Windows) ===");
|
|
196
|
+
}
|
|
197
|
+
|
|
148
198
|
// ─── Test 2: Stale milestone branch detection & fix ────────────────
|
|
149
199
|
// Skip on Windows: git branch glob matching and path resolution
|
|
150
200
|
// behave differently in Windows temp dirs.
|