gsd-pi 2.33.1-dev.ee47f1b → 2.34.0-dev.bbb5216
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/bundled-resource-path.d.ts +8 -0
- package/dist/bundled-resource-path.js +14 -0
- package/dist/headless-query.js +6 -6
- package/dist/resources/extensions/gsd/auto/session.js +27 -32
- package/dist/resources/extensions/gsd/auto-dashboard.js +29 -109
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +6 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +52 -81
- package/dist/resources/extensions/gsd/auto-loop.js +956 -0
- package/dist/resources/extensions/gsd/auto-observability.js +4 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +75 -185
- package/dist/resources/extensions/gsd/auto-prompts.js +133 -101
- package/dist/resources/extensions/gsd/auto-recovery.js +59 -97
- package/dist/resources/extensions/gsd/auto-start.js +330 -309
- package/dist/resources/extensions/gsd/auto-supervisor.js +5 -11
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +7 -7
- package/dist/resources/extensions/gsd/auto-timers.js +3 -4
- package/dist/resources/extensions/gsd/auto-verification.js +35 -73
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +167 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +291 -126
- package/dist/resources/extensions/gsd/auto.js +283 -1013
- package/dist/resources/extensions/gsd/captures.js +10 -4
- package/dist/resources/extensions/gsd/dispatch-guard.js +7 -8
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +25 -18
- package/dist/resources/extensions/gsd/doctor-checks.js +3 -4
- package/dist/resources/extensions/gsd/git-service.js +1 -1
- package/dist/resources/extensions/gsd/gsd-db.js +296 -151
- package/dist/resources/extensions/gsd/index.js +92 -228
- package/dist/resources/extensions/gsd/post-unit-hooks.js +13 -13
- package/dist/resources/extensions/gsd/progress-score.js +61 -156
- package/dist/resources/extensions/gsd/quick.js +98 -122
- package/dist/resources/extensions/gsd/session-lock.js +13 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/undo.js +43 -48
- package/dist/resources/extensions/gsd/unit-runtime.js +16 -15
- package/dist/resources/extensions/gsd/verification-evidence.js +0 -1
- package/dist/resources/extensions/gsd/verification-gate.js +6 -35
- package/dist/resources/extensions/gsd/worktree-command.js +30 -24
- package/dist/resources/extensions/gsd/worktree-manager.js +2 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +344 -0
- package/dist/resources/extensions/gsd/worktree.js +7 -44
- package/dist/tool-bootstrap.js +59 -11
- package/dist/worktree-cli.js +7 -7
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +3630 -5483
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +735 -2588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/src/models.generated.ts +1039 -2892
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/session.ts +47 -30
- package/src/resources/extensions/gsd/auto-dashboard.ts +28 -131
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +6 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +135 -91
- package/src/resources/extensions/gsd/auto-loop.ts +1665 -0
- package/src/resources/extensions/gsd/auto-observability.ts +4 -2
- package/src/resources/extensions/gsd/auto-post-unit.ts +85 -228
- package/src/resources/extensions/gsd/auto-prompts.ts +138 -109
- package/src/resources/extensions/gsd/auto-recovery.ts +124 -118
- package/src/resources/extensions/gsd/auto-start.ts +440 -354
- package/src/resources/extensions/gsd/auto-supervisor.ts +5 -12
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +8 -8
- package/src/resources/extensions/gsd/auto-timers.ts +3 -4
- package/src/resources/extensions/gsd/auto-verification.ts +76 -90
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +204 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +389 -141
- package/src/resources/extensions/gsd/auto.ts +515 -1199
- package/src/resources/extensions/gsd/captures.ts +10 -4
- package/src/resources/extensions/gsd/dispatch-guard.ts +13 -9
- package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -18
- package/src/resources/extensions/gsd/doctor-checks.ts +3 -4
- package/src/resources/extensions/gsd/git-service.ts +8 -1
- package/src/resources/extensions/gsd/gitignore.ts +4 -2
- package/src/resources/extensions/gsd/gsd-db.ts +375 -180
- package/src/resources/extensions/gsd/index.ts +104 -263
- package/src/resources/extensions/gsd/post-unit-hooks.ts +13 -13
- package/src/resources/extensions/gsd/progress-score.ts +65 -200
- package/src/resources/extensions/gsd/quick.ts +121 -125
- package/src/resources/extensions/gsd/session-lock.ts +11 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +32 -59
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +75 -27
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1458 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +8 -162
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -108
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -55
- package/src/resources/extensions/gsd/tests/headless-query.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +8 -11
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/undo.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +24 -26
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +7 -201
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +705 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +57 -106
- package/src/resources/extensions/gsd/tests/worktree.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +43 -132
- package/src/resources/extensions/gsd/types.ts +90 -81
- package/src/resources/extensions/gsd/undo.ts +42 -46
- package/src/resources/extensions/gsd/unit-runtime.ts +14 -18
- package/src/resources/extensions/gsd/verification-evidence.ts +1 -3
- package/src/resources/extensions/gsd/verification-gate.ts +6 -39
- package/src/resources/extensions/gsd/worktree-command.ts +36 -24
- package/src/resources/extensions/gsd/worktree-manager.ts +2 -3
- package/src/resources/extensions/gsd/worktree-resolver.ts +485 -0
- package/src/resources/extensions/gsd/worktree.ts +7 -44
- package/dist/resources/extensions/gsd/auto-constants.js +0 -5
- package/dist/resources/extensions/gsd/auto-idempotency.js +0 -106
- package/dist/resources/extensions/gsd/auto-stuck-detection.js +0 -165
- package/dist/resources/extensions/gsd/mechanical-completion.js +0 -351
- package/src/resources/extensions/gsd/auto-constants.ts +0 -6
- package/src/resources/extensions/gsd/auto-idempotency.ts +0 -151
- package/src/resources/extensions/gsd/auto-stuck-detection.ts +0 -221
- package/src/resources/extensions/gsd/mechanical-completion.ts +0 -430
- package/src/resources/extensions/gsd/tests/auto-dispatch-loop.test.ts +0 -691
- package/src/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +0 -127
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +0 -123
- package/src/resources/extensions/gsd/tests/dispatch-stall-guard.test.ts +0 -126
- package/src/resources/extensions/gsd/tests/loop-regression.test.ts +0 -874
- package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +0 -356
- package/src/resources/extensions/gsd/tests/progress-score.test.ts +0 -206
- package/src/resources/extensions/gsd/tests/session-lock.test.ts +0 -434
|
@@ -11,10 +11,15 @@
|
|
|
11
11
|
|
|
12
12
|
import type { GSDState } from "./types.js";
|
|
13
13
|
import type { GSDPreferences } from "./preferences.js";
|
|
14
|
-
import {
|
|
14
|
+
import type { UatType } from "./files.js";
|
|
15
|
+
import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
|
|
15
16
|
import {
|
|
16
|
-
resolveMilestoneFile,
|
|
17
|
-
|
|
17
|
+
resolveMilestoneFile,
|
|
18
|
+
resolveMilestonePath,
|
|
19
|
+
resolveSliceFile,
|
|
20
|
+
resolveTaskFile,
|
|
21
|
+
relSliceFile,
|
|
22
|
+
buildMilestoneFileName,
|
|
18
23
|
} from "./paths.js";
|
|
19
24
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
20
25
|
import { join } from "node:path";
|
|
@@ -38,7 +43,13 @@ import {
|
|
|
38
43
|
// ─── Types ────────────────────────────────────────────────────────────────
|
|
39
44
|
|
|
40
45
|
export type DispatchAction =
|
|
41
|
-
| {
|
|
46
|
+
| {
|
|
47
|
+
action: "dispatch";
|
|
48
|
+
unitType: string;
|
|
49
|
+
unitId: string;
|
|
50
|
+
prompt: string;
|
|
51
|
+
pauseAfterDispatch?: boolean;
|
|
52
|
+
}
|
|
42
53
|
| { action: "stop"; reason: string; level: "info" | "warning" | "error" }
|
|
43
54
|
| { action: "skip" };
|
|
44
55
|
|
|
@@ -57,6 +68,14 @@ interface DispatchRule {
|
|
|
57
68
|
match: (ctx: DispatchContext) => Promise<DispatchAction | null>;
|
|
58
69
|
}
|
|
59
70
|
|
|
71
|
+
function missingSliceStop(mid: string, phase: string): DispatchAction {
|
|
72
|
+
return {
|
|
73
|
+
action: "stop",
|
|
74
|
+
reason: `${mid}: phase "${phase}" has no active slice — run /gsd doctor.`,
|
|
75
|
+
level: "error",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
60
79
|
// ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
|
|
61
80
|
|
|
62
81
|
const MAX_REWRITE_ATTEMPTS = 3;
|
|
@@ -65,28 +84,6 @@ export function resetRewriteCircuitBreaker(): void {
|
|
|
65
84
|
rewriteAttemptCount = 0;
|
|
66
85
|
}
|
|
67
86
|
|
|
68
|
-
/**
|
|
69
|
-
* Guard for accessing activeSlice/activeTask in dispatch rules.
|
|
70
|
-
* Returns a stop action if the expected ref is null (corrupt state).
|
|
71
|
-
*/
|
|
72
|
-
function requireSlice(state: GSDState): { sid: string; sTitle: string } | DispatchAction {
|
|
73
|
-
if (!state.activeSlice) {
|
|
74
|
-
return { action: "stop", reason: `Phase "${state.phase}" but no active slice — run /gsd doctor.`, level: "error" };
|
|
75
|
-
}
|
|
76
|
-
return { sid: state.activeSlice.id, sTitle: state.activeSlice.title };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function requireTask(state: GSDState): { sid: string; sTitle: string; tid: string; tTitle: string } | DispatchAction {
|
|
80
|
-
if (!state.activeSlice || !state.activeTask) {
|
|
81
|
-
return { action: "stop", reason: `Phase "${state.phase}" but no active slice/task — run /gsd doctor.`, level: "error" };
|
|
82
|
-
}
|
|
83
|
-
return { sid: state.activeSlice.id, sTitle: state.activeSlice.title, tid: state.activeTask.id, tTitle: state.activeTask.title };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function isStopAction(v: unknown): v is DispatchAction {
|
|
87
|
-
return typeof v === "object" && v !== null && "action" in v;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
87
|
// ─── Rules ────────────────────────────────────────────────────────────────
|
|
91
88
|
|
|
92
89
|
const DISPATCH_RULES: DispatchRule[] = [
|
|
@@ -107,7 +104,13 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
107
104
|
action: "dispatch",
|
|
108
105
|
unitType: "rewrite-docs",
|
|
109
106
|
unitId,
|
|
110
|
-
prompt: await buildRewriteDocsPrompt(
|
|
107
|
+
prompt: await buildRewriteDocsPrompt(
|
|
108
|
+
mid,
|
|
109
|
+
midTitle,
|
|
110
|
+
state.activeSlice,
|
|
111
|
+
basePath,
|
|
112
|
+
pendingOverrides,
|
|
113
|
+
),
|
|
111
114
|
};
|
|
112
115
|
},
|
|
113
116
|
},
|
|
@@ -115,74 +118,63 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
115
118
|
name: "summarizing → complete-slice",
|
|
116
119
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
117
120
|
if (state.phase !== "summarizing") return null;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
122
|
+
const sid = state.activeSlice!.id;
|
|
123
|
+
const sTitle = state.activeSlice!.title;
|
|
121
124
|
return {
|
|
122
125
|
action: "dispatch",
|
|
123
126
|
unitType: "complete-slice",
|
|
124
127
|
unitId: `${mid}/${sid}`,
|
|
125
|
-
prompt: await buildCompleteSlicePrompt(
|
|
128
|
+
prompt: await buildCompleteSlicePrompt(
|
|
129
|
+
mid,
|
|
130
|
+
midTitle,
|
|
131
|
+
sid,
|
|
132
|
+
sTitle,
|
|
133
|
+
basePath,
|
|
134
|
+
),
|
|
126
135
|
};
|
|
127
136
|
},
|
|
128
137
|
},
|
|
129
|
-
{
|
|
130
|
-
name: "uat-verdict-gate (non-PASS blocks progression)",
|
|
131
|
-
match: async ({ mid, basePath, prefs }) => {
|
|
132
|
-
// Only applies when UAT dispatch is enabled
|
|
133
|
-
if (!prefs?.uat_dispatch) return null;
|
|
134
|
-
|
|
135
|
-
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
|
|
136
|
-
const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
|
|
137
|
-
if (!roadmapContent) return null;
|
|
138
|
-
|
|
139
|
-
const roadmap = parseRoadmap(roadmapContent);
|
|
140
|
-
for (const slice of roadmap.slices.filter(s => s.done)) {
|
|
141
|
-
const resultFile = resolveSliceFile(basePath, mid, slice.id, "UAT-RESULT");
|
|
142
|
-
if (!resultFile) continue;
|
|
143
|
-
const content = await loadFile(resultFile);
|
|
144
|
-
if (!content) continue;
|
|
145
|
-
const verdictMatch = content.match(/verdict:\s*([\w-]+)/i);
|
|
146
|
-
const verdict = verdictMatch?.[1]?.toLowerCase();
|
|
147
|
-
if (verdict && verdict !== "pass" && verdict !== "passed") {
|
|
148
|
-
return {
|
|
149
|
-
action: "stop" as const,
|
|
150
|
-
reason: `UAT verdict for ${slice.id} is "${verdict}" — blocking progression until resolved.\nReview the UAT result and update the verdict to PASS, or re-run /gsd auto after fixing.`,
|
|
151
|
-
level: "warning" as const,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return null;
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
138
|
{
|
|
159
139
|
name: "run-uat (post-completion)",
|
|
160
140
|
match: async ({ state, mid, basePath, prefs }) => {
|
|
161
141
|
const needsRunUat = await checkNeedsRunUat(basePath, mid, state, prefs);
|
|
162
142
|
if (!needsRunUat) return null;
|
|
163
|
-
const { sliceId } = needsRunUat;
|
|
143
|
+
const { sliceId, uatType } = needsRunUat;
|
|
144
|
+
const uatFile = resolveSliceFile(basePath, mid, sliceId, "UAT")!;
|
|
145
|
+
const uatContent = await loadFile(uatFile);
|
|
164
146
|
return {
|
|
165
147
|
action: "dispatch",
|
|
166
148
|
unitType: "run-uat",
|
|
167
149
|
unitId: `${mid}/${sliceId}`,
|
|
168
150
|
prompt: await buildRunUatPrompt(
|
|
169
|
-
mid,
|
|
151
|
+
mid,
|
|
152
|
+
sliceId,
|
|
153
|
+
relSliceFile(basePath, mid, sliceId, "UAT"),
|
|
154
|
+
uatContent ?? "",
|
|
155
|
+
basePath,
|
|
170
156
|
),
|
|
157
|
+
pauseAfterDispatch: uatType !== "artifact-driven",
|
|
171
158
|
};
|
|
172
159
|
},
|
|
173
160
|
},
|
|
174
161
|
{
|
|
175
162
|
name: "reassess-roadmap (post-completion)",
|
|
176
163
|
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
177
|
-
|
|
178
|
-
|
|
164
|
+
if (prefs?.phases?.skip_reassess || !prefs?.phases?.reassess_after_slice)
|
|
165
|
+
return null;
|
|
179
166
|
const needsReassess = await checkNeedsReassessment(basePath, mid, state);
|
|
180
167
|
if (!needsReassess) return null;
|
|
181
168
|
return {
|
|
182
169
|
action: "dispatch",
|
|
183
170
|
unitType: "reassess-roadmap",
|
|
184
171
|
unitId: `${mid}/${needsReassess.sliceId}`,
|
|
185
|
-
prompt: await buildReassessRoadmapPrompt(
|
|
172
|
+
prompt: await buildReassessRoadmapPrompt(
|
|
173
|
+
mid,
|
|
174
|
+
midTitle,
|
|
175
|
+
needsReassess.sliceId,
|
|
176
|
+
basePath,
|
|
177
|
+
),
|
|
186
178
|
};
|
|
187
179
|
},
|
|
188
180
|
},
|
|
@@ -202,7 +194,7 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
202
194
|
match: async ({ state, mid, basePath }) => {
|
|
203
195
|
if (state.phase !== "pre-planning") return null;
|
|
204
196
|
const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
|
|
205
|
-
const hasContext = !!(contextFile && await loadFile(contextFile));
|
|
197
|
+
const hasContext = !!(contextFile && (await loadFile(contextFile)));
|
|
206
198
|
if (hasContext) return null; // fall through to next rule
|
|
207
199
|
return {
|
|
208
200
|
action: "stop",
|
|
@@ -244,21 +236,32 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
244
236
|
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
245
237
|
if (state.phase !== "planning") return null;
|
|
246
238
|
// Phase skip: skip research when preference or profile says so
|
|
247
|
-
if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
const
|
|
239
|
+
if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
|
|
240
|
+
return null;
|
|
241
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
242
|
+
const sid = state.activeSlice!.id;
|
|
243
|
+
const sTitle = state.activeSlice!.title;
|
|
251
244
|
const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
|
|
252
245
|
if (researchFile) return null; // has research, fall through
|
|
253
246
|
// Skip slice research for S01 when milestone research already exists —
|
|
254
247
|
// the milestone research already covers the same ground for the first slice.
|
|
255
|
-
const milestoneResearchFile = resolveMilestoneFile(
|
|
248
|
+
const milestoneResearchFile = resolveMilestoneFile(
|
|
249
|
+
basePath,
|
|
250
|
+
mid,
|
|
251
|
+
"RESEARCH",
|
|
252
|
+
);
|
|
256
253
|
if (milestoneResearchFile && sid === "S01") return null; // fall through to plan-slice
|
|
257
254
|
return {
|
|
258
255
|
action: "dispatch",
|
|
259
256
|
unitType: "research-slice",
|
|
260
257
|
unitId: `${mid}/${sid}`,
|
|
261
|
-
prompt: await buildResearchSlicePrompt(
|
|
258
|
+
prompt: await buildResearchSlicePrompt(
|
|
259
|
+
mid,
|
|
260
|
+
midTitle,
|
|
261
|
+
sid,
|
|
262
|
+
sTitle,
|
|
263
|
+
basePath,
|
|
264
|
+
),
|
|
262
265
|
};
|
|
263
266
|
},
|
|
264
267
|
},
|
|
@@ -266,14 +269,20 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
266
269
|
name: "planning → plan-slice",
|
|
267
270
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
268
271
|
if (state.phase !== "planning") return null;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
273
|
+
const sid = state.activeSlice!.id;
|
|
274
|
+
const sTitle = state.activeSlice!.title;
|
|
272
275
|
return {
|
|
273
276
|
action: "dispatch",
|
|
274
277
|
unitType: "plan-slice",
|
|
275
278
|
unitId: `${mid}/${sid}`,
|
|
276
|
-
prompt: await buildPlanSlicePrompt(
|
|
279
|
+
prompt: await buildPlanSlicePrompt(
|
|
280
|
+
mid,
|
|
281
|
+
midTitle,
|
|
282
|
+
sid,
|
|
283
|
+
sTitle,
|
|
284
|
+
basePath,
|
|
285
|
+
),
|
|
277
286
|
};
|
|
278
287
|
},
|
|
279
288
|
},
|
|
@@ -281,14 +290,20 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
281
290
|
name: "replanning-slice → replan-slice",
|
|
282
291
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
283
292
|
if (state.phase !== "replanning-slice") return null;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const
|
|
293
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
294
|
+
const sid = state.activeSlice!.id;
|
|
295
|
+
const sTitle = state.activeSlice!.title;
|
|
287
296
|
return {
|
|
288
297
|
action: "dispatch",
|
|
289
298
|
unitType: "replan-slice",
|
|
290
299
|
unitId: `${mid}/${sid}`,
|
|
291
|
-
prompt: await buildReplanSlicePrompt(
|
|
300
|
+
prompt: await buildReplanSlicePrompt(
|
|
301
|
+
mid,
|
|
302
|
+
midTitle,
|
|
303
|
+
sid,
|
|
304
|
+
sTitle,
|
|
305
|
+
basePath,
|
|
306
|
+
),
|
|
292
307
|
};
|
|
293
308
|
},
|
|
294
309
|
},
|
|
@@ -296,9 +311,9 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
296
311
|
name: "executing → execute-task (recover missing task plan → plan-slice)",
|
|
297
312
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
298
313
|
if (state.phase !== "executing" || !state.activeTask) return null;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
314
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
315
|
+
const sid = state.activeSlice!.id;
|
|
316
|
+
const sTitle = state.activeSlice!.title;
|
|
302
317
|
const tid = state.activeTask.id;
|
|
303
318
|
|
|
304
319
|
// Guard: if the slice plan exists but the individual task plan files are
|
|
@@ -312,7 +327,13 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
312
327
|
action: "dispatch",
|
|
313
328
|
unitType: "plan-slice",
|
|
314
329
|
unitId: `${mid}/${sid}`,
|
|
315
|
-
prompt: await buildPlanSlicePrompt(
|
|
330
|
+
prompt: await buildPlanSlicePrompt(
|
|
331
|
+
mid,
|
|
332
|
+
midTitle,
|
|
333
|
+
sid,
|
|
334
|
+
sTitle,
|
|
335
|
+
basePath,
|
|
336
|
+
),
|
|
316
337
|
};
|
|
317
338
|
}
|
|
318
339
|
|
|
@@ -323,9 +344,9 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
323
344
|
name: "executing → execute-task",
|
|
324
345
|
match: async ({ state, mid, basePath }) => {
|
|
325
346
|
if (state.phase !== "executing" || !state.activeTask) return null;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
347
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
348
|
+
const sid = state.activeSlice!.id;
|
|
349
|
+
const sTitle = state.activeSlice!.title;
|
|
329
350
|
const tid = state.activeTask.id;
|
|
330
351
|
const tTitle = state.activeTask.title;
|
|
331
352
|
|
|
@@ -333,7 +354,14 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
333
354
|
action: "dispatch",
|
|
334
355
|
unitType: "execute-task",
|
|
335
356
|
unitId: `${mid}/${sid}/${tid}`,
|
|
336
|
-
prompt: await buildExecuteTaskPrompt(
|
|
357
|
+
prompt: await buildExecuteTaskPrompt(
|
|
358
|
+
mid,
|
|
359
|
+
sid,
|
|
360
|
+
sTitle,
|
|
361
|
+
tid,
|
|
362
|
+
tTitle,
|
|
363
|
+
basePath,
|
|
364
|
+
),
|
|
337
365
|
};
|
|
338
366
|
},
|
|
339
367
|
},
|
|
@@ -346,7 +374,10 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
346
374
|
const mDir = resolveMilestonePath(basePath, mid);
|
|
347
375
|
if (mDir) {
|
|
348
376
|
if (!existsSync(mDir)) mkdirSync(mDir, { recursive: true });
|
|
349
|
-
const validationPath = join(
|
|
377
|
+
const validationPath = join(
|
|
378
|
+
mDir,
|
|
379
|
+
buildMilestoneFileName(mid, "VALIDATION"),
|
|
380
|
+
);
|
|
350
381
|
const content = [
|
|
351
382
|
"---",
|
|
352
383
|
"verdict: pass",
|
|
@@ -381,6 +412,17 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
381
412
|
};
|
|
382
413
|
},
|
|
383
414
|
},
|
|
415
|
+
{
|
|
416
|
+
name: "complete → stop",
|
|
417
|
+
match: async ({ state }) => {
|
|
418
|
+
if (state.phase !== "complete") return null;
|
|
419
|
+
return {
|
|
420
|
+
action: "stop",
|
|
421
|
+
reason: "All milestones complete.",
|
|
422
|
+
level: "info",
|
|
423
|
+
};
|
|
424
|
+
},
|
|
425
|
+
},
|
|
384
426
|
];
|
|
385
427
|
|
|
386
428
|
// ─── Resolver ─────────────────────────────────────────────────────────────
|
|
@@ -389,7 +431,9 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
389
431
|
* Evaluate dispatch rules in order. Returns the first matching action,
|
|
390
432
|
* or a "stop" action if no rule matches (unhandled phase).
|
|
391
433
|
*/
|
|
392
|
-
export async function resolveDispatch(
|
|
434
|
+
export async function resolveDispatch(
|
|
435
|
+
ctx: DispatchContext,
|
|
436
|
+
): Promise<DispatchAction> {
|
|
393
437
|
for (const rule of DISPATCH_RULES) {
|
|
394
438
|
const result = await rule.match(ctx);
|
|
395
439
|
if (result) return result;
|
|
@@ -405,5 +449,5 @@ export async function resolveDispatch(ctx: DispatchContext): Promise<DispatchAct
|
|
|
405
449
|
|
|
406
450
|
/** Exposed for testing — returns the rule names in evaluation order. */
|
|
407
451
|
export function getDispatchRuleNames(): string[] {
|
|
408
|
-
return DISPATCH_RULES.map(r => r.name);
|
|
452
|
+
return DISPATCH_RULES.map((r) => r.name);
|
|
409
453
|
}
|