@opengsd/gsd-pi 1.9.0-dev.e10b60d4 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +24 -4
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
- package/dist/resources/extensions/gsd/auto-start.js +29 -9
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
- package/dist/resources/extensions/gsd/auto.js +12 -1
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +21 -2
- package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
- package/dist/resources/extensions/gsd/gsd-db.js +75 -31
- package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
- 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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
- package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +3 -3
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{layout-4ae2d68984392bbf.js → layout-b35cbfff38aaf4cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-90d1d8d5e5d2dc6b.js → main-app-590a74400e35f685.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +1 -0
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +9 -3
- package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/background-cli.test.js +248 -0
- package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cli.js +121 -15
- package/packages/gsd-cloud/dist/cli.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.js +7 -1
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +1 -3
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.js +9 -7
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
- package/packages/gsd-cloud/dist/runtime-process.d.ts +27 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.js +272 -0
- package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js +97 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
- package/packages/gsd-cloud/package.json +2 -2
- package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli-runner.js +119 -33
- package/packages/mcp-server/dist/cli-runner.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +17 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +17 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +54 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop.ts +48 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
- package/src/resources/extensions/gsd/auto-start.ts +31 -8
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
- package/src/resources/extensions/gsd/auto.ts +13 -1
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +27 -2
- package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
- package/src/resources/extensions/gsd/gsd-db.ts +85 -33
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
- package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +0 -1
- /package/dist/web/standalone/.next/static/{V9MAVyYVPboQUB9KTTAMi → EAhnhYMt5UVOiG9-iPDjc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{V9MAVyYVPboQUB9KTTAMi → EAhnhYMt5UVOiG9-iPDjc}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -28,16 +28,16 @@ See [CHANGELOG.md](./CHANGELOG.md) for release-by-release fixes and [Legacy Rele
|
|
|
28
28
|
## Latest Release Highlights
|
|
29
29
|
|
|
30
30
|
<!-- release-highlights:start -->
|
|
31
|
-
Latest release: **v1.
|
|
32
|
-
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
- **
|
|
37
|
-
- **
|
|
38
|
-
- **gsd:**
|
|
39
|
-
- **gsd:**
|
|
40
|
-
- **gsd:**
|
|
31
|
+
Latest release: **v1.10.0**
|
|
32
|
+
|
|
33
|
+
- **pi-ai:** Add Meta Muse Spark 1.1 to the model catalog.
|
|
34
|
+
- **gsd:** Surface cacheRetention as a settable prompt-cache TTL preference.
|
|
35
|
+
- **models:** Add GPT-5.6 as a first-class model.
|
|
36
|
+
- **models:** Add Grok 4.5 as a first-class xAI model.
|
|
37
|
+
- **agent-core:** Pin compaction summarization reasoning to low, bound degenerate-chunk retry.
|
|
38
|
+
- **gsd:** Filter superseded context injections from provider payload.
|
|
39
|
+
- **gsd:** Stop advertising alias tool schemas on model-facing surfaces.
|
|
40
|
+
- **gsd:** Cap execute-task and discuss-slice inlined context; provider-aware budgets.
|
|
41
41
|
|
|
42
42
|
<!-- release-highlights:end -->
|
|
43
43
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5f0f1c788a126951
|
|
@@ -261,7 +261,22 @@ async function enforceMinRequestInterval(s, prefs) {
|
|
|
261
261
|
await new Promise(r => setTimeout(r, decision.waitMs));
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
-
function
|
|
264
|
+
async function snapshotCrashedUnitWork(ctx, s, deps, iterData) {
|
|
265
|
+
try {
|
|
266
|
+
const commitMsg = await deps.autoCommitUnit?.(s.basePath, iterData.unitType, iterData.unitId, ctx);
|
|
267
|
+
if (commitMsg) {
|
|
268
|
+
debugLog("autoLoop", {
|
|
269
|
+
phase: "crash-snapshot-commit",
|
|
270
|
+
unitType: iterData.unitType,
|
|
271
|
+
unitId: iterData.unitId,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (snapshotErr) {
|
|
276
|
+
logWarning("dispatch", `unit crash snapshot failed: ${snapshotErr instanceof Error ? snapshotErr.message : String(snapshotErr)}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function closeOutCrashedUnit(ctx, s, deps, iterData, err) {
|
|
265
280
|
const summary = formatDispatchExceptionSummary({ error: err });
|
|
266
281
|
try {
|
|
267
282
|
emitOpenUnitEndForUnit(s.basePath, iterData.unitType, iterData.unitId, "cancelled", {
|
|
@@ -278,6 +293,7 @@ function closeOutCrashedUnit(s, iterData, err) {
|
|
|
278
293
|
catch (closeoutErr) {
|
|
279
294
|
logWarning("dispatch", `unit crash closeout failed: ${closeoutErr instanceof Error ? closeoutErr.message : String(closeoutErr)}`);
|
|
280
295
|
}
|
|
296
|
+
await snapshotCrashedUnitWork(ctx, s, deps, iterData);
|
|
281
297
|
}
|
|
282
298
|
/**
|
|
283
299
|
* Main auto-mode execution loop. Iterates: orchestration.advance → guards →
|
|
@@ -400,11 +416,15 @@ export async function autoLoop(ctx, pi, s, deps, options) {
|
|
|
400
416
|
active: s.active,
|
|
401
417
|
iteration,
|
|
402
418
|
maxIterations: MAX_LOOP_ITERATIONS,
|
|
403
|
-
hasCommandContext:
|
|
419
|
+
hasCommandContext: typeof s.cmdCtx?.newSession === "function",
|
|
404
420
|
sessionLockValid: true,
|
|
405
421
|
});
|
|
406
422
|
if (commandContextDecision.action === "stop" && commandContextDecision.reason === "missing-command-context") {
|
|
407
423
|
debugLog("autoLoop", { phase: "exit", reason: "no-cmdCtx" });
|
|
424
|
+
if (s.currentUnit) {
|
|
425
|
+
await deps.autoCommitUnit?.(s.basePath, s.currentUnit.type, s.currentUnit.id, ctx);
|
|
426
|
+
}
|
|
427
|
+
await deps.stopAuto(ctx, pi, commandContextDecision.message, { preserveWorktree: true });
|
|
408
428
|
finishTurn("stopped", "manual-attention", commandContextDecision.reason);
|
|
409
429
|
break;
|
|
410
430
|
}
|
|
@@ -586,7 +606,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
|
|
|
586
606
|
if (err instanceof ModelPolicyDispatchBlockedError) {
|
|
587
607
|
throw err;
|
|
588
608
|
}
|
|
589
|
-
closeOutCrashedUnit(s, iterData, err);
|
|
609
|
+
await closeOutCrashedUnit(ctx, s, deps, iterData, err);
|
|
590
610
|
throw err;
|
|
591
611
|
}
|
|
592
612
|
if (unitPhaseResult.action === "next") {
|
|
@@ -1109,7 +1129,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
|
|
|
1109
1129
|
if (err instanceof ModelPolicyDispatchBlockedError) {
|
|
1110
1130
|
throw err;
|
|
1111
1131
|
}
|
|
1112
|
-
closeOutCrashedUnit(s, iterData, err);
|
|
1132
|
+
await closeOutCrashedUnit(ctx, s, deps, iterData, err);
|
|
1113
1133
|
dispatchSettled = settleDispatchFailed(dispatchId, formatDispatchExceptionSummary({ error: err }), {
|
|
1114
1134
|
markFailed: markDispatchFailed,
|
|
1115
1135
|
logWriteFailure: logDispatchLedgerWriteFailure,
|
|
@@ -58,6 +58,24 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
58
58
|
debugLog("runUnit", { phase: "start", unitType, unitId });
|
|
59
59
|
// ── Session creation with timeout ──
|
|
60
60
|
debugLog("runUnit", { phase: "session-create", unitType, unitId });
|
|
61
|
+
const cmdCtx = s.cmdCtx;
|
|
62
|
+
if (typeof cmdCtx?.newSession !== "function") {
|
|
63
|
+
const msg = "command context is missing newSession";
|
|
64
|
+
debugLog("runUnit", {
|
|
65
|
+
phase: "session-error",
|
|
66
|
+
unitType,
|
|
67
|
+
unitId,
|
|
68
|
+
error: msg,
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
status: "cancelled",
|
|
72
|
+
errorContext: {
|
|
73
|
+
message: `Session creation failed: ${msg}`,
|
|
74
|
+
category: "session-failed",
|
|
75
|
+
isTransient: false,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
61
79
|
let sessionResult;
|
|
62
80
|
let sessionTimeoutHandle;
|
|
63
81
|
const mySessionSwitchGeneration = ++sessionSwitchGeneration;
|
|
@@ -67,7 +85,7 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
67
85
|
const sessionAbortController = new AbortController();
|
|
68
86
|
_setSessionSwitchInFlight(true);
|
|
69
87
|
try {
|
|
70
|
-
const sessionPromise =
|
|
88
|
+
const sessionPromise = cmdCtx.newSession({
|
|
71
89
|
abortSignal: sessionAbortController.signal,
|
|
72
90
|
workspaceRoot: s.basePath,
|
|
73
91
|
}).finally(() => {
|
|
@@ -46,9 +46,10 @@ import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.
|
|
|
46
46
|
import { extractVerdict } from "./verdict-parser.js";
|
|
47
47
|
import { auditOrphanedPreflightStashes } from "./orphan-stash-audit.js";
|
|
48
48
|
import { parseProject } from "./schemas/parsers.js";
|
|
49
|
+
import { LAYOUT_SEGMENTS } from "./layout-policy.js";
|
|
49
50
|
import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath, } from "./debug-logger.js";
|
|
50
51
|
import { logWarning, logError } from "./workflow-logger.js";
|
|
51
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, } from "node:fs";
|
|
52
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, } from "node:fs";
|
|
52
53
|
import { join } from "node:path";
|
|
53
54
|
import { validateDirectory } from "./validate-directory.js";
|
|
54
55
|
import { isCustomProvider, resolveDefaultSessionModel, resolveDynamicRoutingConfig, } from "./preferences-models.js";
|
|
@@ -73,6 +74,30 @@ const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
|
|
|
73
74
|
export function hasGitIndexLockForTest(basePath) {
|
|
74
75
|
return existsSync(join(basePath, ".git", "index.lock"));
|
|
75
76
|
}
|
|
77
|
+
function isEmptyDirectory(dirPath) {
|
|
78
|
+
try {
|
|
79
|
+
return existsSync(dirPath) && readdirSync(dirPath).length === 0;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function reconcileFlatPhaseBootstrapLayout(basePath) {
|
|
86
|
+
const gsdDir = join(basePath, ".gsd");
|
|
87
|
+
const phasesPath = join(gsdDir, LAYOUT_SEGMENTS.level1);
|
|
88
|
+
const milestonesPath = join(gsdDir, "milestones");
|
|
89
|
+
if (isEmptyDirectory(milestonesPath)) {
|
|
90
|
+
if (!existsSync(phasesPath))
|
|
91
|
+
mkdirSync(phasesPath, { recursive: true });
|
|
92
|
+
rmSync(milestonesPath, { recursive: true, force: true });
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
if (!existsSync(phasesPath) && !existsSync(milestonesPath)) {
|
|
96
|
+
mkdirSync(phasesPath, { recursive: true });
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
76
101
|
export function _shouldAbortBootstrapForUnavailableDbForTest(gsdDbPath, dbAvailable, pathExists = existsSync) {
|
|
77
102
|
return pathExists(gsdDbPath) && !dbAvailable;
|
|
78
103
|
}
|
|
@@ -868,20 +893,15 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
868
893
|
ensureGitignore(base, { manageGitignore });
|
|
869
894
|
if (manageGitignore !== false)
|
|
870
895
|
untrackRuntimeFiles(base);
|
|
871
|
-
// Bootstrap
|
|
872
|
-
|
|
873
|
-
// so checking .gsd/ existence would be dead code (#2942).
|
|
874
|
-
const gsdDir = join(base, ".gsd");
|
|
875
|
-
const milestonesPath = join(gsdDir, "milestones");
|
|
876
|
-
if (!existsSync(milestonesPath)) {
|
|
877
|
-
mkdirSync(milestonesPath, { recursive: true });
|
|
896
|
+
// Bootstrap the flat-phase projection root and clean empty legacy scaffolds.
|
|
897
|
+
if (reconcileFlatPhaseBootstrapLayout(base)) {
|
|
878
898
|
try {
|
|
879
899
|
nativeAddAll(base);
|
|
880
900
|
nativeCommit(base, "chore: init gsd");
|
|
881
901
|
}
|
|
882
902
|
catch (err) {
|
|
883
903
|
/* nothing to commit */
|
|
884
|
-
logWarning("engine", `
|
|
904
|
+
logWarning("engine", `layout bootstrap commit failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
885
905
|
}
|
|
886
906
|
}
|
|
887
907
|
{
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { cpSync, existsSync, lstatSync as lstatSyncFn, mkdirSync, readdirSync, } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { _isSamePath as isSamePath, } from "./auto-worktree-cleanup.js";
|
|
8
|
-
import { resolveGsdPathContract } from "./paths.js";
|
|
8
|
+
import { dirIsContentBearingLegacyMilestone, resolveGsdPathContract } from "./paths.js";
|
|
9
9
|
import { WorktreeStateProjection } from "./worktree-state-projection.js";
|
|
10
10
|
import { logWarning } from "./workflow-logger.js";
|
|
11
11
|
const PROJECT_PREFERENCES_FILE = "PREFERENCES.md";
|
|
@@ -143,10 +143,14 @@ function syncMilestoneLayout(mainGsd, wtGsd, layoutSegment, synced) {
|
|
|
143
143
|
if (!existsSync(mainMilestonesDir))
|
|
144
144
|
return;
|
|
145
145
|
try {
|
|
146
|
-
mkdirSync(wtMilestonesDir, { recursive: true });
|
|
147
146
|
const mainMilestones = readdirSync(mainMilestonesDir, { withFileTypes: true })
|
|
148
147
|
.filter((entry) => entry.isDirectory())
|
|
149
|
-
.map((entry) => entry.name)
|
|
148
|
+
.map((entry) => entry.name)
|
|
149
|
+
.filter((name) => layoutSegment !== "milestones" ||
|
|
150
|
+
dirIsContentBearingLegacyMilestone(join(mainMilestonesDir, name)));
|
|
151
|
+
if (mainMilestones.length === 0)
|
|
152
|
+
return;
|
|
153
|
+
mkdirSync(wtMilestonesDir, { recursive: true });
|
|
150
154
|
for (const milestoneId of mainMilestones) {
|
|
151
155
|
syncMilestoneDirectory(mainMilestonesDir, wtMilestonesDir, milestoneId, synced);
|
|
152
156
|
}
|
|
@@ -2440,7 +2440,18 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
|
|
|
2440
2440
|
startedAt: hookStartedAt,
|
|
2441
2441
|
workspaceRoot: s.basePath,
|
|
2442
2442
|
});
|
|
2443
|
-
const
|
|
2443
|
+
const cmdCtx = s.cmdCtx;
|
|
2444
|
+
if (typeof cmdCtx?.newSession !== "function") {
|
|
2445
|
+
const message = "Auto-mode has no command context for dispatch.";
|
|
2446
|
+
ctx.ui.notify(message, "error");
|
|
2447
|
+
const unitToCommit = previousCurrentUnit ?? s.currentUnit;
|
|
2448
|
+
if (unitToCommit) {
|
|
2449
|
+
await autoCommitUnit(s.basePath, unitToCommit.type, unitToCommit.id, ctx);
|
|
2450
|
+
}
|
|
2451
|
+
await stopAuto(ctx, pi, message, { preserveWorktree: true });
|
|
2452
|
+
return false;
|
|
2453
|
+
}
|
|
2454
|
+
const result = await cmdCtx.newSession({ workspaceRoot: s.basePath });
|
|
2444
2455
|
if (result.cancelled) {
|
|
2445
2456
|
await stopAuto(ctx, pi);
|
|
2446
2457
|
return false;
|
|
@@ -58,6 +58,23 @@ function reportCheckboxDbStatusDivergence(issues, basePath, filePath, scope, uni
|
|
|
58
58
|
fixable: false,
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
+
function bareDuplicateMilestoneId(milestoneId) {
|
|
62
|
+
const match = milestoneId.match(/^(M\d{3})-[a-z0-9]{6}$/i);
|
|
63
|
+
return match?.[1]?.toUpperCase() ?? null;
|
|
64
|
+
}
|
|
65
|
+
function checkboxDbStatusMilestoneIds(basePath, milestoneIds) {
|
|
66
|
+
if (!hasFlatPhaseLayout(basePath))
|
|
67
|
+
return milestoneIds;
|
|
68
|
+
// Flat-phase directories are keyed by phase number. A restored
|
|
69
|
+
// M003-xxxxxx row alongside M003 is a stale duplicate for the same
|
|
70
|
+
// phases/03-* projection, so checking both can compare DB status against two
|
|
71
|
+
// competing PLAN files and report a persistent false divergence.
|
|
72
|
+
const allIds = new Set(milestoneIds.map((id) => id.toUpperCase()));
|
|
73
|
+
return milestoneIds.filter((milestoneId) => {
|
|
74
|
+
const bareId = bareDuplicateMilestoneId(milestoneId);
|
|
75
|
+
return !bareId || !allIds.has(bareId);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
61
78
|
function checkProjectionCheckboxDbStatus(basePath, milestoneIds, issues) {
|
|
62
79
|
for (const milestoneId of milestoneIds) {
|
|
63
80
|
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
@@ -322,7 +339,9 @@ function staleMilestonesArtifactRowFixable(basePath, row, artifactRows) {
|
|
|
322
339
|
}
|
|
323
340
|
function stalePhasesArtifactRowFixable(basePath, row, artifactRows) {
|
|
324
341
|
const issuePath = artifactPathRelativeToGsd(row.path);
|
|
325
|
-
return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) &&
|
|
342
|
+
return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) &&
|
|
343
|
+
(hasPresentFlatPhaseReplacement(basePath, row, artifactRows) ||
|
|
344
|
+
hasPresentMilestonesReplacement(basePath, row, artifactRows));
|
|
326
345
|
}
|
|
327
346
|
function staleArtifactRowFixable(basePath, row, artifactRows) {
|
|
328
347
|
return staleMilestonesArtifactRowFixable(basePath, row, artifactRows) ||
|
|
@@ -658,7 +677,7 @@ export async function checkEngineHealth(basePath, issues, fixesApplied, options)
|
|
|
658
677
|
// block doctor.
|
|
659
678
|
try {
|
|
660
679
|
if (isDbAvailable()) {
|
|
661
|
-
checkProjectionCheckboxDbStatus(basePath, getAllMilestones().map((milestone) => milestone.id), issues);
|
|
680
|
+
checkProjectionCheckboxDbStatus(basePath, checkboxDbStatusMilestoneIds(basePath, getAllMilestones().map((milestone) => milestone.id)), issues);
|
|
662
681
|
}
|
|
663
682
|
}
|
|
664
683
|
catch {
|
|
@@ -130,6 +130,17 @@ function isInsideFlatProjectionBackup(backupDir, src) {
|
|
|
130
130
|
const phaseBackupDir = join(backupDir, "__phases");
|
|
131
131
|
return src === phaseBackupDir || src.startsWith(`${phaseBackupDir}/`) || src.startsWith(`${phaseBackupDir}\\`);
|
|
132
132
|
}
|
|
133
|
+
function removeFlatProjectionBackup(backupDir) {
|
|
134
|
+
const phaseBackupDir = join(backupDir, "__phases");
|
|
135
|
+
if (!existsSync(phaseBackupDir))
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
removePathWithRetries(phaseBackupDir);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
logWarning("migration", `flat-phase migration succeeded but could not remove temporary ${LAYOUT_SEGMENTS.level1}/ backup: ${err.message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
133
144
|
function restoreFlatProjectionFromBackup(basePath, backupDir) {
|
|
134
145
|
const phaseBackupDir = join(backupDir, "__phases");
|
|
135
146
|
if (!existsSync(phaseBackupDir))
|
|
@@ -433,4 +444,5 @@ export async function migrateToFlatPhase(basePath) {
|
|
|
433
444
|
catch (err) {
|
|
434
445
|
logWarning("migration", `flat-phase migration succeeded but could not remove ${LEGACY_MIGRATING_SEGMENT}: ${err.message}`);
|
|
435
446
|
}
|
|
447
|
+
removeFlatProjectionBackup(backupDir);
|
|
436
448
|
}
|
|
@@ -38,6 +38,7 @@ export * from "./db/queries.js";
|
|
|
38
38
|
export * from "./db/writers/cascades.js";
|
|
39
39
|
import { TERMINAL_STATUS_SQL } from "./db/sql-constants.js";
|
|
40
40
|
import { applyStatusTransition } from "./db/writers/status.js";
|
|
41
|
+
import { LAYOUT_SEGMENTS, derivePhaseSlug, milestoneIdToPhaseNum, milestoneIdUniqueSuffix, phaseDirName, } from "./layout-policy.js";
|
|
41
42
|
export function insertDecision(d) {
|
|
42
43
|
if (!getDbOrNull())
|
|
43
44
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -169,6 +170,44 @@ export function insertArtifact(a) {
|
|
|
169
170
|
":content_hash": contentHash,
|
|
170
171
|
});
|
|
171
172
|
}
|
|
173
|
+
function canonicalPhaseDirNameForDb(milestoneId, title) {
|
|
174
|
+
const phaseNum = milestoneIdToPhaseNum(milestoneId);
|
|
175
|
+
const slug = derivePhaseSlug(title || milestoneId);
|
|
176
|
+
const suffix = milestoneIdUniqueSuffix(milestoneId);
|
|
177
|
+
return phaseDirName(phaseNum, suffix ? `${suffix}-${slug}` : slug);
|
|
178
|
+
}
|
|
179
|
+
function reconcileMilestonePhaseArtifactPaths(milestoneId, title) {
|
|
180
|
+
const db = getDbOrNull();
|
|
181
|
+
const phaseNumPrefix = String(milestoneIdToPhaseNum(milestoneId)).padStart(2, "0");
|
|
182
|
+
const canonicalDir = canonicalPhaseDirNameForDb(milestoneId, title);
|
|
183
|
+
const staleRows = db.prepare(`SELECT path
|
|
184
|
+
FROM artifacts
|
|
185
|
+
WHERE milestone_id = :milestone_id
|
|
186
|
+
AND path LIKE :phase_prefix`).all({
|
|
187
|
+
":milestone_id": milestoneId,
|
|
188
|
+
":phase_prefix": `${LAYOUT_SEGMENTS.level1}/${phaseNumPrefix}-%/%`,
|
|
189
|
+
});
|
|
190
|
+
const updatePath = db.prepare("UPDATE artifacts SET path = :new_path WHERE path = :old_path");
|
|
191
|
+
const deletePath = db.prepare("DELETE FROM artifacts WHERE path = :path");
|
|
192
|
+
const existingPath = db.prepare("SELECT 1 AS present FROM artifacts WHERE path = :path");
|
|
193
|
+
for (const row of staleRows) {
|
|
194
|
+
const parts = row.path.split("/");
|
|
195
|
+
if (parts.length < 3)
|
|
196
|
+
continue;
|
|
197
|
+
if (parts[0] !== LAYOUT_SEGMENTS.level1)
|
|
198
|
+
continue;
|
|
199
|
+
if (parts[1] === canonicalDir)
|
|
200
|
+
continue;
|
|
201
|
+
if (!parts[1]?.startsWith(`${phaseNumPrefix}-`))
|
|
202
|
+
continue;
|
|
203
|
+
const newPath = [LAYOUT_SEGMENTS.level1, canonicalDir, ...parts.slice(2)].join("/");
|
|
204
|
+
if (existingPath.get({ ":path": newPath })) {
|
|
205
|
+
deletePath.run({ ":path": row.path });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
updatePath.run({ ":new_path": newPath, ":old_path": row.path });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
172
211
|
export function insertMilestone(m) {
|
|
173
212
|
if (!getDbOrNull())
|
|
174
213
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -207,37 +246,42 @@ export function insertMilestone(m) {
|
|
|
207
246
|
export function upsertMilestonePlanning(milestoneId, planning) {
|
|
208
247
|
if (!getDbOrNull())
|
|
209
248
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
249
|
+
transaction(() => {
|
|
250
|
+
getDbOrNull().prepare(`UPDATE milestones SET
|
|
251
|
+
title = COALESCE(NULLIF(:title, ''), title),
|
|
252
|
+
status = COALESCE(NULLIF(:status, ''), status),
|
|
253
|
+
depends_on = COALESCE(:depends_on, depends_on),
|
|
254
|
+
vision = COALESCE(:vision, vision),
|
|
255
|
+
success_criteria = COALESCE(:success_criteria, success_criteria),
|
|
256
|
+
key_risks = COALESCE(:key_risks, key_risks),
|
|
257
|
+
proof_strategy = COALESCE(:proof_strategy, proof_strategy),
|
|
258
|
+
verification_contract = COALESCE(:verification_contract, verification_contract),
|
|
259
|
+
verification_integration = COALESCE(:verification_integration, verification_integration),
|
|
260
|
+
verification_operational = COALESCE(:verification_operational, verification_operational),
|
|
261
|
+
verification_uat = COALESCE(:verification_uat, verification_uat),
|
|
262
|
+
definition_of_done = COALESCE(:definition_of_done, definition_of_done),
|
|
263
|
+
requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
|
|
264
|
+
boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
|
|
265
|
+
WHERE id = :id`).run({
|
|
266
|
+
":id": milestoneId,
|
|
267
|
+
":title": planning.title ?? "",
|
|
268
|
+
":status": planning.status ?? "",
|
|
269
|
+
":depends_on": planning.depends_on ? JSON.stringify(planning.depends_on) : null,
|
|
270
|
+
":vision": planning.vision ?? null,
|
|
271
|
+
":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
|
|
272
|
+
":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
|
|
273
|
+
":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
|
|
274
|
+
":verification_contract": planning.verificationContract ?? null,
|
|
275
|
+
":verification_integration": planning.verificationIntegration ?? null,
|
|
276
|
+
":verification_operational": planning.verificationOperational ?? null,
|
|
277
|
+
":verification_uat": planning.verificationUat ?? null,
|
|
278
|
+
":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
|
|
279
|
+
":requirement_coverage": planning.requirementCoverage ?? null,
|
|
280
|
+
":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
|
|
281
|
+
});
|
|
282
|
+
const finalTitle = planning.title?.trim();
|
|
283
|
+
if (finalTitle)
|
|
284
|
+
reconcileMilestonePhaseArtifactPaths(milestoneId, finalTitle);
|
|
241
285
|
});
|
|
242
286
|
}
|
|
243
287
|
export function insertSlice(s) {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, unlinkSync, } from "node:fs";
|
|
22
22
|
import { join } from "node:path";
|
|
23
23
|
import { reconcileWorktreeDb } from "./gsd-db.js";
|
|
24
|
-
import { resolveGsdPathContract } from "./paths.js";
|
|
24
|
+
import { dirIsContentBearingLegacyMilestone, resolveGsdPathContract } from "./paths.js";
|
|
25
25
|
import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
26
26
|
import { logError, logWarning } from "./workflow-logger.js";
|
|
27
27
|
// ─── Private helpers ──────────────────────────────────────────────────────
|
|
@@ -129,6 +129,8 @@ function syncOtherMilestoneArtifacts(srcMilestonesDir, dstMilestonesDir, current
|
|
|
129
129
|
if (milestoneEntry.name === currentMilestoneId)
|
|
130
130
|
continue;
|
|
131
131
|
const srcMilestoneDir = join(srcMilestonesDir, milestoneEntry.name);
|
|
132
|
+
if (!dirIsContentBearingLegacyMilestone(srcMilestoneDir))
|
|
133
|
+
continue;
|
|
132
134
|
const dstMilestoneDir = join(dstMilestonesDir, milestoneEntry.name);
|
|
133
135
|
// Additively project the entire milestone subtree (force:false), not just
|
|
134
136
|
// top-level files. Prior completed milestones keep their per-slice and
|
|
@@ -219,25 +221,25 @@ export function _projectRootToWorktreeImpl(projectRoot, worktreePath_, milestone
|
|
|
219
221
|
// Without this, worktree-local files (e.g. VALIDATION.md written
|
|
220
222
|
// by validate-milestone) get clobbered by stale project root copies,
|
|
221
223
|
// causing an infinite re-validation loop (#1886).
|
|
222
|
-
safeCopyRecursive(join(prGsd, "milestones", milestoneId), join(wtGsd, "milestones", milestoneId), { force: false });
|
|
223
|
-
// Additively project the full subtree of every OTHER milestone so
|
|
224
|
-
// worktree-bound units can read prior-milestone context artifacts and so
|
|
225
|
-
// completed milestones retain their per-slice/per-task SUMMARY.md and
|
|
226
|
-
// UAT.md on the worktree filesystem (otherwise the stale-render detector
|
|
227
|
-
// flags them as "complete in DB but missing on disk").
|
|
228
|
-
syncOtherMilestoneArtifacts(join(prGsd, "milestones"), join(wtGsd, "milestones"), milestoneId);
|
|
229
|
-
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
230
|
-
// The additive-only copy above preserves worktree-local files, but
|
|
231
|
-
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
232
|
-
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
233
|
-
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
234
|
-
// session resume the DB is rebuilt from disk, and if the stale ASSESSMENT
|
|
235
|
-
// persists, checkNeedsRunUat finds no passing verdict → re-dispatches
|
|
236
|
-
// run-uat indefinitely (stuck-loop ×9).
|
|
237
224
|
const prMilestoneDir = join(prGsd, "milestones", milestoneId);
|
|
238
225
|
const wtMilestoneDir = join(wtGsd, "milestones", milestoneId);
|
|
239
|
-
|
|
240
|
-
|
|
226
|
+
if (dirIsContentBearingLegacyMilestone(prMilestoneDir)) {
|
|
227
|
+
safeCopyRecursive(prMilestoneDir, wtMilestoneDir, { force: false });
|
|
228
|
+
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
229
|
+
// The additive-only copy above preserves worktree-local files, but
|
|
230
|
+
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
231
|
+
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
232
|
+
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
233
|
+
// session resume the DB is rebuilt from disk, and if the stale ASSESSMENT
|
|
234
|
+
// persists, checkNeedsRunUat finds no passing verdict → re-dispatches
|
|
235
|
+
// run-uat indefinitely (stuck-loop ×9).
|
|
236
|
+
forceOverwriteValidationWithVerdict(prMilestoneDir, wtMilestoneDir, milestoneId);
|
|
237
|
+
forceOverwriteAssessmentsWithVerdict(prMilestoneDir, wtMilestoneDir);
|
|
238
|
+
}
|
|
239
|
+
// Additively project the full subtree of every OTHER content-bearing legacy
|
|
240
|
+
// milestone so worktree-bound units can read prior-milestone context artifacts
|
|
241
|
+
// without recreating empty/meta-only milestones/ scaffolds in flat-phase projects.
|
|
242
|
+
syncOtherMilestoneArtifacts(join(prGsd, "milestones"), join(wtGsd, "milestones"), milestoneId);
|
|
241
243
|
// Forward-sync completed-units.json from project root to worktree.
|
|
242
244
|
// Project root is authoritative for completion state after crash recovery;
|
|
243
245
|
// without this, the worktree re-dispatches already-completed units (#1886).
|