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
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// intentionally independent store for cross-worktree claim races and is
|
|
23
23
|
// excluded from this invariant.
|
|
24
24
|
import { createRequire } from "node:module";
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
25
26
|
import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
|
|
26
27
|
import { dirname } from "node:path";
|
|
27
28
|
import { GSDError, GSD_STALE_STATE } from "./errors.js";
|
|
@@ -36,7 +37,7 @@ import { rowToActiveDecision, rowToActiveRequirement, rowToDecision, rowToRequir
|
|
|
36
37
|
import { rowToGate } from "./db-gate-rows.js";
|
|
37
38
|
import { rowToArtifact, rowToMilestone } from "./db-milestone-artifact-rows.js";
|
|
38
39
|
import { backupDatabaseBeforeMigration } from "./db-migration-backup.js";
|
|
39
|
-
import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, applyMigrationV26MilestoneCommitAttributions, } from "./db-migration-steps.js";
|
|
40
|
+
import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, applyMigrationV26MilestoneCommitAttributions, applyMigrationV27ArtifactHash, applyMigrationV28MemoryLastHitAt, } from "./db-migration-steps.js";
|
|
40
41
|
import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
|
|
41
42
|
import { createDbOpenState } from "./db-open-state.js";
|
|
42
43
|
import { createRuntimeKvTableV25 } from "./db-runtime-kv-schema.js";
|
|
@@ -52,7 +53,7 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
52
53
|
nodeVersion: process.versions.node,
|
|
53
54
|
writeStderr: (message) => process.stderr.write(message),
|
|
54
55
|
});
|
|
55
|
-
export const SCHEMA_VERSION =
|
|
56
|
+
export const SCHEMA_VERSION = 28;
|
|
56
57
|
function initSchema(db, fileBacked) {
|
|
57
58
|
if (fileBacked)
|
|
58
59
|
db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -250,6 +251,14 @@ function migrateSchema(db) {
|
|
|
250
251
|
applyMigrationV26MilestoneCommitAttributions(db);
|
|
251
252
|
recordSchemaVersion(db, 26);
|
|
252
253
|
}
|
|
254
|
+
if (currentVersion < 27) {
|
|
255
|
+
applyMigrationV27ArtifactHash(db);
|
|
256
|
+
recordSchemaVersion(db, 27);
|
|
257
|
+
}
|
|
258
|
+
if (currentVersion < 28) {
|
|
259
|
+
applyMigrationV28MemoryLastHitAt(db);
|
|
260
|
+
recordSchemaVersion(db, 28);
|
|
261
|
+
}
|
|
253
262
|
db.exec("COMMIT");
|
|
254
263
|
}
|
|
255
264
|
catch (err) {
|
|
@@ -821,8 +830,9 @@ export function clearArtifacts() {
|
|
|
821
830
|
export function insertArtifact(a) {
|
|
822
831
|
if (!currentDb)
|
|
823
832
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
824
|
-
|
|
825
|
-
|
|
833
|
+
const contentHash = createHash("sha256").update(a.full_content).digest("hex");
|
|
834
|
+
currentDb.prepare(`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash)
|
|
835
|
+
VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at, :content_hash)`).run({
|
|
826
836
|
":path": a.path,
|
|
827
837
|
":artifact_type": a.artifact_type,
|
|
828
838
|
":milestone_id": a.milestone_id,
|
|
@@ -830,6 +840,7 @@ export function insertArtifact(a) {
|
|
|
830
840
|
":task_id": a.task_id,
|
|
831
841
|
":full_content": a.full_content,
|
|
832
842
|
":imported_at": new Date().toISOString(),
|
|
843
|
+
":content_hash": contentHash,
|
|
833
844
|
});
|
|
834
845
|
}
|
|
835
846
|
export function insertMilestone(m) {
|
|
@@ -1521,6 +1532,13 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1521
1532
|
const hasEscalationAwaiting = wtTaskInfo.some((col) => col["name"] === "escalation_awaiting_review");
|
|
1522
1533
|
const hasEscalationArtifact = wtTaskInfo.some((col) => col["name"] === "escalation_artifact_path");
|
|
1523
1534
|
const hasEscalationOverride = wtTaskInfo.some((col) => col["name"] === "escalation_override_applied_at");
|
|
1535
|
+
const wtArtifactInfo = adapter.prepare("PRAGMA wt.table_info('artifacts')").all();
|
|
1536
|
+
const hasArtifactContentHash = wtArtifactInfo.some((col) => col["name"] === "content_hash");
|
|
1537
|
+
const wtMemoryInfo = adapter.prepare("PRAGMA wt.table_info('memories')").all();
|
|
1538
|
+
const hasMemoryScope = wtMemoryInfo.some((col) => col["name"] === "scope");
|
|
1539
|
+
const hasMemoryTags = wtMemoryInfo.some((col) => col["name"] === "tags");
|
|
1540
|
+
const hasMemoryStructuredFields = wtMemoryInfo.some((col) => col["name"] === "structured_fields");
|
|
1541
|
+
const hasMemoryLastHitAt = wtMemoryInfo.some((col) => col["name"] === "last_hit_at");
|
|
1524
1542
|
const decConf = adapter.prepare(`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"} OR m.superseded_by IS NOT w.superseded_by`).all();
|
|
1525
1543
|
for (const row of decConf)
|
|
1526
1544
|
conflicts.push(`decision ${row["id"]}: modified in both`);
|
|
@@ -1553,12 +1571,17 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1553
1571
|
supporting_slices, validation, notes, full_content, superseded_by
|
|
1554
1572
|
FROM wt.requirements
|
|
1555
1573
|
`).run());
|
|
1574
|
+
// V27: preserve content_hash. If the worktree predates V27 (no column),
|
|
1575
|
+
// fall back to the main DB's existing hash so reconcile doesn't null
|
|
1576
|
+
// out integrity fingerprints on artifacts that were unchanged in wt.
|
|
1556
1577
|
merged.artifacts = countChanges(adapter.prepare(`
|
|
1557
1578
|
INSERT OR REPLACE INTO artifacts (
|
|
1558
|
-
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1579
|
+
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
|
|
1559
1580
|
)
|
|
1560
|
-
SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1561
|
-
|
|
1581
|
+
SELECT w.path, w.artifact_type, w.milestone_id, w.slice_id, w.task_id, w.full_content, w.imported_at,
|
|
1582
|
+
${hasArtifactContentHash ? "w.content_hash" : "m.content_hash"}
|
|
1583
|
+
FROM wt.artifacts w
|
|
1584
|
+
LEFT JOIN artifacts m ON m.path = w.path
|
|
1562
1585
|
`).run());
|
|
1563
1586
|
// Merge milestones — worktree may have updated status/planning fields.
|
|
1564
1587
|
// Never downgrade status: complete > active > pre-planning (#4372).
|
|
@@ -1656,15 +1679,25 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1656
1679
|
FROM wt.tasks w
|
|
1657
1680
|
LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
|
|
1658
1681
|
`).run());
|
|
1659
|
-
// Merge memories — keep worktree-learned insights
|
|
1682
|
+
// Merge memories — keep worktree-learned insights.
|
|
1683
|
+
// V18 (scope, tags), V21 (structured_fields), V28 (last_hit_at): for each
|
|
1684
|
+
// column the wt may not yet have (older worktree DB), fall back to the
|
|
1685
|
+
// main DB's existing value via LEFT JOIN so reconcile never silently
|
|
1686
|
+
// resets these fields to defaults on rows that already had them.
|
|
1660
1687
|
merged.memories = countChanges(adapter.prepare(`
|
|
1661
1688
|
INSERT OR REPLACE INTO memories (
|
|
1662
1689
|
seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1663
|
-
created_at, updated_at, superseded_by, hit_count
|
|
1690
|
+
created_at, updated_at, superseded_by, hit_count,
|
|
1691
|
+
scope, tags, structured_fields, last_hit_at
|
|
1664
1692
|
)
|
|
1665
|
-
SELECT seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1666
|
-
created_at, updated_at, superseded_by, hit_count
|
|
1667
|
-
|
|
1693
|
+
SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
|
|
1694
|
+
w.created_at, w.updated_at, w.superseded_by, w.hit_count,
|
|
1695
|
+
${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
|
|
1696
|
+
${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
|
|
1697
|
+
${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
|
|
1698
|
+
${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
|
|
1699
|
+
FROM wt.memories w
|
|
1700
|
+
LEFT JOIN memories m ON m.id = w.id
|
|
1668
1701
|
`).run());
|
|
1669
1702
|
// Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
|
|
1670
1703
|
merged.verification_evidence = countChanges(adapter.prepare(`
|
|
@@ -2423,7 +2456,7 @@ export function updateMemoryContentRow(id, content, confidence, updatedAt) {
|
|
|
2423
2456
|
export function incrementMemoryHitCount(id, updatedAt) {
|
|
2424
2457
|
if (!currentDb)
|
|
2425
2458
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2426
|
-
currentDb.prepare("UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id").run({ ":updated_at": updatedAt, ":id": id });
|
|
2459
|
+
currentDb.prepare("UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at, last_hit_at = :last_hit_at WHERE id = :id").run({ ":updated_at": updatedAt, ":last_hit_at": updatedAt, ":id": id });
|
|
2427
2460
|
}
|
|
2428
2461
|
export function supersedeMemoryRow(oldId, newId, updatedAt) {
|
|
2429
2462
|
if (!currentDb)
|
|
@@ -51,6 +51,21 @@ export { showQueue, handleQueueReorder, showQueueAdd, buildExistingMilestonesCon
|
|
|
51
51
|
import { logWarning } from "./workflow-logger.js";
|
|
52
52
|
import { deleteRuntimeKv } from "./db/runtime-kv.js";
|
|
53
53
|
import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
|
|
54
|
+
function scheduleAutoStartAfterIdle(ctx, pi, basePath, verboseMode, options, launch = startAutoDetached) {
|
|
55
|
+
const waitForIdle = typeof ctx.waitForIdle === "function"
|
|
56
|
+
? ctx.waitForIdle.bind(ctx)
|
|
57
|
+
: async () => { };
|
|
58
|
+
void waitForIdle()
|
|
59
|
+
.then(() => {
|
|
60
|
+
setTimeout(() => launch(ctx, pi, basePath, verboseMode, options), 0);
|
|
61
|
+
})
|
|
62
|
+
.catch((err) => {
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
ctx.ui.notify(`Auto-start failed while waiting for the prior turn to settle: ${message}`, "error");
|
|
65
|
+
logWarning("guided", `auto-start idle wait failed: ${message}`);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export const _scheduleAutoStartAfterIdleForTest = scheduleAutoStartAfterIdle;
|
|
54
69
|
// ─── Scope-based validator wrappers ──────────────────────────────────────────
|
|
55
70
|
// These thin wrappers accept a MilestoneScope so callers that already hold a
|
|
56
71
|
// pinned scope never have to re-derive (basePath, milestoneId) separately.
|
|
@@ -325,7 +340,7 @@ async function dispatchNextDeepProjectSetupStage(entry) {
|
|
|
325
340
|
const { DISPATCH_RULES, hasPendingDeepStage } = await import("./auto-dispatch.js");
|
|
326
341
|
if (!hasPendingDeepStage(prefs, entry.basePath)) {
|
|
327
342
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
328
|
-
|
|
343
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
329
344
|
return true;
|
|
330
345
|
}
|
|
331
346
|
const state = await deriveState(entry.basePath);
|
|
@@ -359,14 +374,14 @@ async function dispatchNextDeepProjectSetupStage(entry) {
|
|
|
359
374
|
}
|
|
360
375
|
else if (hasPendingDeepStage(prefs, entry.basePath)) {
|
|
361
376
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
362
|
-
|
|
377
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
363
378
|
return true;
|
|
364
379
|
}
|
|
365
380
|
return false;
|
|
366
381
|
}
|
|
367
382
|
if (!USER_DRIVEN_DEEP_SETUP_UNITS.has(result.unitType)) {
|
|
368
383
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
369
|
-
|
|
384
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
370
385
|
return true;
|
|
371
386
|
}
|
|
372
387
|
entry.currentUnitType = result.unitType;
|
|
@@ -544,7 +559,7 @@ export function checkAutoStartAfterDiscuss() {
|
|
|
544
559
|
}
|
|
545
560
|
pendingAutoStartMap.delete(basePath);
|
|
546
561
|
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
|
|
547
|
-
|
|
562
|
+
scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
|
|
548
563
|
return true;
|
|
549
564
|
}
|
|
550
565
|
/**
|
|
@@ -14,6 +14,27 @@ const CATEGORY_PRIORITY = {
|
|
|
14
14
|
environment: 4,
|
|
15
15
|
preference: 5,
|
|
16
16
|
};
|
|
17
|
+
// ─── Scoring Helpers ─────────────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Time-decay factor for memory relevance scoring.
|
|
20
|
+
* Returns 1.0 for never-hit or recently-hit memories, decaying linearly to
|
|
21
|
+
* 0.7 for memories not accessed in 90+ days. Floor at 0.7 keeps old-but-valid
|
|
22
|
+
* knowledge from being fully suppressed.
|
|
23
|
+
*
|
|
24
|
+
* Defensive parsing: invalid timestamp strings (NaN from Date.parse) are
|
|
25
|
+
* treated as "no decay" rather than propagating NaN into score arithmetic.
|
|
26
|
+
* Future timestamps (clock skew, manual DB edits) clamp to daysAgo=0 so the
|
|
27
|
+
* factor stays in the documented [0.7, 1.0] contract.
|
|
28
|
+
*/
|
|
29
|
+
export function memoryDecayFactor(lastHitAt) {
|
|
30
|
+
if (!lastHitAt)
|
|
31
|
+
return 1.0;
|
|
32
|
+
const ts = Date.parse(lastHitAt);
|
|
33
|
+
if (!Number.isFinite(ts))
|
|
34
|
+
return 1.0;
|
|
35
|
+
const daysAgo = Math.max(0, (Date.now() - ts) / 86_400_000);
|
|
36
|
+
return Math.max(0.7, 1.0 - 0.3 * Math.min(1.0, daysAgo / 90));
|
|
37
|
+
}
|
|
17
38
|
// ─── Row Mapping ────────────────────────────────────────────────────────────
|
|
18
39
|
function rowToMemory(row) {
|
|
19
40
|
return {
|
|
@@ -31,6 +52,7 @@ function rowToMemory(row) {
|
|
|
31
52
|
scope: row['scope'] ?? 'project',
|
|
32
53
|
tags: parseTags(row['tags']),
|
|
33
54
|
structured_fields: parseStructuredFields(row['structured_fields']),
|
|
55
|
+
last_hit_at: row['last_hit_at'] ?? null,
|
|
34
56
|
};
|
|
35
57
|
}
|
|
36
58
|
function parseStructuredFields(raw) {
|
|
@@ -114,15 +136,37 @@ export function queryMemoriesRanked(opts) {
|
|
|
114
136
|
? semanticSearch(adapter, opts.queryVector, activeClause, 50)
|
|
115
137
|
: [];
|
|
116
138
|
if (keywordHits.length === 0 && semanticHits.length === 0 && !trimmedQuery) {
|
|
117
|
-
// No query at all —
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
139
|
+
// No query at all — return top-k by decay-aware ranked score.
|
|
140
|
+
//
|
|
141
|
+
// Build the candidate pool from a direct SQL query that honors the
|
|
142
|
+
// request's activeClause (i.e. include_superseded). Using
|
|
143
|
+
// getActiveMemoriesRanked here would silently drop superseded rows even
|
|
144
|
+
// when the caller explicitly opted in, and would slice by raw score
|
|
145
|
+
// before decay/filters had a chance to reorder.
|
|
146
|
+
const candidatePool = Math.min(Math.max(k * 5, 50), 500);
|
|
147
|
+
const rows = adapter
|
|
148
|
+
.prepare(`SELECT * FROM memories ${activeClause}
|
|
149
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
|
|
150
|
+
LIMIT :limit`)
|
|
151
|
+
.all({ ':limit': candidatePool });
|
|
152
|
+
const ranked = [];
|
|
153
|
+
for (const row of rows) {
|
|
154
|
+
const memory = rowToMemory(row);
|
|
155
|
+
if (!passesFilters(memory, opts))
|
|
156
|
+
continue;
|
|
157
|
+
const decay = memoryDecayFactor(memory.last_hit_at);
|
|
158
|
+
const score = memory.confidence * (1 + memory.hit_count * 0.1) * decay;
|
|
159
|
+
ranked.push({
|
|
160
|
+
memory,
|
|
161
|
+
score,
|
|
162
|
+
keywordRank: null,
|
|
163
|
+
semanticRank: null,
|
|
164
|
+
confidenceBoost: score,
|
|
165
|
+
reason: 'ranked',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
169
|
+
return ranked.slice(0, k);
|
|
126
170
|
}
|
|
127
171
|
// 3) Reciprocal rank fusion — each hit contributes 1/(rrfK + rank).
|
|
128
172
|
const fused = new Map();
|
|
@@ -155,7 +199,7 @@ export function queryMemoriesRanked(opts) {
|
|
|
155
199
|
for (const entry of fused.values()) {
|
|
156
200
|
if (!passesFilters(entry.memory, opts))
|
|
157
201
|
continue;
|
|
158
|
-
const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1);
|
|
202
|
+
const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1) * memoryDecayFactor(entry.memory.last_hit_at);
|
|
159
203
|
const reason = entry.kwRank != null && entry.semRank != null
|
|
160
204
|
? 'both'
|
|
161
205
|
: entry.kwRank != null
|
|
@@ -194,6 +238,7 @@ function passesFilters(memory, filters) {
|
|
|
194
238
|
}
|
|
195
239
|
return true;
|
|
196
240
|
}
|
|
241
|
+
let ftsWarningEmitted = false;
|
|
197
242
|
function keywordSearch(adapter, rawQuery, activeClause, limit) {
|
|
198
243
|
const ftsAvailable = isFtsAvailable(adapter);
|
|
199
244
|
if (ftsAvailable) {
|
|
@@ -215,14 +260,26 @@ function keywordSearch(adapter, rawQuery, activeClause, limit) {
|
|
|
215
260
|
// fall through to LIKE
|
|
216
261
|
}
|
|
217
262
|
}
|
|
218
|
-
// LIKE fallback — scans
|
|
263
|
+
// LIKE fallback — scans a capped candidate pool.
|
|
264
|
+
if (!ftsWarningEmitted) {
|
|
265
|
+
ftsWarningEmitted = true;
|
|
266
|
+
logWarning('memory-store', 'FTS5 unavailable — using LIKE fallback scan (consider enabling FTS5)');
|
|
267
|
+
}
|
|
219
268
|
const terms = rawQuery
|
|
220
269
|
.toLowerCase()
|
|
221
270
|
.split(/[^a-z0-9_]+/)
|
|
222
271
|
.filter((t) => t.length >= 2);
|
|
223
272
|
if (terms.length === 0)
|
|
224
273
|
return [];
|
|
225
|
-
const
|
|
274
|
+
const preScanCap = Math.min(limit * 20, 2000);
|
|
275
|
+
// ORDER BY confidence-weighted hit_count DESC so the cap keeps the most
|
|
276
|
+
// valuable candidates instead of the oldest-by-rowid (which would silently
|
|
277
|
+
// exclude recently-stored memories on tables larger than preScanCap).
|
|
278
|
+
const rows = adapter
|
|
279
|
+
.prepare(`SELECT * FROM memories ${activeClause}
|
|
280
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
|
|
281
|
+
LIMIT :preScanCap`)
|
|
282
|
+
.all({ ':preScanCap': preScanCap });
|
|
226
283
|
const scored = [];
|
|
227
284
|
for (const row of rows) {
|
|
228
285
|
const memory = rowToMemory(row);
|
|
@@ -111,15 +111,41 @@ function warmCache() {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
let warmCacheScheduled = false;
|
|
114
|
+
let warmCacheRan = false;
|
|
115
|
+
let warmCacheTimer;
|
|
116
|
+
/**
|
|
117
|
+
* Synchronously snapshot the prompt/template tree into the cache.
|
|
118
|
+
*
|
|
119
|
+
* Safe to call immediately after `initResources()` because that function uses
|
|
120
|
+
* synchronous fs APIs — there is no race window that requires deferring the
|
|
121
|
+
* cache warm via setTimeout. Idempotent: subsequent calls are no-ops.
|
|
122
|
+
*
|
|
123
|
+
* Cancels the fallback `scheduleWarmCache` timer if it is still pending.
|
|
124
|
+
*/
|
|
125
|
+
export function primeCache() {
|
|
126
|
+
if (warmCacheRan)
|
|
127
|
+
return;
|
|
128
|
+
if (warmCacheTimer) {
|
|
129
|
+
clearTimeout(warmCacheTimer);
|
|
130
|
+
warmCacheTimer = undefined;
|
|
131
|
+
}
|
|
132
|
+
warmCacheScheduled = true;
|
|
133
|
+
warmCacheRan = true;
|
|
134
|
+
warmCache();
|
|
135
|
+
}
|
|
114
136
|
function scheduleWarmCache() {
|
|
115
137
|
if (warmCacheScheduled)
|
|
116
138
|
return;
|
|
117
139
|
warmCacheScheduled = true;
|
|
118
140
|
const run = () => {
|
|
141
|
+
warmCacheTimer = undefined;
|
|
142
|
+
if (warmCacheRan)
|
|
143
|
+
return;
|
|
144
|
+
warmCacheRan = true;
|
|
119
145
|
warmCache();
|
|
120
146
|
};
|
|
121
|
-
|
|
122
|
-
|
|
147
|
+
warmCacheTimer = setTimeout(run, 1000);
|
|
148
|
+
warmCacheTimer.unref?.();
|
|
123
149
|
}
|
|
124
150
|
// Snapshot the full prompt/template tree after import so extension startup only
|
|
125
151
|
// pays for prompts that are actually needed immediately.
|
|
@@ -29,14 +29,15 @@ Subagents report only; they do not write user source. Fold any findings into Dec
|
|
|
29
29
|
|
|
30
30
|
## Steps
|
|
31
31
|
|
|
32
|
-
1.
|
|
33
|
-
2.
|
|
34
|
-
3.
|
|
35
|
-
4. Verify
|
|
36
|
-
5. Verify **
|
|
37
|
-
6. If
|
|
38
|
-
7.
|
|
39
|
-
8.
|
|
32
|
+
1. **Duplicate completion guard:** Call `gsd_milestone_status` for `{{milestoneId}}` before any durable writes. If the returned milestone **status is `complete`**, this is a stale or duplicate closeout turn: do NOT mutate requirements, do NOT refresh the project document, do NOT write LEARNINGS, and do NOT persist milestone completion again. Say: "Milestone {{milestoneId}} is already complete." and stop.
|
|
33
|
+
2. Use the **Milestone Summary** output template from the inlined context above
|
|
34
|
+
3. {{skillActivation}}
|
|
35
|
+
4. **Verify code changes exist.** Compare milestone work against the integration branch (`main`, `master`, or recorded branch), using merge-base as older revision and `HEAD` as newer. If the diff lists non-`.gsd/` files, pass. If `HEAD` equals the integration branch/merge-base, treat it as a self-diff retry: inspect milestone-scoped commit evidence (`GSD-Unit: {{milestoneId}}` or production `GSD-Task: Sxx/Tyy` trailers touching `.gsd/milestones/{{milestoneId}}/`) and verify those commits touched non-`.gsd/` files. Record **verification failure** only when neither source shows implementation files.
|
|
36
|
+
5. Verify every **success criterion** from `{{roadmapPath}}`. If passing validation is present, summarize the validation evidence instead of re-auditing it; otherwise verify with evidence from summaries, tests, or observable behavior. Record unmet criteria as **verification failure**.
|
|
37
|
+
6. Verify **definition of done**: all slices `[x]`, summaries exist, and integrations work. If passing validation is present, trust its integration/verification verdict unless inconsistent with current artifacts. Record unmet items as **verification failure**.
|
|
38
|
+
7. If the roadmap includes a **Horizontal Checklist**, verify each item and note unchecked items in the summary.
|
|
39
|
+
8. Fill the **Decision Re-evaluation** table: compare each key `.gsd/DECISIONS.md` decision from this milestone with what shipped, and flag decisions to revisit.
|
|
40
|
+
9. Validate **requirement status transitions**. For each changed requirement, confirm evidence supports the new status. Requirements may move between Active, Validated, Deferred, Blocked, or Out of Scope only with proof.
|
|
40
41
|
|
|
41
42
|
**DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')`; the engine owns the WAL connection. Use `gsd_milestone_status`, inlined context, or `gsd_*` tools; never direct SQL.
|
|
42
43
|
|
|
@@ -53,13 +54,13 @@ Subagents report only; they do not write user source. Fold any findings into Dec
|
|
|
53
54
|
|
|
54
55
|
**Success path** (all verifications passed):
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
10. For each requirement whose status changed in step 9, call `gsd_requirement_update` with the requirement ID and updated `status` and `validation` fields — the tool regenerates `.gsd/REQUIREMENTS.md` automatically. Do this BEFORE completing the milestone so requirement updates are persisted.
|
|
58
|
+
11. Update `.gsd/PROJECT.md`: use the `write` tool with `path: ".gsd/PROJECT.md"` and `content` containing the full updated document reflecting milestone completion and current project state. Do NOT use the `edit` tool for this — PROJECT.md is a full-document refresh.
|
|
59
|
+
12. Extract structured learnings from this milestone and persist them to the GSD memory store. Follow the procedure block immediately below — it writes `{{milestoneId}}-LEARNINGS.md` as the audit trail and persists Patterns, Lessons, and Decisions via `capture_thought` (categories: pattern, gotcha/convention, architecture). The memory store is the single source of truth for cross-session durable knowledge (ADR-013).
|
|
59
60
|
|
|
60
61
|
{{extractLearningsSteps}}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
13. **Persist completion through `gsd_complete_milestone`.** Call it with the parameters below. This must be the final persistent write in the unit. The tool updates the milestone status in the DB, renders `{{milestoneSummaryPath}}`, and validates all slices are complete.
|
|
63
64
|
|
|
64
65
|
**Required parameters:**
|
|
65
66
|
- `milestoneId` (string) — Milestone ID (e.g. M001)
|
|
@@ -78,7 +79,7 @@ Subagents report only; they do not write user source. Fold any findings into Dec
|
|
|
78
79
|
- `followUps` (string) — Follow-up items for future milestones
|
|
79
80
|
- `deviations` (string) — Deviations from the original plan
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
14. Do not commit manually — the system auto-commits your changes after this unit completes.
|
|
82
83
|
- Say: "Milestone {{milestoneId}} complete."
|
|
83
84
|
|
|
84
85
|
**Important:** Do NOT skip code-change, success-criteria, or definition-of-done verification (steps 3-5). The summary must reflect verified outcomes. Verification failures block completion; there is no override. If a verification tool fails, errors, or returns unexpected output, treat it as failure.
|
|
@@ -680,7 +680,7 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
|
|
|
680
680
|
};
|
|
681
681
|
return {
|
|
682
682
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
683
|
-
details: { operation: "milestone_status",
|
|
683
|
+
details: { operation: "milestone_status", ...result },
|
|
684
684
|
};
|
|
685
685
|
});
|
|
686
686
|
}
|
|
@@ -4,6 +4,8 @@ import { atomicWriteSync } from "./atomic-write.js";
|
|
|
4
4
|
import { gsdRoot, relSliceFile, relTaskFile, resolveSliceFile, resolveTaskFile, } from "./paths.js";
|
|
5
5
|
import { loadFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
|
|
6
6
|
import { parseUnitId } from "./unit-id.js";
|
|
7
|
+
import { getTask, isDbAvailable, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
|
|
8
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
7
9
|
// Per-record advisory lock — prevents read-modify-write races between
|
|
8
10
|
// concurrent writers updating disjoint fields of the same runtime record.
|
|
9
11
|
// Within a single Node process this is moot (writeUnitRuntimeRecord is sync),
|
|
@@ -165,6 +167,12 @@ export async function inspectExecuteTaskDurability(basePath, unitId) {
|
|
|
165
167
|
const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
166
168
|
const taskChecked = !!planContent && new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m").test(planContent);
|
|
167
169
|
const nextActionAdvanced = !new RegExp(`Execute ${tid}\\b`).test(stateContent);
|
|
170
|
+
let dbComplete = false;
|
|
171
|
+
if (isDbAvailable()) {
|
|
172
|
+
refreshOpenDatabaseFromDisk();
|
|
173
|
+
const task = getTask(mid, sid, tid);
|
|
174
|
+
dbComplete = !!task && isClosedStatus(task.status);
|
|
175
|
+
}
|
|
168
176
|
// Must-have coverage: load task plan and count mentions in summary
|
|
169
177
|
let mustHaveCount = 0;
|
|
170
178
|
let mustHavesMentionedInSummary = 0;
|
|
@@ -188,11 +196,14 @@ export async function inspectExecuteTaskDurability(basePath, unitId) {
|
|
|
188
196
|
summaryExists,
|
|
189
197
|
taskChecked,
|
|
190
198
|
nextActionAdvanced,
|
|
199
|
+
dbComplete,
|
|
191
200
|
mustHaveCount,
|
|
192
201
|
mustHavesMentionedInSummary,
|
|
193
202
|
};
|
|
194
203
|
}
|
|
195
204
|
export function formatExecuteTaskRecoveryStatus(status) {
|
|
205
|
+
if (status.dbComplete)
|
|
206
|
+
return "DB task status is closed";
|
|
196
207
|
const missing = [];
|
|
197
208
|
if (!status.summaryExists)
|
|
198
209
|
missing.push(`summary missing (${status.summaryPath})`);
|
|
@@ -22,7 +22,7 @@ import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js
|
|
|
22
22
|
import { getCollapseCadence, getMilestoneResquash, resquashMilestoneOnMain } from "./slice-cadence.js";
|
|
23
23
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
24
24
|
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
25
|
-
import { claimMilestoneLease, releaseMilestoneLease } from "./db/milestone-leases.js";
|
|
25
|
+
import { claimMilestoneLease, refreshMilestoneLease, releaseMilestoneLease } from "./db/milestone-leases.js";
|
|
26
26
|
// ─── Path Comparison Helper ────────────────────────────────────────────────
|
|
27
27
|
/**
|
|
28
28
|
* Compare two paths for physical identity, tolerating trailing slashes,
|
|
@@ -119,24 +119,40 @@ export class WorktreeResolver {
|
|
|
119
119
|
// milestone (re-entry within the same session).
|
|
120
120
|
if (this.s.workerId) {
|
|
121
121
|
if (this.s.currentMilestoneId === milestoneId && this.s.milestoneLeaseToken !== null) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
122
|
+
const refreshed = refreshMilestoneLease(this.s.workerId, milestoneId, this.s.milestoneLeaseToken);
|
|
123
|
+
if (refreshed) {
|
|
124
|
+
debugLog("WorktreeResolver", {
|
|
125
|
+
action: "enterMilestone",
|
|
126
|
+
milestoneId,
|
|
127
|
+
leaseRefreshed: true,
|
|
128
|
+
fencingToken: this.s.milestoneLeaseToken,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
debugLog("WorktreeResolver", {
|
|
133
|
+
action: "enterMilestone",
|
|
134
|
+
milestoneId,
|
|
135
|
+
staleLeaseToken: this.s.milestoneLeaseToken,
|
|
136
|
+
});
|
|
138
137
|
this.s.milestoneLeaseToken = null;
|
|
139
138
|
}
|
|
139
|
+
}
|
|
140
|
+
// If we held a different milestone, release it first so other
|
|
141
|
+
// workers don't have to wait for TTL.
|
|
142
|
+
if (this.s.currentMilestoneId && this.s.currentMilestoneId !== milestoneId && this.s.milestoneLeaseToken !== null) {
|
|
143
|
+
try {
|
|
144
|
+
releaseMilestoneLease(this.s.workerId, this.s.currentMilestoneId, this.s.milestoneLeaseToken);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
debugLog("WorktreeResolver", {
|
|
148
|
+
action: "enterMilestone",
|
|
149
|
+
milestoneId,
|
|
150
|
+
releasePriorLeaseError: err instanceof Error ? err.message : String(err),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
this.s.milestoneLeaseToken = null;
|
|
154
|
+
}
|
|
155
|
+
if (this.s.milestoneLeaseToken === null) {
|
|
140
156
|
try {
|
|
141
157
|
const claim = claimMilestoneLease(this.s.workerId, milestoneId);
|
|
142
158
|
if (claim.ok) {
|