gsd-pi 2.80.0-dev.2be7caf18 → 2.80.0-dev.42bd34838
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +29 -15
- package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +13 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
- package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
- package/dist/resources/extensions/gsd/auto.js +61 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +21 -2
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
- package/dist/resources/extensions/gsd/git-service.js +36 -4
- package/dist/resources/extensions/gsd/gsd-db.js +46 -13
- package/dist/resources/extensions/gsd/guided-flow.js +19 -4
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +14 -13
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +33 -17
- 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 +11 -11
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +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.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 +11 -11
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +3 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +9 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +5 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +5 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/phases.ts +35 -20
- package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +18 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
- package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
- package/src/resources/extensions/gsd/auto.ts +69 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +21 -1
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
- package/src/resources/extensions/gsd/git-service.ts +46 -8
- package/src/resources/extensions/gsd/gsd-db.ts +50 -13
- package/src/resources/extensions/gsd/guided-flow.ts +32 -4
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +14 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +63 -1
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +11 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +36 -15
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → tht0ltP8EIjsohPOA_WZf}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → tht0ltP8EIjsohPOA_WZf}/_ssgManifest.js +0 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
markFailed,
|
|
22
22
|
markStuck,
|
|
23
23
|
markCanceled,
|
|
24
|
+
markLatestActiveForWorkerCanceled,
|
|
24
25
|
getRecentForUnit,
|
|
25
26
|
getLatestForUnit,
|
|
26
27
|
} from "../db/unit-dispatches.ts";
|
|
@@ -196,6 +197,35 @@ test("markStuck and markCanceled set their respective statuses", (t) => {
|
|
|
196
197
|
assert.equal(getLatestForUnit("M001/S01/T01")!.status, "canceled");
|
|
197
198
|
});
|
|
198
199
|
|
|
200
|
+
test("markLatestActiveForWorkerCanceled cancels only the latest active dispatch for a worker", (t) => {
|
|
201
|
+
const base = makeBase();
|
|
202
|
+
t.after(() => cleanup(base));
|
|
203
|
+
const { workerId, leaseToken } = setup(base);
|
|
204
|
+
|
|
205
|
+
const first = recordDispatchClaim({
|
|
206
|
+
traceId: "tc-1", workerId, milestoneLeaseToken: leaseToken,
|
|
207
|
+
milestoneId: "M001", unitType: "plan-slice", unitId: "M001/S01",
|
|
208
|
+
});
|
|
209
|
+
assert.equal(first.ok, true);
|
|
210
|
+
if (!first.ok) return;
|
|
211
|
+
markCompleted(first.dispatchId);
|
|
212
|
+
|
|
213
|
+
const second = recordDispatchClaim({
|
|
214
|
+
traceId: "tc-2", workerId, milestoneLeaseToken: leaseToken,
|
|
215
|
+
milestoneId: "M001", unitType: "run-task", unitId: "M001/S01/T01",
|
|
216
|
+
});
|
|
217
|
+
assert.equal(second.ok, true);
|
|
218
|
+
if (!second.ok) return;
|
|
219
|
+
markRunning(second.dispatchId);
|
|
220
|
+
|
|
221
|
+
assert.equal(markLatestActiveForWorkerCanceled(workerId, "signal-exit"), true);
|
|
222
|
+
assert.equal(getLatestForUnit("M001/S01")!.status, "completed");
|
|
223
|
+
const latest = getLatestForUnit("M001/S01/T01")!;
|
|
224
|
+
assert.equal(latest.status, "canceled");
|
|
225
|
+
assert.equal(latest.exit_reason, "signal-exit");
|
|
226
|
+
assert.equal(markLatestActiveForWorkerCanceled(workerId, "signal-exit"), false);
|
|
227
|
+
});
|
|
228
|
+
|
|
199
229
|
test("terminal transitions do not overwrite an already terminal dispatch", (t) => {
|
|
200
230
|
const base = makeBase();
|
|
201
231
|
t.after(() => cleanup(base));
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
readUnitRuntimeRecord,
|
|
10
10
|
writeUnitRuntimeRecord,
|
|
11
11
|
} from "../unit-runtime.ts";
|
|
12
|
+
import { closeDatabase, insertMilestone, insertSlice, insertTask, openDatabase } from "../gsd-db.ts";
|
|
12
13
|
import { clearPathCache } from '../paths.ts';
|
|
13
14
|
import { test } from 'node:test';
|
|
14
15
|
import assert from 'node:assert/strict';
|
|
@@ -72,6 +73,35 @@ console.log("\n=== runtime record cleanup ===");
|
|
|
72
73
|
assert.deepStrictEqual(loaded, null, "record removed");
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
console.log("\n=== execute-task durability trusts closed DB task status ===");
|
|
77
|
+
{
|
|
78
|
+
const dbBase = mkdtempSync(join(tmpdir(), "gsd-unit-runtime-db-test-"));
|
|
79
|
+
mkdirSync(join(dbBase, ".gsd", "milestones", "M300", "slices", "S01", "tasks"), { recursive: true });
|
|
80
|
+
try {
|
|
81
|
+
openDatabase(join(dbBase, ".gsd", "gsd.db"));
|
|
82
|
+
insertMilestone({ id: "M300", title: "DB Milestone", status: "active" });
|
|
83
|
+
insertSlice({ id: "S01", milestoneId: "M300", title: "DB Slice", status: "in_progress" });
|
|
84
|
+
insertTask({ id: "T01", milestoneId: "M300", sliceId: "S01", title: "DB Task", status: "complete" });
|
|
85
|
+
writeFileSync(
|
|
86
|
+
join(dbBase, ".gsd", "milestones", "M300", "slices", "S01", "S01-PLAN.md"),
|
|
87
|
+
"# S01\n\n## Tasks\n\n- [ ] **T01: DB Task** `est:10m`\n",
|
|
88
|
+
"utf-8",
|
|
89
|
+
);
|
|
90
|
+
writeFileSync(join(dbBase, ".gsd", "STATE.md"), "## Next Action\nExecute T01 for S01: DB task\n", "utf-8");
|
|
91
|
+
|
|
92
|
+
const status = await inspectExecuteTaskDurability(dbBase, "M300/S01/T01");
|
|
93
|
+
assert.ok(status !== null, "db-complete: status exists");
|
|
94
|
+
assert.equal(status!.dbComplete, true, "db-complete: closed DB status is captured");
|
|
95
|
+
assert.equal(status!.summaryExists, false, "db-complete: summary can still be missing");
|
|
96
|
+
assert.equal(status!.taskChecked, false, "db-complete: checkbox can still be unchecked");
|
|
97
|
+
assert.equal(status!.nextActionAdvanced, false, "db-complete: next action can still point at task");
|
|
98
|
+
assert.equal(formatExecuteTaskRecoveryStatus(status!), "DB task status is closed");
|
|
99
|
+
} finally {
|
|
100
|
+
closeDatabase();
|
|
101
|
+
rmSync(dbBase, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
75
105
|
console.log("\n=== hook unit type sanitization (slash in unitType) ===");
|
|
76
106
|
{
|
|
77
107
|
// Hook units have unitType like "hook/code-review" with a slash
|
|
@@ -165,6 +165,9 @@ test("executeMilestoneStatus returns milestone metadata and slice counts", async
|
|
|
165
165
|
assert.equal(parsed.sliceCount, 1);
|
|
166
166
|
assert.equal(parsed.slices[0].id, "S01");
|
|
167
167
|
assert.equal(parsed.slices[0].taskCounts.pending, 1);
|
|
168
|
+
assert.equal(result.details.status, "active");
|
|
169
|
+
assert.equal(result.details.title, "Milestone One");
|
|
170
|
+
assert.deepEqual(result.details.slices, parsed.slices);
|
|
168
171
|
} finally {
|
|
169
172
|
closeDatabase();
|
|
170
173
|
cleanup(base);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: WorktreeResolver unit and regression tests.
|
|
1
3
|
import test from "node:test";
|
|
2
4
|
import assert from "node:assert/strict";
|
|
3
5
|
import { mkdtempSync, rmSync, mkdirSync, realpathSync } from "node:fs";
|
|
@@ -9,6 +11,17 @@ import {
|
|
|
9
11
|
type NotifyCtx,
|
|
10
12
|
} from "../worktree-resolver.js";
|
|
11
13
|
import { AutoSession } from "../auto/session.js";
|
|
14
|
+
import {
|
|
15
|
+
closeDatabase,
|
|
16
|
+
insertMilestone,
|
|
17
|
+
openDatabase,
|
|
18
|
+
} from "../gsd-db.js";
|
|
19
|
+
import { registerAutoWorker } from "../db/auto-workers.js";
|
|
20
|
+
import {
|
|
21
|
+
claimMilestoneLease,
|
|
22
|
+
getMilestoneLease,
|
|
23
|
+
releaseMilestoneLease,
|
|
24
|
+
} from "../db/milestone-leases.js";
|
|
12
25
|
|
|
13
26
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
14
27
|
|
|
@@ -19,11 +32,12 @@ interface CallLog {
|
|
|
19
32
|
}
|
|
20
33
|
|
|
21
34
|
function makeSession(
|
|
22
|
-
overrides?: Partial<
|
|
35
|
+
overrides?: Partial<AutoSession>,
|
|
23
36
|
): AutoSession {
|
|
24
37
|
const s = new AutoSession();
|
|
25
38
|
s.basePath = overrides?.basePath ?? "/project";
|
|
26
39
|
s.originalBasePath = overrides?.originalBasePath ?? "/project";
|
|
40
|
+
Object.assign(s, overrides);
|
|
27
41
|
return s;
|
|
28
42
|
}
|
|
29
43
|
|
|
@@ -182,6 +196,17 @@ function findCalls(calls: CallLog[], fn: string): CallLog[] {
|
|
|
182
196
|
return calls.filter((c) => c.fn === fn);
|
|
183
197
|
}
|
|
184
198
|
|
|
199
|
+
function makeDbBase(): string {
|
|
200
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-worktree-resolver-"));
|
|
201
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
202
|
+
return base;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function cleanupDbBase(base: string): void {
|
|
206
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
207
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
|
|
208
|
+
}
|
|
209
|
+
|
|
185
210
|
// ─── Getter Tests ────────────────────────────────────────────────────────────
|
|
186
211
|
|
|
187
212
|
test("workPath returns s.basePath", () => {
|
|
@@ -371,6 +396,43 @@ test("enterMilestone does not create double-nested worktree when originalBasePat
|
|
|
371
396
|
);
|
|
372
397
|
});
|
|
373
398
|
|
|
399
|
+
test("enterMilestone reacquires a released same-milestone lease before worktree entry", (t) => {
|
|
400
|
+
const base = makeDbBase();
|
|
401
|
+
t.after(() => cleanupDbBase(base));
|
|
402
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
403
|
+
insertMilestone({ id: "M001", title: "Test milestone", status: "active" });
|
|
404
|
+
|
|
405
|
+
const workerId = registerAutoWorker({ projectRootRealpath: base });
|
|
406
|
+
const originalClaim = claimMilestoneLease(workerId, "M001");
|
|
407
|
+
assert.equal(originalClaim.ok, true);
|
|
408
|
+
if (!originalClaim.ok) throw new Error("expected test lease claim");
|
|
409
|
+
assert.equal(releaseMilestoneLease(workerId, "M001", originalClaim.token), true);
|
|
410
|
+
|
|
411
|
+
const s = makeSession({
|
|
412
|
+
basePath: base,
|
|
413
|
+
originalBasePath: base,
|
|
414
|
+
workerId,
|
|
415
|
+
currentMilestoneId: "M001",
|
|
416
|
+
milestoneLeaseToken: originalClaim.token,
|
|
417
|
+
});
|
|
418
|
+
const deps = makeDeps({
|
|
419
|
+
createAutoWorktree: (basePath: string, milestoneId: string) => join(basePath, ".gsd", "worktrees", milestoneId),
|
|
420
|
+
});
|
|
421
|
+
const ctx = makeNotifyCtx();
|
|
422
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
423
|
+
|
|
424
|
+
resolver.enterMilestone("M001", ctx);
|
|
425
|
+
|
|
426
|
+
const row = getMilestoneLease("M001");
|
|
427
|
+
assert.ok(row);
|
|
428
|
+
assert.equal(row.worker_id, workerId);
|
|
429
|
+
assert.equal(row.status, "held");
|
|
430
|
+
assert.equal(row.fencing_token, originalClaim.token + 1);
|
|
431
|
+
assert.equal(s.milestoneLeaseToken, originalClaim.token + 1);
|
|
432
|
+
assert.equal(s.basePath, join(base, ".gsd", "worktrees", "M001"));
|
|
433
|
+
assert.equal(ctx.messages.some((m) => m.level === "error"), false);
|
|
434
|
+
});
|
|
435
|
+
|
|
374
436
|
// ─── enterMilestone Tests (branch mode) ──────────────────────────────────────
|
|
375
437
|
|
|
376
438
|
test("enterMilestone in branch mode calls enterBranchModeForMilestone and rebuilds GitService", () => {
|
|
@@ -308,6 +308,7 @@ function includeSupersededMemories(rankedActive: Memory[]): Memory[] {
|
|
|
308
308
|
scope: (row["scope"] as string) ?? "project",
|
|
309
309
|
tags,
|
|
310
310
|
structured_fields: structuredFields,
|
|
311
|
+
last_hit_at: (row["last_hit_at"] as string | null) ?? null,
|
|
311
312
|
};
|
|
312
313
|
});
|
|
313
314
|
} catch {
|
|
@@ -813,7 +813,7 @@ export async function executeMilestoneStatus(
|
|
|
813
813
|
|
|
814
814
|
return {
|
|
815
815
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
816
|
-
details: { operation: "milestone_status",
|
|
816
|
+
details: { operation: "milestone_status", ...result },
|
|
817
817
|
};
|
|
818
818
|
});
|
|
819
819
|
} catch (err) {
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
} from "./paths.js";
|
|
11
11
|
import { loadFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
|
|
12
12
|
import { parseUnitId } from "./unit-id.js";
|
|
13
|
+
import { getTask, isDbAvailable, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
|
|
14
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
13
15
|
|
|
14
16
|
// Per-record advisory lock — prevents read-modify-write races between
|
|
15
17
|
// concurrent writers updating disjoint fields of the same runtime record.
|
|
@@ -92,6 +94,7 @@ export interface ExecuteTaskRecoveryStatus {
|
|
|
92
94
|
summaryExists: boolean;
|
|
93
95
|
taskChecked: boolean;
|
|
94
96
|
nextActionAdvanced: boolean;
|
|
97
|
+
dbComplete: boolean;
|
|
95
98
|
mustHaveCount: number;
|
|
96
99
|
mustHavesMentionedInSummary: number;
|
|
97
100
|
}
|
|
@@ -213,6 +216,12 @@ export async function inspectExecuteTaskDurability(
|
|
|
213
216
|
const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
214
217
|
const taskChecked = !!planContent && new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m").test(planContent);
|
|
215
218
|
const nextActionAdvanced = !new RegExp(`Execute ${tid}\\b`).test(stateContent);
|
|
219
|
+
let dbComplete = false;
|
|
220
|
+
if (isDbAvailable()) {
|
|
221
|
+
refreshOpenDatabaseFromDisk();
|
|
222
|
+
const task = getTask(mid, sid, tid);
|
|
223
|
+
dbComplete = !!task && isClosedStatus(task.status);
|
|
224
|
+
}
|
|
216
225
|
|
|
217
226
|
// Must-have coverage: load task plan and count mentions in summary
|
|
218
227
|
let mustHaveCount = 0;
|
|
@@ -239,12 +248,14 @@ export async function inspectExecuteTaskDurability(
|
|
|
239
248
|
summaryExists,
|
|
240
249
|
taskChecked,
|
|
241
250
|
nextActionAdvanced,
|
|
251
|
+
dbComplete,
|
|
242
252
|
mustHaveCount,
|
|
243
253
|
mustHavesMentionedInSummary,
|
|
244
254
|
};
|
|
245
255
|
}
|
|
246
256
|
|
|
247
257
|
export function formatExecuteTaskRecoveryStatus(status: ExecuteTaskRecoveryStatus): string {
|
|
258
|
+
if (status.dbComplete) return "DB task status is closed";
|
|
248
259
|
const missing = [] as string[];
|
|
249
260
|
if (!status.summaryExists) missing.push(`summary missing (${status.summaryPath})`);
|
|
250
261
|
if (!status.taskChecked) missing.push(`task checkbox unchecked in ${status.planPath}`);
|
|
@@ -25,7 +25,7 @@ import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js
|
|
|
25
25
|
import { getCollapseCadence, getMilestoneResquash, resquashMilestoneOnMain } from "./slice-cadence.js";
|
|
26
26
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
27
27
|
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
28
|
-
import { claimMilestoneLease, releaseMilestoneLease } from "./db/milestone-leases.js";
|
|
28
|
+
import { claimMilestoneLease, refreshMilestoneLease, releaseMilestoneLease } from "./db/milestone-leases.js";
|
|
29
29
|
|
|
30
30
|
// ─── Path Comparison Helper ────────────────────────────────────────────────
|
|
31
31
|
/**
|
|
@@ -207,23 +207,44 @@ export class WorktreeResolver {
|
|
|
207
207
|
// milestone (re-entry within the same session).
|
|
208
208
|
if (this.s.workerId) {
|
|
209
209
|
if (this.s.currentMilestoneId === milestoneId && this.s.milestoneLeaseToken !== null) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
210
|
+
const refreshed = refreshMilestoneLease(
|
|
211
|
+
this.s.workerId,
|
|
212
|
+
milestoneId,
|
|
213
|
+
this.s.milestoneLeaseToken,
|
|
214
|
+
);
|
|
215
|
+
if (refreshed) {
|
|
216
|
+
debugLog("WorktreeResolver", {
|
|
217
|
+
action: "enterMilestone",
|
|
218
|
+
milestoneId,
|
|
219
|
+
leaseRefreshed: true,
|
|
220
|
+
fencingToken: this.s.milestoneLeaseToken,
|
|
221
|
+
});
|
|
222
|
+
} else {
|
|
223
|
+
debugLog("WorktreeResolver", {
|
|
224
|
+
action: "enterMilestone",
|
|
225
|
+
milestoneId,
|
|
226
|
+
staleLeaseToken: this.s.milestoneLeaseToken,
|
|
227
|
+
});
|
|
224
228
|
this.s.milestoneLeaseToken = null;
|
|
225
229
|
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// If we held a different milestone, release it first so other
|
|
233
|
+
// workers don't have to wait for TTL.
|
|
234
|
+
if (this.s.currentMilestoneId && this.s.currentMilestoneId !== milestoneId && this.s.milestoneLeaseToken !== null) {
|
|
235
|
+
try {
|
|
236
|
+
releaseMilestoneLease(this.s.workerId, this.s.currentMilestoneId, this.s.milestoneLeaseToken);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
debugLog("WorktreeResolver", {
|
|
239
|
+
action: "enterMilestone",
|
|
240
|
+
milestoneId,
|
|
241
|
+
releasePriorLeaseError: err instanceof Error ? err.message : String(err),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
this.s.milestoneLeaseToken = null;
|
|
245
|
+
}
|
|
226
246
|
|
|
247
|
+
if (this.s.milestoneLeaseToken === null) {
|
|
227
248
|
try {
|
|
228
249
|
const claim = claimMilestoneLease(this.s.workerId, milestoneId);
|
|
229
250
|
if (claim.ok) {
|
|
File without changes
|
|
File without changes
|