gsd-pi 2.73.1-dev.47ed32a → 2.73.1-dev.49397e5
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/cli-web-branch.d.ts +4 -3
- package/dist/cli-web-branch.js +10 -7
- package/dist/cli.js +99 -206
- package/dist/headless-query.js +4 -1
- package/dist/logo.d.ts +1 -1
- package/dist/logo.js +1 -1
- package/dist/onboarding.js +59 -53
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +59 -1
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
- package/dist/resources/extensions/gsd/auto/phases.js +60 -10
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -3
- package/dist/resources/extensions/gsd/auto-post-unit.js +93 -57
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
- package/dist/resources/extensions/gsd/auto-verification.js +88 -3
- package/dist/resources/extensions/gsd/auto.js +29 -10
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
- package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
- package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
- package/dist/resources/extensions/gsd/commands-do.js +79 -0
- package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
- package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
- package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
- package/dist/resources/extensions/gsd/commands-ship.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +3 -5
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/gsd-db.js +321 -0
- package/dist/resources/extensions/gsd/index.js +15 -2
- package/dist/resources/extensions/gsd/md-importer.js +3 -4
- package/dist/resources/extensions/gsd/memory-store.js +19 -51
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
- package/dist/resources/extensions/gsd/notification-widget.js +2 -2
- package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/dist/resources/extensions/gsd/state.js +66 -15
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
- package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
- package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
- package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
- package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
- package/dist/tsconfig.extensions.tsbuildinfo +1 -0
- 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/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/middleware-manifest.json +5 -5
- 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/package.json +3 -3
- package/packages/mcp-server/tsconfig.json +1 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
- package/packages/native/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-agent-core/tsconfig.json +1 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-ai/tsconfig.json +1 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +10 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +5 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +12 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -1
- package/packages/pi-coding-agent/tsconfig.json +1 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +30 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +1 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +22 -3
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/tui.test.ts +38 -0
- package/packages/pi-tui/src/tui.ts +20 -3
- package/packages/pi-tui/tsconfig.json +1 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
- package/packages/rpc-client/tsconfig.json +1 -0
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +95 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +88 -0
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
- package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
- package/src/resources/extensions/gsd/auto/phases.ts +90 -10
- package/src/resources/extensions/gsd/auto-dispatch.ts +10 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +107 -58
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
- package/src/resources/extensions/gsd/auto-verification.ts +98 -3
- package/src/resources/extensions/gsd/auto.ts +33 -14
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
- package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
- package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
- package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
- package/src/resources/extensions/gsd/commands-do.ts +109 -0
- package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
- package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
- package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
- package/src/resources/extensions/gsd/commands-ship.ts +219 -0
- package/src/resources/extensions/gsd/db-writer.ts +3 -5
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +467 -0
- package/src/resources/extensions/gsd/index.ts +18 -2
- package/src/resources/extensions/gsd/md-importer.ts +3 -5
- package/src/resources/extensions/gsd/memory-store.ts +31 -62
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
- package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
- package/src/resources/extensions/gsd/notification-widget.ts +2 -2
- package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/src/resources/extensions/gsd/state.ts +80 -17
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
- package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
- package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
- package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
- /package/dist/web/standalone/.next/static/{WawMb3TMcYLkGJLTg_x1u → tAsSblRKK1lG7MVOwsO92}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{WawMb3TMcYLkGJLTg_x1u → tAsSblRKK1lG7MVOwsO92}/_ssgManifest.js +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Storage layer for auto-learned project memories. Follows context-store.ts patterns.
|
|
4
4
|
// All functions degrade gracefully: return empty results when DB unavailable, never throw.
|
|
5
|
-
import { isDbAvailable, _getAdapter, transaction } from './gsd-db.js';
|
|
5
|
+
import { isDbAvailable, _getAdapter, transaction, insertMemoryRow, rewriteMemoryId, updateMemoryContentRow, incrementMemoryHitCount, supersedeMemoryRow, markMemoryUnitProcessed, decayMemoriesBefore, supersedeLowestRankedMemories, } from './gsd-db.js';
|
|
6
6
|
// ─── Category Display Order ─────────────────────────────────────────────────
|
|
7
7
|
const CATEGORY_PRIORITY = {
|
|
8
8
|
gotcha: 0,
|
|
@@ -112,27 +112,23 @@ export function createMemory(fields) {
|
|
|
112
112
|
const now = new Date().toISOString();
|
|
113
113
|
// Insert with a temporary placeholder ID — seq is auto-assigned
|
|
114
114
|
const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
':updated_at': now,
|
|
115
|
+
insertMemoryRow({
|
|
116
|
+
id: placeholder,
|
|
117
|
+
category: fields.category,
|
|
118
|
+
content: fields.content,
|
|
119
|
+
confidence: fields.confidence ?? 0.8,
|
|
120
|
+
sourceUnitType: fields.source_unit_type ?? null,
|
|
121
|
+
sourceUnitId: fields.source_unit_id ?? null,
|
|
122
|
+
createdAt: now,
|
|
123
|
+
updatedAt: now,
|
|
125
124
|
});
|
|
126
|
-
// Derive the real ID from the assigned seq
|
|
125
|
+
// Derive the real ID from the assigned seq (SELECT is still fine via adapter)
|
|
127
126
|
const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
|
|
128
127
|
if (!row)
|
|
129
128
|
return placeholder; // fallback — should not happen
|
|
130
129
|
const seq = row['seq'];
|
|
131
130
|
const realId = `MEM${String(seq).padStart(3, '0')}`;
|
|
132
|
-
|
|
133
|
-
':real_id': realId,
|
|
134
|
-
':placeholder': placeholder,
|
|
135
|
-
});
|
|
131
|
+
rewriteMemoryId(placeholder, realId);
|
|
136
132
|
return realId;
|
|
137
133
|
}
|
|
138
134
|
catch {
|
|
@@ -145,17 +141,8 @@ export function createMemory(fields) {
|
|
|
145
141
|
export function updateMemoryContent(id, content, confidence) {
|
|
146
142
|
if (!isDbAvailable())
|
|
147
143
|
return false;
|
|
148
|
-
const adapter = _getAdapter();
|
|
149
|
-
if (!adapter)
|
|
150
|
-
return false;
|
|
151
144
|
try {
|
|
152
|
-
|
|
153
|
-
if (confidence != null) {
|
|
154
|
-
adapter.prepare('UPDATE memories SET content = :content, confidence = :confidence, updated_at = :updated_at WHERE id = :id').run({ ':content': content, ':confidence': confidence, ':updated_at': now, ':id': id });
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
adapter.prepare('UPDATE memories SET content = :content, updated_at = :updated_at WHERE id = :id').run({ ':content': content, ':updated_at': now, ':id': id });
|
|
158
|
-
}
|
|
145
|
+
updateMemoryContentRow(id, content, confidence, new Date().toISOString());
|
|
159
146
|
return true;
|
|
160
147
|
}
|
|
161
148
|
catch {
|
|
@@ -168,11 +155,8 @@ export function updateMemoryContent(id, content, confidence) {
|
|
|
168
155
|
export function reinforceMemory(id) {
|
|
169
156
|
if (!isDbAvailable())
|
|
170
157
|
return false;
|
|
171
|
-
const adapter = _getAdapter();
|
|
172
|
-
if (!adapter)
|
|
173
|
-
return false;
|
|
174
158
|
try {
|
|
175
|
-
|
|
159
|
+
incrementMemoryHitCount(id, new Date().toISOString());
|
|
176
160
|
return true;
|
|
177
161
|
}
|
|
178
162
|
catch {
|
|
@@ -185,11 +169,8 @@ export function reinforceMemory(id) {
|
|
|
185
169
|
export function supersedeMemory(oldId, newId) {
|
|
186
170
|
if (!isDbAvailable())
|
|
187
171
|
return false;
|
|
188
|
-
const adapter = _getAdapter();
|
|
189
|
-
if (!adapter)
|
|
190
|
-
return false;
|
|
191
172
|
try {
|
|
192
|
-
|
|
173
|
+
supersedeMemoryRow(oldId, newId, new Date().toISOString());
|
|
193
174
|
return true;
|
|
194
175
|
}
|
|
195
176
|
catch {
|
|
@@ -220,12 +201,8 @@ export function isUnitProcessed(unitKey) {
|
|
|
220
201
|
export function markUnitProcessed(unitKey, activityFile) {
|
|
221
202
|
if (!isDbAvailable())
|
|
222
203
|
return false;
|
|
223
|
-
const adapter = _getAdapter();
|
|
224
|
-
if (!adapter)
|
|
225
|
-
return false;
|
|
226
204
|
try {
|
|
227
|
-
|
|
228
|
-
VALUES (:key, :file, :at)`).run({ ':key': unitKey, ':file': activityFile, ':at': new Date().toISOString() });
|
|
205
|
+
markMemoryUnitProcessed(unitKey, activityFile, new Date().toISOString());
|
|
229
206
|
return true;
|
|
230
207
|
}
|
|
231
208
|
catch {
|
|
@@ -244,16 +221,14 @@ export function decayStaleMemories(thresholdUnits = 20) {
|
|
|
244
221
|
if (!adapter)
|
|
245
222
|
return;
|
|
246
223
|
try {
|
|
247
|
-
// Find the timestamp of the Nth most recent processed unit
|
|
224
|
+
// Find the timestamp of the Nth most recent processed unit (read-only SELECT)
|
|
248
225
|
const row = adapter.prepare(`SELECT processed_at FROM memory_processed_units
|
|
249
226
|
ORDER BY processed_at DESC
|
|
250
227
|
LIMIT 1 OFFSET :offset`).get({ ':offset': thresholdUnits - 1 });
|
|
251
228
|
if (!row)
|
|
252
229
|
return; // not enough processed units yet
|
|
253
230
|
const cutoff = row['processed_at'];
|
|
254
|
-
|
|
255
|
-
SET confidence = MAX(0.1, confidence - 0.1), updated_at = :now
|
|
256
|
-
WHERE superseded_by IS NULL AND updated_at < :cutoff AND confidence > 0.1`).run({ ':now': new Date().toISOString(), ':cutoff': cutoff });
|
|
231
|
+
decayMemoriesBefore(cutoff, new Date().toISOString());
|
|
257
232
|
}
|
|
258
233
|
catch {
|
|
259
234
|
// non-fatal
|
|
@@ -274,14 +249,7 @@ export function enforceMemoryCap(max = 50) {
|
|
|
274
249
|
if (count <= max)
|
|
275
250
|
return;
|
|
276
251
|
const excess = count - max;
|
|
277
|
-
|
|
278
|
-
adapter.prepare(`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
|
|
279
|
-
WHERE id IN (
|
|
280
|
-
SELECT id FROM memories
|
|
281
|
-
WHERE superseded_by IS NULL
|
|
282
|
-
ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
|
|
283
|
-
LIMIT :limit
|
|
284
|
-
)`).run({ ':now': new Date().toISOString(), ':limit': excess });
|
|
252
|
+
supersedeLowestRankedMemories(excess, new Date().toISOString());
|
|
285
253
|
}
|
|
286
254
|
catch {
|
|
287
255
|
// non-fatal
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* gate registry so the definitions stay in lockstep with prompt builders,
|
|
11
11
|
* dispatch rules, and state derivation. See gate-registry.ts.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { isDbAvailable, upsertQualityGate } from "./gsd-db.js";
|
|
14
14
|
import { getGatesForTurn } from "./gate-registry.js";
|
|
15
15
|
/**
|
|
16
16
|
* Insert milestone-level quality_gates records for a validation run.
|
|
@@ -24,21 +24,22 @@ import { getGatesForTurn } from "./gate-registry.js";
|
|
|
24
24
|
* in gate-registry.ts automatically flows through here.
|
|
25
25
|
*/
|
|
26
26
|
export function insertMilestoneValidationGates(milestoneId, sliceId, verdict, evaluatedAt) {
|
|
27
|
-
|
|
28
|
-
if (!db)
|
|
27
|
+
if (!isDbAvailable())
|
|
29
28
|
return;
|
|
30
29
|
const gateVerdict = verdict === "pass" ? "pass" : "flag";
|
|
31
30
|
const milestoneGates = getGatesForTurn("validate-milestone");
|
|
32
31
|
for (const def of milestoneGates) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
upsertQualityGate({
|
|
33
|
+
milestoneId,
|
|
34
|
+
sliceId,
|
|
35
|
+
gateId: def.id,
|
|
36
|
+
scope: "milestone",
|
|
37
|
+
taskId: "",
|
|
38
|
+
status: "complete",
|
|
39
|
+
verdict: gateVerdict,
|
|
40
|
+
rationale: `${def.promptSection} — milestone validation verdict: ${verdict}`,
|
|
41
|
+
findings: "",
|
|
42
|
+
evaluatedAt,
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
45
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Both READ and WRITE operations are native — push operations remain as
|
|
6
6
|
// execSync calls because git2 credential handling is too complex.
|
|
7
|
-
import {
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
8
|
import { GSDError, GSD_GIT_ERROR } from "./errors.js";
|
|
9
9
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
10
10
|
import { getErrorMessage } from "./error-utils.js";
|
|
@@ -206,7 +206,7 @@ export function nativeIsRepo(basePath) {
|
|
|
206
206
|
return native.gitIsRepo(basePath);
|
|
207
207
|
}
|
|
208
208
|
try {
|
|
209
|
-
|
|
209
|
+
execFileSync("git", ["rev-parse", "--git-dir"], { cwd: basePath, stdio: "pipe" });
|
|
210
210
|
return true;
|
|
211
211
|
}
|
|
212
212
|
catch {
|
|
@@ -629,7 +629,10 @@ export function nativeCommit(basePath, message, options) {
|
|
|
629
629
|
}
|
|
630
630
|
// Fallback: use git commit with stdin pipe for safe multi-line messages
|
|
631
631
|
try {
|
|
632
|
-
const
|
|
632
|
+
const args = ["commit", "--no-verify", "-F", "-"];
|
|
633
|
+
if (options?.allowEmpty)
|
|
634
|
+
args.push("--allow-empty");
|
|
635
|
+
const result = execFileSync("git", args, {
|
|
633
636
|
cwd: basePath,
|
|
634
637
|
stdio: ["pipe", "pipe", "pipe"],
|
|
635
638
|
encoding: "utf-8",
|
|
@@ -767,7 +770,7 @@ export function nativeResetHard(basePath) {
|
|
|
767
770
|
native.gitResetHard(basePath);
|
|
768
771
|
return;
|
|
769
772
|
}
|
|
770
|
-
|
|
773
|
+
execFileSync("git", ["reset", "--hard", "HEAD"], { cwd: basePath, stdio: "pipe" });
|
|
771
774
|
}
|
|
772
775
|
/**
|
|
773
776
|
* Soft reset to a target ref (git reset --soft <ref>).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// GSD Extension — Notification Widget
|
|
2
2
|
// Always-on ambient widget rendered belowEditor showing unread count and
|
|
3
|
-
// the most recent notification message. Refreshes every
|
|
3
|
+
// the most recent notification message. Refreshes every 30 seconds.
|
|
4
4
|
// Widget key: "gsd-notifications", placement: "belowEditor"
|
|
5
5
|
import { getUnreadCount, onNotificationStoreChange } from "./notification-store.js";
|
|
6
6
|
import { formattedShortcutPair } from "./shortcut-defs.js";
|
|
@@ -12,7 +12,7 @@ export function buildNotificationWidgetLines() {
|
|
|
12
12
|
return [` 🔔 Notifications: ${unread} unread (${formattedShortcutPair("notifications")})`];
|
|
13
13
|
}
|
|
14
14
|
// ─── Widget init ────────────────────────────────────────────────────────
|
|
15
|
-
const REFRESH_INTERVAL_MS =
|
|
15
|
+
const REFRESH_INTERVAL_MS = 30_000;
|
|
16
16
|
/**
|
|
17
17
|
* Initialize the always-on notification widget (belowEditor).
|
|
18
18
|
* Call once from session_start after the notification store is initialized.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
You are generating tests for recently completed GSD work.
|
|
2
|
+
|
|
3
|
+
## Slice: {{sliceId}} — {{sliceTitle}}
|
|
4
|
+
|
|
5
|
+
### Summary
|
|
6
|
+
|
|
7
|
+
{{sliceSummary}}
|
|
8
|
+
|
|
9
|
+
### Existing Test Patterns
|
|
10
|
+
|
|
11
|
+
{{existingTestPatterns}}
|
|
12
|
+
|
|
13
|
+
## Working Directory
|
|
14
|
+
|
|
15
|
+
`{{workingDirectory}}`
|
|
16
|
+
|
|
17
|
+
## Instructions
|
|
18
|
+
|
|
19
|
+
1. Read the slice summary above to understand what was built
|
|
20
|
+
2. Identify the source files that were created or modified for this slice
|
|
21
|
+
3. Read the implementation code to understand behavior, edge cases, and error paths
|
|
22
|
+
4. Write comprehensive tests following the project's existing test patterns and framework
|
|
23
|
+
5. Run the tests to verify they pass
|
|
24
|
+
6. Fix any failures
|
|
25
|
+
|
|
26
|
+
### Rules
|
|
27
|
+
|
|
28
|
+
- Follow the project's existing test patterns (framework, assertions, file structure)
|
|
29
|
+
- Test behavior, not implementation details
|
|
30
|
+
- Cover: happy path, edge cases, error conditions, boundary values
|
|
31
|
+
- Do NOT modify implementation files — only create or update test files
|
|
32
|
+
- Name test files consistently with the project's conventions
|
|
33
|
+
- Keep tests focused and readable
|
|
34
|
+
|
|
35
|
+
{{skillActivation}}
|
|
@@ -271,7 +271,11 @@ function reconcileDiskToDb(basePath) {
|
|
|
271
271
|
try {
|
|
272
272
|
roadmapContent = readFileSync(roadmapPath, "utf-8");
|
|
273
273
|
}
|
|
274
|
-
catch {
|
|
274
|
+
catch (err) {
|
|
275
|
+
logWarning("state", "reconcileDiskToDb: roadmap read failed, skipping milestone", {
|
|
276
|
+
mid,
|
|
277
|
+
error: err.message,
|
|
278
|
+
});
|
|
275
279
|
continue;
|
|
276
280
|
}
|
|
277
281
|
const parsed = parseRoadmap(roadmapContent);
|
|
@@ -312,6 +316,10 @@ function reconcileDiskToDb(basePath) {
|
|
|
312
316
|
function buildCompletenessSet(basePath, milestones) {
|
|
313
317
|
const completeMilestoneIds = new Set();
|
|
314
318
|
const parkedMilestoneIds = new Set();
|
|
319
|
+
// DB-authoritative: a milestone is only "complete" when its DB row says so.
|
|
320
|
+
// SUMMARY-file presence is NOT a completion signal here — an orphan SUMMARY
|
|
321
|
+
// (crashed complete-milestone turn, partial merge, manual edit) must not
|
|
322
|
+
// flip derived state to complete and cascade into a false auto-merge (#4179).
|
|
315
323
|
for (const m of milestones) {
|
|
316
324
|
const parkedFile = resolveMilestoneFile(basePath, m.id, "PARKED");
|
|
317
325
|
if (parkedFile || m.status === 'parked') {
|
|
@@ -322,11 +330,6 @@ function buildCompletenessSet(basePath, milestones) {
|
|
|
322
330
|
completeMilestoneIds.add(m.id);
|
|
323
331
|
continue;
|
|
324
332
|
}
|
|
325
|
-
const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
|
|
326
|
-
if (summaryFile) {
|
|
327
|
-
completeMilestoneIds.add(m.id);
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
333
|
}
|
|
331
334
|
return { completeMilestoneIds, parkedMilestoneIds };
|
|
332
335
|
}
|
|
@@ -347,17 +350,22 @@ async function buildRegistryAndFindActive(basePath, milestones, completeMileston
|
|
|
347
350
|
if (isGhostMilestone(basePath, m.id))
|
|
348
351
|
continue;
|
|
349
352
|
}
|
|
350
|
-
|
|
351
|
-
|
|
353
|
+
// DB-authoritative completeness (#4179): only trust completeMilestoneIds,
|
|
354
|
+
// which is itself derived from DB status. SUMMARY-file presence alone must
|
|
355
|
+
// not imply completion. The summary file may still be consulted below as a
|
|
356
|
+
// title source for legitimately-complete milestones whose DB row has no title.
|
|
357
|
+
if (completeMilestoneIds.has(m.id)) {
|
|
352
358
|
let title = stripMilestonePrefix(m.title) || m.id;
|
|
353
|
-
if (
|
|
354
|
-
const
|
|
355
|
-
if (
|
|
356
|
-
|
|
359
|
+
if (!m.title) {
|
|
360
|
+
const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
|
|
361
|
+
if (summaryFile) {
|
|
362
|
+
const summaryContent = await loadFile(summaryFile);
|
|
363
|
+
if (summaryContent) {
|
|
364
|
+
title = parseSummary(summaryContent).title || m.id;
|
|
365
|
+
}
|
|
357
366
|
}
|
|
358
367
|
}
|
|
359
368
|
registry.push({ id: m.id, title, status: 'complete' });
|
|
360
|
-
completeMilestoneIds.add(m.id);
|
|
361
369
|
continue;
|
|
362
370
|
}
|
|
363
371
|
const allSlicesDone = slices.length > 0 && slices.every(s => isStatusDone(s.status));
|
|
@@ -391,7 +399,14 @@ async function buildRegistryAndFindActive(basePath, milestones, completeMileston
|
|
|
391
399
|
const validationFile = resolveMilestoneFile(basePath, m.id, "VALIDATION");
|
|
392
400
|
const validationContent = validationFile ? await loadFile(validationFile) : null;
|
|
393
401
|
const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
|
|
394
|
-
|
|
402
|
+
// DB-authoritative (#4179): completeness is already decided by
|
|
403
|
+
// completeMilestoneIds above. If we reached this branch, the DB says
|
|
404
|
+
// the milestone is NOT complete — so any SUMMARY file on disk is an
|
|
405
|
+
// orphan (crashed complete-milestone, partial merge, manual edit) and
|
|
406
|
+
// must not short-circuit this path. When validation is terminal, fall
|
|
407
|
+
// through to the default active-push below so `complete-milestone` can
|
|
408
|
+
// re-run idempotently.
|
|
409
|
+
if (!validationTerminal) {
|
|
395
410
|
activeMilestone = { id: m.id, title };
|
|
396
411
|
activeMilestoneSlices = slices;
|
|
397
412
|
activeMilestoneFound = true;
|
|
@@ -516,6 +531,9 @@ function resolveSliceDependencies(activeMilestoneSlices) {
|
|
|
516
531
|
return { activeSlice: null, activeSliceRow: null };
|
|
517
532
|
}
|
|
518
533
|
}
|
|
534
|
+
// First pass: find a slice with ALL dependencies satisfied (strict)
|
|
535
|
+
let bestFallback = null;
|
|
536
|
+
let bestFallbackSatisfied = -1;
|
|
519
537
|
for (const s of activeMilestoneSlices) {
|
|
520
538
|
if (isStatusDone(s.status))
|
|
521
539
|
continue;
|
|
@@ -524,6 +542,23 @@ function resolveSliceDependencies(activeMilestoneSlices) {
|
|
|
524
542
|
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
525
543
|
return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
|
|
526
544
|
}
|
|
545
|
+
// Track the slice with the most satisfied dependencies as fallback
|
|
546
|
+
const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
|
|
547
|
+
if (satisfied > bestFallbackSatisfied || (satisfied === bestFallbackSatisfied && !bestFallback)) {
|
|
548
|
+
bestFallback = s;
|
|
549
|
+
bestFallbackSatisfied = satisfied;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// Fallback: if no slice has all deps met but there ARE incomplete non-deferred
|
|
553
|
+
// slices, pick the one with the most deps satisfied. This prevents hard-blocking
|
|
554
|
+
// when dependency metadata is stale (e.g. after reassessment added/removed slices)
|
|
555
|
+
// or when deps reference slices from previous milestones.
|
|
556
|
+
if (bestFallback) {
|
|
557
|
+
const unmet = bestFallback.depends.filter(dep => !doneSliceIds.has(dep));
|
|
558
|
+
logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallback.id} ` +
|
|
559
|
+
`(${bestFallbackSatisfied}/${bestFallback.depends.length} deps met, ` +
|
|
560
|
+
`unmet: ${unmet.join(", ")})`, { mid: activeMilestoneSlices[0]?.milestone_id, sid: bestFallback.id });
|
|
561
|
+
return { activeSlice: { id: bestFallback.id, title: bestFallback.title }, activeSliceRow: bestFallback };
|
|
527
562
|
}
|
|
528
563
|
return { activeSlice: null, activeSliceRow: null };
|
|
529
564
|
}
|
|
@@ -567,7 +602,7 @@ async function reconcileSliceTasks(basePath, milestoneId, sliceId, planFile) {
|
|
|
567
602
|
const summaryPath = resolveTaskFile(basePath, milestoneId, sliceId, t.id, "SUMMARY");
|
|
568
603
|
if (summaryPath && existsSync(summaryPath)) {
|
|
569
604
|
try {
|
|
570
|
-
updateTaskStatus(milestoneId, sliceId, t.id, "complete");
|
|
605
|
+
updateTaskStatus(milestoneId, sliceId, t.id, "complete", new Date().toISOString());
|
|
571
606
|
logWarning("reconcile", `task ${milestoneId}/${sliceId}/${t.id} status reconciled from "${t.status}" to "complete" (#2514)`, { mid: milestoneId, sid: sliceId, tid: t.id });
|
|
572
607
|
reconciled = true;
|
|
573
608
|
}
|
|
@@ -1269,6 +1304,8 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1269
1304
|
}
|
|
1270
1305
|
}
|
|
1271
1306
|
else {
|
|
1307
|
+
let bestFallbackLegacy = null;
|
|
1308
|
+
let bestFallbackLegacySatisfied = -1;
|
|
1272
1309
|
for (const s of activeRoadmap.slices) {
|
|
1273
1310
|
if (s.done)
|
|
1274
1311
|
continue;
|
|
@@ -1276,6 +1313,20 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1276
1313
|
activeSlice = { id: s.id, title: s.title };
|
|
1277
1314
|
break;
|
|
1278
1315
|
}
|
|
1316
|
+
// Track best fallback
|
|
1317
|
+
const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
|
|
1318
|
+
if (satisfied > bestFallbackLegacySatisfied) {
|
|
1319
|
+
bestFallbackLegacy = s;
|
|
1320
|
+
bestFallbackLegacySatisfied = satisfied;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
// Fallback: if no slice has all deps met, pick the one with the most deps satisfied
|
|
1324
|
+
if (!activeSlice && bestFallbackLegacy) {
|
|
1325
|
+
const unmet = bestFallbackLegacy.depends.filter(dep => !doneSliceIds.has(dep));
|
|
1326
|
+
logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallbackLegacy.id} ` +
|
|
1327
|
+
`(${bestFallbackLegacySatisfied}/${bestFallbackLegacy.depends.length} deps met, ` +
|
|
1328
|
+
`unmet: ${unmet.join(", ")})`);
|
|
1329
|
+
activeSlice = { id: bestFallbackLegacy.id, title: bestFallbackLegacy.title };
|
|
1279
1330
|
}
|
|
1280
1331
|
}
|
|
1281
1332
|
if (!activeSlice) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
2
2
|
import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
|
|
3
3
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
4
|
-
import { getMilestone, getSliceStatusSummary, getSliceTaskCounts,
|
|
4
|
+
import { getMilestone, getSliceStatusSummary, getSliceTaskCounts, readTransaction, saveGateResult, } from "../gsd-db.js";
|
|
5
5
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
6
6
|
import { saveArtifactToDb } from "../db-writer.js";
|
|
7
7
|
import { handleCompleteMilestone } from "./complete-milestone.js";
|
|
@@ -493,12 +493,9 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
|
|
|
493
493
|
isError: true,
|
|
494
494
|
};
|
|
495
495
|
}
|
|
496
|
-
|
|
497
|
-
adapter.exec("BEGIN");
|
|
498
|
-
try {
|
|
496
|
+
return readTransaction(() => {
|
|
499
497
|
const milestone = getMilestone(params.milestoneId);
|
|
500
498
|
if (!milestone) {
|
|
501
|
-
adapter.exec("COMMIT");
|
|
502
499
|
return {
|
|
503
500
|
content: [{ type: "text", text: `Milestone ${params.milestoneId} not found in database.` }],
|
|
504
501
|
details: { operation: "milestone_status", milestoneId: params.milestoneId, found: false },
|
|
@@ -510,7 +507,6 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
|
|
|
510
507
|
status: s.status,
|
|
511
508
|
taskCounts: getSliceTaskCounts(params.milestoneId, s.id),
|
|
512
509
|
}));
|
|
513
|
-
adapter.exec("COMMIT");
|
|
514
510
|
const result = {
|
|
515
511
|
milestoneId: milestone.id,
|
|
516
512
|
title: milestone.title,
|
|
@@ -524,14 +520,7 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
|
|
|
524
520
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
525
521
|
details: { operation: "milestone_status", milestoneId: milestone.id, sliceCount: slices.length },
|
|
526
522
|
};
|
|
527
|
-
}
|
|
528
|
-
catch (txErr) {
|
|
529
|
-
try {
|
|
530
|
-
adapter.exec("ROLLBACK");
|
|
531
|
-
}
|
|
532
|
-
catch { /* swallow */ }
|
|
533
|
-
throw txErr;
|
|
534
|
-
}
|
|
523
|
+
});
|
|
535
524
|
}
|
|
536
525
|
catch (err) {
|
|
537
526
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -83,12 +83,9 @@ export function executeReplan(basePath, mid, sid, capture) {
|
|
|
83
83
|
// Also write replan_triggered_at column for DB-backed detection
|
|
84
84
|
try {
|
|
85
85
|
const req = createRequire(import.meta.url);
|
|
86
|
-
const { isDbAvailable,
|
|
86
|
+
const { isDbAvailable, setSliceReplanTriggeredAt } = req("./gsd-db.js");
|
|
87
87
|
if (isDbAvailable()) {
|
|
88
|
-
|
|
89
|
-
if (adapter) {
|
|
90
|
-
adapter.prepare("UPDATE slices SET replan_triggered_at = :ts WHERE milestone_id = :mid AND id = :sid").run({ ":ts": ts, ":mid": mid, ":sid": sid });
|
|
91
|
-
}
|
|
88
|
+
setSliceReplanTriggeredAt(mid, sid, ts);
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
catch {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _getAdapter,
|
|
1
|
+
import { _getAdapter, readTransaction, restoreManifest, } from "./gsd-db.js";
|
|
2
2
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
3
3
|
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -41,8 +41,7 @@ export function snapshotState() {
|
|
|
41
41
|
const db = requireDb();
|
|
42
42
|
// Wrap all reads in a deferred transaction so the snapshot is consistent
|
|
43
43
|
// (all SELECTs see the same DB state even if a concurrent write lands between them).
|
|
44
|
-
|
|
45
|
-
try {
|
|
44
|
+
return readTransaction(() => {
|
|
46
45
|
const rawMilestones = db.prepare("SELECT * FROM milestones ORDER BY id").all();
|
|
47
46
|
const milestones = rawMilestones.map((r) => ({
|
|
48
47
|
id: r["id"],
|
|
@@ -146,74 +145,14 @@ export function snapshotState() {
|
|
|
146
145
|
decisions,
|
|
147
146
|
verification_evidence,
|
|
148
147
|
};
|
|
149
|
-
db.exec("COMMIT");
|
|
150
148
|
return result;
|
|
151
|
-
}
|
|
152
|
-
catch (err) {
|
|
153
|
-
try {
|
|
154
|
-
db.exec("ROLLBACK");
|
|
155
|
-
}
|
|
156
|
-
catch { /* ignore rollback failure */ }
|
|
157
|
-
throw err;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// ─── restore ─────────────────────────────────────────────────────────────
|
|
161
|
-
/**
|
|
162
|
-
* Atomically replace all workflow state from a manifest.
|
|
163
|
-
* Runs inside a transaction — if any insert fails, no tables are modified.
|
|
164
|
-
* Only touches engine tables + decisions. Does NOT modify artifacts or memories.
|
|
165
|
-
*/
|
|
166
|
-
function restore(manifest) {
|
|
167
|
-
const db = requireDb();
|
|
168
|
-
transaction(() => {
|
|
169
|
-
// Clear engine tables (order matters for foreign-key-like consistency)
|
|
170
|
-
db.exec("DELETE FROM verification_evidence");
|
|
171
|
-
db.exec("DELETE FROM tasks");
|
|
172
|
-
db.exec("DELETE FROM slices");
|
|
173
|
-
db.exec("DELETE FROM milestones");
|
|
174
|
-
db.exec("DELETE FROM decisions WHERE 1=1");
|
|
175
|
-
// Restore milestones
|
|
176
|
-
const msStmt = db.prepare(`INSERT INTO milestones (id, title, status, depends_on, created_at, completed_at,
|
|
177
|
-
vision, success_criteria, key_risks, proof_strategy,
|
|
178
|
-
verification_contract, verification_integration, verification_operational, verification_uat,
|
|
179
|
-
definition_of_done, requirement_coverage, boundary_map_markdown)
|
|
180
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
181
|
-
for (const m of manifest.milestones) {
|
|
182
|
-
msStmt.run(m.id, m.title, m.status, JSON.stringify(m.depends_on), m.created_at, m.completed_at, m.vision, JSON.stringify(m.success_criteria), JSON.stringify(m.key_risks), JSON.stringify(m.proof_strategy), m.verification_contract, m.verification_integration, m.verification_operational, m.verification_uat, JSON.stringify(m.definition_of_done), m.requirement_coverage, m.boundary_map_markdown);
|
|
183
|
-
}
|
|
184
|
-
// Restore slices
|
|
185
|
-
const slStmt = db.prepare(`INSERT INTO slices (milestone_id, id, title, status, risk, depends, demo,
|
|
186
|
-
created_at, completed_at, full_summary_md, full_uat_md,
|
|
187
|
-
goal, success_criteria, proof_level, integration_closure, observability_impact,
|
|
188
|
-
sequence, replan_triggered_at)
|
|
189
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
190
|
-
for (const s of manifest.slices) {
|
|
191
|
-
slStmt.run(s.milestone_id, s.id, s.title, s.status, s.risk, JSON.stringify(s.depends), s.demo, s.created_at, s.completed_at, s.full_summary_md, s.full_uat_md, s.goal, s.success_criteria, s.proof_level, s.integration_closure, s.observability_impact, s.sequence, s.replan_triggered_at);
|
|
192
|
-
}
|
|
193
|
-
// Restore tasks
|
|
194
|
-
const tkStmt = db.prepare(`INSERT INTO tasks (milestone_id, slice_id, id, title, status,
|
|
195
|
-
one_liner, narrative, verification_result, duration, completed_at,
|
|
196
|
-
blocker_discovered, deviations, known_issues, key_files, key_decisions,
|
|
197
|
-
full_summary_md, description, estimate, files, verify,
|
|
198
|
-
inputs, expected_output, observability_impact, sequence)
|
|
199
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
200
|
-
for (const t of manifest.tasks) {
|
|
201
|
-
tkStmt.run(t.milestone_id, t.slice_id, t.id, t.title, t.status, t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at, t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues, JSON.stringify(t.key_files), JSON.stringify(t.key_decisions), t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify, JSON.stringify(t.inputs), JSON.stringify(t.expected_output), t.observability_impact, t.sequence);
|
|
202
|
-
}
|
|
203
|
-
// Restore decisions
|
|
204
|
-
const dcStmt = db.prepare(`INSERT INTO decisions (seq, id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
205
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
206
|
-
for (const d of manifest.decisions) {
|
|
207
|
-
dcStmt.run(d.seq, d.id, d.when_context, d.scope, d.decision, d.choice, d.rationale, d.revisable, d.made_by, d.superseded_by);
|
|
208
|
-
}
|
|
209
|
-
// Restore verification evidence
|
|
210
|
-
const evStmt = db.prepare(`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
|
|
211
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
212
|
-
for (const e of manifest.verification_evidence) {
|
|
213
|
-
evStmt.run(e.task_id, e.slice_id, e.milestone_id, e.command, e.exit_code, e.verdict, e.duration_ms, e.created_at);
|
|
214
|
-
}
|
|
215
149
|
});
|
|
216
150
|
}
|
|
151
|
+
// ─── restore ─────────────────────────────────────────────────────────────
|
|
152
|
+
//
|
|
153
|
+
// The actual restore() implementation lives in gsd-db.ts (single-writer
|
|
154
|
+
// invariant). This module only orchestrates reading the manifest file
|
|
155
|
+
// and handing it to the writer.
|
|
217
156
|
// ─── writeManifest ───────────────────────────────────────────────────────
|
|
218
157
|
/**
|
|
219
158
|
* Write current DB state to .gsd/state-manifest.json via atomicWriteSync.
|
|
@@ -258,6 +197,6 @@ export function bootstrapFromManifest(basePath) {
|
|
|
258
197
|
if (!manifest) {
|
|
259
198
|
return false;
|
|
260
199
|
}
|
|
261
|
-
|
|
200
|
+
restoreManifest(manifest);
|
|
262
201
|
return true;
|
|
263
202
|
}
|