gsd-pi 2.48.0-dev.ced2eca → 2.49.0-dev.9e177e9
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/headless-ui.js +12 -2
- package/dist/headless.js +29 -13
- package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
- package/dist/resources/extensions/gsd/auto/phases.js +11 -11
- package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
- package/dist/resources/extensions/gsd/auto-dispatch.js +33 -21
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -24
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
- package/dist/resources/extensions/gsd/auto-start.js +4 -31
- package/dist/resources/extensions/gsd/auto-timers.js +2 -2
- package/dist/resources/extensions/gsd/auto-verification.js +4 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +257 -113
- package/dist/resources/extensions/gsd/auto.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
- package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
- package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
- package/dist/resources/extensions/gsd/doctor.js +9 -1
- package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
- package/dist/resources/extensions/gsd/git-service.js +9 -10
- package/dist/resources/extensions/gsd/gsd-db.js +124 -1
- package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
- package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
- package/dist/resources/extensions/gsd/preferences-types.js +2 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +29 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
- package/dist/resources/extensions/gsd/session-forensics.js +6 -11
- package/dist/resources/extensions/gsd/session-lock.js +67 -56
- package/dist/resources/extensions/gsd/state.js +34 -7
- package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/dist/resources/extensions/gsd/templates/plan.md +16 -0
- package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
- package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
- package/dist/resources/extensions/gsd/worktree.js +3 -2
- package/dist/resources/extensions/remote-questions/config.js +3 -5
- package/dist/resources/extensions/search-the-web/native-search.js +8 -3
- package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
- package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- 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/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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 +19 -19
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.7c75ac378de0f2b5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
- package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- 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 +7 -32
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
- package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
- package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +10 -11
- package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto/types.ts +13 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +39 -21
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -28
- package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
- package/src/resources/extensions/gsd/auto-start.ts +7 -27
- package/src/resources/extensions/gsd/auto-timers.ts +2 -2
- package/src/resources/extensions/gsd/auto-verification.ts +4 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +305 -108
- package/src/resources/extensions/gsd/auto.ts +11 -10
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
- package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
- package/src/resources/extensions/gsd/doctor.ts +9 -1
- package/src/resources/extensions/gsd/extension-manifest.json +1 -1
- package/src/resources/extensions/gsd/git-service.ts +7 -15
- package/src/resources/extensions/gsd/gsd-db.ts +150 -2
- package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
- package/src/resources/extensions/gsd/preferences-types.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +28 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
- package/src/resources/extensions/gsd/session-forensics.ts +6 -11
- package/src/resources/extensions/gsd/session-lock.ts +92 -64
- package/src/resources/extensions/gsd/state.ts +38 -5
- package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/src/resources/extensions/gsd/templates/plan.md +16 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +9 -12
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/quality-gates.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
- package/src/resources/extensions/gsd/types.ts +30 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
- package/src/resources/extensions/gsd/verification-gate.ts +0 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
- package/src/resources/extensions/gsd/worktree.ts +3 -2
- package/src/resources/extensions/remote-questions/config.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +8 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
- package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
- package/dist/resources/extensions/gsd/resource-version.js +0 -97
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
- package/src/resources/extensions/gsd/resource-version.ts +0 -101
- /package/dist/web/standalone/.next/static/{PTL5V00OW8q4-092tUQKx → vNN0h0emdEi8l_npi8poE}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{PTL5V00OW8q4-092tUQKx → vNN0h0emdEi8l_npi8poE}/_ssgManifest.js +0 -0
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* utility.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { loadFile, parseContinue, parseSummary,
|
|
9
|
+
import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection, parseTaskPlanFile } from "./files.js";
|
|
10
10
|
import type { Override, UatType } from "./files.js";
|
|
11
|
+
import { hasVerdict, getUatType } from "./verdict-parser.js";
|
|
11
12
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
12
13
|
import {
|
|
13
14
|
resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
|
|
@@ -23,6 +24,7 @@ import { getLoadedSkills, type Skill } from "@gsd/pi-coding-agent";
|
|
|
23
24
|
import { join, basename } from "node:path";
|
|
24
25
|
import { existsSync } from "node:fs";
|
|
25
26
|
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
27
|
+
import { getPendingGates } from "./gsd-db.js";
|
|
26
28
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
27
29
|
|
|
28
30
|
// ─── Preamble Cap ─────────────────────────────────────────────────────────────
|
|
@@ -419,9 +421,17 @@ function resolvePreferredSkillNames(
|
|
|
419
421
|
.map(skill => normalizeSkillReference(skill.name));
|
|
420
422
|
}
|
|
421
423
|
|
|
424
|
+
/** Skill names must be lowercase alphanumeric with hyphens — reject anything else
|
|
425
|
+
* to prevent prompt injection via crafted directory names. */
|
|
426
|
+
const SAFE_SKILL_NAME = /^[a-z0-9][a-z0-9-]*$/;
|
|
427
|
+
|
|
422
428
|
function formatSkillActivationBlock(skillNames: string[]): string {
|
|
423
|
-
|
|
424
|
-
|
|
429
|
+
const safe = skillNames.filter(name => SAFE_SKILL_NAME.test(name));
|
|
430
|
+
if (safe.length === 0) return "";
|
|
431
|
+
// Use explicit parameter syntax so LLMs pass { skill: "..." } instead of { name: "..." }.
|
|
432
|
+
// The function-call-like syntax `Skill('name')` led LLMs to infer a positional
|
|
433
|
+
// parameter name, causing tool validation failures — see #2224.
|
|
434
|
+
const calls = safe.map(name => `Call Skill({ skill: '${name}' })`).join('. ');
|
|
425
435
|
return `<skill_activation>${calls}.</skill_activation>`;
|
|
426
436
|
}
|
|
427
437
|
|
|
@@ -772,12 +782,9 @@ export async function checkNeedsRunUat(
|
|
|
772
782
|
if (!uatFile) return null;
|
|
773
783
|
const uatContent = await loadFile(uatFile);
|
|
774
784
|
if (!uatContent) return null;
|
|
775
|
-
|
|
776
|
-
if (
|
|
777
|
-
|
|
778
|
-
if (hasResult) return null;
|
|
779
|
-
}
|
|
780
|
-
const uatType = extractUatType(uatContent) ?? "artifact-driven";
|
|
785
|
+
// If the UAT file already contains a verdict, UAT has been run — skip
|
|
786
|
+
if (hasVerdict(uatContent)) return null;
|
|
787
|
+
const uatType = getUatType(uatContent);
|
|
781
788
|
return { sliceId: sid, uatType };
|
|
782
789
|
}
|
|
783
790
|
}
|
|
@@ -799,12 +806,9 @@ export async function checkNeedsRunUat(
|
|
|
799
806
|
if (!uatFileFb) return null;
|
|
800
807
|
const uatContentFb = await loadFile(uatFileFb);
|
|
801
808
|
if (!uatContentFb) return null;
|
|
802
|
-
|
|
803
|
-
if (
|
|
804
|
-
|
|
805
|
-
if (hasResultFb) return null;
|
|
806
|
-
}
|
|
807
|
-
const uatTypeFb = extractUatType(uatContentFb) ?? "artifact-driven";
|
|
809
|
+
// If the UAT file already contains a verdict, UAT has been run — skip
|
|
810
|
+
if (hasVerdict(uatContentFb)) return null;
|
|
811
|
+
const uatTypeFb = getUatType(uatContentFb);
|
|
808
812
|
return { sliceId: uatSid, uatType: uatTypeFb };
|
|
809
813
|
}
|
|
810
814
|
|
|
@@ -1349,8 +1353,8 @@ export async function buildValidateMilestonePrompt(
|
|
|
1349
1353
|
const summaryRel = relSliceFile(base, mid, sid, "SUMMARY");
|
|
1350
1354
|
inlined.push(await inlineFile(summaryPath, summaryRel, `${sid} Summary`));
|
|
1351
1355
|
|
|
1352
|
-
const uatPath = resolveSliceFile(base, mid, sid, "UAT
|
|
1353
|
-
const uatRel = relSliceFile(base, mid, sid, "UAT
|
|
1356
|
+
const uatPath = resolveSliceFile(base, mid, sid, "UAT");
|
|
1357
|
+
const uatRel = relSliceFile(base, mid, sid, "UAT");
|
|
1354
1358
|
const uatInline = await inlineFileOptional(uatPath, uatRel, `${sid} UAT Result`);
|
|
1355
1359
|
if (uatInline) inlined.push(uatInline);
|
|
1356
1360
|
}
|
|
@@ -1501,8 +1505,8 @@ export async function buildRunUatPrompt(
|
|
|
1501
1505
|
|
|
1502
1506
|
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1503
1507
|
|
|
1504
|
-
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT
|
|
1505
|
-
const uatType =
|
|
1508
|
+
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT"));
|
|
1509
|
+
const uatType = getUatType(uatContent);
|
|
1506
1510
|
|
|
1507
1511
|
return loadPrompt("run-uat", {
|
|
1508
1512
|
workingDirectory: base,
|
|
@@ -1659,6 +1663,96 @@ export async function buildReactiveExecutePrompt(
|
|
|
1659
1663
|
});
|
|
1660
1664
|
}
|
|
1661
1665
|
|
|
1666
|
+
// ─── Gate Evaluation ──────────────────────────────────────────────────────
|
|
1667
|
+
|
|
1668
|
+
const GATE_QUESTIONS: Record<string, { question: string; guidance: string }> = {
|
|
1669
|
+
Q3: {
|
|
1670
|
+
question: "How can this be exploited?",
|
|
1671
|
+
guidance: [
|
|
1672
|
+
"Identify abuse scenarios: parameter tampering, replay attacks, privilege escalation.",
|
|
1673
|
+
"Map data exposure risks: PII, tokens, secrets accessible through this slice.",
|
|
1674
|
+
"Define input trust boundaries: untrusted user input reaching DB, API, or filesystem.",
|
|
1675
|
+
"If none apply, return verdict 'omitted' with rationale explaining why.",
|
|
1676
|
+
].join("\n"),
|
|
1677
|
+
},
|
|
1678
|
+
Q4: {
|
|
1679
|
+
question: "What existing promises does this break?",
|
|
1680
|
+
guidance: [
|
|
1681
|
+
"List which existing requirements (R001, R003, etc.) are touched by this slice.",
|
|
1682
|
+
"Identify what must be re-tested after shipping.",
|
|
1683
|
+
"Flag decisions that should be revisited given the new scope.",
|
|
1684
|
+
"If no existing requirements are affected, return verdict 'omitted'.",
|
|
1685
|
+
].join("\n"),
|
|
1686
|
+
},
|
|
1687
|
+
};
|
|
1688
|
+
|
|
1689
|
+
export async function buildGateEvaluatePrompt(
|
|
1690
|
+
mid: string, midTitle: string, sid: string, sTitle: string,
|
|
1691
|
+
base: string,
|
|
1692
|
+
): Promise<string> {
|
|
1693
|
+
const pending = getPendingGates(mid, sid, "slice");
|
|
1694
|
+
|
|
1695
|
+
// Load the slice plan for context
|
|
1696
|
+
const planFile = resolveSliceFile(base, mid, sid, "PLAN");
|
|
1697
|
+
const planContent = planFile ? (await loadFile(planFile)) ?? "(plan file empty)" : "(plan file not found)";
|
|
1698
|
+
|
|
1699
|
+
// Build per-gate subagent prompts
|
|
1700
|
+
const subagentSections: string[] = [];
|
|
1701
|
+
const gateListLines: string[] = [];
|
|
1702
|
+
|
|
1703
|
+
for (const gate of pending) {
|
|
1704
|
+
const meta = GATE_QUESTIONS[gate.gate_id];
|
|
1705
|
+
if (!meta) continue;
|
|
1706
|
+
|
|
1707
|
+
gateListLines.push(`- **${gate.gate_id}**: ${meta.question}`);
|
|
1708
|
+
|
|
1709
|
+
const subPrompt = [
|
|
1710
|
+
`You are evaluating quality gate **${gate.gate_id}** for slice ${sid} (${sTitle}).`,
|
|
1711
|
+
"",
|
|
1712
|
+
`## Question: ${meta.question}`,
|
|
1713
|
+
"",
|
|
1714
|
+
meta.guidance,
|
|
1715
|
+
"",
|
|
1716
|
+
"## Slice Plan",
|
|
1717
|
+
"",
|
|
1718
|
+
planContent,
|
|
1719
|
+
"",
|
|
1720
|
+
"## Instructions",
|
|
1721
|
+
"",
|
|
1722
|
+
"Analyze the slice plan above and answer the gate question.",
|
|
1723
|
+
`Call the \`gsd_save_gate_result\` tool with:`,
|
|
1724
|
+
`- \`milestoneId\`: "${mid}"`,
|
|
1725
|
+
`- \`sliceId\`: "${sid}"`,
|
|
1726
|
+
`- \`gateId\`: "${gate.gate_id}"`,
|
|
1727
|
+
"- `verdict`: \"pass\" (no concerns), \"flag\" (concerns found), or \"omitted\" (not applicable)",
|
|
1728
|
+
"- `rationale`: one-sentence justification",
|
|
1729
|
+
"- `findings`: detailed markdown findings (or empty if omitted)",
|
|
1730
|
+
].join("\n");
|
|
1731
|
+
|
|
1732
|
+
subagentSections.push([
|
|
1733
|
+
`### ${gate.gate_id}: ${meta.question}`,
|
|
1734
|
+
"",
|
|
1735
|
+
"Use this as the prompt for a `subagent` call:",
|
|
1736
|
+
"",
|
|
1737
|
+
"```",
|
|
1738
|
+
subPrompt,
|
|
1739
|
+
"```",
|
|
1740
|
+
].join("\n"));
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
return loadPrompt("gate-evaluate", {
|
|
1744
|
+
workingDirectory: base,
|
|
1745
|
+
milestoneId: mid,
|
|
1746
|
+
milestoneTitle: midTitle,
|
|
1747
|
+
sliceId: sid,
|
|
1748
|
+
sliceTitle: sTitle,
|
|
1749
|
+
slicePlanContent: planContent,
|
|
1750
|
+
gateCount: String(pending.length),
|
|
1751
|
+
gateList: gateListLines.join("\n"),
|
|
1752
|
+
subagentPrompts: subagentSections.join("\n\n---\n\n"),
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1662
1756
|
export async function buildRewriteDocsPrompt(
|
|
1663
1757
|
mid: string, midTitle: string,
|
|
1664
1758
|
activeSlice: { id: string; title: string } | null,
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
11
11
|
import { parseUnitId } from "./unit-id.js";
|
|
12
12
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
13
|
-
import { clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
14
13
|
import { clearParseCache } from "./files.js";
|
|
15
14
|
import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
|
|
16
15
|
import { isDbAvailable, getTask, getSlice, getSliceTasks } from "./gsd-db.js";
|
|
@@ -24,18 +23,13 @@ import {
|
|
|
24
23
|
nativeResetHard,
|
|
25
24
|
} from "./native-git-bridge.js";
|
|
26
25
|
import {
|
|
27
|
-
resolveMilestonePath,
|
|
28
26
|
resolveSlicePath,
|
|
29
27
|
resolveSliceFile,
|
|
30
28
|
resolveTasksDir,
|
|
31
29
|
resolveTaskFiles,
|
|
32
30
|
relMilestoneFile,
|
|
33
31
|
relSliceFile,
|
|
34
|
-
relSlicePath,
|
|
35
|
-
relTaskFile,
|
|
36
|
-
buildMilestoneFileName,
|
|
37
32
|
buildSliceFileName,
|
|
38
|
-
buildTaskFileName,
|
|
39
33
|
resolveMilestoneFile,
|
|
40
34
|
clearPathCache,
|
|
41
35
|
resolveGsdRootFile,
|
|
@@ -49,81 +43,15 @@ import {
|
|
|
49
43
|
} from "node:fs";
|
|
50
44
|
import { execFileSync } from "node:child_process";
|
|
51
45
|
import { dirname, join } from "node:path";
|
|
46
|
+
import {
|
|
47
|
+
resolveExpectedArtifactPath,
|
|
48
|
+
diagnoseExpectedArtifact,
|
|
49
|
+
} from "./auto-artifact-paths.js";
|
|
52
50
|
|
|
53
|
-
//
|
|
51
|
+
// Re-export so existing consumers of auto-recovery.ts keep working.
|
|
52
|
+
export { resolveExpectedArtifactPath, diagnoseExpectedArtifact };
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
* Resolve the expected artifact for a unit to an absolute path.
|
|
57
|
-
*/
|
|
58
|
-
export function resolveExpectedArtifactPath(
|
|
59
|
-
unitType: string,
|
|
60
|
-
unitId: string,
|
|
61
|
-
base: string,
|
|
62
|
-
): string | null {
|
|
63
|
-
const parts = unitId.split("/");
|
|
64
|
-
const mid = parts[0]!;
|
|
65
|
-
const sid = parts[1];
|
|
66
|
-
switch (unitType) {
|
|
67
|
-
case "discuss-milestone": {
|
|
68
|
-
const dir = resolveMilestonePath(base, mid);
|
|
69
|
-
return dir ? join(dir, buildMilestoneFileName(mid, "CONTEXT")) : null;
|
|
70
|
-
}
|
|
71
|
-
case "research-milestone": {
|
|
72
|
-
const dir = resolveMilestonePath(base, mid);
|
|
73
|
-
return dir ? join(dir, buildMilestoneFileName(mid, "RESEARCH")) : null;
|
|
74
|
-
}
|
|
75
|
-
case "plan-milestone": {
|
|
76
|
-
const dir = resolveMilestonePath(base, mid);
|
|
77
|
-
return dir ? join(dir, buildMilestoneFileName(mid, "ROADMAP")) : null;
|
|
78
|
-
}
|
|
79
|
-
case "research-slice": {
|
|
80
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
81
|
-
return dir ? join(dir, buildSliceFileName(sid!, "RESEARCH")) : null;
|
|
82
|
-
}
|
|
83
|
-
case "plan-slice": {
|
|
84
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
85
|
-
return dir ? join(dir, buildSliceFileName(sid!, "PLAN")) : null;
|
|
86
|
-
}
|
|
87
|
-
case "reassess-roadmap": {
|
|
88
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
89
|
-
return dir ? join(dir, buildSliceFileName(sid!, "ASSESSMENT")) : null;
|
|
90
|
-
}
|
|
91
|
-
case "run-uat": {
|
|
92
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
93
|
-
return dir ? join(dir, buildSliceFileName(sid!, "UAT-RESULT")) : null;
|
|
94
|
-
}
|
|
95
|
-
case "execute-task": {
|
|
96
|
-
const tid = parts[2];
|
|
97
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
98
|
-
return dir && tid
|
|
99
|
-
? join(dir, "tasks", buildTaskFileName(tid, "SUMMARY"))
|
|
100
|
-
: null;
|
|
101
|
-
}
|
|
102
|
-
case "complete-slice": {
|
|
103
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
104
|
-
return dir ? join(dir, buildSliceFileName(sid!, "SUMMARY")) : null;
|
|
105
|
-
}
|
|
106
|
-
case "validate-milestone": {
|
|
107
|
-
const dir = resolveMilestonePath(base, mid);
|
|
108
|
-
return dir ? join(dir, buildMilestoneFileName(mid, "VALIDATION")) : null;
|
|
109
|
-
}
|
|
110
|
-
case "complete-milestone": {
|
|
111
|
-
const dir = resolveMilestonePath(base, mid);
|
|
112
|
-
return dir ? join(dir, buildMilestoneFileName(mid, "SUMMARY")) : null;
|
|
113
|
-
}
|
|
114
|
-
case "replan-slice": {
|
|
115
|
-
const dir = resolveSlicePath(base, mid, sid!);
|
|
116
|
-
return dir ? join(dir, buildSliceFileName(sid!, "REPLAN")) : null;
|
|
117
|
-
}
|
|
118
|
-
case "rewrite-docs":
|
|
119
|
-
return null;
|
|
120
|
-
case "reactive-execute":
|
|
121
|
-
// Reactive execute produces multiple task summaries — verified separately
|
|
122
|
-
return null;
|
|
123
|
-
default:
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
54
|
+
// ─── Artifact Resolution & Verification ───────────────────────────────────────
|
|
127
55
|
|
|
128
56
|
/**
|
|
129
57
|
* Check whether a milestone produced implementation artifacts (non-`.gsd/` files)
|
|
@@ -302,6 +230,35 @@ export function verifyExpectedArtifact(
|
|
|
302
230
|
return true;
|
|
303
231
|
}
|
|
304
232
|
|
|
233
|
+
// Gate-evaluate: verify that each dispatched gate has been resolved in the DB.
|
|
234
|
+
// The unitId encodes the batch: "{mid}/{sid}/gates+Q3,Q4"
|
|
235
|
+
if (unitType === "gate-evaluate") {
|
|
236
|
+
const parts = unitId.split("/");
|
|
237
|
+
const mid = parts[0];
|
|
238
|
+
const sid = parts[1];
|
|
239
|
+
const batchPart = parts[2]; // "gates+Q3,Q4"
|
|
240
|
+
if (!mid || !sid || !batchPart) return false;
|
|
241
|
+
|
|
242
|
+
const plusIdx = batchPart.indexOf("+");
|
|
243
|
+
if (plusIdx === -1) return true; // no specific gates encoded — pass
|
|
244
|
+
|
|
245
|
+
const gateIds = batchPart.slice(plusIdx + 1).split(",").filter(Boolean);
|
|
246
|
+
if (gateIds.length === 0) return true;
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const { getPendingGates: getPending } = require("./gsd-db.js");
|
|
250
|
+
const pending = getPending(mid, sid, "slice");
|
|
251
|
+
const pendingIds = new Set(pending.map((g: any) => g.gate_id));
|
|
252
|
+
// All dispatched gates must no longer be pending
|
|
253
|
+
for (const gid of gateIds) {
|
|
254
|
+
if (pendingIds.has(gid)) return false;
|
|
255
|
+
}
|
|
256
|
+
} catch {
|
|
257
|
+
// DB unavailable — treat as verified to avoid blocking
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
305
262
|
const absPath = resolveExpectedArtifactPath(unitType, unitId, base);
|
|
306
263
|
// For unit types with no verifiable artifact (null path), the parent directory
|
|
307
264
|
// is missing on disk — treat as stale completion state so the key gets evicted (#313).
|
|
@@ -471,50 +428,37 @@ export function writeBlockerPlaceholder(
|
|
|
471
428
|
return diagnoseExpectedArtifact(unitType, unitId, base);
|
|
472
429
|
}
|
|
473
430
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
)
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
431
|
+
// ─── Merge State Reconciliation ───────────────────────────────────────────────
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Best-effort abort of a pending merge/squash and hard-reset to HEAD.
|
|
435
|
+
* Handles both real merges (MERGE_HEAD) and squash merges (SQUASH_MSG).
|
|
436
|
+
*/
|
|
437
|
+
function abortAndResetMerge(
|
|
438
|
+
basePath: string,
|
|
439
|
+
hasMergeHead: boolean,
|
|
440
|
+
squashMsgPath: string,
|
|
441
|
+
): void {
|
|
442
|
+
if (hasMergeHead) {
|
|
443
|
+
try {
|
|
444
|
+
nativeMergeAbort(basePath);
|
|
445
|
+
} catch {
|
|
446
|
+
/* best-effort */
|
|
447
|
+
}
|
|
448
|
+
} else if (squashMsgPath) {
|
|
449
|
+
try {
|
|
450
|
+
unlinkSync(squashMsgPath);
|
|
451
|
+
} catch {
|
|
452
|
+
/* best-effort */
|
|
496
453
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
return "Active overrides resolved in .gsd/OVERRIDES.md + plan documents updated";
|
|
503
|
-
case "reassess-roadmap":
|
|
504
|
-
return `${relSliceFile(base, mid!, sid!, "ASSESSMENT")} (roadmap reassessment)`;
|
|
505
|
-
case "run-uat":
|
|
506
|
-
return `${relSliceFile(base, mid!, sid!, "UAT-RESULT")} (UAT result)`;
|
|
507
|
-
case "validate-milestone":
|
|
508
|
-
return `${relMilestoneFile(base, mid!, "VALIDATION")} (milestone validation report)`;
|
|
509
|
-
case "complete-milestone":
|
|
510
|
-
return `${relMilestoneFile(base, mid!, "SUMMARY")} (milestone summary)`;
|
|
511
|
-
default:
|
|
512
|
-
return null;
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
nativeResetHard(basePath);
|
|
457
|
+
} catch {
|
|
458
|
+
/* best-effort */
|
|
513
459
|
}
|
|
514
460
|
}
|
|
515
461
|
|
|
516
|
-
// ─── Merge State Reconciliation ───────────────────────────────────────────────
|
|
517
|
-
|
|
518
462
|
/**
|
|
519
463
|
* Detect leftover merge state from a prior session and reconcile it.
|
|
520
464
|
* If MERGE_HEAD or SQUASH_MSG exists, check whether conflicts are resolved.
|
|
@@ -571,24 +515,7 @@ export function reconcileMergeState(
|
|
|
571
515
|
}
|
|
572
516
|
}
|
|
573
517
|
if (!resolved) {
|
|
574
|
-
|
|
575
|
-
try {
|
|
576
|
-
nativeMergeAbort(basePath);
|
|
577
|
-
} catch {
|
|
578
|
-
/* best-effort */
|
|
579
|
-
}
|
|
580
|
-
} else if (hasSquashMsg) {
|
|
581
|
-
try {
|
|
582
|
-
unlinkSync(squashMsgPath);
|
|
583
|
-
} catch {
|
|
584
|
-
/* best-effort */
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
try {
|
|
588
|
-
nativeResetHard(basePath);
|
|
589
|
-
} catch {
|
|
590
|
-
/* best-effort */
|
|
591
|
-
}
|
|
518
|
+
abortAndResetMerge(basePath, hasMergeHead, squashMsgPath);
|
|
592
519
|
ctx.ui.notify(
|
|
593
520
|
"Detected leftover merge state — auto-resolve failed, cleaned up. Re-deriving state.",
|
|
594
521
|
"warning",
|
|
@@ -596,24 +523,7 @@ export function reconcileMergeState(
|
|
|
596
523
|
}
|
|
597
524
|
} else {
|
|
598
525
|
// Code conflicts present — abort and reset
|
|
599
|
-
|
|
600
|
-
try {
|
|
601
|
-
nativeMergeAbort(basePath);
|
|
602
|
-
} catch {
|
|
603
|
-
/* best-effort */
|
|
604
|
-
}
|
|
605
|
-
} else if (hasSquashMsg) {
|
|
606
|
-
try {
|
|
607
|
-
unlinkSync(squashMsgPath);
|
|
608
|
-
} catch {
|
|
609
|
-
/* best-effort */
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
try {
|
|
613
|
-
nativeResetHard(basePath);
|
|
614
|
-
} catch {
|
|
615
|
-
/* best-effort */
|
|
616
|
-
}
|
|
526
|
+
abortAndResetMerge(basePath, hasMergeHead, squashMsgPath);
|
|
617
527
|
ctx.ui.notify(
|
|
618
528
|
"Detected leftover merge state with unresolved conflicts — cleaned up. Re-deriving state.",
|
|
619
529
|
"warning",
|
|
@@ -623,50 +533,6 @@ export function reconcileMergeState(
|
|
|
623
533
|
return true;
|
|
624
534
|
}
|
|
625
535
|
|
|
626
|
-
// ─── Self-Heal Runtime Records ────────────────────────────────────────────────
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Self-heal: scan runtime records in .gsd/ and clear stale ones.
|
|
630
|
-
* Clears dispatched records older than 1 hour (process crashed before
|
|
631
|
-
* completing the unit). deriveState() handles re-derivation — no need
|
|
632
|
-
* for completion key persistence here.
|
|
633
|
-
*/
|
|
634
|
-
export async function selfHealRuntimeRecords(
|
|
635
|
-
base: string,
|
|
636
|
-
ctx: ExtensionContext,
|
|
637
|
-
): Promise<void> {
|
|
638
|
-
try {
|
|
639
|
-
const { listUnitRuntimeRecords } = await import("./unit-runtime.js");
|
|
640
|
-
const records = listUnitRuntimeRecords(base);
|
|
641
|
-
let healed = 0;
|
|
642
|
-
const STALE_THRESHOLD_MS = 60 * 60 * 1000; // 1 hour
|
|
643
|
-
const now = Date.now();
|
|
644
|
-
for (const record of records) {
|
|
645
|
-
const { unitType, unitId } = record;
|
|
646
|
-
|
|
647
|
-
// Case 0 removed — roadmap checkbox auto-fix is no longer needed.
|
|
648
|
-
// With DB-as-truth, stale checkboxes are fixed by repairStaleRenders().
|
|
649
|
-
|
|
650
|
-
// Clear stale dispatched records (dispatched > 1h ago, process crashed)
|
|
651
|
-
const age = now - (record.startedAt ?? 0);
|
|
652
|
-
if (record.phase === "dispatched" && age > STALE_THRESHOLD_MS) {
|
|
653
|
-
clearUnitRuntimeRecord(base, unitType, unitId);
|
|
654
|
-
healed++;
|
|
655
|
-
continue;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
if (healed > 0) {
|
|
659
|
-
ctx.ui.notify(
|
|
660
|
-
`Self-heal: cleared ${healed} stale runtime record(s).`,
|
|
661
|
-
"info",
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
} catch (e) {
|
|
665
|
-
// Non-fatal — self-heal should never block auto-mode start
|
|
666
|
-
void e;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
536
|
// ─── Loop Remediation ─────────────────────────────────────────────────────────
|
|
671
537
|
|
|
672
538
|
/**
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
setActiveMilestoneId,
|
|
53
53
|
} from "./worktree.js";
|
|
54
54
|
import { getAutoWorktreePath, isInAutoWorktree } from "./auto-worktree.js";
|
|
55
|
-
import { readResourceVersion } from "./auto-worktree
|
|
55
|
+
import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
|
|
56
56
|
import { initMetrics } from "./metrics.js";
|
|
57
57
|
import { initRoutingHistory } from "./routing-history.js";
|
|
58
58
|
import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
|
|
@@ -66,6 +66,7 @@ import {
|
|
|
66
66
|
isDebugEnabled,
|
|
67
67
|
getDebugLogPath,
|
|
68
68
|
} from "./debug-logger.js";
|
|
69
|
+
import { parseUnitId } from "./unit-id.js";
|
|
69
70
|
import type { AutoSession } from "./auto/session.js";
|
|
70
71
|
import {
|
|
71
72
|
existsSync,
|
|
@@ -200,7 +201,7 @@ export async function bootstrapAutoSession(
|
|
|
200
201
|
);
|
|
201
202
|
return releaseLockAndReturn();
|
|
202
203
|
}
|
|
203
|
-
const recoveredMid = crashLock.unitId.
|
|
204
|
+
const recoveredMid = parseUnitId(crashLock.unitId).milestone;
|
|
204
205
|
const milestoneAlreadyComplete = recoveredMid
|
|
205
206
|
? !!resolveMilestoneFile(base, recoveredMid, "SUMMARY")
|
|
206
207
|
: false;
|
|
@@ -258,31 +259,10 @@ export async function bootstrapAutoSession(
|
|
|
258
259
|
invalidateAllCaches();
|
|
259
260
|
|
|
260
261
|
// Clean stale runtime unit files for completed milestones (#887)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (!file.endsWith(".json")) continue;
|
|
266
|
-
const midMatch = file.match(/(M\d+(?:-[a-z0-9]{6})?)/);
|
|
267
|
-
if (!midMatch) continue;
|
|
268
|
-
const mid = midMatch[1];
|
|
269
|
-
if (resolveMilestoneFile(base, mid, "SUMMARY")) {
|
|
270
|
-
try {
|
|
271
|
-
unlinkSync(join(runtimeUnitsDir, file));
|
|
272
|
-
} catch (e) {
|
|
273
|
-
debugLog("stale-unit-cleanup-failed", {
|
|
274
|
-
file,
|
|
275
|
-
error: e instanceof Error ? e.message : String(e),
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
} catch (e) {
|
|
282
|
-
debugLog("stale-unit-dir-cleanup-failed", {
|
|
283
|
-
error: e instanceof Error ? e.message : String(e),
|
|
284
|
-
});
|
|
285
|
-
}
|
|
262
|
+
cleanStaleRuntimeUnits(
|
|
263
|
+
gsdRoot(base),
|
|
264
|
+
(mid) => !!resolveMilestoneFile(base, mid, "SUMMARY"),
|
|
265
|
+
);
|
|
286
266
|
|
|
287
267
|
let state = await deriveState(base);
|
|
288
268
|
|
|
@@ -192,7 +192,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
192
192
|
const message = err instanceof Error ? err.message : String(err);
|
|
193
193
|
console.error(`[idle-watchdog] Unhandled error: ${message}`);
|
|
194
194
|
// Unblock any pending unit promise so the auto-loop is not orphaned.
|
|
195
|
-
resolveAgentEndCancelled();
|
|
195
|
+
resolveAgentEndCancelled({ message: `Idle watchdog error: ${message}`, category: "idle", isTransient: true });
|
|
196
196
|
try {
|
|
197
197
|
ctx.ui.notify(`Idle watchdog error: ${message}`, "warning");
|
|
198
198
|
} catch { /* best effort */ }
|
|
@@ -226,7 +226,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
226
226
|
const message = err instanceof Error ? err.message : String(err);
|
|
227
227
|
console.error(`[hard-timeout] Unhandled error: ${message}`);
|
|
228
228
|
// Unblock any pending unit promise so the auto-loop is not orphaned.
|
|
229
|
-
resolveAgentEndCancelled();
|
|
229
|
+
resolveAgentEndCancelled({ message: `Hard timeout error: ${message}`, category: "timeout", isTransient: true });
|
|
230
230
|
try {
|
|
231
231
|
ctx.ui.notify(`Hard timeout error: ${message}`, "warning");
|
|
232
232
|
} catch { /* best effort */ }
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import type { ExtensionContext, ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
14
14
|
import { resolveSliceFile, resolveSlicePath } from "./paths.js";
|
|
15
|
+
import { parseUnitId } from "./unit-id.js";
|
|
15
16
|
import { isDbAvailable, getTask } from "./gsd-db.js";
|
|
16
17
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
17
18
|
import {
|
|
@@ -60,10 +61,9 @@ export async function runPostUnitVerification(
|
|
|
60
61
|
const prefs = effectivePrefs?.preferences;
|
|
61
62
|
|
|
62
63
|
// Read task plan verify field
|
|
63
|
-
const
|
|
64
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(s.currentUnit.id);
|
|
64
65
|
let taskPlanVerify: string | undefined;
|
|
65
|
-
if (
|
|
66
|
-
const [mid, sid, tid] = parts;
|
|
66
|
+
if (mid && sid && tid) {
|
|
67
67
|
if (isDbAvailable()) {
|
|
68
68
|
taskPlanVerify = getTask(mid, sid, tid)?.verify;
|
|
69
69
|
}
|
|
@@ -71,8 +71,6 @@ export async function runPostUnitVerification(
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const result = runVerificationGate({
|
|
74
|
-
basePath: s.basePath,
|
|
75
|
-
unitId: s.currentUnit.id,
|
|
76
74
|
cwd: s.basePath,
|
|
77
75
|
preferenceCommands: prefs?.verification_commands,
|
|
78
76
|
taskPlanVerify,
|
|
@@ -141,9 +139,8 @@ export async function runPostUnitVerification(
|
|
|
141
139
|
|
|
142
140
|
// Write verification evidence JSON
|
|
143
141
|
const attempt = s.verificationRetryCount.get(s.currentUnit.id) ?? 0;
|
|
144
|
-
if (
|
|
142
|
+
if (mid && sid && tid) {
|
|
145
143
|
try {
|
|
146
|
-
const [mid, sid, tid] = parts;
|
|
147
144
|
const sDir = resolveSlicePath(s.basePath, mid, sid);
|
|
148
145
|
if (sDir) {
|
|
149
146
|
const tasksDir = join(sDir, "tasks");
|