@linimin/pi-letscook 0.1.45 → 0.1.47
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/CHANGELOG.md +10 -1
- package/README.md +13 -13
- package/extensions/completion/driver.ts +673 -0
- package/extensions/completion/index.ts +468 -3488
- package/extensions/completion/policy-guards.ts +110 -0
- package/extensions/completion/prompt-surfaces.ts +536 -0
- package/extensions/completion/proposal.ts +1217 -0
- package/extensions/completion/role-runner.ts +460 -0
- package/extensions/completion/state-store.ts +458 -0
- package/extensions/completion/status-surface.ts +516 -0
- package/extensions/completion/transcription.ts +77 -0
- package/extensions/completion/types.ts +87 -0
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +5 -4
- package/scripts/canonical-evidence-artifact-test.sh +4 -4
- package/scripts/context-proposal-test.sh +295 -23
- package/scripts/legacy-cleanup-test.sh +107 -0
- package/scripts/observability-status-test.sh +39 -0
- package/scripts/refocus-test.sh +6 -6
- package/scripts/release-check.sh +17 -16
- package/scripts/role-runner-contract-test.sh +44 -0
- package/scripts/rubric-contract-test.sh +8 -6
- package/scripts/smoke-test.sh +5 -5
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import type { JsonRecord } from "./types";
|
|
3
|
+
|
|
4
|
+
function asString(value: unknown): string | undefined {
|
|
5
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isPathInside(root: string, candidatePath: string): boolean {
|
|
9
|
+
const resolvedRoot = path.resolve(root);
|
|
10
|
+
const resolvedCandidate = path.resolve(candidatePath);
|
|
11
|
+
return resolvedCandidate === resolvedRoot || resolvedCandidate.startsWith(`${resolvedRoot}${path.sep}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function resolveToolPath(cwd: string, rawPath: string): string {
|
|
15
|
+
return path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isAllowedControlPlanePath(root: string, rawPath: string): boolean {
|
|
19
|
+
const resolved = resolveToolPath(root, rawPath);
|
|
20
|
+
if (path.basename(resolved) === ".gitignore") return true;
|
|
21
|
+
return isPathInside(path.join(root, ".agent"), resolved);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function startsWithAny(value: string, prefixes: string[]): boolean {
|
|
25
|
+
return prefixes.some((prefix) => value.startsWith(prefix));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function normalizeCommand(command: string): string {
|
|
29
|
+
return command.trim().replace(/\s+/g, " ");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isMutatingBash(command: string): boolean {
|
|
33
|
+
const normalized = normalizeCommand(command);
|
|
34
|
+
return (
|
|
35
|
+
startsWithAny(normalized, [
|
|
36
|
+
"git add",
|
|
37
|
+
"git commit",
|
|
38
|
+
"git push",
|
|
39
|
+
"rm ",
|
|
40
|
+
"mv ",
|
|
41
|
+
"cp ",
|
|
42
|
+
"mkdir ",
|
|
43
|
+
"touch ",
|
|
44
|
+
"chmod ",
|
|
45
|
+
"chown ",
|
|
46
|
+
"sed -i",
|
|
47
|
+
"perl -pi",
|
|
48
|
+
"python -c",
|
|
49
|
+
"python3 -c",
|
|
50
|
+
"node -e",
|
|
51
|
+
"bun -e",
|
|
52
|
+
"tee ",
|
|
53
|
+
]) ||
|
|
54
|
+
normalized.includes(">") ||
|
|
55
|
+
normalized.includes("| tee") ||
|
|
56
|
+
normalized.includes("apply_patch")
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function toolCallBlockReason(args: {
|
|
61
|
+
toolName: string;
|
|
62
|
+
input?: JsonRecord;
|
|
63
|
+
role?: string;
|
|
64
|
+
completionActive: boolean;
|
|
65
|
+
root: string;
|
|
66
|
+
}): string | undefined {
|
|
67
|
+
const { toolName, input, role, completionActive, root } = args;
|
|
68
|
+
|
|
69
|
+
if (toolName === "completion_role" && role) {
|
|
70
|
+
return `Nested completion role dispatch is forbidden for ${role}.`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (toolName === "edit" || toolName === "write") {
|
|
74
|
+
const rawPath = asString(input?.path);
|
|
75
|
+
if (!rawPath) return undefined;
|
|
76
|
+
|
|
77
|
+
if (role === "completion-reviewer" || role === "completion-auditor" || role === "completion-stop-judge") {
|
|
78
|
+
return `${role} is read-only.`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if ((role === "completion-bootstrapper" || role === "completion-regrounder") && !isAllowedControlPlanePath(root, rawPath)) {
|
|
82
|
+
return `${role} may only edit .agent/** or .gitignore.`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!role && completionActive && !isAllowedControlPlanePath(root, rawPath)) {
|
|
86
|
+
return "The workflow driver may not edit tracked product files directly during completion.";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (toolName !== "bash") return undefined;
|
|
93
|
+
const command = asString(input?.command);
|
|
94
|
+
if (!command) return undefined;
|
|
95
|
+
const normalized = normalizeCommand(command);
|
|
96
|
+
|
|
97
|
+
if (["completion-reviewer", "completion-auditor", "completion-stop-judge"].includes(role ?? "") && isMutatingBash(normalized)) {
|
|
98
|
+
return `${role} is read-only and cannot run mutating bash.`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if ((role === "completion-bootstrapper" || role === "completion-regrounder") && startsWithAny(normalized, ["git add", "git commit"])) {
|
|
102
|
+
return `${role} may not create commits.`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!role && completionActive && startsWithAny(normalized, ["git add", "git commit"])) {
|
|
106
|
+
return "The workflow driver may not create commits directly during completion.";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { CompletionStateSnapshot, LiveRoleActivity } from "./types";
|
|
4
|
+
import type {
|
|
5
|
+
ContextProposal,
|
|
6
|
+
ContextProposalAnalysis,
|
|
7
|
+
ContextProposalConfirmationActionItem,
|
|
8
|
+
ContextProposalConfirmationLayout,
|
|
9
|
+
} from "./proposal";
|
|
10
|
+
|
|
11
|
+
export function buildContextProposalGoalText(proposal: {
|
|
12
|
+
mission: string;
|
|
13
|
+
scope: string[];
|
|
14
|
+
constraints: string[];
|
|
15
|
+
acceptance: string[];
|
|
16
|
+
}): string {
|
|
17
|
+
const lines = [`Mission: ${proposal.mission}`];
|
|
18
|
+
if (proposal.scope.length > 0) {
|
|
19
|
+
lines.push("", "Scope:");
|
|
20
|
+
for (const item of proposal.scope) lines.push(`- ${item}`);
|
|
21
|
+
}
|
|
22
|
+
if (proposal.constraints.length > 0) {
|
|
23
|
+
lines.push("", "Constraints:");
|
|
24
|
+
for (const item of proposal.constraints) lines.push(`- ${item}`);
|
|
25
|
+
}
|
|
26
|
+
if (proposal.acceptance.length > 0) {
|
|
27
|
+
lines.push("", "Acceptance:");
|
|
28
|
+
for (const item of proposal.acceptance) lines.push(`- ${item}`);
|
|
29
|
+
}
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function buildContextProposalDisplayText(proposal: ContextProposal): string {
|
|
34
|
+
const lines = ["Mission", proposal.mission];
|
|
35
|
+
if (proposal.scope.length > 0) {
|
|
36
|
+
lines.push("", "Scope");
|
|
37
|
+
for (const item of proposal.scope) lines.push(`- ${item}`);
|
|
38
|
+
}
|
|
39
|
+
if (proposal.constraints.length > 0) {
|
|
40
|
+
lines.push("", "Constraints");
|
|
41
|
+
for (const item of proposal.constraints) lines.push(`- ${item}`);
|
|
42
|
+
}
|
|
43
|
+
if (proposal.acceptance.length > 0) {
|
|
44
|
+
lines.push("", "Acceptance");
|
|
45
|
+
for (const item of proposal.acceptance) lines.push(`- ${item}`);
|
|
46
|
+
}
|
|
47
|
+
return lines.join("\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildContextProposalCritiqueText(analysis: ContextProposalAnalysis): string {
|
|
51
|
+
const lines: string[] = [];
|
|
52
|
+
if (analysis.critique.length > 0) {
|
|
53
|
+
lines.push("Critique");
|
|
54
|
+
for (const item of analysis.critique) lines.push(`- ${item}`);
|
|
55
|
+
}
|
|
56
|
+
if (analysis.risks.length > 0) {
|
|
57
|
+
if (lines.length > 0) lines.push("");
|
|
58
|
+
lines.push("Risks");
|
|
59
|
+
for (const item of analysis.risks) lines.push(`- ${item}`);
|
|
60
|
+
}
|
|
61
|
+
if (analysis.possibleNoise.length > 0) {
|
|
62
|
+
if (lines.length > 0) lines.push("");
|
|
63
|
+
lines.push("Possible noise");
|
|
64
|
+
for (const item of analysis.possibleNoise) lines.push(`- ${item}`);
|
|
65
|
+
}
|
|
66
|
+
if (analysis.alternateMissions.length > 0) {
|
|
67
|
+
if (lines.length > 0) lines.push("");
|
|
68
|
+
lines.push("Alternate recent missions");
|
|
69
|
+
for (const item of analysis.alternateMissions) lines.push(`- ${item}`);
|
|
70
|
+
}
|
|
71
|
+
if (analysis.suppressedCompletedTopics.length > 0) {
|
|
72
|
+
if (lines.length > 0) lines.push("");
|
|
73
|
+
lines.push("Suppressed completed topics");
|
|
74
|
+
for (const item of analysis.suppressedCompletedTopics) lines.push(`- ${item}`);
|
|
75
|
+
}
|
|
76
|
+
if (analysis.suppressedNegatedTopics.length > 0) {
|
|
77
|
+
if (lines.length > 0) lines.push("");
|
|
78
|
+
lines.push("Suppressed negated topics");
|
|
79
|
+
for (const item of analysis.suppressedNegatedTopics) lines.push(`- ${item}`);
|
|
80
|
+
}
|
|
81
|
+
if (lines.length === 0) {
|
|
82
|
+
return "No critique, risk, noise, alternate-mission, or suppression notes were derived for this startup proposal.";
|
|
83
|
+
}
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function buildContextProposalRoutingText(
|
|
88
|
+
analysis: ContextProposalAnalysis,
|
|
89
|
+
defaults: { taskType: string; evaluationProfile: string },
|
|
90
|
+
): string {
|
|
91
|
+
return [`- task_type: ${analysis.taskType ?? defaults.taskType}`, `- evaluation_profile: ${analysis.evaluationProfile ?? defaults.evaluationProfile}`].join(
|
|
92
|
+
"\n",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function summarizeContextProposalAnalysisItems(
|
|
97
|
+
label: string,
|
|
98
|
+
items: string[],
|
|
99
|
+
truncateInline: (text: string, maxLength?: number) => string,
|
|
100
|
+
): string | undefined {
|
|
101
|
+
if (items.length === 0) return undefined;
|
|
102
|
+
return `${label}=${truncateInline(items.join(" | "), 160)}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function buildContextProposalContinuationReason(
|
|
106
|
+
prefix: string,
|
|
107
|
+
goalText: string,
|
|
108
|
+
analysis: ContextProposalAnalysis,
|
|
109
|
+
deps: {
|
|
110
|
+
defaultTaskType: string;
|
|
111
|
+
defaultEvaluationProfile: string;
|
|
112
|
+
truncateInline: (text: string, maxLength?: number) => string;
|
|
113
|
+
},
|
|
114
|
+
): string {
|
|
115
|
+
const critiqueParts = [
|
|
116
|
+
analysis.critique.length > 0 ? `accepted critique=${deps.truncateInline(analysis.critique.join(" | "), 160)}` : "accepted critique=none",
|
|
117
|
+
summarizeContextProposalAnalysisItems("risks", analysis.risks, deps.truncateInline),
|
|
118
|
+
summarizeContextProposalAnalysisItems("possible_noise", analysis.possibleNoise, deps.truncateInline),
|
|
119
|
+
summarizeContextProposalAnalysisItems("alternate_missions", analysis.alternateMissions, deps.truncateInline),
|
|
120
|
+
summarizeContextProposalAnalysisItems("suppressed_completed", analysis.suppressedCompletedTopics, deps.truncateInline),
|
|
121
|
+
summarizeContextProposalAnalysisItems("suppressed_negated", analysis.suppressedNegatedTopics, deps.truncateInline),
|
|
122
|
+
].filter((part): part is string => Boolean(part));
|
|
123
|
+
return `${prefix} ${deps.truncateInline(goalText, 220)} | startup routing: task_type=${analysis.taskType ?? deps.defaultTaskType}; evaluation_profile=${analysis.evaluationProfile ?? deps.defaultEvaluationProfile}; critique outcome=${critiqueParts.join("; ")}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function buildContextProposalConfirmationActions(mainChatRerunGuidance: string): ContextProposalConfirmationActionItem[] {
|
|
127
|
+
return [
|
|
128
|
+
{
|
|
129
|
+
id: "start",
|
|
130
|
+
label: "Start",
|
|
131
|
+
description: "Accept this proposal and let /cook write or refocus canonical workflow state.",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "cancel",
|
|
135
|
+
label: "Cancel",
|
|
136
|
+
description: `Stop here without changing canonical workflow state. ${mainChatRerunGuidance}`,
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function buildContextProposalConfirmationLayout(args: {
|
|
142
|
+
title: string;
|
|
143
|
+
proposal: ContextProposal;
|
|
144
|
+
analysis: ContextProposalAnalysis;
|
|
145
|
+
mainChatRerunGuidance: string;
|
|
146
|
+
defaultTaskType: string;
|
|
147
|
+
defaultEvaluationProfile: string;
|
|
148
|
+
}): ContextProposalConfirmationLayout {
|
|
149
|
+
return {
|
|
150
|
+
title: args.title,
|
|
151
|
+
intro: "Review the proposed mission, scope, constraints, acceptance, critique, and routing details before /cook writes canonical workflow state. This gate is approval-only: either Start it as-is or Cancel, discuss changes in the main chat, and rerun /cook.",
|
|
152
|
+
proposalHeading: "Proposed workflow",
|
|
153
|
+
proposalBody: buildContextProposalDisplayText(args.proposal),
|
|
154
|
+
critiqueHeading: "Critique and risks",
|
|
155
|
+
critiqueBody: buildContextProposalCritiqueText(args.analysis),
|
|
156
|
+
routingHeading: "Routing recommendations",
|
|
157
|
+
routingBody: buildContextProposalRoutingText(args.analysis, {
|
|
158
|
+
taskType: args.defaultTaskType,
|
|
159
|
+
evaluationProfile: args.defaultEvaluationProfile,
|
|
160
|
+
}),
|
|
161
|
+
actionsHeading: "Actions",
|
|
162
|
+
actions: buildContextProposalConfirmationActions(args.mainChatRerunGuidance),
|
|
163
|
+
footer: "↑↓ navigate • enter select • esc cancel",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function maybeWriteContextProposalConfirmationSnapshot(
|
|
168
|
+
layout: ContextProposalConfirmationLayout,
|
|
169
|
+
snapshotPath: string | undefined,
|
|
170
|
+
): void {
|
|
171
|
+
if (!snapshotPath) return;
|
|
172
|
+
try {
|
|
173
|
+
fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });
|
|
174
|
+
fs.writeFileSync(snapshotPath, `${JSON.stringify(layout, null, 2)}\n`, "utf8");
|
|
175
|
+
} catch {
|
|
176
|
+
// ignore malformed or unwritable test snapshot paths
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function maybeWriteContextProposalSnapshot(proposal: ContextProposal, snapshotPath: string | undefined): void {
|
|
181
|
+
if (!snapshotPath) return;
|
|
182
|
+
try {
|
|
183
|
+
fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });
|
|
184
|
+
fs.writeFileSync(snapshotPath, `${JSON.stringify(proposal, null, 2)}\n`, "utf8");
|
|
185
|
+
} catch {
|
|
186
|
+
// ignore malformed or unwritable test snapshot paths
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function buildContextProposalConfirmationSelectItems(layout: ContextProposalConfirmationLayout) {
|
|
191
|
+
return layout.actions.map((action) => ({
|
|
192
|
+
value: action.id,
|
|
193
|
+
label: action.label,
|
|
194
|
+
description: action.description,
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function buildContextProposalAnalystPrompt(projectName: string, discussion: string, contextLines: string[] = []): string {
|
|
199
|
+
const lines = [
|
|
200
|
+
`Project: ${projectName}`,
|
|
201
|
+
"Infer the current implementation mission from the discussion.",
|
|
202
|
+
"Prefer the latest clear user implementation intent over older background context.",
|
|
203
|
+
"Treat stale, completed, or explicitly negated topics as context to ignore unless the latest discussion clearly reopens them.",
|
|
204
|
+
];
|
|
205
|
+
if (contextLines.length > 0) lines.push("", "Canonical workflow context:", ...contextLines);
|
|
206
|
+
lines.push("", "Recent discussion:", discussion || "(none)");
|
|
207
|
+
return lines.join("\n");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function contextProposalAnalystProgressLines(
|
|
211
|
+
activity: LiveRoleActivity,
|
|
212
|
+
buildInlineRunningLines: (details: {
|
|
213
|
+
role?: string;
|
|
214
|
+
startedAt?: number;
|
|
215
|
+
updatedAt?: number;
|
|
216
|
+
currentAction?: string;
|
|
217
|
+
toolActivity?: string[];
|
|
218
|
+
toolRecentActivity?: string[];
|
|
219
|
+
recentActivity?: string[];
|
|
220
|
+
assistantSummary?: string;
|
|
221
|
+
progress?: string;
|
|
222
|
+
rationale?: string;
|
|
223
|
+
nextStep?: string;
|
|
224
|
+
verifying?: string;
|
|
225
|
+
stateDeltas?: string[];
|
|
226
|
+
}) => string[],
|
|
227
|
+
): string[] {
|
|
228
|
+
return [
|
|
229
|
+
...buildInlineRunningLines({
|
|
230
|
+
role: activity.role,
|
|
231
|
+
startedAt: activity.startedAt,
|
|
232
|
+
updatedAt: activity.updatedAt,
|
|
233
|
+
currentAction: activity.currentAction,
|
|
234
|
+
toolActivity: activity.toolActivity,
|
|
235
|
+
toolRecentActivity: activity.toolRecentActivity,
|
|
236
|
+
recentActivity: activity.recentActivity,
|
|
237
|
+
assistantSummary: activity.assistantSummary,
|
|
238
|
+
progress: activity.progress,
|
|
239
|
+
rationale: activity.rationale,
|
|
240
|
+
nextStep: activity.nextStep,
|
|
241
|
+
verifying: activity.verifying,
|
|
242
|
+
stateDeltas: activity.stateDeltas,
|
|
243
|
+
}),
|
|
244
|
+
"",
|
|
245
|
+
"This step only prepares a proposal for confirmation.",
|
|
246
|
+
];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function buildEvaluationRoleContextLines(
|
|
250
|
+
snapshot: CompletionStateSnapshot,
|
|
251
|
+
role: string,
|
|
252
|
+
deps: {
|
|
253
|
+
asString: (value: unknown) => string | undefined;
|
|
254
|
+
currentTaskType: (snapshot: CompletionStateSnapshot) => string | undefined;
|
|
255
|
+
currentEvaluationProfile: (snapshot: CompletionStateSnapshot) => string | undefined;
|
|
256
|
+
activeSliceContext: (snapshot: CompletionStateSnapshot) => {
|
|
257
|
+
sliceId?: string;
|
|
258
|
+
status?: string;
|
|
259
|
+
goal?: string;
|
|
260
|
+
contractIds: string[];
|
|
261
|
+
acceptance: string[];
|
|
262
|
+
implementationSurfaces: string[];
|
|
263
|
+
verificationCommands: string[];
|
|
264
|
+
lockedNotes: string[];
|
|
265
|
+
mustFixFindings: string[];
|
|
266
|
+
remainingBefore: string[];
|
|
267
|
+
basisCommit?: string;
|
|
268
|
+
releaseBlockerCountBefore?: number;
|
|
269
|
+
highValueGapCountBefore?: number;
|
|
270
|
+
};
|
|
271
|
+
verificationEvidenceContext: (snapshot: CompletionStateSnapshot) => {
|
|
272
|
+
path: string;
|
|
273
|
+
status: string;
|
|
274
|
+
subjectType?: string;
|
|
275
|
+
sliceId?: string;
|
|
276
|
+
contractIds: string[];
|
|
277
|
+
outcome?: string;
|
|
278
|
+
recordedAt?: string;
|
|
279
|
+
headSha?: string;
|
|
280
|
+
basisCommit?: string;
|
|
281
|
+
verificationCommands: string[];
|
|
282
|
+
summary: string;
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
): string[] {
|
|
286
|
+
const context = deps.activeSliceContext(snapshot);
|
|
287
|
+
const evidence = deps.verificationEvidenceContext(snapshot);
|
|
288
|
+
return [
|
|
289
|
+
`Canonical evaluation handoff for ${role}:`,
|
|
290
|
+
`- task_type: ${deps.currentTaskType(snapshot) ?? "(missing)"}`,
|
|
291
|
+
`- evaluation_profile: ${deps.currentEvaluationProfile(snapshot) ?? "(missing)"}`,
|
|
292
|
+
`- latest_completed_slice: ${deps.asString(snapshot.state?.latest_completed_slice) ?? "(none)"}`,
|
|
293
|
+
`- active_slice_id: ${context.sliceId ?? "(none)"}`,
|
|
294
|
+
`- active_slice_status: ${context.status ?? "(unknown)"}`,
|
|
295
|
+
`- active_slice_goal: ${context.goal ?? "(unknown)"}`,
|
|
296
|
+
`- contract_ids: ${context.contractIds.length > 0 ? context.contractIds.join(", ") : "(none)"}`,
|
|
297
|
+
`- acceptance_criteria: ${context.acceptance.length > 0 ? context.acceptance.join(" | ") : "(none)"}`,
|
|
298
|
+
`- implementation_surfaces: ${context.implementationSurfaces.length > 0 ? context.implementationSurfaces.join(" | ") : "(none)"}`,
|
|
299
|
+
`- verification_commands: ${context.verificationCommands.length > 0 ? context.verificationCommands.join(" | ") : "(none)"}`,
|
|
300
|
+
`- locked_notes: ${context.lockedNotes.length > 0 ? context.lockedNotes.join(" | ") : "(none)"}`,
|
|
301
|
+
`- must_fix_findings: ${context.mustFixFindings.length > 0 ? context.mustFixFindings.join(" | ") : "(none)"}`,
|
|
302
|
+
`- basis_commit: ${context.basisCommit ?? "(none)"}`,
|
|
303
|
+
`- remaining_contract_ids_before: ${context.remainingBefore.length > 0 ? context.remainingBefore.join(", ") : "(none)"}`,
|
|
304
|
+
`- release_blocker_count_before: ${context.releaseBlockerCountBefore ?? "(unknown)"}`,
|
|
305
|
+
`- high_value_gap_count_before: ${context.highValueGapCountBefore ?? "(unknown)"}`,
|
|
306
|
+
`- verification_evidence_path: ${evidence.path}`,
|
|
307
|
+
`- verification_evidence_status: ${evidence.status}`,
|
|
308
|
+
`- verification_evidence_subject_type: ${evidence.subjectType ?? "(missing)"}`,
|
|
309
|
+
`- verification_evidence_slice_id: ${evidence.sliceId ?? "(none)"}`,
|
|
310
|
+
`- verification_evidence_contract_ids: ${evidence.contractIds.length > 0 ? evidence.contractIds.join(", ") : "(none)"}`,
|
|
311
|
+
`- verification_evidence_outcome: ${evidence.outcome ?? "(missing)"}`,
|
|
312
|
+
`- verification_evidence_recorded_at: ${evidence.recordedAt ?? "(missing)"}`,
|
|
313
|
+
`- verification_evidence_head_sha: ${evidence.headSha ?? "(missing)"}`,
|
|
314
|
+
`- verification_evidence_basis_commit: ${evidence.basisCommit ?? "(missing)"}`,
|
|
315
|
+
`- verification_evidence_commands: ${evidence.verificationCommands.length > 0 ? evidence.verificationCommands.join(" | ") : "(none)"}`,
|
|
316
|
+
`- verification_evidence_summary: ${evidence.summary}`,
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function buildEvaluationRoleReminderText(
|
|
321
|
+
snapshot: CompletionStateSnapshot,
|
|
322
|
+
role: string,
|
|
323
|
+
deps: Parameters<typeof buildEvaluationRoleContextLines>[2],
|
|
324
|
+
): string {
|
|
325
|
+
return buildEvaluationRoleContextLines(snapshot, role, deps).join(" ");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
type CompletionHistoryCounts = {
|
|
329
|
+
reviewed: number;
|
|
330
|
+
audited: number;
|
|
331
|
+
accepted: number;
|
|
332
|
+
reopened: number;
|
|
333
|
+
judgments: number;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
type CompletionVerificationEvidenceSummary = {
|
|
337
|
+
path: string;
|
|
338
|
+
status: string;
|
|
339
|
+
subjectType?: string;
|
|
340
|
+
sliceId?: string;
|
|
341
|
+
contractIds: string[];
|
|
342
|
+
outcome?: string;
|
|
343
|
+
recordedAt?: string;
|
|
344
|
+
headSha?: string;
|
|
345
|
+
basisCommit?: string;
|
|
346
|
+
verificationCommands: string[];
|
|
347
|
+
summary: string;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
export function buildSystemReminder(args: {
|
|
351
|
+
missionAnchor?: string;
|
|
352
|
+
taskType?: string;
|
|
353
|
+
evaluationProfile?: string;
|
|
354
|
+
currentPhase?: string;
|
|
355
|
+
continuationPolicy?: string;
|
|
356
|
+
continuationReason?: string;
|
|
357
|
+
nextMandatoryRole?: string;
|
|
358
|
+
nextMandatoryAction?: string;
|
|
359
|
+
remainingSliceCount: number | string;
|
|
360
|
+
remainingStopJudges: number | string;
|
|
361
|
+
history: CompletionHistoryCounts;
|
|
362
|
+
exactActiveContract: boolean;
|
|
363
|
+
activeContractDrift: string;
|
|
364
|
+
activePriority?: number;
|
|
365
|
+
activeWhyNow?: string;
|
|
366
|
+
implementationSurfaces: string[];
|
|
367
|
+
verificationCommands: string[];
|
|
368
|
+
activePriorityLine?: string;
|
|
369
|
+
activeWhyNowLine?: string;
|
|
370
|
+
implementationSurfacesLine?: string;
|
|
371
|
+
verificationCommandsLine?: string;
|
|
372
|
+
evidence: CompletionVerificationEvidenceSummary;
|
|
373
|
+
evaluationRoleReminderText?: string;
|
|
374
|
+
}): string {
|
|
375
|
+
const lines = [
|
|
376
|
+
"Completion workflow detected.",
|
|
377
|
+
"Canonical truth lives in .agent/state.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json.",
|
|
378
|
+
`Mission anchor: ${args.missionAnchor ?? "(unknown)"}`,
|
|
379
|
+
`Task type: ${args.taskType ?? "(missing)"}`,
|
|
380
|
+
`Evaluation profile: ${args.evaluationProfile ?? "(missing)"}`,
|
|
381
|
+
`Current phase: ${args.currentPhase ?? "unknown"}`,
|
|
382
|
+
`Continuation policy: ${args.continuationPolicy ?? "unknown"}`,
|
|
383
|
+
`Continuation reason: ${args.continuationReason ?? "(unknown)"}`,
|
|
384
|
+
`Next mandatory role: ${args.nextMandatoryRole ?? "unknown"}`,
|
|
385
|
+
`Next mandatory action: ${args.nextMandatoryAction ?? "unknown"}`,
|
|
386
|
+
`Remaining slice count: ${args.remainingSliceCount}`,
|
|
387
|
+
`Remaining stop judges: ${args.remainingStopJudges}`,
|
|
388
|
+
`History counts: reviewed=${args.history.reviewed}, audited=${args.history.audited}, accepted=${args.history.accepted}, reopened=${args.history.reopened}, judgments=${args.history.judgments}.`,
|
|
389
|
+
"Re-read canonical .agent state after compaction or recovery instead of relying on conversation memory.",
|
|
390
|
+
"If continuation_policy == continue, do not stop after a slice or ask whether to continue; dispatch the next mandatory role directly.",
|
|
391
|
+
"Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.",
|
|
392
|
+
"If canonical state is stale, invalid, ambiguous, or missing, route to completion-regrounder.",
|
|
393
|
+
"When recovering from compaction, prefer a deterministic restart from canonical files over conversational inference.",
|
|
394
|
+
];
|
|
395
|
+
if (args.exactActiveContract) {
|
|
396
|
+
lines.push("Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.");
|
|
397
|
+
lines.push(`Active slice contract drift: ${args.activeContractDrift}`);
|
|
398
|
+
}
|
|
399
|
+
if (args.activePriorityLine) lines.push(args.activePriorityLine);
|
|
400
|
+
else if (args.activePriority !== undefined) lines.push(`Active slice priority: ${args.activePriority}`);
|
|
401
|
+
if (args.activeWhyNowLine) lines.push(args.activeWhyNowLine);
|
|
402
|
+
else if (args.activeWhyNow) lines.push(`Active slice why_now: ${args.activeWhyNow}`);
|
|
403
|
+
if (args.implementationSurfacesLine) lines.push(args.implementationSurfacesLine);
|
|
404
|
+
else if (args.implementationSurfaces.length > 0) lines.push(`Active implementation surfaces: ${args.implementationSurfaces.join(", ")}`);
|
|
405
|
+
if (args.verificationCommandsLine) lines.push(args.verificationCommandsLine);
|
|
406
|
+
else if (args.verificationCommands.length > 0) lines.push(`Active verification commands: ${args.verificationCommands.join(" | ")}`);
|
|
407
|
+
lines.push(`Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})`);
|
|
408
|
+
if (args.evidence.subjectType) lines.push(`Verification evidence subject: ${args.evidence.subjectType}`);
|
|
409
|
+
if (args.evidence.outcome) lines.push(`Verification evidence outcome: ${args.evidence.outcome}`);
|
|
410
|
+
if (args.evidence.recordedAt) lines.push(`Verification evidence recorded_at: ${args.evidence.recordedAt}`);
|
|
411
|
+
if (args.evidence.verificationCommands.length > 0) {
|
|
412
|
+
lines.push(`Verification evidence commands: ${args.evidence.verificationCommands.join(" | ")}`);
|
|
413
|
+
}
|
|
414
|
+
lines.push(`Verification evidence summary: ${args.evidence.summary}`);
|
|
415
|
+
if (args.evaluationRoleReminderText) lines.push(args.evaluationRoleReminderText);
|
|
416
|
+
return lines.join(" ");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function buildResumeCapsule(args: {
|
|
420
|
+
missionAnchor?: string;
|
|
421
|
+
taskType?: string;
|
|
422
|
+
evaluationProfile?: string;
|
|
423
|
+
currentPhase?: string;
|
|
424
|
+
continuationPolicy?: string;
|
|
425
|
+
continuationReason?: string;
|
|
426
|
+
requiresReground: boolean | string;
|
|
427
|
+
nextMandatoryRole?: string;
|
|
428
|
+
nextMandatoryAction?: string;
|
|
429
|
+
remainingSliceCount: number | string;
|
|
430
|
+
remainingStopJudges: number | string;
|
|
431
|
+
history: CompletionHistoryCounts;
|
|
432
|
+
activeSliceMatchesPlan: "yes" | "no" | "unknown";
|
|
433
|
+
activeSliceContractDrift: string;
|
|
434
|
+
implementerHandoffSnapshot: "present" | "missing_or_unclear";
|
|
435
|
+
evidence: CompletionVerificationEvidenceSummary;
|
|
436
|
+
activeSlice: {
|
|
437
|
+
sliceId?: string;
|
|
438
|
+
status?: string;
|
|
439
|
+
goal?: string;
|
|
440
|
+
priority?: number;
|
|
441
|
+
whyNow?: string;
|
|
442
|
+
contractIds: string[];
|
|
443
|
+
blockedOn: string[];
|
|
444
|
+
lockedNotes: string[];
|
|
445
|
+
mustFixFindings: string[];
|
|
446
|
+
implementationSurfaces: string[];
|
|
447
|
+
verificationCommands: string[];
|
|
448
|
+
implementationSurfacesLine?: string;
|
|
449
|
+
verificationCommandsLine?: string;
|
|
450
|
+
basisCommit?: string;
|
|
451
|
+
remainingContractIdsBefore: string[];
|
|
452
|
+
releaseBlockerCountBefore?: number;
|
|
453
|
+
highValueGapCountBefore?: number;
|
|
454
|
+
acceptanceCriteria: string[];
|
|
455
|
+
};
|
|
456
|
+
}): string {
|
|
457
|
+
const lines = [
|
|
458
|
+
"Authoritative completion resume capsule:",
|
|
459
|
+
"",
|
|
460
|
+
"<completion-state>",
|
|
461
|
+
`mission_anchor: ${args.missionAnchor ?? "(unknown)"}`,
|
|
462
|
+
`task_type: ${args.taskType ?? "(missing)"}`,
|
|
463
|
+
`evaluation_profile: ${args.evaluationProfile ?? "(missing)"}`,
|
|
464
|
+
`current_phase: ${args.currentPhase ?? "unknown"}`,
|
|
465
|
+
`continuation_policy: ${args.continuationPolicy ?? "unknown"}`,
|
|
466
|
+
`continuation_reason: ${args.continuationReason ?? "(unknown)"}`,
|
|
467
|
+
`requires_reground: ${args.requiresReground}`,
|
|
468
|
+
`next_mandatory_role: ${args.nextMandatoryRole ?? "unknown"}`,
|
|
469
|
+
`next_mandatory_action: ${args.nextMandatoryAction ?? "unknown"}`,
|
|
470
|
+
`remaining_slice_count: ${args.remainingSliceCount}`,
|
|
471
|
+
`remaining_stop_judges: ${args.remainingStopJudges}`,
|
|
472
|
+
`active_slice_matches_plan: ${args.activeSliceMatchesPlan}`,
|
|
473
|
+
`active_slice_contract_drift_fields: ${args.activeSliceContractDrift}`,
|
|
474
|
+
`implementer_handoff_snapshot: ${args.implementerHandoffSnapshot}`,
|
|
475
|
+
`history_counts: reviewed=${args.history.reviewed}, audited=${args.history.audited}, accepted=${args.history.accepted}, reopened=${args.history.reopened}, judgments=${args.history.judgments}`,
|
|
476
|
+
"",
|
|
477
|
+
"verification_evidence:",
|
|
478
|
+
`- path: ${args.evidence.path}`,
|
|
479
|
+
`- status: ${args.evidence.status}`,
|
|
480
|
+
`- subject_type: ${args.evidence.subjectType ?? "(missing)"}`,
|
|
481
|
+
`- slice_id: ${args.evidence.sliceId ?? "(none)"}`,
|
|
482
|
+
`- contract_ids: ${args.evidence.contractIds.length > 0 ? args.evidence.contractIds.join(", ") : "(none)"}`,
|
|
483
|
+
`- outcome: ${args.evidence.outcome ?? "(missing)"}`,
|
|
484
|
+
`- recorded_at: ${args.evidence.recordedAt ?? "(missing)"}`,
|
|
485
|
+
`- head_sha: ${args.evidence.headSha ?? "(missing)"}`,
|
|
486
|
+
`- basis_commit: ${args.evidence.basisCommit ?? "(missing)"}`,
|
|
487
|
+
`- verification_commands: ${args.evidence.verificationCommands.length > 0 ? args.evidence.verificationCommands.join(" | ") : "(none)"}`,
|
|
488
|
+
`- summary: ${args.evidence.summary}`,
|
|
489
|
+
"",
|
|
490
|
+
"active_slice:",
|
|
491
|
+
`- slice_id: ${args.activeSlice.sliceId ?? "(none)"}`,
|
|
492
|
+
`- status: ${args.activeSlice.status ?? "unknown"}`,
|
|
493
|
+
`- goal: ${args.activeSlice.goal ?? "(unknown)"}`,
|
|
494
|
+
`- priority: ${args.activeSlice.priority ?? "(unknown)"}`,
|
|
495
|
+
`- why_now: ${args.activeSlice.whyNow ?? "(unknown)"}`,
|
|
496
|
+
`- contract_ids: ${args.activeSlice.contractIds.length > 0 ? args.activeSlice.contractIds.join(", ") : "(none)"}`,
|
|
497
|
+
];
|
|
498
|
+
if (args.activeSlice.blockedOn.length > 0) lines.push(`- blocked_on: ${args.activeSlice.blockedOn.join(", ")}`);
|
|
499
|
+
if (args.activeSlice.lockedNotes.length > 0) lines.push(`- locked_notes: ${args.activeSlice.lockedNotes.join(" | ")}`);
|
|
500
|
+
if (args.activeSlice.mustFixFindings.length > 0) lines.push(`- must_fix_findings: ${args.activeSlice.mustFixFindings.join(" | ")}`);
|
|
501
|
+
if (args.activeSlice.implementationSurfacesLine) {
|
|
502
|
+
lines.push(args.activeSlice.implementationSurfacesLine);
|
|
503
|
+
} else if (args.activeSlice.implementationSurfaces.length > 0) {
|
|
504
|
+
lines.push(`- implementation_surfaces: ${args.activeSlice.implementationSurfaces.join(" | ")}`);
|
|
505
|
+
}
|
|
506
|
+
if (args.activeSlice.verificationCommandsLine) {
|
|
507
|
+
lines.push(args.activeSlice.verificationCommandsLine);
|
|
508
|
+
} else if (args.activeSlice.verificationCommands.length > 0) {
|
|
509
|
+
lines.push(`- verification_commands: ${args.activeSlice.verificationCommands.join(" | ")}`);
|
|
510
|
+
}
|
|
511
|
+
lines.push(`- basis_commit: ${args.activeSlice.basisCommit ?? "(none)"}`);
|
|
512
|
+
lines.push(`- remaining_contract_ids_before: ${args.activeSlice.remainingContractIdsBefore.length > 0 ? args.activeSlice.remainingContractIdsBefore.join(", ") : "(none)"}`);
|
|
513
|
+
lines.push(`- release_blocker_count_before: ${args.activeSlice.releaseBlockerCountBefore ?? "(unknown)"}`);
|
|
514
|
+
lines.push(`- high_value_gap_count_before: ${args.activeSlice.highValueGapCountBefore ?? "(unknown)"}`);
|
|
515
|
+
lines.push("", "acceptance_criteria:");
|
|
516
|
+
if (args.activeSlice.acceptanceCriteria.length === 0) lines.push("- (none)");
|
|
517
|
+
else lines.push(...args.activeSlice.acceptanceCriteria.map((item) => `- ${item}`));
|
|
518
|
+
lines.push(
|
|
519
|
+
"",
|
|
520
|
+
"Rules:",
|
|
521
|
+
"- Treat this block as continuity support derived from canonical .agent state.",
|
|
522
|
+
"- For selected/in-progress/committed/done slices, .agent/active-slice.json is the canonical implementation contract and the selected plan slice must mirror it exactly.",
|
|
523
|
+
"- Preserve exact slice_id, goal, contract_ids, acceptance criteria, blocked_on, priority, why_now, implementation surfaces, verification commands, locked notes, must-fix findings, basis_commit, and before-slice counters where still true.",
|
|
524
|
+
"- When populated, .agent/verification-evidence.json is the durable canonical verification record for the selected slice or current HEAD and should be consumed instead of temp-only artifacts or conversational summaries.",
|
|
525
|
+
"- After compaction, re-read .agent/state.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json before resuming long-running completion work.",
|
|
526
|
+
"- Invoke completion-regrounder before continuing when requires_reground is true or unknown.",
|
|
527
|
+
"- Invoke completion-regrounder before continuing when next_mandatory_role or next_mandatory_action is unknown or ambiguous.",
|
|
528
|
+
"- Invoke completion-regrounder before continuing when active_slice_matches_plan is no, active_slice_contract_drift_fields is not none, or implementer_handoff_snapshot is missing_or_unclear.",
|
|
529
|
+
"- If continuation_policy is continue, do not stop after a slice or ask whether to continue. Dispatch the next mandatory role directly.",
|
|
530
|
+
"- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.",
|
|
531
|
+
"- If you are completion-implementer after compaction, resume from the canonical active-slice implementation contract instead of asking the user to resend the original caller payload.",
|
|
532
|
+
"- Do not replace canonical .agent state with summary inference.",
|
|
533
|
+
"</completion-state>",
|
|
534
|
+
);
|
|
535
|
+
return lines.join("\n");
|
|
536
|
+
}
|