gsd-pi 2.37.1 → 2.38.0-dev.29edcdc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/app-paths.js +1 -1
- package/dist/cli.js +9 -0
- package/dist/extension-discovery.d.ts +5 -3
- package/dist/extension-discovery.js +14 -9
- package/dist/extension-registry.js +2 -2
- package/dist/onboarding.js +1 -0
- package/dist/remote-questions-config.js +2 -2
- package/dist/resource-loader.js +34 -1
- package/dist/resources/extensions/browser-tools/package.json +3 -1
- package/dist/resources/extensions/cmux/index.js +55 -1
- package/dist/resources/extensions/context7/package.json +1 -1
- package/dist/resources/extensions/env-utils.js +29 -0
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/github-sync/cli.js +284 -0
- package/dist/resources/extensions/github-sync/index.js +73 -0
- package/dist/resources/extensions/github-sync/mapping.js +67 -0
- package/dist/resources/extensions/github-sync/sync.js +424 -0
- package/dist/resources/extensions/github-sync/templates.js +118 -0
- package/dist/resources/extensions/github-sync/types.js +7 -0
- package/dist/resources/extensions/google-search/package.json +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +6 -23
- package/dist/resources/extensions/gsd/auto-dispatch.js +75 -10
- package/dist/resources/extensions/gsd/auto-loop.js +597 -588
- package/dist/resources/extensions/gsd/auto-post-unit.js +111 -68
- package/dist/resources/extensions/gsd/auto-prompts.js +114 -45
- package/dist/resources/extensions/gsd/auto-recovery.js +37 -1
- package/dist/resources/extensions/gsd/auto-start.js +13 -2
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/auto.js +143 -96
- package/dist/resources/extensions/gsd/captures.js +9 -1
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +16 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +24 -3
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/detection.js +1 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
- package/dist/resources/extensions/gsd/doctor-format.js +15 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +62 -12
- package/dist/resources/extensions/gsd/doctor.js +204 -12
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/export.js +1 -1
- package/dist/resources/extensions/gsd/files.js +47 -2
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-service.js +15 -12
- package/dist/resources/extensions/gsd/guided-flow.js +82 -32
- package/dist/resources/extensions/gsd/index.js +24 -20
- package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/observability-validator.js +24 -0
- package/dist/resources/extensions/gsd/package.json +1 -1
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +3 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +101 -11
- package/dist/resources/extensions/gsd/preferences.js +8 -5
- package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
- package/dist/resources/extensions/gsd/prompts/run-uat.md +27 -10
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/reactive-graph.js +227 -0
- package/dist/resources/extensions/gsd/repo-identity.js +21 -4
- package/dist/resources/extensions/gsd/resource-version.js +2 -1
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/gsd/state.js +1 -1
- package/dist/resources/extensions/gsd/templates/task-plan.md +11 -3
- package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
- package/dist/resources/extensions/gsd/worktree.js +35 -16
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/dist/resources/extensions/remote-questions/status.js +2 -1
- package/dist/resources/extensions/remote-questions/store.js +2 -1
- package/dist/resources/extensions/search-the-web/provider.js +2 -1
- package/dist/resources/extensions/subagent/index.js +12 -3
- package/dist/resources/extensions/subagent/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/dist/resources/extensions/universal-config/package.json +1 -1
- package/dist/welcome-screen.d.ts +12 -0
- package/dist/welcome-screen.js +53 -0
- package/package.json +2 -1
- package/packages/pi-ai/dist/env-api-keys.js +13 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +172 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +172 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +64 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.js +668 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts +5 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.js +85 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts +4 -30
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +47 -764
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js +6 -0
- package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -2
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/package.json +1 -0
- package/packages/pi-ai/src/env-api-keys.ts +14 -0
- package/packages/pi-ai/src/models.generated.ts +172 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +761 -0
- package/packages/pi-ai/src/providers/anthropic-vertex.ts +130 -0
- package/packages/pi-ai/src/providers/anthropic.ts +76 -868
- package/packages/pi-ai/src/providers/register-builtins.ts +7 -0
- package/packages/pi-ai/src/types.ts +2 -0
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +57 -1
- package/src/resources/extensions/env-utils.ts +31 -0
- package/src/resources/extensions/get-secrets-from-user.ts +5 -24
- package/src/resources/extensions/github-sync/cli.ts +364 -0
- package/src/resources/extensions/github-sync/index.ts +93 -0
- package/src/resources/extensions/github-sync/mapping.ts +81 -0
- package/src/resources/extensions/github-sync/sync.ts +556 -0
- package/src/resources/extensions/github-sync/templates.ts +183 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
- package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
- package/src/resources/extensions/github-sync/types.ts +47 -0
- package/src/resources/extensions/gsd/auto/session.ts +7 -25
- package/src/resources/extensions/gsd/auto-dispatch.ts +100 -9
- package/src/resources/extensions/gsd/auto-loop.ts +484 -546
- package/src/resources/extensions/gsd/auto-post-unit.ts +92 -42
- package/src/resources/extensions/gsd/auto-prompts.ts +150 -48
- package/src/resources/extensions/gsd/auto-recovery.ts +42 -0
- package/src/resources/extensions/gsd/auto-start.ts +18 -2
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/auto.ts +139 -101
- package/src/resources/extensions/gsd/captures.ts +10 -1
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +17 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +26 -4
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/detection.ts +2 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
- package/src/resources/extensions/gsd/doctor-format.ts +20 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +64 -10
- package/src/resources/extensions/gsd/doctor-types.ts +16 -1
- package/src/resources/extensions/gsd/doctor.ts +199 -14
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/export.ts +1 -1
- package/src/resources/extensions/gsd/files.ts +50 -3
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +20 -10
- package/src/resources/extensions/gsd/guided-flow.ts +110 -38
- package/src/resources/extensions/gsd/index.ts +24 -17
- package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/observability-validator.ts +27 -0
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +9 -5
- package/src/resources/extensions/gsd/preferences-validation.ts +92 -11
- package/src/resources/extensions/gsd/preferences.ts +8 -5
- package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +27 -10
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/reactive-graph.ts +289 -0
- package/src/resources/extensions/gsd/repo-identity.ts +23 -4
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/state.ts +1 -1
- package/src/resources/extensions/gsd/templates/task-plan.md +11 -3
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
- package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +191 -3
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +111 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +511 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +299 -0
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
- package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
- package/src/resources/extensions/gsd/types.ts +43 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
- package/src/resources/extensions/gsd/worktree.ts +35 -15
- package/src/resources/extensions/mcp-client/index.ts +17 -1
- package/src/resources/extensions/remote-questions/status.ts +3 -1
- package/src/resources/extensions/remote-questions/store.ts +3 -1
- package/src/resources/extensions/search-the-web/provider.ts +2 -1
- package/src/resources/extensions/subagent/index.ts +12 -3
- package/src/resources/extensions/subagent/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -33,7 +33,6 @@ import { writeUnitRuntimeRecord, clearUnitRuntimeRecord } from "./unit-runtime.j
|
|
|
33
33
|
import { runGSDDoctor, rebuildState, summarizeDoctorIssues } from "./doctor.js";
|
|
34
34
|
import { recordHealthSnapshot, checkHealEscalation } from "./doctor-proactive.js";
|
|
35
35
|
import { syncStateToProjectRoot } from "./auto-worktree-sync.js";
|
|
36
|
-
import { resetRewriteCircuitBreaker } from "./auto-dispatch.js";
|
|
37
36
|
import { isDbAvailable } from "./gsd-db.js";
|
|
38
37
|
import { consumeSignal } from "./session-status-io.js";
|
|
39
38
|
import {
|
|
@@ -56,6 +55,13 @@ import { join } from "node:path";
|
|
|
56
55
|
/** Throttle STATE.md rebuilds — at most once per 30 seconds */
|
|
57
56
|
const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
58
57
|
|
|
58
|
+
export interface PreVerificationOpts {
|
|
59
|
+
skipSettleDelay?: boolean;
|
|
60
|
+
skipDoctor?: boolean;
|
|
61
|
+
skipStateRebuild?: boolean;
|
|
62
|
+
skipWorktreeSync?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
export interface PostUnitContext {
|
|
60
66
|
s: AutoSession;
|
|
61
67
|
ctx: ExtensionContext;
|
|
@@ -73,7 +79,7 @@ export interface PostUnitContext {
|
|
|
73
79
|
*
|
|
74
80
|
* Returns "dispatched" if a signal caused stop/pause, "continue" to proceed.
|
|
75
81
|
*/
|
|
76
|
-
export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"dispatched" | "continue"> {
|
|
82
|
+
export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreVerificationOpts): Promise<"dispatched" | "continue"> {
|
|
77
83
|
const { s, ctx, pi, buildSnapshotOpts, stopAuto, pauseAuto } = pctx;
|
|
78
84
|
|
|
79
85
|
// ── Parallel worker signal check ──
|
|
@@ -95,8 +101,10 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
95
101
|
// Invalidate all caches
|
|
96
102
|
invalidateAllCaches();
|
|
97
103
|
|
|
98
|
-
// Small delay to let files settle
|
|
99
|
-
|
|
104
|
+
// Small delay to let files settle (skipped for sidecars where latency matters more)
|
|
105
|
+
if (!opts?.skipSettleDelay) {
|
|
106
|
+
await new Promise(r => setTimeout(r, 100));
|
|
107
|
+
}
|
|
100
108
|
|
|
101
109
|
// Auto-commit
|
|
102
110
|
if (s.currentUnit) {
|
|
@@ -113,15 +121,25 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
113
121
|
const summaryContent = await loadFile(summaryPath);
|
|
114
122
|
if (summaryContent) {
|
|
115
123
|
const summary = parseSummary(summaryContent);
|
|
124
|
+
// Look up GitHub issue number for commit linking
|
|
125
|
+
let ghIssueNumber: number | undefined;
|
|
126
|
+
try {
|
|
127
|
+
const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
|
|
128
|
+
ghIssueNumber = getTaskIssueNumberForCommit(s.basePath, mid, sid, tid) ?? undefined;
|
|
129
|
+
} catch {
|
|
130
|
+
// GitHub sync not available — skip
|
|
131
|
+
}
|
|
132
|
+
|
|
116
133
|
taskContext = {
|
|
117
134
|
taskId: `${sid}/${tid}`,
|
|
118
135
|
taskTitle: summary.title?.replace(/^T\d+:\s*/, "") || tid,
|
|
119
136
|
oneLiner: summary.oneLiner || undefined,
|
|
120
137
|
keyFiles: summary.frontmatter.key_files?.filter(f => !f.includes("{{")) || undefined,
|
|
138
|
+
issueNumber: ghIssueNumber,
|
|
121
139
|
};
|
|
122
140
|
}
|
|
123
|
-
} catch {
|
|
124
|
-
|
|
141
|
+
} catch (e) {
|
|
142
|
+
debugLog("postUnit", { phase: "task-summary-parse", error: String(e) });
|
|
125
143
|
}
|
|
126
144
|
}
|
|
127
145
|
}
|
|
@@ -131,12 +149,20 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
131
149
|
if (commitMsg) {
|
|
132
150
|
ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
|
|
133
151
|
}
|
|
134
|
-
} catch {
|
|
135
|
-
|
|
152
|
+
} catch (e) {
|
|
153
|
+
debugLog("postUnit", { phase: "auto-commit", error: String(e) });
|
|
136
154
|
}
|
|
137
155
|
|
|
138
|
-
//
|
|
156
|
+
// GitHub sync (non-blocking, opt-in)
|
|
139
157
|
try {
|
|
158
|
+
const { runGitHubSync } = await import("../github-sync/sync.js");
|
|
159
|
+
await runGitHubSync(s.basePath, s.currentUnit.type, s.currentUnit.id);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
debugLog("postUnit", { phase: "github-sync", error: String(e) });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Doctor: fix mechanical bookkeeping (skipped for lightweight sidecars)
|
|
165
|
+
if (!opts?.skipDoctor) try {
|
|
140
166
|
const scopeParts = s.currentUnit.id.split("/").slice(0, 2);
|
|
141
167
|
const doctorScope = scopeParts.join("/");
|
|
142
168
|
const sliceTerminalUnits = new Set(["complete-slice", "run-uat"]);
|
|
@@ -146,13 +172,20 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
146
172
|
ctx.ui.notify(`Post-hook: applied ${report.fixesApplied.length} fix(es).`, "info");
|
|
147
173
|
}
|
|
148
174
|
|
|
149
|
-
// Proactive health tracking
|
|
150
|
-
|
|
175
|
+
// Proactive health tracking — filter to current milestone to avoid
|
|
176
|
+
// cross-milestone stale errors inflating the escalation counter
|
|
177
|
+
const currentMilestoneId = s.currentUnit.id.split("/")[0];
|
|
178
|
+
const milestoneIssues = currentMilestoneId
|
|
179
|
+
? report.issues.filter(i =>
|
|
180
|
+
i.unitId === currentMilestoneId ||
|
|
181
|
+
i.unitId.startsWith(`${currentMilestoneId}/`))
|
|
182
|
+
: report.issues;
|
|
183
|
+
const summary = summarizeDoctorIssues(milestoneIssues);
|
|
151
184
|
recordHealthSnapshot(summary.errors, summary.warnings, report.fixesApplied.length);
|
|
152
185
|
|
|
153
186
|
// Check if we should escalate to LLM-assisted heal
|
|
154
187
|
if (summary.errors > 0) {
|
|
155
|
-
const unresolvedErrors =
|
|
188
|
+
const unresolvedErrors = milestoneIssues
|
|
156
189
|
.filter(i => i.severity === "error" && !i.fixable)
|
|
157
190
|
.map(i => ({ code: i.code, message: i.message, unitId: i.unitId }));
|
|
158
191
|
const escalation = checkHealEscalation(summary.errors, unresolvedErrors);
|
|
@@ -168,24 +201,27 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
168
201
|
const reportText = formatDoctorReport(report, { scope: doctorScope, includeWarnings: true });
|
|
169
202
|
const structuredIssues = formatDoctorIssuesForPrompt(actionable);
|
|
170
203
|
dispatchDoctorHeal(pi, doctorScope, reportText, structuredIssues);
|
|
171
|
-
|
|
172
|
-
|
|
204
|
+
return "dispatched";
|
|
205
|
+
} catch (e) {
|
|
206
|
+
debugLog("postUnit", { phase: "doctor-heal-dispatch", error: String(e) });
|
|
173
207
|
}
|
|
174
208
|
}
|
|
175
209
|
}
|
|
176
|
-
} catch {
|
|
177
|
-
|
|
210
|
+
} catch (e) {
|
|
211
|
+
debugLog("postUnit", { phase: "doctor", error: String(e) });
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
// Throttled STATE.md rebuild
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
214
|
+
// Throttled STATE.md rebuild (skipped for lightweight sidecars)
|
|
215
|
+
if (!opts?.skipStateRebuild) {
|
|
216
|
+
const now = Date.now();
|
|
217
|
+
if (now - s.lastStateRebuildAt >= STATE_REBUILD_MIN_INTERVAL_MS) {
|
|
218
|
+
try {
|
|
219
|
+
await rebuildState(s.basePath);
|
|
220
|
+
s.lastStateRebuildAt = now;
|
|
221
|
+
autoCommitCurrentBranch(s.basePath, "state-rebuild", s.currentUnit.id);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
debugLog("postUnit", { phase: "state-rebuild", error: String(e) });
|
|
224
|
+
}
|
|
189
225
|
}
|
|
190
226
|
}
|
|
191
227
|
|
|
@@ -193,16 +229,16 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
193
229
|
try {
|
|
194
230
|
const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
|
|
195
231
|
pruneDeadProcesses();
|
|
196
|
-
} catch {
|
|
197
|
-
|
|
232
|
+
} catch (e) {
|
|
233
|
+
debugLog("postUnit", { phase: "prune-bg-shell", error: String(e) });
|
|
198
234
|
}
|
|
199
235
|
|
|
200
|
-
// Sync worktree state back to project root
|
|
201
|
-
if (s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
236
|
+
// Sync worktree state back to project root (skipped for lightweight sidecars)
|
|
237
|
+
if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
202
238
|
try {
|
|
203
239
|
syncStateToProjectRoot(s.basePath, s.originalBasePath, s.currentMilestoneId);
|
|
204
|
-
} catch {
|
|
205
|
-
|
|
240
|
+
} catch (e) {
|
|
241
|
+
debugLog("postUnit", { phase: "worktree-sync", error: String(e) });
|
|
206
242
|
}
|
|
207
243
|
}
|
|
208
244
|
|
|
@@ -210,10 +246,24 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
210
246
|
if (s.currentUnit.type === "rewrite-docs") {
|
|
211
247
|
try {
|
|
212
248
|
await resolveAllOverrides(s.basePath);
|
|
213
|
-
|
|
249
|
+
s.rewriteAttemptCount = 0;
|
|
214
250
|
ctx.ui.notify("Override(s) resolved — rewrite-docs completed.", "info");
|
|
215
|
-
} catch {
|
|
216
|
-
|
|
251
|
+
} catch (e) {
|
|
252
|
+
debugLog("postUnit", { phase: "rewrite-docs-resolve", error: String(e) });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Reactive state cleanup on slice completion
|
|
257
|
+
if (s.currentUnit.type === "complete-slice") {
|
|
258
|
+
try {
|
|
259
|
+
const parts = s.currentUnit.id.split("/");
|
|
260
|
+
const [mid, sid] = parts;
|
|
261
|
+
if (mid && sid) {
|
|
262
|
+
const { clearReactiveState } = await import("./reactive-graph.js");
|
|
263
|
+
clearReactiveState(s.basePath, mid, sid);
|
|
264
|
+
}
|
|
265
|
+
} catch (e) {
|
|
266
|
+
debugLog("postUnit", { phase: "reactive-state-cleanup", error: String(e) });
|
|
217
267
|
}
|
|
218
268
|
}
|
|
219
269
|
|
|
@@ -266,8 +316,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
266
316
|
if (triggerArtifactVerified) {
|
|
267
317
|
invalidateAllCaches();
|
|
268
318
|
}
|
|
269
|
-
} catch {
|
|
270
|
-
|
|
319
|
+
} catch (e) {
|
|
320
|
+
debugLog("postUnit", { phase: "artifact-verify", error: String(e) });
|
|
271
321
|
}
|
|
272
322
|
} else {
|
|
273
323
|
// Hook unit completed — finalize its runtime record
|
|
@@ -278,8 +328,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
278
328
|
lastProgressKind: "hook-completed",
|
|
279
329
|
});
|
|
280
330
|
clearUnitRuntimeRecord(s.basePath, s.currentUnit.type, s.currentUnit.id);
|
|
281
|
-
} catch {
|
|
282
|
-
|
|
331
|
+
} catch (e) {
|
|
332
|
+
debugLog("postUnit", { phase: "hook-finalize", error: String(e) });
|
|
283
333
|
}
|
|
284
334
|
}
|
|
285
335
|
}
|
|
@@ -415,8 +465,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
415
465
|
}
|
|
416
466
|
}
|
|
417
467
|
}
|
|
418
|
-
} catch {
|
|
419
|
-
|
|
468
|
+
} catch (e) {
|
|
469
|
+
debugLog("postUnit", { phase: "triage-check", error: String(e) });
|
|
420
470
|
}
|
|
421
471
|
}
|
|
422
472
|
|
|
@@ -461,8 +511,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
461
511
|
);
|
|
462
512
|
|
|
463
513
|
return "continue";
|
|
464
|
-
} catch {
|
|
465
|
-
|
|
514
|
+
} catch (e) {
|
|
515
|
+
debugLog("postUnit", { phase: "quick-task-dispatch", error: String(e) });
|
|
466
516
|
}
|
|
467
517
|
}
|
|
468
518
|
|
|
@@ -20,11 +20,17 @@ import type { GSDState, InlineLevel } from "./types.js";
|
|
|
20
20
|
import type { GSDPreferences } from "./preferences.js";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { existsSync } from "node:fs";
|
|
23
|
-
import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
|
|
24
|
-
import { compressToTarget } from "./prompt-compressor.js";
|
|
25
|
-
import { distillSummaries } from "./summary-distiller.js";
|
|
23
|
+
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
26
24
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
// ─── Preamble Cap ─────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const MAX_PREAMBLE_CHARS = 30_000;
|
|
29
|
+
|
|
30
|
+
function capPreamble(preamble: string): string {
|
|
31
|
+
if (preamble.length <= MAX_PREAMBLE_CHARS) return preamble;
|
|
32
|
+
return truncateAtSectionBoundary(preamble, MAX_PREAMBLE_CHARS).content;
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
// ─── Executor Constraints ─────────────────────────────────────────────────────
|
|
30
36
|
|
|
@@ -159,16 +165,9 @@ export async function inlineFileSmart(
|
|
|
159
165
|
return `### ${label}\nSource: \`${relPath}\`\n\n${content.trim()}`;
|
|
160
166
|
}
|
|
161
167
|
|
|
162
|
-
//
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
// If chunking didn't save much (< 20%), just include full content
|
|
166
|
-
if (result.savingsPercent < 20) {
|
|
167
|
-
return `### ${label}\nSource: \`${relPath}\`\n\n${content.trim()}`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const formatted = formatChunks(result, relPath);
|
|
171
|
-
return `### ${label} (${result.omittedChunks} sections omitted for relevance)\nSource: \`${relPath}\`\n\n${formatted}`;
|
|
168
|
+
// For large files, truncate at section boundary
|
|
169
|
+
const truncated = truncateAtSectionBoundary(content, threshold).content;
|
|
170
|
+
return `### ${label}\nSource: \`${relPath}\`\n\n${truncated}`;
|
|
172
171
|
}
|
|
173
172
|
|
|
174
173
|
/**
|
|
@@ -202,21 +201,6 @@ export async function inlineDependencySummaries(
|
|
|
202
201
|
|
|
203
202
|
const result = sections.join("\n\n");
|
|
204
203
|
if (budgetChars !== undefined && result.length > budgetChars) {
|
|
205
|
-
// For 3+ summaries, try distillation first (preserves more information)
|
|
206
|
-
if (sections.length >= 3) {
|
|
207
|
-
const rawSummaries = sections.map(s => {
|
|
208
|
-
// Extract content after the header line
|
|
209
|
-
const lines = s.split("\n");
|
|
210
|
-
const contentStart = lines.findIndex(l => l.startsWith("Source:"));
|
|
211
|
-
return contentStart >= 0 ? lines.slice(contentStart + 1).join("\n").trim() : s;
|
|
212
|
-
});
|
|
213
|
-
const distilled = distillSummaries(rawSummaries, budgetChars);
|
|
214
|
-
if (distilled.content.length <= budgetChars) {
|
|
215
|
-
return distilled.content;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// Fall back to section-boundary truncation
|
|
219
|
-
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
220
204
|
return truncateAtSectionBoundary(result, budgetChars).content;
|
|
221
205
|
}
|
|
222
206
|
return result;
|
|
@@ -485,6 +469,41 @@ export async function getPriorTaskSummaryPaths(
|
|
|
485
469
|
.map(f => `${sRel}/tasks/${f}`);
|
|
486
470
|
}
|
|
487
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Get carry-forward summary paths scoped to a task's derived dependencies.
|
|
474
|
+
*
|
|
475
|
+
* Instead of all prior tasks (order-based), returns only summaries for task
|
|
476
|
+
* IDs in `dependsOn`. Used by reactive-execute to give each subagent only
|
|
477
|
+
* the context it actually needs — not sibling tasks from a parallel batch.
|
|
478
|
+
*
|
|
479
|
+
* Falls back to order-based when dependsOn is empty (root tasks still get
|
|
480
|
+
* any available prior summaries for continuity).
|
|
481
|
+
*/
|
|
482
|
+
export async function getDependencyTaskSummaryPaths(
|
|
483
|
+
mid: string, sid: string, currentTid: string,
|
|
484
|
+
dependsOn: string[], base: string,
|
|
485
|
+
): Promise<string[]> {
|
|
486
|
+
// If no dependencies, fall back to order-based for root tasks
|
|
487
|
+
if (dependsOn.length === 0) {
|
|
488
|
+
return getPriorTaskSummaryPaths(mid, sid, currentTid, base);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const tDir = resolveTasksDir(base, mid, sid);
|
|
492
|
+
if (!tDir) return [];
|
|
493
|
+
|
|
494
|
+
const summaryFiles = resolveTaskFiles(tDir, "SUMMARY");
|
|
495
|
+
const sRel = relSlicePath(base, mid, sid);
|
|
496
|
+
const depSet = new Set(dependsOn.map((d) => d.toUpperCase()));
|
|
497
|
+
|
|
498
|
+
return summaryFiles
|
|
499
|
+
.filter((f) => {
|
|
500
|
+
// Extract task ID from filename: "T02-SUMMARY.md" → "T02"
|
|
501
|
+
const tid = f.replace(/-SUMMARY\.md$/i, "").toUpperCase();
|
|
502
|
+
return depSet.has(tid);
|
|
503
|
+
})
|
|
504
|
+
.map((f) => `${sRel}/tasks/${f}`);
|
|
505
|
+
}
|
|
506
|
+
|
|
488
507
|
// ─── Adaptive Replanning Checks ────────────────────────────────────────────
|
|
489
508
|
|
|
490
509
|
/**
|
|
@@ -599,7 +618,7 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
599
618
|
if (knowledgeInlineRM) inlined.push(knowledgeInlineRM);
|
|
600
619
|
inlined.push(inlineTemplate("research", "Research"));
|
|
601
620
|
|
|
602
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
621
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
603
622
|
|
|
604
623
|
const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
|
|
605
624
|
return loadPrompt("research-milestone", {
|
|
@@ -649,7 +668,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
649
668
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
650
669
|
}
|
|
651
670
|
|
|
652
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
671
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
653
672
|
|
|
654
673
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
655
674
|
const researchOutputPath = join(base, relMilestoneFile(base, mid, "RESEARCH"));
|
|
@@ -698,7 +717,7 @@ export async function buildResearchSlicePrompt(
|
|
|
698
717
|
const overridesInline = formatOverridesSection(activeOverrides);
|
|
699
718
|
if (overridesInline) inlined.unshift(overridesInline);
|
|
700
719
|
|
|
701
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
720
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
702
721
|
|
|
703
722
|
const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
|
|
704
723
|
return loadPrompt("research-slice", {
|
|
@@ -746,7 +765,7 @@ export async function buildPlanSlicePrompt(
|
|
|
746
765
|
const planOverridesInline = formatOverridesSection(planActiveOverrides);
|
|
747
766
|
if (planOverridesInline) inlined.unshift(planOverridesInline);
|
|
748
767
|
|
|
749
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
768
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
750
769
|
|
|
751
770
|
// Build executor context constraints from the budget engine
|
|
752
771
|
const executorContextConstraints = formatExecutorConstraints();
|
|
@@ -772,13 +791,24 @@ export async function buildPlanSlicePrompt(
|
|
|
772
791
|
});
|
|
773
792
|
}
|
|
774
793
|
|
|
794
|
+
/** Options for customizing execute-task prompt construction. */
|
|
795
|
+
export interface ExecuteTaskPromptOptions {
|
|
796
|
+
level?: InlineLevel;
|
|
797
|
+
/** Override carry-forward paths (dependency-based instead of order-based). */
|
|
798
|
+
carryForwardPaths?: string[];
|
|
799
|
+
}
|
|
800
|
+
|
|
775
801
|
export async function buildExecuteTaskPrompt(
|
|
776
802
|
mid: string, sid: string, sTitle: string,
|
|
777
|
-
tid: string, tTitle: string, base: string,
|
|
803
|
+
tid: string, tTitle: string, base: string,
|
|
804
|
+
level?: InlineLevel | ExecuteTaskPromptOptions,
|
|
778
805
|
): Promise<string> {
|
|
779
|
-
const
|
|
806
|
+
const opts: ExecuteTaskPromptOptions = typeof level === "object" && level !== null && !Array.isArray(level)
|
|
807
|
+
? level
|
|
808
|
+
: { level: level as InlineLevel | undefined };
|
|
809
|
+
const inlineLevel = opts.level ?? resolveInlineLevel();
|
|
780
810
|
|
|
781
|
-
const priorSummaries = await getPriorTaskSummaryPaths(mid, sid, tid, base);
|
|
811
|
+
const priorSummaries = opts.carryForwardPaths ?? await getPriorTaskSummaryPaths(mid, sid, tid, base);
|
|
782
812
|
const priorLines = priorSummaries.length > 0
|
|
783
813
|
? priorSummaries.map(p => `- \`${p}\``).join("\n")
|
|
784
814
|
: "- (no prior tasks)";
|
|
@@ -854,15 +884,11 @@ export async function buildExecuteTaskPrompt(
|
|
|
854
884
|
const budgets = computeBudgets(contextWindow);
|
|
855
885
|
const verificationBudget = `~${Math.round(budgets.verificationBudgetChars / 1000)}K chars`;
|
|
856
886
|
|
|
857
|
-
//
|
|
858
|
-
// Only compress when compression_strategy is "compress" (budget/balanced profiles).
|
|
887
|
+
// Truncate carry-forward section when it exceeds 40% of inline context budget.
|
|
859
888
|
const carryForwardBudget = Math.floor(budgets.inlineContextBudgetChars * 0.4);
|
|
860
889
|
let finalCarryForward = carryForwardSection;
|
|
861
890
|
if (carryForwardSection.length > carryForwardBudget) {
|
|
862
|
-
|
|
863
|
-
if (resolveCompressionStrategy() === "compress") {
|
|
864
|
-
finalCarryForward = compressToTarget(carryForwardSection, carryForwardBudget).content;
|
|
865
|
-
}
|
|
891
|
+
finalCarryForward = truncateAtSectionBoundary(carryForwardSection, carryForwardBudget).content;
|
|
866
892
|
}
|
|
867
893
|
|
|
868
894
|
return loadPrompt("execute-task", {
|
|
@@ -925,7 +951,7 @@ export async function buildCompleteSlicePrompt(
|
|
|
925
951
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
926
952
|
if (completeOverridesInline) inlined.unshift(completeOverridesInline);
|
|
927
953
|
|
|
928
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
954
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
929
955
|
|
|
930
956
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
931
957
|
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
@@ -984,7 +1010,7 @@ export async function buildCompleteMilestonePrompt(
|
|
|
984
1010
|
if (contextInline) inlined.push(contextInline);
|
|
985
1011
|
inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
|
|
986
1012
|
|
|
987
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1013
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
988
1014
|
|
|
989
1015
|
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
990
1016
|
|
|
@@ -1055,7 +1081,7 @@ export async function buildValidateMilestonePrompt(
|
|
|
1055
1081
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
1056
1082
|
if (contextInline) inlined.push(contextInline);
|
|
1057
1083
|
|
|
1058
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1084
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1059
1085
|
|
|
1060
1086
|
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
1061
1087
|
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
@@ -1109,7 +1135,7 @@ export async function buildReplanSlicePrompt(
|
|
|
1109
1135
|
const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
|
|
1110
1136
|
if (replanOverridesInline) inlined.unshift(replanOverridesInline);
|
|
1111
1137
|
|
|
1112
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1138
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1113
1139
|
|
|
1114
1140
|
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
1115
1141
|
|
|
@@ -1157,7 +1183,7 @@ export async function buildRunUatPrompt(
|
|
|
1157
1183
|
const projectInline = await inlineProjectFromDb(base);
|
|
1158
1184
|
if (projectInline) inlined.push(projectInline);
|
|
1159
1185
|
|
|
1160
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1186
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1161
1187
|
|
|
1162
1188
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT-RESULT"));
|
|
1163
1189
|
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
@@ -1196,7 +1222,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1196
1222
|
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
1197
1223
|
if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
|
|
1198
1224
|
|
|
1199
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1225
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1200
1226
|
|
|
1201
1227
|
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
1202
1228
|
|
|
@@ -1234,6 +1260,82 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1234
1260
|
});
|
|
1235
1261
|
}
|
|
1236
1262
|
|
|
1263
|
+
// ─── Reactive Execute Prompt ──────────────────────────────────────────────
|
|
1264
|
+
|
|
1265
|
+
export async function buildReactiveExecutePrompt(
|
|
1266
|
+
mid: string, midTitle: string, sid: string, sTitle: string,
|
|
1267
|
+
readyTaskIds: string[], base: string,
|
|
1268
|
+
): Promise<string> {
|
|
1269
|
+
const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
|
|
1270
|
+
|
|
1271
|
+
// Build graph for context
|
|
1272
|
+
const taskIO = await loadSliceTaskIO(base, mid, sid);
|
|
1273
|
+
const graph = deriveTaskGraph(taskIO);
|
|
1274
|
+
const metrics = graphMetrics(graph);
|
|
1275
|
+
|
|
1276
|
+
// Build graph context section
|
|
1277
|
+
const graphLines: string[] = [];
|
|
1278
|
+
for (const node of graph) {
|
|
1279
|
+
const status = node.done ? "✅ done" : readyTaskIds.includes(node.id) ? "🟢 ready" : "⏳ waiting";
|
|
1280
|
+
const deps = node.dependsOn.length > 0 ? ` (depends on: ${node.dependsOn.join(", ")})` : "";
|
|
1281
|
+
graphLines.push(`- **${node.id}: ${node.title}** — ${status}${deps}`);
|
|
1282
|
+
if (node.outputFiles.length > 0) {
|
|
1283
|
+
graphLines.push(` - Outputs: ${node.outputFiles.map(f => `\`${f}\``).join(", ")}`);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const graphContext = [
|
|
1287
|
+
`Tasks: ${metrics.taskCount}, Edges: ${metrics.edgeCount}, Ready: ${metrics.readySetSize}`,
|
|
1288
|
+
"",
|
|
1289
|
+
...graphLines,
|
|
1290
|
+
].join("\n");
|
|
1291
|
+
|
|
1292
|
+
// Build individual subagent prompts for each ready task
|
|
1293
|
+
const subagentSections: string[] = [];
|
|
1294
|
+
const readyTaskListLines: string[] = [];
|
|
1295
|
+
|
|
1296
|
+
for (const tid of readyTaskIds) {
|
|
1297
|
+
const node = graph.find((n) => n.id === tid);
|
|
1298
|
+
const tTitle = node?.title ?? tid;
|
|
1299
|
+
readyTaskListLines.push(`- **${tid}: ${tTitle}**`);
|
|
1300
|
+
|
|
1301
|
+
// Build dependency-scoped carry-forward paths for this task
|
|
1302
|
+
const depPaths = await getDependencyTaskSummaryPaths(
|
|
1303
|
+
mid, sid, tid, node?.dependsOn ?? [], base,
|
|
1304
|
+
);
|
|
1305
|
+
|
|
1306
|
+
// Build a full execute-task prompt with dependency-based carry-forward
|
|
1307
|
+
const taskPrompt = await buildExecuteTaskPrompt(
|
|
1308
|
+
mid, sid, sTitle, tid, tTitle, base,
|
|
1309
|
+
{ carryForwardPaths: depPaths },
|
|
1310
|
+
);
|
|
1311
|
+
|
|
1312
|
+
subagentSections.push([
|
|
1313
|
+
`### ${tid}: ${tTitle}`,
|
|
1314
|
+
"",
|
|
1315
|
+
"Use this as the prompt for a `subagent` call:",
|
|
1316
|
+
"",
|
|
1317
|
+
"```",
|
|
1318
|
+
taskPrompt,
|
|
1319
|
+
"```",
|
|
1320
|
+
].join("\n"));
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const inlinedTemplates = inlineTemplate("task-summary", "Task Summary");
|
|
1324
|
+
|
|
1325
|
+
return loadPrompt("reactive-execute", {
|
|
1326
|
+
workingDirectory: base,
|
|
1327
|
+
milestoneId: mid,
|
|
1328
|
+
milestoneTitle: midTitle,
|
|
1329
|
+
sliceId: sid,
|
|
1330
|
+
sliceTitle: sTitle,
|
|
1331
|
+
graphContext,
|
|
1332
|
+
readyTaskCount: String(readyTaskIds.length),
|
|
1333
|
+
readyTaskList: readyTaskListLines.join("\n"),
|
|
1334
|
+
subagentPrompts: subagentSections.join("\n\n---\n\n"),
|
|
1335
|
+
inlinedTemplates,
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1237
1339
|
export async function buildRewriteDocsPrompt(
|
|
1238
1340
|
mid: string, midTitle: string,
|
|
1239
1341
|
activeSlice: { id: string; title: string } | null,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
resolveSlicePath,
|
|
27
27
|
resolveSliceFile,
|
|
28
28
|
resolveTasksDir,
|
|
29
|
+
resolveTaskFiles,
|
|
29
30
|
relMilestoneFile,
|
|
30
31
|
relSliceFile,
|
|
31
32
|
relSlicePath,
|
|
@@ -110,6 +111,9 @@ export function resolveExpectedArtifactPath(
|
|
|
110
111
|
}
|
|
111
112
|
case "rewrite-docs":
|
|
112
113
|
return null;
|
|
114
|
+
case "reactive-execute":
|
|
115
|
+
// Reactive execute produces multiple task summaries — verified separately
|
|
116
|
+
return null;
|
|
113
117
|
default:
|
|
114
118
|
return null;
|
|
115
119
|
}
|
|
@@ -148,6 +152,44 @@ export function verifyExpectedArtifact(
|
|
|
148
152
|
return !content.includes("**Scope:** active");
|
|
149
153
|
}
|
|
150
154
|
|
|
155
|
+
// Reactive-execute: verify that each dispatched task's summary exists.
|
|
156
|
+
// The unitId encodes the batch: "{mid}/{sid}/reactive+T02,T03"
|
|
157
|
+
if (unitType === "reactive-execute") {
|
|
158
|
+
const parts = unitId.split("/");
|
|
159
|
+
const mid = parts[0];
|
|
160
|
+
const sidAndBatch = parts[1];
|
|
161
|
+
const batchPart = parts[2]; // "reactive+T02,T03"
|
|
162
|
+
if (!mid || !sidAndBatch || !batchPart) return false;
|
|
163
|
+
|
|
164
|
+
const sid = sidAndBatch;
|
|
165
|
+
const plusIdx = batchPart.indexOf("+");
|
|
166
|
+
if (plusIdx === -1) {
|
|
167
|
+
// Legacy format "reactive" without batch IDs — fall back to "any summary"
|
|
168
|
+
const tDir = resolveTasksDir(base, mid, sid);
|
|
169
|
+
if (!tDir) return false;
|
|
170
|
+
const summaryFiles = resolveTaskFiles(tDir, "SUMMARY");
|
|
171
|
+
return summaryFiles.length > 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const batchIds = batchPart.slice(plusIdx + 1).split(",").filter(Boolean);
|
|
175
|
+
if (batchIds.length === 0) return false;
|
|
176
|
+
|
|
177
|
+
const tDir = resolveTasksDir(base, mid, sid);
|
|
178
|
+
if (!tDir) return false;
|
|
179
|
+
|
|
180
|
+
const existingSummaries = new Set(
|
|
181
|
+
resolveTaskFiles(tDir, "SUMMARY").map((f) =>
|
|
182
|
+
f.replace(/-SUMMARY\.md$/i, "").toUpperCase(),
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Every dispatched task must have a summary file
|
|
187
|
+
for (const tid of batchIds) {
|
|
188
|
+
if (!existingSummaries.has(tid.toUpperCase())) return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
151
193
|
const absPath = resolveExpectedArtifactPath(unitType, unitId, base);
|
|
152
194
|
// For unit types with no verifiable artifact (null path), the parent directory
|
|
153
195
|
// is missing on disk — treat as stale completion state so the key gets evicted (#313).
|