@opengsd/gsd-pi 1.2.0-dev.d6c5343c → 1.2.0-dev.ddc97c10
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/mcp-server.js +2 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +28 -10
- package/dist/resources/extensions/gsd/auto/phases.js +47 -4
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +3 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -2
- package/dist/resources/extensions/gsd/auto-model-selection.js +11 -7
- package/dist/resources/extensions/gsd/auto-post-unit.js +18 -6
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
- package/dist/resources/extensions/gsd/auto-verification.js +14 -2
- package/dist/resources/extensions/gsd/auto.js +37 -1
- package/dist/resources/extensions/gsd/blocked-models.js +28 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +26 -6
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
- package/dist/resources/extensions/gsd/closeout-wizard.js +92 -0
- package/dist/resources/extensions/gsd/commands/context.js +16 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +46 -3
- package/dist/resources/extensions/gsd/consent-question.js +16 -0
- package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +3 -3
- package/dist/resources/extensions/gsd/doctor-git-checks.js +2 -18
- package/dist/resources/extensions/gsd/gsd-command-home.js +22 -12
- package/dist/resources/extensions/gsd/gsd-db.js +2 -1
- package/dist/resources/extensions/gsd/guided-flow.js +6 -3
- package/dist/resources/extensions/gsd/milestone-closeout.js +73 -2
- package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
- package/dist/resources/extensions/gsd/projection-flush.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
- package/dist/resources/extensions/gsd/session-lock.js +1 -1
- package/dist/resources/extensions/gsd/tool-contract.js +14 -3
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +67 -2
- package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
- package/dist/resources/extensions/shared/gsd-browser-cli.js +21 -2
- package/dist/resources/shared/gsd-browser-path-sync.js +214 -0
- package/dist/resources/shared/package-manager-detection.js +1 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/update-check.d.ts +2 -0
- package/dist/update-check.js +24 -1
- package/dist/update-cmd.js +20 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- 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 +12 -12
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +1 -1
- 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/mcp-server/dist/cli.js +10 -5
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/moonshot-tool-schema.d.ts +29 -0
- package/packages/mcp-server/dist/moonshot-tool-schema.d.ts.map +1 -0
- package/packages/mcp-server/dist/moonshot-tool-schema.js +50 -0
- package/packages/mcp-server/dist/moonshot-tool-schema.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +4 -0
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +18 -18
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +99 -38
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +5 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/index.d.ts +2 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +2 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +12 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.d.ts +5 -0
- package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js +12 -3
- package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +7 -3
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts +9 -0
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.js +34 -0
- package/packages/pi-ai/dist/utils/moonshot-tool-schema.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +6 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/package.json +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/browser-tools/tests/gsd-browser-launch-config.test.mjs +11 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +28 -10
- package/src/resources/extensions/gsd/auto/phases.ts +63 -24
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +10 -16
- package/src/resources/extensions/gsd/auto-dispatch.ts +11 -10
- package/src/resources/extensions/gsd/auto-model-selection.ts +16 -7
- package/src/resources/extensions/gsd/auto-post-unit.ts +21 -6
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
- package/src/resources/extensions/gsd/auto-verification.ts +18 -2
- package/src/resources/extensions/gsd/auto.ts +44 -1
- package/src/resources/extensions/gsd/blocked-models.ts +49 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -5
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
- package/src/resources/extensions/gsd/closeout-wizard.ts +102 -0
- package/src/resources/extensions/gsd/commands/context.ts +16 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +46 -3
- package/src/resources/extensions/gsd/consent-question.ts +15 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +3 -3
- package/src/resources/extensions/gsd/doctor-git-checks.ts +2 -19
- package/src/resources/extensions/gsd/gsd-command-home.ts +13 -3
- package/src/resources/extensions/gsd/gsd-db.ts +4 -3
- package/src/resources/extensions/gsd/guided-flow.ts +21 -26
- package/src/resources/extensions/gsd/milestone-closeout.ts +97 -2
- package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
- package/src/resources/extensions/gsd/projection-flush.ts +20 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +1 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
- package/src/resources/extensions/gsd/session-lock.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
- package/src/resources/extensions/gsd/tests/blocked-models.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/consent-question.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/doctor-git-checks-terminal.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/gsd-command-home.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +2 -6
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +95 -4
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +19 -1
- package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +273 -38
- package/src/resources/extensions/gsd/tool-contract.ts +38 -3
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
- package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/complete-task.ts +3 -2
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +81 -2
- package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
- package/src/resources/extensions/shared/gsd-browser-cli.ts +23 -2
- package/src/resources/shared/gsd-browser-path-sync.ts +273 -0
- package/src/resources/shared/package-manager-detection.ts +1 -1
- /package/dist/web/standalone/.next/static/{jmTLg6xZmAuq_LIqKOxrH → McokybTayhff1xEVc-d3T}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{jmTLg6xZmAuq_LIqKOxrH → McokybTayhff1xEVc-d3T}/_ssgManifest.js +0 -0
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
// - preflight: dispatch git clean before complete-milestone agent (auto-dispatch)
|
|
5
5
|
// - postUnit: git commit, artifact verify, DB settle, then GitHub finalize
|
|
6
6
|
// - recovery: DB repair from artifacts, then GitHub finalize
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
7
8
|
import { loadFile } from "./files.js";
|
|
8
9
|
import { resolveMilestoneFile } from "./paths.js";
|
|
9
|
-
import { getMilestone, getClosedSliceIds, isDbAvailable } from "./gsd-db.js";
|
|
10
|
+
import { getMilestone, getClosedSliceIds, getLatestAssessmentByScope, getMilestoneSlices, isDbAvailable, } from "./gsd-db.js";
|
|
10
11
|
import { isClosedStatus } from "./status-guards.js";
|
|
12
|
+
import { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
|
|
13
|
+
import { handleCompleteMilestone } from "./tools/complete-milestone.js";
|
|
11
14
|
import { runSafely } from "./auto-utils.js";
|
|
12
15
|
import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
|
|
13
16
|
import { uatSignoffBlockerGuidance } from "./guidance.js";
|
|
@@ -19,6 +22,60 @@ import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
|
|
|
19
22
|
import { commitPendingMilestoneCloseoutChanges, findMissingSummaries, isVerificationNotApplicable, readUatGateVerdict, } from "./auto-dispatch.js";
|
|
20
23
|
const COMPLETE_MILESTONE_DB_SETTLE_MS = 1500;
|
|
21
24
|
const COMPLETE_MILESTONE_DB_SETTLE_POLL_MS = 100;
|
|
25
|
+
/**
|
|
26
|
+
* True when a milestone is terminal for git cleanup (orphaned worktrees, stale branches).
|
|
27
|
+
* DB-authoritative (ADR-017): closed status, or validation-pass with all slices closed.
|
|
28
|
+
* When the DB is unavailable we cannot make this decision and conservatively
|
|
29
|
+
* return false so callers leave the worktree/branch alone instead of cleaning
|
|
30
|
+
* up based on parsed projections.
|
|
31
|
+
*/
|
|
32
|
+
export async function isCompletedMilestoneTerminal(_basePath, milestoneId) {
|
|
33
|
+
if (!isDbAvailable())
|
|
34
|
+
return false;
|
|
35
|
+
const milestone = getMilestone(milestoneId);
|
|
36
|
+
if (!milestone)
|
|
37
|
+
return false;
|
|
38
|
+
if (isClosedStatus(milestone.status)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
const validation = getLatestAssessmentByScope(milestoneId, "milestone-validation");
|
|
42
|
+
if (validation?.status !== "pass") {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const slices = getMilestoneSlices(milestoneId);
|
|
46
|
+
if (slices.length === 0)
|
|
47
|
+
return false;
|
|
48
|
+
return slices.every((slice) => isClosedStatus(slice.status));
|
|
49
|
+
}
|
|
50
|
+
/** Write a missing milestone SUMMARY projection when canonical DB closeout already settled. */
|
|
51
|
+
export async function repairMissingMilestoneSummaryProjection(basePath, milestoneId) {
|
|
52
|
+
const milestone = getMilestone(milestoneId);
|
|
53
|
+
if (!milestone) {
|
|
54
|
+
return { ok: false, error: `milestone not found: ${milestoneId}` };
|
|
55
|
+
}
|
|
56
|
+
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, milestoneId);
|
|
57
|
+
const summaryPath = resolveExpectedArtifactPath("complete-milestone", milestoneId, artifactBasePath);
|
|
58
|
+
if (summaryPath && existsSync(summaryPath)) {
|
|
59
|
+
return { ok: true };
|
|
60
|
+
}
|
|
61
|
+
const result = await handleCompleteMilestone({
|
|
62
|
+
milestoneId,
|
|
63
|
+
title: milestone.title,
|
|
64
|
+
oneLiner: "Canonical closeout completed; summary projection repaired automatically.",
|
|
65
|
+
narrative: "The workflow database recorded this milestone as complete, but the milestone SUMMARY artifact was missing on disk. " +
|
|
66
|
+
"Dispatch policy repaired the projection so closeout proof and cleanup can proceed.",
|
|
67
|
+
verificationPassed: true,
|
|
68
|
+
triggerReason: "closeout-projection-repair",
|
|
69
|
+
}, basePath);
|
|
70
|
+
if ("error" in result) {
|
|
71
|
+
return { ok: false, error: result.error };
|
|
72
|
+
}
|
|
73
|
+
const writtenSummaryPath = result.summaryPath;
|
|
74
|
+
if (result.stale || !writtenSummaryPath || !existsSync(writtenSummaryPath)) {
|
|
75
|
+
return { ok: false, error: "milestone SUMMARY projection write failed" };
|
|
76
|
+
}
|
|
77
|
+
return { ok: true };
|
|
78
|
+
}
|
|
22
79
|
/**
|
|
23
80
|
* True when the milestone is closed in the DB and the completion summary artifact exists.
|
|
24
81
|
* Polls briefly so post-unit verification can observe the tool's DB write.
|
|
@@ -65,7 +122,21 @@ export async function evaluateCompleteMilestoneDispatch(ctx) {
|
|
|
65
122
|
if (isDbAvailable()) {
|
|
66
123
|
const milestone = getMilestone(mid);
|
|
67
124
|
if (milestone && isClosedStatus(milestone.status)) {
|
|
68
|
-
|
|
125
|
+
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, mid);
|
|
126
|
+
const summaryPath = resolveExpectedArtifactPath("complete-milestone", mid, artifactBasePath);
|
|
127
|
+
const summaryMissing = !summaryPath || !existsSync(summaryPath);
|
|
128
|
+
if (summaryMissing) {
|
|
129
|
+
const repair = await repairMissingMilestoneSummaryProjection(basePath, mid);
|
|
130
|
+
if (!repair.ok) {
|
|
131
|
+
logWarning("dispatch", `Milestone ${mid} is closed in DB but SUMMARY repair failed: ${repair.error}. Dispatching complete-milestone to retry.`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
return { action: "skip" };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return { action: "skip" };
|
|
139
|
+
}
|
|
69
140
|
}
|
|
70
141
|
}
|
|
71
142
|
const closeoutGitStop = commitPendingMilestoneCloseoutChanges(basePath, mid);
|
|
@@ -5,7 +5,7 @@ import { isClosedStatus } from "./status-guards.js";
|
|
|
5
5
|
import { transaction, getMilestone, getMilestoneSlices, getSlice, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "./gsd-db.js";
|
|
6
6
|
import { invalidateStateCache } from "./state.js";
|
|
7
7
|
import { renderRoadmapFromDb } from "./markdown-renderer.js";
|
|
8
|
-
import {
|
|
8
|
+
import { flushWorkflowProjections } from "./projection-flush.js";
|
|
9
9
|
import { writeManifest } from "./workflow-manifest.js";
|
|
10
10
|
import { appendEvent } from "./workflow-events.js";
|
|
11
11
|
import { logWarning } from "./workflow-logger.js";
|
|
@@ -109,7 +109,7 @@ async function renderPlanArtifacts(basePath, params) {
|
|
|
109
109
|
}
|
|
110
110
|
async function runPostPlanHooks(basePath, params) {
|
|
111
111
|
try {
|
|
112
|
-
await
|
|
112
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
113
113
|
writeManifest(basePath);
|
|
114
114
|
appendEvent(basePath, {
|
|
115
115
|
cmd: "plan-milestone",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Single workflow projection flush seam for mutation exits.
|
|
3
|
+
import { renderAllProjections } from "./workflow-projections.js";
|
|
4
|
+
export async function flushWorkflowProjections(basePath, scope) {
|
|
5
|
+
await renderAllProjections(basePath, scope.milestoneId);
|
|
6
|
+
return { milestoneId: scope.milestoneId };
|
|
7
|
+
}
|
|
@@ -49,4 +49,4 @@ Use `subagent` only when useful: reviewer, security, or tester. Apply findings b
|
|
|
49
49
|
|
|
50
50
|
**You MUST call `gsd_slice_complete` with summary and UAT content only after verification passes.**
|
|
51
51
|
|
|
52
|
-
When done, say: "Slice {{sliceId}} complete."
|
|
52
|
+
When done, say: "Slice {{sliceId}} complete." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -76,4 +76,4 @@ Keep about **{{verificationBudget}}** for verification and summary. If context i
|
|
|
76
76
|
|
|
77
77
|
**You MUST call `gsd_task_complete` before finishing, including when the stale-path safety rule stops execution.**
|
|
78
78
|
|
|
79
|
-
When done, say: "Task {{taskId}} complete."
|
|
79
|
+
When done, say: "Task {{taskId}} complete." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -118,4 +118,4 @@ If external API keys or secrets are required, use the inlined **Secrets Manifest
|
|
|
118
118
|
|
|
119
119
|
If no external API keys or secrets are required, skip this step; do not create an empty manifest.
|
|
120
120
|
|
|
121
|
-
When done, say: "Milestone {{milestoneId}} planned."
|
|
121
|
+
When done, say: "Milestone {{milestoneId}} planned." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -57,4 +57,4 @@ The slice directory already exists. Do not mkdir.
|
|
|
57
57
|
|
|
58
58
|
**You MUST call `gsd_plan_slice` to persist planning state before finishing, unless the stale-path safety rule above stops the unit before safe planning can occur.**
|
|
59
59
|
|
|
60
|
-
When done, say: "Slice {{sliceId}} planned."
|
|
60
|
+
When done, say: "Slice {{sliceId}} planned." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -37,4 +37,4 @@ You are executing a GSD quick task — a lightweight, focused unit of work outsi
|
|
|
37
37
|
- <what was tested/verified>
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
When done, say: "Quick task {{taskNum}} complete."
|
|
40
|
+
When done, say: "Quick task {{taskNum}} complete." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -67,4 +67,4 @@ If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, up
|
|
|
67
67
|
|
|
68
68
|
**DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')`. Use `gsd_milestone_status` to read current milestone and slice state. All roadmap mutations go through `gsd_reassess_roadmap` — the tool writes to the DB and re-renders ROADMAP.md atomically.
|
|
69
69
|
|
|
70
|
-
When done, say: "Roadmap reassessed."
|
|
70
|
+
When done, say: "Roadmap reassessed." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -77,4 +77,4 @@ The slice directory and tasks/ subdirectory already exist. Do NOT mkdir.
|
|
|
77
77
|
|
|
78
78
|
**You MUST call `gsd_plan_slice` to persist planning state before finishing.** After success, the pipeline clears the sketch flag on next state derivation; the on-disk PLAN file is the signal.
|
|
79
79
|
|
|
80
|
-
When done, say: "Slice {{sliceId}} refined."
|
|
80
|
+
When done, say: "Slice {{sliceId}} refined." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -38,4 +38,4 @@ Consider these captures when rewriting the remaining tasks — they represent th
|
|
|
38
38
|
4. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
|
|
39
39
|
5. Do not commit manually — the system auto-commits your changes after this unit completes.
|
|
40
40
|
|
|
41
|
-
When done, say: "Slice {{sliceId}} replanned."
|
|
41
|
+
When done, say: "Slice {{sliceId}} replanned." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -46,4 +46,4 @@ Then research the codebase and relevant technologies. Narrate key findings and s
|
|
|
46
46
|
|
|
47
47
|
**You MUST call `gsd_summary_save` with the research content before finishing.**
|
|
48
48
|
|
|
49
|
-
When done, say: "Milestone {{milestoneId}} researched."
|
|
49
|
+
When done, say: "Milestone {{milestoneId}} researched." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -55,4 +55,4 @@ The slice directory already exists at `{{slicePath}}/`. Do NOT mkdir.
|
|
|
55
55
|
|
|
56
56
|
**You MUST call `gsd_summary_save` with the research content before finishing.**
|
|
57
57
|
|
|
58
|
-
When done, say: "Slice {{sliceId}} researched."
|
|
58
|
+
When done, say: "Slice {{sliceId}} researched." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -30,4 +30,4 @@ An override was issued by the user that changes a fundamental decision or approa
|
|
|
30
30
|
|
|
31
31
|
**You MUST update the relevant documents AND mark overrides as resolved in `{{overridesPath}}` before finishing.**
|
|
32
32
|
|
|
33
|
-
When done, say: "Override applied across all documents."
|
|
33
|
+
When done, say: "Override applied across all documents." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -100,4 +100,4 @@ checks: [{
|
|
|
100
100
|
|
|
101
101
|
**You MUST call `gsd_uat_result_save` before finishing. Do not write the assessment file directly, and do not call `gsd_summary_save` as a substitute.**
|
|
102
102
|
|
|
103
|
-
When done, say: "UAT {{sliceId}} complete."
|
|
103
|
+
When done, say: "UAT {{sliceId}} complete." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -65,4 +65,4 @@ Classify each capture as one of:
|
|
|
65
65
|
|
|
66
66
|
**Important:** Do NOT execute any resolutions. Only classify and update CAPTURES.md. Resolution execution happens separately (in auto-mode dispatch or manually by the user).
|
|
67
67
|
|
|
68
|
-
When done, say: "Triage complete."
|
|
68
|
+
When done, say: "Triage complete." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -87,4 +87,4 @@ If verdict is `needs-remediation`:
|
|
|
87
87
|
|
|
88
88
|
**File system safety:** When scanning milestone directories, use `ls` or `find` first. Never pass a directory path such as `tasks/` or `slices/` directly to `read`; it only accepts files.
|
|
89
89
|
|
|
90
|
-
When done, say: "Milestone {{milestoneId}} validation complete — verdict: <verdict>."
|
|
90
|
+
When done, say: "Milestone {{milestoneId}} validation complete — verdict: <verdict>." Say this exactly once — if you already said it in a prior message, do not repeat it.
|
|
@@ -161,6 +161,26 @@ function parseTableSlices(section) {
|
|
|
161
161
|
}
|
|
162
162
|
return slices;
|
|
163
163
|
}
|
|
164
|
+
function looksLikeTable(section) {
|
|
165
|
+
const lines = section.split("\n");
|
|
166
|
+
// Checkbox format takes precedence — embedded demo tables must not switch mode (#721).
|
|
167
|
+
if (lines.some(line => /^\s*-\s+\[[ xX]\]/.test(line))) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
const pipeLines = lines.filter(line => /^\s*\|/.test(line));
|
|
171
|
+
if (pipeLines.length < 2)
|
|
172
|
+
return false;
|
|
173
|
+
const hasSeparatorRow = pipeLines.some((line, index) => {
|
|
174
|
+
if (index === 0)
|
|
175
|
+
return false;
|
|
176
|
+
const cells = line.split("|").map(c => c.trim()).filter(Boolean);
|
|
177
|
+
return /^\s*\|[\s:-]+\|/.test(line) && cells.length >= 2 && cells.every(c => /^[\s:-]+$/.test(c));
|
|
178
|
+
});
|
|
179
|
+
if (hasSeparatorRow)
|
|
180
|
+
return true;
|
|
181
|
+
// Tables without a separator row still expose slice IDs in data rows.
|
|
182
|
+
return pipeLines.some(line => /\bS\d+\b/.test(line));
|
|
183
|
+
}
|
|
164
184
|
export function parseRoadmapSlices(content) {
|
|
165
185
|
const slicesSection = extractSlicesSection(content);
|
|
166
186
|
if (!slicesSection) {
|
|
@@ -171,9 +191,11 @@ export function parseRoadmapSlices(content) {
|
|
|
171
191
|
}
|
|
172
192
|
// Try table format first — if the section contains pipe-delimited rows with
|
|
173
193
|
// slice IDs, parse them as a table (#1736).
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
if (looksLikeTable(slicesSection)) {
|
|
195
|
+
const tableSlices = parseTableSlices(slicesSection);
|
|
196
|
+
if (tableSlices.length > 0) {
|
|
197
|
+
return tableSlices;
|
|
198
|
+
}
|
|
177
199
|
}
|
|
178
200
|
// Standard checkbox format
|
|
179
201
|
const slices = [];
|
|
@@ -329,7 +329,7 @@ export function acquireSessionLock(basePath) {
|
|
|
329
329
|
// #3218: Provide actionable workaround when lock recovery fails
|
|
330
330
|
const lockDirPath = lockTarget + ".lock";
|
|
331
331
|
const reason = existingPid
|
|
332
|
-
? `Another auto-mode session (PID ${existingPid}) appears to be running.\
|
|
332
|
+
? `Another auto-mode session (PID ${existingPid}) appears to be running.\nRun \`/gsd stop\` for graceful shutdown, or choose "Force start" from \`/gsd auto\` to terminate it.`
|
|
333
333
|
: `Another auto-mode session lock is stuck on this project.\nRun: rm -rf "${lockDirPath}" && rm -f "${lp}"`;
|
|
334
334
|
return { acquired: false, reason, existingPid };
|
|
335
335
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: ADR-015 Tool Contract module for Unit prompt, policy, and tool parity.
|
|
3
3
|
import { resolveManifest, } from "./unit-context-manifest.js";
|
|
4
|
-
import {
|
|
5
|
-
import { getUnitToolSurfaceContract } from "./unit-tool-contracts.js";
|
|
4
|
+
import { getWorkflowTransportSupportError, } from "./workflow-mcp.js";
|
|
5
|
+
import { getRequiredWorkflowToolsForUnit, getUnitToolSurfaceContract, } from "./unit-tool-contracts.js";
|
|
6
6
|
import { WHOLE_FILE_OBSERVATION_MAX_BYTES, WHOLE_FILE_OBSERVATION_MAX_LINES, } from "./source-observations.js";
|
|
7
7
|
export function compileUnitContextContract(unitType) {
|
|
8
8
|
const manifest = resolveManifest(unitType);
|
|
@@ -15,6 +15,17 @@ export function compileUnitContextContract(unitType) {
|
|
|
15
15
|
}
|
|
16
16
|
return { ok: true, contract: buildPromptContextContract(unitType, manifest) };
|
|
17
17
|
}
|
|
18
|
+
export function getUnitWorkflowDispatchReadinessError(input) {
|
|
19
|
+
return getWorkflowTransportSupportError(input.provider, getRequiredWorkflowToolsForUnit(input.unitType), {
|
|
20
|
+
projectRoot: input.projectRoot,
|
|
21
|
+
env: input.env,
|
|
22
|
+
surface: input.surface,
|
|
23
|
+
unitType: input.unitType,
|
|
24
|
+
authMode: input.authMode,
|
|
25
|
+
baseUrl: input.baseUrl,
|
|
26
|
+
activeTools: input.activeTools,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
18
29
|
export function compileUnitToolContract(unitType) {
|
|
19
30
|
const manifest = resolveManifest(unitType);
|
|
20
31
|
const surfaceContract = getUnitToolSurfaceContract(unitType);
|
|
@@ -25,7 +36,7 @@ export function compileUnitToolContract(unitType) {
|
|
|
25
36
|
detail: `No Unit manifest is registered for ${unitType}`,
|
|
26
37
|
};
|
|
27
38
|
}
|
|
28
|
-
const requiredWorkflowTools =
|
|
39
|
+
const requiredWorkflowTools = getRequiredWorkflowToolsForUnit(unitType);
|
|
29
40
|
const forbiddenWorkflowTools = Object.entries(surfaceContract?.forbiddenGsdTools ?? {})
|
|
30
41
|
.map(([name, reason]) => ({ name, reason }));
|
|
31
42
|
const closeoutTools = requiredWorkflowTools.filter((tool) => /^gsd_(?:task|slice|milestone|complete|validate|save|summary|uat)/.test(tool));
|
|
@@ -16,7 +16,8 @@ import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
|
|
|
16
16
|
import { isClosedStatus } from "../status-guards.js";
|
|
17
17
|
import { saveFile, clearParseCache } from "../files.js";
|
|
18
18
|
import { invalidateStateCache } from "../state.js";
|
|
19
|
-
import {
|
|
19
|
+
import { stripIdPrefix } from "../workflow-projections.js";
|
|
20
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
20
21
|
import { writeManifest } from "../workflow-manifest.js";
|
|
21
22
|
import { appendEvent } from "../workflow-events.js";
|
|
22
23
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
@@ -166,7 +167,7 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
166
167
|
// Separate try/catch per step so a projection failure doesn't prevent
|
|
167
168
|
// the event log entry (critical for worktree reconciliation).
|
|
168
169
|
try {
|
|
169
|
-
await
|
|
170
|
+
await flushWorkflowProjections(artifactBasePath, { milestoneId: params.milestoneId });
|
|
170
171
|
}
|
|
171
172
|
catch (projErr) {
|
|
172
173
|
logWarning("tool", `complete-milestone projection warning: ${projErr.message}`);
|
|
@@ -21,7 +21,7 @@ import { classifyUatContent, escalatesArtifactUatToBrowser } from "../uat-policy
|
|
|
21
21
|
import { invalidateStateCache } from "../state.js";
|
|
22
22
|
import { renderRoadmapFromDb, roadmapRenderMarksSliceDone } from "../markdown-renderer.js";
|
|
23
23
|
import { isStaleWrite } from "../auto/turn-epoch.js";
|
|
24
|
-
import {
|
|
24
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
25
25
|
import { writeManifest } from "../workflow-manifest.js";
|
|
26
26
|
import { appendEvent } from "../workflow-events.js";
|
|
27
27
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
@@ -448,7 +448,7 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
448
448
|
// Separate try/catch per step so a projection failure doesn't prevent
|
|
449
449
|
// the event log entry (critical for worktree reconciliation).
|
|
450
450
|
try {
|
|
451
|
-
await
|
|
451
|
+
await flushWorkflowProjections(artifactBasePath, { milestoneId: params.milestoneId });
|
|
452
452
|
}
|
|
453
453
|
catch (projErr) {
|
|
454
454
|
logWarning("tool", `complete-slice projection warning for ${params.milestoneId}/${params.sliceId}: ${projErr.message}`);
|
|
@@ -18,7 +18,8 @@ import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
|
|
|
18
18
|
import { saveFile, clearParseCache } from "../files.js";
|
|
19
19
|
import { invalidateStateCache } from "../state.js";
|
|
20
20
|
import { renderPlanCheckboxes } from "../markdown-renderer.js";
|
|
21
|
-
import {
|
|
21
|
+
import { renderSummaryContent } from "../workflow-projections.js";
|
|
22
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
22
23
|
import { writeManifest } from "../workflow-manifest.js";
|
|
23
24
|
import { appendEvent } from "../workflow-events.js";
|
|
24
25
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
@@ -358,7 +359,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
358
359
|
// Separate try/catch per step so a projection failure doesn't prevent
|
|
359
360
|
// the event log entry (critical for worktree reconciliation).
|
|
360
361
|
try {
|
|
361
|
-
await
|
|
362
|
+
await flushWorkflowProjections(artifactBasePath, { milestoneId: params.milestoneId });
|
|
362
363
|
}
|
|
363
364
|
catch (projErr) {
|
|
364
365
|
logWarning("tool", `complete-task projection warning: ${projErr.message}`);
|
|
@@ -7,7 +7,7 @@ import { getGateIdsForTurn } from "../gate-registry.js";
|
|
|
7
7
|
import { transaction, getMilestone, getSlice, getSliceTasks, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, updateSliceStatus, setSliceSketchFlag, deleteTask, deleteArtifactByPath, } from "../gsd-db.js";
|
|
8
8
|
import { invalidateStateCache } from "../state.js";
|
|
9
9
|
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
10
|
-
import {
|
|
10
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
11
11
|
import { writeManifest } from "../workflow-manifest.js";
|
|
12
12
|
import { appendEvent } from "../workflow-events.js";
|
|
13
13
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -367,7 +367,7 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
367
367
|
clearParseCache();
|
|
368
368
|
// ── Post-mutation hook: projections, manifest, event log ─────────────
|
|
369
369
|
try {
|
|
370
|
-
await
|
|
370
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
371
371
|
writeManifest(basePath);
|
|
372
372
|
appendEvent(basePath, {
|
|
373
373
|
cmd: "plan-slice",
|
|
@@ -4,7 +4,7 @@ import { isNonEmptyString, validateStringArray } from "../validation.js";
|
|
|
4
4
|
import { transaction, getSlice, getTask, insertTask, upsertTaskPlanning } from "../gsd-db.js";
|
|
5
5
|
import { invalidateStateCache } from "../state.js";
|
|
6
6
|
import { renderTaskPlanFromDb } from "../markdown-renderer.js";
|
|
7
|
-
import {
|
|
7
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
8
8
|
import { writeManifest } from "../workflow-manifest.js";
|
|
9
9
|
import { appendEvent } from "../workflow-events.js";
|
|
10
10
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -110,7 +110,7 @@ export async function handlePlanTask(rawParams, basePath) {
|
|
|
110
110
|
clearParseCache();
|
|
111
111
|
// ── Post-mutation hook: projections, manifest, event log ─────────────
|
|
112
112
|
try {
|
|
113
|
-
await
|
|
113
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
114
114
|
writeManifest(basePath);
|
|
115
115
|
appendEvent(basePath, {
|
|
116
116
|
cmd: "plan-task",
|
|
@@ -6,7 +6,7 @@ import { isNonEmptyString } from "../validation.js";
|
|
|
6
6
|
import { transaction, getMilestone, getMilestoneSlices, getSlice, insertSlice, updateSliceFields, insertAssessment, deleteAssessmentByScope, deleteSlice, } from "../gsd-db.js";
|
|
7
7
|
import { invalidateStateCache } from "../state.js";
|
|
8
8
|
import { renderRoadmapFromDb, renderAssessmentFromDb } from "../markdown-renderer.js";
|
|
9
|
-
import {
|
|
9
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
10
10
|
import { writeManifest } from "../workflow-manifest.js";
|
|
11
11
|
import { appendEvent } from "../workflow-events.js";
|
|
12
12
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -234,7 +234,7 @@ export async function handleReassessRoadmap(rawParams, basePath) {
|
|
|
234
234
|
clearParseCache();
|
|
235
235
|
// ── Post-mutation hook: projections, manifest, event log ─────
|
|
236
236
|
try {
|
|
237
|
-
await
|
|
237
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
238
238
|
writeManifest(basePath);
|
|
239
239
|
appendEvent(basePath, {
|
|
240
240
|
cmd: "reassess-roadmap",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { getMilestoneSlices, getSliceTasks, reopenMilestoneCascade, } from "../gsd-db.js";
|
|
11
11
|
import { invalidateStateCache } from "../state.js";
|
|
12
|
-
import {
|
|
12
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
13
13
|
import { writeManifest } from "../workflow-manifest.js";
|
|
14
14
|
import { appendEvent } from "../workflow-events.js";
|
|
15
15
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -74,7 +74,7 @@ export async function handleReopenMilestone(params, basePath) {
|
|
|
74
74
|
clearPathCache();
|
|
75
75
|
// ── Post-mutation hook ───────────────────────────────────────────────────
|
|
76
76
|
try {
|
|
77
|
-
await
|
|
77
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
78
78
|
writeManifest(basePath);
|
|
79
79
|
appendEvent(basePath, {
|
|
80
80
|
cmd: "reopen-milestone",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
12
12
|
import { getSliceTasks, reopenSliceCascade, } from "../gsd-db.js";
|
|
13
13
|
import { invalidateStateCache } from "../state.js";
|
|
14
|
-
import {
|
|
14
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
15
15
|
import { writeManifest } from "../workflow-manifest.js";
|
|
16
16
|
import { appendEvent } from "../workflow-events.js";
|
|
17
17
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -72,7 +72,7 @@ export async function handleReopenSlice(params, basePath) {
|
|
|
72
72
|
clearPathCache();
|
|
73
73
|
// ── Post-mutation hook ───────────────────────────────────────────────────
|
|
74
74
|
try {
|
|
75
|
-
await
|
|
75
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
76
76
|
writeManifest(basePath);
|
|
77
77
|
appendEvent(basePath, {
|
|
78
78
|
cmd: "reopen-slice",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { getMilestone, getSlice, getTask, updateTaskStatus, transaction, } from "../gsd-db.js";
|
|
12
12
|
import { invalidateStateCache } from "../state.js";
|
|
13
13
|
import { isClosedStatus } from "../status-guards.js";
|
|
14
|
-
import {
|
|
14
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
15
15
|
import { writeManifest } from "../workflow-manifest.js";
|
|
16
16
|
import { appendEvent } from "../workflow-events.js";
|
|
17
17
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -83,7 +83,7 @@ export async function handleReopenTask(params, basePath) {
|
|
|
83
83
|
clearPathCache();
|
|
84
84
|
// ── Post-mutation hook ───────────────────────────────────────────────────
|
|
85
85
|
try {
|
|
86
|
-
await
|
|
86
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
87
87
|
writeManifest(basePath);
|
|
88
88
|
appendEvent(basePath, {
|
|
89
89
|
cmd: "reopen-task",
|
|
@@ -4,7 +4,7 @@ import { invalidateStateCache } from "../state.js";
|
|
|
4
4
|
import { isClosedStatus } from "../status-guards.js";
|
|
5
5
|
import { isNonEmptyString } from "../validation.js";
|
|
6
6
|
import { renderPlanFromDb, renderReplanFromDb } from "../markdown-renderer.js";
|
|
7
|
-
import {
|
|
7
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
8
8
|
import { writeManifest } from "../workflow-manifest.js";
|
|
9
9
|
import { appendEvent } from "../workflow-events.js";
|
|
10
10
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -162,7 +162,7 @@ export async function handleReplanSlice(rawParams, basePath) {
|
|
|
162
162
|
clearParseCache();
|
|
163
163
|
// ── Post-mutation hook: projections, manifest, event log ─────
|
|
164
164
|
try {
|
|
165
|
-
await
|
|
165
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
166
166
|
writeManifest(basePath);
|
|
167
167
|
appendEvent(basePath, {
|
|
168
168
|
cmd: "replan-slice",
|
|
@@ -6,9 +6,10 @@ import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, should
|
|
|
6
6
|
import { getActiveRequirements, getAllMilestones, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertMilestone, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
|
|
7
7
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
8
8
|
import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
|
|
9
|
-
import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
9
|
+
import { clearPathCache, normalizeRealPath, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
10
10
|
import { saveFile, clearParseCache } from "../files.js";
|
|
11
11
|
import { unlinkSync } from "node:fs";
|
|
12
|
+
import { hostname } from "node:os";
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { handleCompleteMilestone } from "./complete-milestone.js";
|
|
14
15
|
import { handleCompleteTask } from "./complete-task.js";
|
|
@@ -25,9 +26,11 @@ import { logError, logWarning } from "../workflow-logger.js";
|
|
|
25
26
|
import { invalidateStateCache } from "../state.js";
|
|
26
27
|
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
27
28
|
import { parseProject } from "../schemas/parsers.js";
|
|
28
|
-
import { getAutoRuntimeSnapshot } from "../auto-runtime-state.js";
|
|
29
|
+
import { autoSession, getAutoRuntimeSnapshot, isAutoActive } from "../auto-runtime-state.js";
|
|
29
30
|
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
30
31
|
import { prepareUatRun, saveUatAttemptArtifact, } from "../uat-run.js";
|
|
32
|
+
import { registerAutoWorker, markWorkerStopping, getAutoWorker } from "../db/auto-workers.js";
|
|
33
|
+
import { claimMilestoneLease, releaseMilestoneLease, getMilestoneLease, refreshMilestoneLease, milestoneLeaseTtlSeconds, } from "../db/milestone-leases.js";
|
|
31
34
|
export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = [
|
|
32
35
|
"SUMMARY",
|
|
33
36
|
"RESEARCH",
|
|
@@ -61,6 +64,19 @@ function blockIfWrongAutoUnit(requiredUnitType, operation) {
|
|
|
61
64
|
isError: true,
|
|
62
65
|
};
|
|
63
66
|
}
|
|
67
|
+
function milestoneLeaseConflictResult(milestoneId, byWorker, expiresAt) {
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: `Milestone ${milestoneId} is currently leased by ${byWorker}. Retry after ${expiresAt}.` }],
|
|
70
|
+
details: {
|
|
71
|
+
operation: "plan_milestone",
|
|
72
|
+
error: "milestone_lease_conflict",
|
|
73
|
+
milestoneId,
|
|
74
|
+
byWorker,
|
|
75
|
+
expiresAt,
|
|
76
|
+
},
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
64
80
|
function registerProjectMilestoneSequence(content) {
|
|
65
81
|
const parsed = parseProject(content);
|
|
66
82
|
const registered = [];
|
|
@@ -1066,7 +1082,45 @@ export async function executePlanMilestone(params, basePath = process.cwd()) {
|
|
|
1066
1082
|
isError: true,
|
|
1067
1083
|
};
|
|
1068
1084
|
}
|
|
1085
|
+
let workerId = null;
|
|
1086
|
+
let acquiredToken = null;
|
|
1087
|
+
let leaseRefreshTimer;
|
|
1069
1088
|
try {
|
|
1089
|
+
// Re-read at the gate so a peer-created milestone is not treated as fresh.
|
|
1090
|
+
const milestoneExists = getMilestone(params.milestoneId) !== null;
|
|
1091
|
+
if (milestoneExists) {
|
|
1092
|
+
const heldLease = getMilestoneLease(params.milestoneId);
|
|
1093
|
+
if (heldLease?.status === "held" && Date.parse(heldLease.expires_at) > Date.now()) {
|
|
1094
|
+
const holder = getAutoWorker(heldLease.worker_id);
|
|
1095
|
+
// Let the one-shot claim path recover stale same-process worker rows.
|
|
1096
|
+
const projectRoot = normalizeRealPath(basePath);
|
|
1097
|
+
const isOurAutoLease = isAutoActive() && heldLease.worker_id === autoSession.workerId;
|
|
1098
|
+
const holderIsOneShotReentrantPeer = !isAutoActive()
|
|
1099
|
+
&& !!holder
|
|
1100
|
+
&& holder.host === hostname()
|
|
1101
|
+
&& holder.pid === process.pid
|
|
1102
|
+
&& holder.project_root_realpath === projectRoot;
|
|
1103
|
+
if (holder?.status === "active" && !isOurAutoLease && !holderIsOneShotReentrantPeer) {
|
|
1104
|
+
return milestoneLeaseConflictResult(params.milestoneId, heldLease.worker_id, heldLease.expires_at);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
// Fresh creation cannot claim a lease because the FK row does not exist.
|
|
1109
|
+
// In-process auto already owns its lease; re-claiming would bump its token.
|
|
1110
|
+
if (!isAutoActive() && milestoneExists) {
|
|
1111
|
+
workerId = registerAutoWorker({ projectRootRealpath: normalizeRealPath(basePath) });
|
|
1112
|
+
const lease = claimMilestoneLease(workerId, params.milestoneId);
|
|
1113
|
+
if (!lease.ok) {
|
|
1114
|
+
return milestoneLeaseConflictResult(params.milestoneId, lease.byWorker, lease.expiresAt);
|
|
1115
|
+
}
|
|
1116
|
+
acquiredToken = lease.token;
|
|
1117
|
+
const leaseRefreshMs = (milestoneLeaseTtlSeconds() / 2) * 1000;
|
|
1118
|
+
leaseRefreshTimer = setInterval(() => {
|
|
1119
|
+
if (acquiredToken !== null && workerId !== null) {
|
|
1120
|
+
refreshMilestoneLease(workerId, params.milestoneId, acquiredToken);
|
|
1121
|
+
}
|
|
1122
|
+
}, leaseRefreshMs);
|
|
1123
|
+
}
|
|
1070
1124
|
const result = await handlePlanMilestone(params, basePath);
|
|
1071
1125
|
if ("error" in result) {
|
|
1072
1126
|
return {
|
|
@@ -1093,6 +1147,17 @@ export async function executePlanMilestone(params, basePath = process.cwd()) {
|
|
|
1093
1147
|
isError: true,
|
|
1094
1148
|
};
|
|
1095
1149
|
}
|
|
1150
|
+
finally {
|
|
1151
|
+
if (leaseRefreshTimer !== undefined) {
|
|
1152
|
+
clearInterval(leaseRefreshTimer);
|
|
1153
|
+
}
|
|
1154
|
+
if (workerId !== null && acquiredToken !== null) {
|
|
1155
|
+
releaseMilestoneLease(workerId, params.milestoneId, acquiredToken);
|
|
1156
|
+
}
|
|
1157
|
+
if (workerId !== null) {
|
|
1158
|
+
markWorkerStopping(workerId);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1096
1161
|
}
|
|
1097
1162
|
export async function executePlanSlice(params, basePath = process.cwd()) {
|
|
1098
1163
|
const dbAvailable = await ensureDbOpen(basePath);
|