gsd-pi 2.79.0-dev.ece5fd8ba → 2.80.0
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/loader.js +0 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/contracts.js +1 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +55 -6
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +45 -52
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -0
- package/dist/resources/extensions/gsd/auto.js +159 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/gsd-db.js +34 -1
- package/dist/resources/extensions/gsd/post-execution-checks.js +25 -6
- package/dist/resources/extensions/shared/interview-ui.js +15 -4
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-ff639266d978f2a0.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/package.json +2 -2
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/contracts.ts +87 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -3
- package/src/resources/extensions/gsd/auto/orchestrator.ts +161 -0
- package/src/resources/extensions/gsd/auto/phases.ts +82 -8
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +42 -50
- package/src/resources/extensions/gsd/auto-runtime-state.ts +7 -0
- package/src/resources/extensions/gsd/auto.ts +167 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +14 -1
- package/src/resources/extensions/gsd/gsd-db.ts +35 -1
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/post-execution-checks.ts +31 -6
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +46 -0
- package/src/resources/extensions/shared/interview-ui.ts +18 -5
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +41 -0
- package/dist/web/standalone/.next/static/chunks/app/page-fab3ebb85b006001.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- /package/dist/web/standalone/.next/static/{TzEVJ1Lh8vbez4n4Q9TqQ → V-3Ehy4B24f9FCGiLPWIM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{TzEVJ1Lh8vbez4n4Q9TqQ → V-3Ehy4B24f9FCGiLPWIM}/_ssgManifest.js +0 -0
|
@@ -399,6 +399,140 @@ test("runDispatch pauses when complete-milestone summary exists on disk but the
|
|
|
399
399
|
assert.equal(stopCalls, 0, "mismatch pause should not hard-stop the loop");
|
|
400
400
|
});
|
|
401
401
|
|
|
402
|
+
test("runDispatch clears stuck state after Level 1 artifact recovery", async (t) => {
|
|
403
|
+
const capture = createEventCapture();
|
|
404
|
+
let invalidateCalls = 0;
|
|
405
|
+
let stopCalls = 0;
|
|
406
|
+
const base = join(tmpdir(), `gsd-stuck-plan-${randomUUID()}`);
|
|
407
|
+
t.after(() => {
|
|
408
|
+
closeDatabase();
|
|
409
|
+
rmSync(base, { recursive: true, force: true });
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
413
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
414
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
415
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
416
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
417
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
|
|
418
|
+
insertTask({ id: "T01", milestoneId: "M001", sliceId: "S01", title: "First task", status: "pending" });
|
|
419
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), "# S01\n\n## Tasks\n\n- [ ] **T01: First task** `est:1h`\n");
|
|
420
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n");
|
|
421
|
+
|
|
422
|
+
const deps = makeMockDeps(capture, {
|
|
423
|
+
invalidateAllCaches: () => { invalidateCalls++; },
|
|
424
|
+
stopAuto: async () => { stopCalls++; },
|
|
425
|
+
resolveDispatch: async () => ({
|
|
426
|
+
action: "dispatch" as const,
|
|
427
|
+
unitType: "plan-slice",
|
|
428
|
+
unitId: "M001/S01",
|
|
429
|
+
prompt: "plan the slice",
|
|
430
|
+
matchedRule: "planning → plan-slice",
|
|
431
|
+
}),
|
|
432
|
+
});
|
|
433
|
+
const ic = makeIC(deps, {
|
|
434
|
+
s: {
|
|
435
|
+
...makeSession(),
|
|
436
|
+
basePath: base,
|
|
437
|
+
originalBasePath: base,
|
|
438
|
+
} as any,
|
|
439
|
+
});
|
|
440
|
+
const preData: PreDispatchData = {
|
|
441
|
+
state: {
|
|
442
|
+
phase: "planning",
|
|
443
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
444
|
+
activeSlice: { id: "S01", title: "Slice" },
|
|
445
|
+
registry: [{ id: "M001", status: "active" }],
|
|
446
|
+
blockers: [],
|
|
447
|
+
} as any,
|
|
448
|
+
mid: "M001",
|
|
449
|
+
midTitle: "Test Milestone",
|
|
450
|
+
};
|
|
451
|
+
const loopState: LoopState = {
|
|
452
|
+
recentUnits: [
|
|
453
|
+
{ key: "plan-slice/M001/S01" },
|
|
454
|
+
{ key: "plan-slice/M001/S01" },
|
|
455
|
+
],
|
|
456
|
+
stuckRecoveryAttempts: 0,
|
|
457
|
+
consecutiveFinalizeTimeouts: 0,
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const result = await runDispatch(ic, preData, loopState);
|
|
461
|
+
|
|
462
|
+
assert.equal(result.action, "continue");
|
|
463
|
+
assert.equal(invalidateCalls, 1, "Level 1 artifact recovery should invalidate caches");
|
|
464
|
+
assert.equal(stopCalls, 0, "Level 1 artifact recovery should not hard-stop");
|
|
465
|
+
assert.deepEqual(loopState.recentUnits, [], "Level 1 artifact recovery should clear the stuck window");
|
|
466
|
+
assert.equal(loopState.stuckRecoveryAttempts, 0, "Level 1 artifact recovery should reset the recovery counter");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
test("runDispatch escapes Level 2 stuck stop when artifact verifies after cache invalidation", async (t) => {
|
|
470
|
+
const capture = createEventCapture();
|
|
471
|
+
let invalidateCalls = 0;
|
|
472
|
+
let stopCalls = 0;
|
|
473
|
+
const base = join(tmpdir(), `gsd-stuck-plan-l2-${randomUUID()}`);
|
|
474
|
+
t.after(() => {
|
|
475
|
+
closeDatabase();
|
|
476
|
+
rmSync(base, { recursive: true, force: true });
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
480
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
481
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
482
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
483
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
484
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
|
|
485
|
+
insertTask({ id: "T01", milestoneId: "M001", sliceId: "S01", title: "First task", status: "pending" });
|
|
486
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), "# S01\n\n## Tasks\n\n- [ ] **T01: First task** `est:1h`\n");
|
|
487
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n");
|
|
488
|
+
|
|
489
|
+
const deps = makeMockDeps(capture, {
|
|
490
|
+
invalidateAllCaches: () => { invalidateCalls++; },
|
|
491
|
+
stopAuto: async () => { stopCalls++; },
|
|
492
|
+
resolveDispatch: async () => ({
|
|
493
|
+
action: "dispatch" as const,
|
|
494
|
+
unitType: "plan-slice",
|
|
495
|
+
unitId: "M001/S01",
|
|
496
|
+
prompt: "plan the slice",
|
|
497
|
+
matchedRule: "planning → plan-slice",
|
|
498
|
+
}),
|
|
499
|
+
});
|
|
500
|
+
const ic = makeIC(deps, {
|
|
501
|
+
s: {
|
|
502
|
+
...makeSession(),
|
|
503
|
+
basePath: base,
|
|
504
|
+
originalBasePath: base,
|
|
505
|
+
} as any,
|
|
506
|
+
});
|
|
507
|
+
const preData: PreDispatchData = {
|
|
508
|
+
state: {
|
|
509
|
+
phase: "planning",
|
|
510
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
511
|
+
activeSlice: { id: "S01", title: "Slice" },
|
|
512
|
+
registry: [{ id: "M001", status: "active" }],
|
|
513
|
+
blockers: [],
|
|
514
|
+
} as any,
|
|
515
|
+
mid: "M001",
|
|
516
|
+
midTitle: "Test Milestone",
|
|
517
|
+
};
|
|
518
|
+
const loopState: LoopState = {
|
|
519
|
+
recentUnits: [
|
|
520
|
+
{ key: "plan-slice/M001/S01" },
|
|
521
|
+
{ key: "plan-slice/M001/S01" },
|
|
522
|
+
],
|
|
523
|
+
stuckRecoveryAttempts: 1,
|
|
524
|
+
consecutiveFinalizeTimeouts: 0,
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const result = await runDispatch(ic, preData, loopState);
|
|
528
|
+
|
|
529
|
+
assert.equal(result.action, "continue");
|
|
530
|
+
assert.equal(invalidateCalls, 1, "Level 2 escape should invalidate caches before rechecking artifacts");
|
|
531
|
+
assert.equal(stopCalls, 0, "verified artifacts should escape Level 2 hard stop");
|
|
532
|
+
assert.deepEqual(loopState.recentUnits, [], "Level 2 artifact escape should clear the stuck window");
|
|
533
|
+
assert.equal(loopState.stuckRecoveryAttempts, 0, "Level 2 artifact escape should reset the recovery counter");
|
|
534
|
+
});
|
|
535
|
+
|
|
402
536
|
test("runUnitPhase emits unit-start and unit-end with causedBy reference", async () => {
|
|
403
537
|
const capture = createEventCapture();
|
|
404
538
|
|
|
@@ -64,6 +64,7 @@ test("readPausedSessionMetadata round-trips a real PausedSessionMetadata payload
|
|
|
64
64
|
activeRunDir: null,
|
|
65
65
|
autoStartTime: Date.now(),
|
|
66
66
|
milestoneLock: null,
|
|
67
|
+
pauseReason: "Blocked: waiting for UAT",
|
|
67
68
|
};
|
|
68
69
|
setRuntimeKv("global", "", PAUSED_SESSION_KV_KEY, meta);
|
|
69
70
|
|
|
@@ -73,6 +74,7 @@ test("readPausedSessionMetadata round-trips a real PausedSessionMetadata payload
|
|
|
73
74
|
assert.equal(loaded!.unitType, "plan-slice");
|
|
74
75
|
assert.equal(loaded!.unitId, "M001/S01");
|
|
75
76
|
assert.equal(loaded!.sessionFile, "/tmp/session.jsonl");
|
|
77
|
+
assert.equal(loaded!.pauseReason, "Blocked: waiting for UAT");
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
test("readPausedSessionMetadata auto-deletes stale pseudo-milestone pause rows", (t) => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// GSD Extension — Plan-slice tool integration tests.
|
|
2
|
+
|
|
1
3
|
import test from 'node:test';
|
|
2
4
|
import assert from 'node:assert/strict';
|
|
3
5
|
import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
@@ -8,6 +10,7 @@ import { openDatabase, closeDatabase, insertMilestone, insertSlice, getSlice, ge
|
|
|
8
10
|
import { handlePlanSlice } from '../tools/plan-slice.ts';
|
|
9
11
|
import { parsePlan } from '../parsers-legacy.ts';
|
|
10
12
|
import { parseTaskPlanFile } from '../files.ts';
|
|
13
|
+
import { deriveState, invalidateStateCache } from '../state.ts';
|
|
11
14
|
|
|
12
15
|
function makeTmpBase(): string {
|
|
13
16
|
const base = mkdtempSync(join(tmpdir(), 'gsd-plan-slice-'));
|
|
@@ -98,6 +101,30 @@ test('handlePlanSlice writes slice/task planning state and renders plan artifact
|
|
|
98
101
|
}
|
|
99
102
|
});
|
|
100
103
|
|
|
104
|
+
test('handlePlanSlice advances DB-derived state out of planning immediately', async () => {
|
|
105
|
+
const base = makeTmpBase();
|
|
106
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
seedParentSlice();
|
|
110
|
+
|
|
111
|
+
invalidateStateCache();
|
|
112
|
+
const before = await deriveState(base);
|
|
113
|
+
assert.equal(before.phase, 'planning');
|
|
114
|
+
assert.equal(before.progress?.tasks?.total, 0);
|
|
115
|
+
|
|
116
|
+
const result = await handlePlanSlice(validParams(), base);
|
|
117
|
+
assert.ok(!('error' in result), `unexpected error: ${'error' in result ? result.error : ''}`);
|
|
118
|
+
|
|
119
|
+
invalidateStateCache();
|
|
120
|
+
const after = await deriveState(base);
|
|
121
|
+
assert.notEqual(after.phase, 'planning');
|
|
122
|
+
assert.equal(after.progress?.tasks?.total, 2);
|
|
123
|
+
} finally {
|
|
124
|
+
cleanup(base);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
101
128
|
test('handlePlanSlice leaves omitted enrichment fields empty instead of rendering placeholders', async () => {
|
|
102
129
|
const base = makeTmpBase();
|
|
103
130
|
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
@@ -304,6 +304,30 @@ describe("resolveImportPath", () => {
|
|
|
304
304
|
assert.ok(!result.exists);
|
|
305
305
|
assert.equal(result.resolvedPath, null);
|
|
306
306
|
});
|
|
307
|
+
|
|
308
|
+
test("resolves dotted TS module stem like .server via extension probing", (t) => {
|
|
309
|
+
const dir = mkdtempSync(join(tmpdir(), "post-exec-test-server-dot-"));
|
|
310
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
311
|
+
mkdirSync(join(dir, "src"), { recursive: true });
|
|
312
|
+
writeFileSync(join(dir, "src", "route.server.ts"), "export {};\n");
|
|
313
|
+
writeFileSync(join(dir, "src", "main.ts"), "");
|
|
314
|
+
|
|
315
|
+
const result = resolveImportPath("./route.server", "src/main.ts", dir);
|
|
316
|
+
assert.ok(result.exists);
|
|
317
|
+
assert.ok(result.resolvedPath?.endsWith("route.server.ts"));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("missing unknown explicit extension does not match code-extension shadow", (t) => {
|
|
321
|
+
const dir = mkdtempSync(join(tmpdir(), "post-exec-test-unknown-shadow-"));
|
|
322
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
323
|
+
mkdirSync(join(dir, "src"), { recursive: true });
|
|
324
|
+
writeFileSync(join(dir, "src", "video.mp4.ts"), "export {};\n");
|
|
325
|
+
writeFileSync(join(dir, "src", "main.ts"), "");
|
|
326
|
+
|
|
327
|
+
const result = resolveImportPath("./video.mp4", "src/main.ts", dir);
|
|
328
|
+
assert.ok(!result.exists);
|
|
329
|
+
assert.equal(result.resolvedPath, null);
|
|
330
|
+
});
|
|
307
331
|
});
|
|
308
332
|
|
|
309
333
|
// ─── Import Resolution Check Tests ───────────────────────────────────────────
|
|
@@ -334,6 +358,28 @@ describe("checkImportResolution", () => {
|
|
|
334
358
|
}
|
|
335
359
|
});
|
|
336
360
|
|
|
361
|
+
test("ignores generated React Router +types imports", () => {
|
|
362
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
363
|
+
mkdirSync(tempDir, { recursive: true });
|
|
364
|
+
mkdirSync(join(tempDir, "app", "routes"), { recursive: true });
|
|
365
|
+
writeFileSync(
|
|
366
|
+
join(tempDir, "app", "routes", "root.tsx"),
|
|
367
|
+
"import type { Route } from './+types/root';\nexport default function Root() { return null; }"
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const task = createTask({
|
|
372
|
+
id: "T01",
|
|
373
|
+
key_files: ["app/routes/root.tsx"],
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
377
|
+
assert.deepEqual(results, []);
|
|
378
|
+
} finally {
|
|
379
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
337
383
|
test("fails when import doesn't resolve", () => {
|
|
338
384
|
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
339
385
|
mkdirSync(tempDir, { recursive: true });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD2 — Shared interview round UI widget
|
|
1
2
|
/**
|
|
2
3
|
* Shared interview round UI widget.
|
|
3
4
|
*
|
|
@@ -224,12 +225,24 @@ export async function showInterviewRound(
|
|
|
224
225
|
let showingExitConfirm = false;
|
|
225
226
|
let exitCursor = 0; // 0 = keep going (default), 1 = end interview
|
|
226
227
|
let cachedLines: string[] | undefined;
|
|
228
|
+
let completed = false;
|
|
229
|
+
let removeAbortListener: (() => void) | undefined;
|
|
230
|
+
|
|
231
|
+
function finish(result: RoundResult) {
|
|
232
|
+
if (completed) return;
|
|
233
|
+
completed = true;
|
|
234
|
+
removeAbortListener?.();
|
|
235
|
+
done(result);
|
|
236
|
+
}
|
|
227
237
|
|
|
228
238
|
// External cancellation (e.g. remote channel won the race)
|
|
229
239
|
if (opts.signal) {
|
|
230
|
-
const onAbort = () =>
|
|
240
|
+
const onAbort = () => finish({ endInterview: false, answers: {} });
|
|
231
241
|
if (opts.signal.aborted) { onAbort(); }
|
|
232
|
-
else {
|
|
242
|
+
else {
|
|
243
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
244
|
+
removeAbortListener = () => opts.signal?.removeEventListener("abort", onAbort);
|
|
245
|
+
}
|
|
233
246
|
}
|
|
234
247
|
|
|
235
248
|
// Editor is created once; editorTheme comes from the design system
|
|
@@ -312,7 +325,7 @@ export async function showInterviewRound(
|
|
|
312
325
|
|
|
313
326
|
function submit() {
|
|
314
327
|
saveEditorToState();
|
|
315
|
-
|
|
328
|
+
finish(buildResult());
|
|
316
329
|
}
|
|
317
330
|
|
|
318
331
|
function goNextOrSubmit() {
|
|
@@ -355,10 +368,10 @@ export async function showInterviewRound(
|
|
|
355
368
|
if (matchesKey(data, Key.up) || matchesKey(data, Key.left)) { exitCursor = 0; refresh(); return; }
|
|
356
369
|
if (matchesKey(data, Key.down) || matchesKey(data, Key.right)) { exitCursor = 1; refresh(); return; }
|
|
357
370
|
if (data === "1") { showingExitConfirm = false; refresh(); return; }
|
|
358
|
-
if (data === "2") {
|
|
371
|
+
if (data === "2") { finish({ endInterview: false, answers: {} }); return; }
|
|
359
372
|
if (matchesKey(data, Key.enter) || matchesKey(data, Key.space)) {
|
|
360
373
|
if (exitCursor === 0) { showingExitConfirm = false; refresh(); }
|
|
361
|
-
else {
|
|
374
|
+
else { finish({ endInterview: false, answers: {} }); }
|
|
362
375
|
return;
|
|
363
376
|
}
|
|
364
377
|
if (matchesKey(data, Key.escape)) { showingExitConfirm = false; refresh(); return; }
|
|
@@ -139,4 +139,45 @@ describe("interview-ui notes loop regression (#3502)", () => {
|
|
|
139
139
|
assert.ok(answer, "answer for q1 should exist");
|
|
140
140
|
assert.equal(answer.selected, "Web App");
|
|
141
141
|
});
|
|
142
|
+
|
|
143
|
+
it("ignores abort signals after a submitted answer", async () => {
|
|
144
|
+
const controller = new AbortController();
|
|
145
|
+
const doneCalls: RoundResult[] = [];
|
|
146
|
+
let widget: { handleInput(input: string): void } | undefined;
|
|
147
|
+
|
|
148
|
+
const resultPromise = showInterviewRound(questions, { signal: controller.signal }, {
|
|
149
|
+
ui: {
|
|
150
|
+
custom: (factory: any) => new Promise<RoundResult>((resolve) => {
|
|
151
|
+
const mockTui = { requestRender: () => {} };
|
|
152
|
+
const mockTheme = {
|
|
153
|
+
fg: (_c: string, t: string) => t,
|
|
154
|
+
bold: (t: string) => t,
|
|
155
|
+
dim: (t: string) => t,
|
|
156
|
+
italic: (t: string) => t,
|
|
157
|
+
strikethrough: (t: string) => t,
|
|
158
|
+
accent: (t: string) => t,
|
|
159
|
+
success: (t: string) => t,
|
|
160
|
+
warning: (t: string) => t,
|
|
161
|
+
error: (t: string) => t,
|
|
162
|
+
info: (t: string) => t,
|
|
163
|
+
muted: (t: string) => t,
|
|
164
|
+
dimmed: (t: string) => t,
|
|
165
|
+
};
|
|
166
|
+
widget = factory(mockTui, mockTheme, {}, (result: RoundResult) => {
|
|
167
|
+
doneCalls.push(result);
|
|
168
|
+
resolve(result);
|
|
169
|
+
});
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
} as any);
|
|
173
|
+
|
|
174
|
+
assert.ok(widget, "widget should be created synchronously");
|
|
175
|
+
widget.handleInput(ENTER);
|
|
176
|
+
widget.handleInput(ENTER);
|
|
177
|
+
controller.abort();
|
|
178
|
+
|
|
179
|
+
const result = await resultPromise;
|
|
180
|
+
assert.equal(doneCalls.length, 1, "abort after submit must not emit a second empty result");
|
|
181
|
+
assert.deepEqual(result.answers.q1, { selected: "Web App", notes: "" });
|
|
182
|
+
});
|
|
142
183
|
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8974],{2600:(e,t,n)=>{Promise.resolve().then(n.bind(n,66919))},5214:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"workAsyncStorage",{enumerable:!0,get:function(){return r.workAsyncStorageInstance}});let r=n(17828)},17828:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"workAsyncStorageInstance",{enumerable:!0,get:function(){return r}});let r=(0,n(64054).createAsyncLocalStorage)()},21957:(e,t,n)=>{"use strict";function r({moduleIds:e}){return null}Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"PreloadChunks",{enumerable:!0,get:function(){return r}}),n(95155),n(47650),n(5214),n(2451),n(53887)},37206:(e,t,n)=>{"use strict";n.d(t,{default:()=>u.a});var r=n(75707),u=n.n(r)},41112:(e,t,n)=>{"use strict";function r({reason:e,children:t}){return t}Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"BailoutToCSR",{enumerable:!0,get:function(){return r}}),n(1980)},64054:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={bindSnapshot:function(){return s},createAsyncLocalStorage:function(){return a},createSnapshot:function(){return i}};for(var r in n)Object.defineProperty(t,r,{enumerable:!0,get:n[r]});let u=Object.defineProperty(Error("Invariant: AsyncLocalStorage accessed in runtime where it is not available"),"__NEXT_ERROR_CODE",{value:"E504",enumerable:!1,configurable:!0});class l{disable(){throw u}getStore(){}run(){throw u}exit(){throw u}enterWith(){throw u}static bind(e){return e}}let o="u">typeof globalThis&&globalThis.AsyncLocalStorage;function a(){return o?new o:new l}function s(e){return o?o.bind(e):l.bind(e)}function i(){return o?o.snapshot():function(e,...t){return e(...t)}}},66919:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(95155);let u=(0,n(37206).default)(()=>Promise.all([n.e(1838),n.e(4986),n.e(2059),n.e(8336)]).then(n.bind(n,78336)).then(e=>e.GSDAppShell),{loadableGenerated:{webpack:()=>[78336]},ssr:!1,loading:()=>(0,r.jsx)("div",{className:"flex h-screen items-center justify-center bg-background text-sm text-muted-foreground",children:"Loading workspace…"})});function l(){return(0,r.jsx)(u,{})}},68635:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}});let r=n(95155),u=n(12115),l=n(41112);function o(e){return{default:e&&"default"in e?e.default:e}}n(21957);let a={loader:()=>Promise.resolve(o(()=>null)),loading:null,ssr:!0},s=function(e){let t={...a,...e},n=(0,u.lazy)(()=>t.loader().then(o)),s=t.loading;function i(e){let o=s?(0,r.jsx)(s,{isLoading:!0,pastDelay:!0,error:null}):null,a=!t.ssr||!!t.loading,i=a?u.Suspense:u.Fragment,c=t.ssr?(0,r.jsxs)(r.Fragment,{children:[null,(0,r.jsx)(n,{...e})]}):(0,r.jsx)(l.BailoutToCSR,{reason:"next/dynamic",children:(0,r.jsx)(n,{...e})});return(0,r.jsx)(i,{...a?{fallback:o}:{},children:c})}return i.displayName="LoadableComponent",i}},75707:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return u}});let r=n(73623)._(n(68635));function u(e,t){let n={};"function"==typeof e&&(n.loader=e);let u={...n,...t};return(0,r.default)({...u,modules:u.loadableGenerated?.modules})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},e=>{e.O(0,[8441,3794,7358],()=>e(e.s=2600)),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7358],{19393:()=>{},55548:(e,s,n)=>{Promise.resolve().then(n.t.bind(n,27123,23)),Promise.resolve().then(n.t.bind(n,61304,23)),Promise.resolve().then(n.t.bind(n,78616,23)),Promise.resolve().then(n.t.bind(n,64777,23)),Promise.resolve().then(n.t.bind(n,57121,23)),Promise.resolve().then(n.t.bind(n,74581,23)),Promise.resolve().then(n.t.bind(n,90484,23)),Promise.resolve().then(n.bind(n,86869))}},e=>{var s=s=>e(e.s=s);e.O(0,[8441,3794],()=>(s(83861),s(55548))),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9337],{43946:(e,s,_)=>{Promise.resolve().then(_.t.bind(_,27123,23))}},e=>{e.O(0,[8441,3794,7358],()=>e(e.s=43946)),_N_E=e.O()}]);
|
|
File without changes
|
|
File without changes
|