@linimin/pi-letscook 0.1.68 → 0.1.70
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/.agent/README.md +2 -3
- package/.agent/verify_completion_control_plane.sh +21 -34
- package/CHANGELOG.md +16 -0
- package/README.md +23 -26
- package/agents/completion-bootstrapper.md +1 -2
- package/agents/completion-regrounder.md +10 -16
- package/extensions/completion/driver.ts +69 -136
- package/extensions/completion/index.ts +94 -81
- package/extensions/completion/policy-guards.ts +1 -1
- package/extensions/completion/prompt-surfaces.ts +63 -161
- package/extensions/completion/proposal.ts +26 -61
- package/extensions/completion/role-runner.ts +161 -57
- package/extensions/completion/state-store.ts +43 -85
- package/extensions/completion/types.ts +2 -3
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +21 -0
- package/scripts/canonical-evidence-artifact-test.sh +57 -65
- package/scripts/context-proposal-test.sh +1430 -310
- package/scripts/legacy-cleanup-test.sh +1 -1
- package/scripts/refocus-test.sh +459 -185
- package/scripts/release-check.sh +14 -15
- package/scripts/role-runner-contract-test.sh +4 -2
- package/scripts/smoke-test.sh +36 -78
- package/skills/completion-protocol/SKILL.md +8 -9
- package/skills/completion-protocol/references/completion.md +2 -37
- package/skills/cook-handoff-boundary/SKILL.md +19 -18
|
@@ -24,26 +24,6 @@ export type AdvisoryStartupBrief = {
|
|
|
24
24
|
evaluation_profile?: string;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
export type ApprovedStartupPlan = {
|
|
28
|
-
artifact_type: "completion-startup-plan";
|
|
29
|
-
schema_version: 1;
|
|
30
|
-
status: "approved";
|
|
31
|
-
source: AdvisoryStartupBrief["source"];
|
|
32
|
-
captured_at: string;
|
|
33
|
-
mission_anchor: string;
|
|
34
|
-
goal_text: string;
|
|
35
|
-
task_type?: string;
|
|
36
|
-
evaluation_profile?: string;
|
|
37
|
-
scope: string[];
|
|
38
|
-
constraints: string[];
|
|
39
|
-
acceptance: string[];
|
|
40
|
-
risks: string[];
|
|
41
|
-
notes: string[];
|
|
42
|
-
planned_surfaces: string[];
|
|
43
|
-
verification_intent: string[];
|
|
44
|
-
sequencing_hints: string[];
|
|
45
|
-
};
|
|
46
|
-
|
|
47
27
|
export function buildCookHandoffBoundaryReminder(): string {
|
|
48
28
|
return [
|
|
49
29
|
"You are in ordinary main chat unless the user explicitly runs /cook.",
|
|
@@ -53,11 +33,10 @@ export function buildCookHandoffBoundaryReminder(): string {
|
|
|
53
33
|
"In ordinary chat, do not load or follow completion-protocol, and do not call completion_role.",
|
|
54
34
|
"If the user wants direct implementation now, stay in ordinary chat and help directly instead of blocking on /cook.",
|
|
55
35
|
"If the user asks follow-up questions or wants to keep refining scope, continue helping naturally in ordinary chat.",
|
|
56
|
-
"If the user explicitly runs /cook, the extension should call a primary-agent
|
|
57
|
-
"Do not expect /cook to infer or guess startup intent from recent discussion alone
|
|
58
|
-
"Only provide a preview startup
|
|
59
|
-
"Any preview capsule is
|
|
60
|
-
"When /cook starts, the approved startup plan should be written into .agent and then handed to completion-regrounder so canonical slices can be derived from repo truth.",
|
|
36
|
+
"If the user explicitly runs /cook, the extension should call a primary-agent handoff synthesis step from the current task context, show Start/Cancel confirmation, and persist the confirmed startup brief into .agent/** without making the user rerun /cook.",
|
|
37
|
+
"Do not expect /cook to infer or guess startup intent from recent discussion alone; /cook should use explicit primary-agent handoff data, whether it already exists or is synthesized in the same /cook entry.",
|
|
38
|
+
"Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior.",
|
|
39
|
+
"Any preview capsule is startup intake for /cook only: do not present it as canonical .agent state, an active slice, or a persistent repo contract.",
|
|
61
40
|
"When you continue in ordinary chat, do not pretend /cook already started and do not silently rewrite discussion into canonical workflow state.",
|
|
62
41
|
].join(" ");
|
|
63
42
|
}
|
|
@@ -106,28 +85,7 @@ function buildAdvisoryStartupBriefNotes(analysis: ContextProposalAnalysis): stri
|
|
|
106
85
|
...analysis.critique,
|
|
107
86
|
...analysis.possibleNoise.map((item) => `Possible noise: ${item}`),
|
|
108
87
|
];
|
|
109
|
-
return notes.length > 0 ? notes : ["No additional operator notes were
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function startupPlanSourceForProposal(source: ContextProposal["source"]): AdvisoryStartupBrief["source"] {
|
|
113
|
-
return source === "handoff_capsule"
|
|
114
|
-
? "primary_agent_handoff"
|
|
115
|
-
: source === "deferred_primary_agent_handoff"
|
|
116
|
-
? "deferred_primary_agent_handoff"
|
|
117
|
-
: "recent_discussion";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function extractDelimitedNoteValues(notes: string[], prefix: string): string[] {
|
|
121
|
-
return notes.flatMap((note) => {
|
|
122
|
-
if (!note.startsWith(prefix)) return [];
|
|
123
|
-
return note.slice(prefix.length).split("|").map((item) => item.trim()).filter(Boolean);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function extractSequencingHints(notes: string[]): string[] {
|
|
128
|
-
return notes.filter((note) =>
|
|
129
|
-
note.startsWith("First slice goal:") || note.startsWith("First slice non-goals:") || note.startsWith("Why this slice first:"),
|
|
130
|
-
);
|
|
88
|
+
return notes.length > 0 ? notes : ["No additional operator notes were derived from recent discussion."];
|
|
131
89
|
}
|
|
132
90
|
|
|
133
91
|
export function buildAdvisoryStartupBrief(args: {
|
|
@@ -137,7 +95,12 @@ export function buildAdvisoryStartupBrief(args: {
|
|
|
137
95
|
}): AdvisoryStartupBrief {
|
|
138
96
|
return {
|
|
139
97
|
kind: "startup_brief",
|
|
140
|
-
source:
|
|
98
|
+
source:
|
|
99
|
+
args.proposal.source === "handoff_capsule"
|
|
100
|
+
? "primary_agent_handoff"
|
|
101
|
+
: args.proposal.source === "deferred_primary_agent_handoff"
|
|
102
|
+
? "deferred_primary_agent_handoff"
|
|
103
|
+
: "recent_discussion",
|
|
141
104
|
confirmed: true,
|
|
142
105
|
captured_at: args.capturedAt ?? new Date().toISOString(),
|
|
143
106
|
goal_text: args.proposal.goalText,
|
|
@@ -152,81 +115,6 @@ export function buildAdvisoryStartupBrief(args: {
|
|
|
152
115
|
};
|
|
153
116
|
}
|
|
154
117
|
|
|
155
|
-
export function buildApprovedStartupPlan(args: {
|
|
156
|
-
proposal: Pick<ContextProposal, "goalText" | "mission" | "scope" | "constraints" | "acceptance" | "source">;
|
|
157
|
-
analysis: ContextProposalAnalysis;
|
|
158
|
-
missionAnchor?: string;
|
|
159
|
-
capturedAt?: string;
|
|
160
|
-
}): ApprovedStartupPlan {
|
|
161
|
-
const capturedAt = args.capturedAt ?? new Date().toISOString();
|
|
162
|
-
const notes = buildAdvisoryStartupBriefNotes(args.analysis);
|
|
163
|
-
return {
|
|
164
|
-
artifact_type: "completion-startup-plan",
|
|
165
|
-
schema_version: 1,
|
|
166
|
-
status: "approved",
|
|
167
|
-
source: startupPlanSourceForProposal(args.proposal.source),
|
|
168
|
-
captured_at: capturedAt,
|
|
169
|
-
mission_anchor: args.missionAnchor ?? args.proposal.mission,
|
|
170
|
-
goal_text: args.proposal.goalText,
|
|
171
|
-
task_type: args.analysis.taskType,
|
|
172
|
-
evaluation_profile: args.analysis.evaluationProfile,
|
|
173
|
-
scope: [...args.proposal.scope],
|
|
174
|
-
constraints: [...args.proposal.constraints],
|
|
175
|
-
acceptance: [...args.proposal.acceptance],
|
|
176
|
-
risks: [...args.analysis.risks],
|
|
177
|
-
notes: [...notes],
|
|
178
|
-
planned_surfaces: extractDelimitedNoteValues(notes, "Implementation surfaces:"),
|
|
179
|
-
verification_intent: extractDelimitedNoteValues(notes, "Verification commands:"),
|
|
180
|
-
sequencing_hints: extractSequencingHints(notes),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function buildApprovedStartupPlanMarkdown(plan: ApprovedStartupPlan): string {
|
|
185
|
-
const lines = [
|
|
186
|
-
"# Approved Startup Plan",
|
|
187
|
-
"",
|
|
188
|
-
`Mission anchor: ${plan.mission_anchor}`,
|
|
189
|
-
`Source: ${plan.source}`,
|
|
190
|
-
`Captured at: ${plan.captured_at}`,
|
|
191
|
-
];
|
|
192
|
-
if (plan.task_type) lines.push(`Task type: ${plan.task_type}`);
|
|
193
|
-
if (plan.evaluation_profile) lines.push(`Evaluation profile: ${plan.evaluation_profile}`);
|
|
194
|
-
lines.push("", "## Goal", "", plan.goal_text);
|
|
195
|
-
if (plan.scope.length > 0) {
|
|
196
|
-
lines.push("", "## Scope", "");
|
|
197
|
-
for (const item of plan.scope) lines.push(`- ${item}`);
|
|
198
|
-
}
|
|
199
|
-
if (plan.constraints.length > 0) {
|
|
200
|
-
lines.push("", "## Constraints", "");
|
|
201
|
-
for (const item of plan.constraints) lines.push(`- ${item}`);
|
|
202
|
-
}
|
|
203
|
-
if (plan.acceptance.length > 0) {
|
|
204
|
-
lines.push("", "## Acceptance", "");
|
|
205
|
-
for (const item of plan.acceptance) lines.push(`- ${item}`);
|
|
206
|
-
}
|
|
207
|
-
if (plan.risks.length > 0) {
|
|
208
|
-
lines.push("", "## Risks", "");
|
|
209
|
-
for (const item of plan.risks) lines.push(`- ${item}`);
|
|
210
|
-
}
|
|
211
|
-
if (plan.planned_surfaces.length > 0) {
|
|
212
|
-
lines.push("", "## Planned surfaces", "");
|
|
213
|
-
for (const item of plan.planned_surfaces) lines.push(`- ${item}`);
|
|
214
|
-
}
|
|
215
|
-
if (plan.verification_intent.length > 0) {
|
|
216
|
-
lines.push("", "## Verification intent", "");
|
|
217
|
-
for (const item of plan.verification_intent) lines.push(`- ${item}`);
|
|
218
|
-
}
|
|
219
|
-
if (plan.sequencing_hints.length > 0) {
|
|
220
|
-
lines.push("", "## Sequencing hints", "");
|
|
221
|
-
for (const item of plan.sequencing_hints) lines.push(`- ${item}`);
|
|
222
|
-
}
|
|
223
|
-
if (plan.notes.length > 0) {
|
|
224
|
-
lines.push("", "## Notes", "");
|
|
225
|
-
for (const item of plan.notes) lines.push(`- ${item}`);
|
|
226
|
-
}
|
|
227
|
-
return `${lines.join("\n")}\n`;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
118
|
export function buildContextProposalCritiqueText(analysis: ContextProposalAnalysis): string {
|
|
231
119
|
const lines: string[] = [];
|
|
232
120
|
if (analysis.critique.length > 0) {
|
|
@@ -259,7 +147,7 @@ export function buildContextProposalCritiqueText(analysis: ContextProposalAnalys
|
|
|
259
147
|
for (const item of analysis.suppressedNegatedTopics) lines.push(`- ${item}`);
|
|
260
148
|
}
|
|
261
149
|
if (lines.length === 0) {
|
|
262
|
-
return "No additional operator notes or risks were derived for this startup
|
|
150
|
+
return "No additional operator notes or risks were derived for this startup brief.";
|
|
263
151
|
}
|
|
264
152
|
return lines.join("\n");
|
|
265
153
|
}
|
|
@@ -308,7 +196,7 @@ export function buildContextProposalConfirmationActions(mainChatRerunGuidance: s
|
|
|
308
196
|
{
|
|
309
197
|
id: "start",
|
|
310
198
|
label: "Start",
|
|
311
|
-
description: "Accept this startup
|
|
199
|
+
description: "Accept this startup brief and let /cook write or refocus canonical workflow state.",
|
|
312
200
|
},
|
|
313
201
|
{
|
|
314
202
|
id: "cancel",
|
|
@@ -328,8 +216,8 @@ export function buildContextProposalConfirmationLayout(args: {
|
|
|
328
216
|
}): ContextProposalConfirmationLayout {
|
|
329
217
|
return {
|
|
330
218
|
title: args.title,
|
|
331
|
-
intro: "Review the startup
|
|
332
|
-
proposalHeading: "Startup
|
|
219
|
+
intro: "Review the startup brief (mission, scope, constraints, acceptance, and notes/risks) plus the 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.",
|
|
220
|
+
proposalHeading: "Startup brief",
|
|
333
221
|
proposalBody: buildContextProposalDisplayText(args.proposal),
|
|
334
222
|
critiqueHeading: "Notes and risks",
|
|
335
223
|
critiqueBody: buildContextProposalCritiqueText(args.analysis),
|
|
@@ -389,14 +277,14 @@ export function buildContextProposalAnalystPrompt(projectName: string, discussio
|
|
|
389
277
|
return lines.join("\n");
|
|
390
278
|
}
|
|
391
279
|
|
|
392
|
-
|
|
280
|
+
function buildCookStartupProgressLines(
|
|
393
281
|
activity: LiveRoleActivity,
|
|
394
282
|
buildInlineRunningLines: (details: {
|
|
395
283
|
role?: string;
|
|
396
284
|
startedAt?: number;
|
|
397
285
|
updatedAt?: number;
|
|
398
286
|
currentAction?: string;
|
|
399
|
-
toolActivity?: string
|
|
287
|
+
toolActivity?: string;
|
|
400
288
|
toolRecentActivity?: string[];
|
|
401
289
|
recentActivity?: string[];
|
|
402
290
|
assistantSummary?: string;
|
|
@@ -406,6 +294,7 @@ export function contextProposalAnalystProgressLines(
|
|
|
406
294
|
verifying?: string;
|
|
407
295
|
stateDeltas?: string[];
|
|
408
296
|
}) => string[],
|
|
297
|
+
footerLine: string,
|
|
409
298
|
): string[] {
|
|
410
299
|
return [
|
|
411
300
|
...buildInlineRunningLines({
|
|
@@ -424,10 +313,52 @@ export function contextProposalAnalystProgressLines(
|
|
|
424
313
|
stateDeltas: activity.stateDeltas,
|
|
425
314
|
}),
|
|
426
315
|
"",
|
|
427
|
-
|
|
316
|
+
footerLine,
|
|
428
317
|
];
|
|
429
318
|
}
|
|
430
319
|
|
|
320
|
+
export function contextProposalAnalystProgressLines(
|
|
321
|
+
activity: LiveRoleActivity,
|
|
322
|
+
buildInlineRunningLines: (details: {
|
|
323
|
+
role?: string;
|
|
324
|
+
startedAt?: number;
|
|
325
|
+
updatedAt?: number;
|
|
326
|
+
currentAction?: string;
|
|
327
|
+
toolActivity?: string;
|
|
328
|
+
toolRecentActivity?: string[];
|
|
329
|
+
recentActivity?: string[];
|
|
330
|
+
assistantSummary?: string;
|
|
331
|
+
progress?: string;
|
|
332
|
+
rationale?: string;
|
|
333
|
+
nextStep?: string;
|
|
334
|
+
verifying?: string;
|
|
335
|
+
stateDeltas?: string[];
|
|
336
|
+
}) => string[],
|
|
337
|
+
): string[] {
|
|
338
|
+
return buildCookStartupProgressLines(activity, buildInlineRunningLines, "This step only prepares a proposal for confirmation.");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function primaryAgentHandoffProgressLines(
|
|
342
|
+
activity: LiveRoleActivity,
|
|
343
|
+
buildInlineRunningLines: (details: {
|
|
344
|
+
role?: string;
|
|
345
|
+
startedAt?: number;
|
|
346
|
+
updatedAt?: number;
|
|
347
|
+
currentAction?: string;
|
|
348
|
+
toolActivity?: string;
|
|
349
|
+
toolRecentActivity?: string[];
|
|
350
|
+
recentActivity?: string[];
|
|
351
|
+
assistantSummary?: string;
|
|
352
|
+
progress?: string;
|
|
353
|
+
rationale?: string;
|
|
354
|
+
nextStep?: string;
|
|
355
|
+
verifying?: string;
|
|
356
|
+
stateDeltas?: string[];
|
|
357
|
+
}) => string[],
|
|
358
|
+
): string[] {
|
|
359
|
+
return buildCookStartupProgressLines(activity, buildInlineRunningLines, "This step only synthesizes the startup plan for Start/Cancel confirmation.");
|
|
360
|
+
}
|
|
361
|
+
|
|
431
362
|
export function buildEvaluationRoleContextLines(
|
|
432
363
|
snapshot: CompletionStateSnapshot,
|
|
433
364
|
role: string,
|
|
@@ -529,15 +460,6 @@ type CompletionVerificationEvidenceSummary = {
|
|
|
529
460
|
summary: string;
|
|
530
461
|
};
|
|
531
462
|
|
|
532
|
-
type CompletionStartupPlanSummary = {
|
|
533
|
-
path: string;
|
|
534
|
-
status: string;
|
|
535
|
-
source?: string;
|
|
536
|
-
plannedSurfaces: string[];
|
|
537
|
-
verificationIntent: string[];
|
|
538
|
-
summary: string;
|
|
539
|
-
};
|
|
540
|
-
|
|
541
463
|
export function buildSystemReminder(args: {
|
|
542
464
|
missionAnchor?: string;
|
|
543
465
|
taskType?: string;
|
|
@@ -561,12 +483,11 @@ export function buildSystemReminder(args: {
|
|
|
561
483
|
implementationSurfacesLine?: string;
|
|
562
484
|
verificationCommandsLine?: string;
|
|
563
485
|
evidence: CompletionVerificationEvidenceSummary;
|
|
564
|
-
startupPlan: CompletionStartupPlanSummary;
|
|
565
486
|
evaluationRoleReminderText?: string;
|
|
566
487
|
}): string {
|
|
567
488
|
const lines = [
|
|
568
489
|
"Completion workflow detected.",
|
|
569
|
-
"Canonical truth lives in .agent/state.json, .agent/
|
|
490
|
+
"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.",
|
|
570
491
|
`Mission anchor: ${args.missionAnchor ?? "(unknown)"}`,
|
|
571
492
|
`Task type: ${args.taskType ?? "(missing)"}`,
|
|
572
493
|
`Evaluation profile: ${args.evaluationProfile ?? "(missing)"}`,
|
|
@@ -583,7 +504,6 @@ export function buildSystemReminder(args: {
|
|
|
583
504
|
"Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.",
|
|
584
505
|
"If canonical state is stale, invalid, ambiguous, or missing, route to completion-regrounder.",
|
|
585
506
|
"When recovering from compaction, prefer a deterministic restart from canonical files over conversational inference.",
|
|
586
|
-
`Approved startup plan: ${args.startupPlan.path} (${args.startupPlan.status})`,
|
|
587
507
|
];
|
|
588
508
|
if (args.exactActiveContract) {
|
|
589
509
|
lines.push("Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.");
|
|
@@ -597,14 +517,6 @@ export function buildSystemReminder(args: {
|
|
|
597
517
|
else if (args.implementationSurfaces.length > 0) lines.push(`Active implementation surfaces: ${args.implementationSurfaces.join(", ")}`);
|
|
598
518
|
if (args.verificationCommandsLine) lines.push(args.verificationCommandsLine);
|
|
599
519
|
else if (args.verificationCommands.length > 0) lines.push(`Active verification commands: ${args.verificationCommands.join(" | ")}`);
|
|
600
|
-
if (args.startupPlan.source) lines.push(`Approved startup plan source: ${args.startupPlan.source}`);
|
|
601
|
-
if (args.startupPlan.plannedSurfaces.length > 0) {
|
|
602
|
-
lines.push(`Approved startup plan surfaces: ${args.startupPlan.plannedSurfaces.join(" | ")}`);
|
|
603
|
-
}
|
|
604
|
-
if (args.startupPlan.verificationIntent.length > 0) {
|
|
605
|
-
lines.push(`Approved startup plan verification intent: ${args.startupPlan.verificationIntent.join(" | ")}`);
|
|
606
|
-
}
|
|
607
|
-
lines.push(`Approved startup plan summary: ${args.startupPlan.summary}`);
|
|
608
520
|
lines.push(`Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})`);
|
|
609
521
|
if (args.evidence.subjectType) lines.push(`Verification evidence subject: ${args.evidence.subjectType}`);
|
|
610
522
|
if (args.evidence.outcome) lines.push(`Verification evidence outcome: ${args.evidence.outcome}`);
|
|
@@ -633,7 +545,6 @@ export function buildResumeCapsule(args: {
|
|
|
633
545
|
activeSliceMatchesPlan: "yes" | "no" | "unknown";
|
|
634
546
|
activeSliceContractDrift: string;
|
|
635
547
|
implementerHandoffSnapshot: "present" | "missing_or_unclear";
|
|
636
|
-
startupPlan: CompletionStartupPlanSummary;
|
|
637
548
|
evidence: CompletionVerificationEvidenceSummary;
|
|
638
549
|
activeSlice: {
|
|
639
550
|
sliceId?: string;
|
|
@@ -676,14 +587,6 @@ export function buildResumeCapsule(args: {
|
|
|
676
587
|
`implementer_handoff_snapshot: ${args.implementerHandoffSnapshot}`,
|
|
677
588
|
`history_counts: reviewed=${args.history.reviewed}, audited=${args.history.audited}, accepted=${args.history.accepted}, reopened=${args.history.reopened}, judgments=${args.history.judgments}`,
|
|
678
589
|
"",
|
|
679
|
-
"startup_plan:",
|
|
680
|
-
`- path: ${args.startupPlan.path}`,
|
|
681
|
-
`- status: ${args.startupPlan.status}`,
|
|
682
|
-
`- source: ${args.startupPlan.source ?? "(missing)"}`,
|
|
683
|
-
`- planned_surfaces: ${args.startupPlan.plannedSurfaces.length > 0 ? args.startupPlan.plannedSurfaces.join(" | ") : "(none)"}`,
|
|
684
|
-
`- verification_intent: ${args.startupPlan.verificationIntent.length > 0 ? args.startupPlan.verificationIntent.join(" | ") : "(none)"}`,
|
|
685
|
-
`- summary: ${args.startupPlan.summary}`,
|
|
686
|
-
"",
|
|
687
590
|
"verification_evidence:",
|
|
688
591
|
`- path: ${args.evidence.path}`,
|
|
689
592
|
`- status: ${args.evidence.status}`,
|
|
@@ -731,9 +634,8 @@ export function buildResumeCapsule(args: {
|
|
|
731
634
|
"- Treat this block as continuity support derived from canonical .agent state.",
|
|
732
635
|
"- 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.",
|
|
733
636
|
"- 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.",
|
|
734
|
-
"- .agent/startup-plan.json is the approved workflow plan captured at /cook entry. completion-regrounder must treat it as planning input, then reconcile canonical slices against repo truth instead of copying it blindly into plan.json.",
|
|
735
637
|
"- 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.",
|
|
736
|
-
"- After compaction, re-read .agent/state.json, .agent/
|
|
638
|
+
"- 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.",
|
|
737
639
|
"- Invoke completion-regrounder before continuing when requires_reground is true or unknown.",
|
|
738
640
|
"- Invoke completion-regrounder before continuing when next_mandatory_role or next_mandatory_action is unknown or ambiguous.",
|
|
739
641
|
"- 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.",
|
|
@@ -60,11 +60,11 @@ export type CookHandoffCapsule = {
|
|
|
60
60
|
risks: string[];
|
|
61
61
|
notes: string[];
|
|
62
62
|
handoff_kind: "implementation_workflow_handoff";
|
|
63
|
-
first_slice_goal
|
|
63
|
+
first_slice_goal: string;
|
|
64
64
|
first_slice_non_goals: string[];
|
|
65
65
|
implementation_surfaces: string[];
|
|
66
66
|
verification_commands: string[];
|
|
67
|
-
why_this_slice_first
|
|
67
|
+
why_this_slice_first: string;
|
|
68
68
|
task_type?: string;
|
|
69
69
|
evaluation_profile?: string;
|
|
70
70
|
why_cook_now?: string;
|
|
@@ -1279,7 +1279,7 @@ function parseCookHandoffCapsulesFromText(
|
|
|
1279
1279
|
const mission = deps.asString(parsed.mission);
|
|
1280
1280
|
const firstSliceGoal = deps.asString(parsed.first_slice_goal ?? parsed.firstSliceGoal);
|
|
1281
1281
|
const whyThisSliceFirst = deps.asString(parsed.why_this_slice_first ?? parsed.whyThisSliceFirst);
|
|
1282
|
-
if (!mission) continue;
|
|
1282
|
+
if (!mission || !firstSliceGoal || !whyThisSliceFirst) continue;
|
|
1283
1283
|
const scope = deps.asStringArray(parsed.scope);
|
|
1284
1284
|
const constraints = deps.asStringArray(parsed.constraints);
|
|
1285
1285
|
const nonGoals = deps.asStringArray(parsed.non_goals ?? parsed.nonGoals);
|
|
@@ -1325,12 +1325,12 @@ function buildCookHandoffBasisPreview(capsule: CookHandoffCapsule): string {
|
|
|
1325
1325
|
...capsule.constraints,
|
|
1326
1326
|
...capsule.non_goals,
|
|
1327
1327
|
...capsule.acceptance,
|
|
1328
|
+
`first_slice_goal: ${capsule.first_slice_goal}`,
|
|
1329
|
+
...capsule.first_slice_non_goals.map((item) => `first_slice_non_goals: ${item}`),
|
|
1330
|
+
...capsule.implementation_surfaces.map((item) => `implementation_surfaces: ${item}`),
|
|
1331
|
+
...capsule.verification_commands.map((item) => `verification_commands: ${item}`),
|
|
1332
|
+
`why_this_slice_first: ${capsule.why_this_slice_first}`,
|
|
1328
1333
|
];
|
|
1329
|
-
if (capsule.first_slice_goal) parts.push(`first_slice_goal: ${capsule.first_slice_goal}`);
|
|
1330
|
-
parts.push(...capsule.first_slice_non_goals.map((item) => `first_slice_non_goals: ${item}`));
|
|
1331
|
-
parts.push(...capsule.implementation_surfaces.map((item) => `implementation_surfaces: ${item}`));
|
|
1332
|
-
parts.push(...capsule.verification_commands.map((item) => `verification_commands: ${item}`));
|
|
1333
|
-
if (capsule.why_this_slice_first) parts.push(`why_this_slice_first: ${capsule.why_this_slice_first}`);
|
|
1334
1334
|
if (capsule.why_cook_now) parts.push(`why_cook_now: ${capsule.why_cook_now}`);
|
|
1335
1335
|
return parts.join("\n").trim();
|
|
1336
1336
|
}
|
|
@@ -1363,32 +1363,23 @@ function cookHandoffStartabilityFailures(
|
|
|
1363
1363
|
else if (!cookHandoffAcceptanceIsRepoChangeOriented(capsule)) {
|
|
1364
1364
|
failures.push("acceptance is not anchored to concrete repo changes or verification");
|
|
1365
1365
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
failures.push("first_slice_goal is planning-only instead of a repo-change sequencing hint");
|
|
1372
|
-
}
|
|
1366
|
+
const firstSliceGoal = deps.normalizeMissionAnchorText(capsule.first_slice_goal);
|
|
1367
|
+
if (!firstSliceGoal || deps.isWeakMissionAnchor(firstSliceGoal) || COOK_HANDOFF_NEGATIVE_MISSION_REGEX.test(firstSliceGoal)) {
|
|
1368
|
+
failures.push("first_slice_goal is not a bounded implementation slice");
|
|
1369
|
+
} else if (hasExplicitPlanningOnlyDeliverable([capsule.first_slice_goal]) || hasClearNoImplementationSignal([capsule.first_slice_goal])) {
|
|
1370
|
+
failures.push("first_slice_goal is planning-only instead of a repo-change slice");
|
|
1373
1371
|
}
|
|
1372
|
+
if (capsule.implementation_surfaces.length === 0) failures.push("implementation_surfaces is empty");
|
|
1373
|
+
if (capsule.verification_commands.length === 0) failures.push("verification_commands is empty");
|
|
1374
1374
|
return failures;
|
|
1375
1375
|
}
|
|
1376
1376
|
|
|
1377
|
-
function buildNonStartableCookHandoffMessage(
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
"/cook failed closed because the same-entry primary-agent startup-plan synthesis step returned a startup plan that is still not concrete enough to seed workflow planning yet.",
|
|
1384
|
-
"Clarify the mission, scope, acceptance, or verification intent in the main chat, then rerun /cook so the primary agent can synthesize a tighter startup plan.",
|
|
1385
|
-
`Blocking details: ${failures.join("; ")}.`,
|
|
1386
|
-
].join(" ")
|
|
1387
|
-
: [
|
|
1388
|
-
"/cook failed closed because a fresh explicit primary-agent startup plan exists, but it is not concrete enough to seed workflow planning yet.",
|
|
1389
|
-
"Tighten the startup plan in the main chat so it captures a concrete mission, repo-change-oriented acceptance, and truthful verification intent, then rerun /cook.",
|
|
1390
|
-
`Blocking details: ${failures.join("; ")}.`,
|
|
1391
|
-
].join(" ");
|
|
1377
|
+
function buildNonStartableCookHandoffMessage(failures: string[]): string {
|
|
1378
|
+
return [
|
|
1379
|
+
"/cook failed closed because a fresh explicit primary-agent handoff exists, but it is not concrete enough to start implementation workflow yet.",
|
|
1380
|
+
"Tighten the handoff in the main chat so it names a bounded first implementation slice, repo-change-oriented acceptance, implementation_surfaces, and verification_commands, then rerun /cook.",
|
|
1381
|
+
`Blocking details: ${failures.join("; ")}.`,
|
|
1382
|
+
].join(" ");
|
|
1392
1383
|
}
|
|
1393
1384
|
|
|
1394
1385
|
function isStartableCookHandoffCapsule(
|
|
@@ -1444,11 +1435,11 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1444
1435
|
evaluationProfile: capsule.evaluation_profile,
|
|
1445
1436
|
critique: [
|
|
1446
1437
|
...capsule.notes,
|
|
1447
|
-
|
|
1438
|
+
`First slice goal: ${capsule.first_slice_goal}`,
|
|
1448
1439
|
...(capsule.first_slice_non_goals.length > 0 ? [`First slice non-goals: ${capsule.first_slice_non_goals.join(" | ")}`] : []),
|
|
1449
1440
|
...(capsule.implementation_surfaces.length > 0 ? [`Implementation surfaces: ${capsule.implementation_surfaces.join(" | ")}`] : []),
|
|
1450
1441
|
...(capsule.verification_commands.length > 0 ? [`Verification commands: ${capsule.verification_commands.join(" | ")}`] : []),
|
|
1451
|
-
|
|
1442
|
+
`Why this slice first: ${capsule.why_this_slice_first}`,
|
|
1452
1443
|
...(capsule.why_cook_now ? [`Primary-agent /cook handoff rationale: ${capsule.why_cook_now}`] : []),
|
|
1453
1444
|
],
|
|
1454
1445
|
risks: capsule.risks,
|
|
@@ -1464,11 +1455,11 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1464
1455
|
...capsule.scope,
|
|
1465
1456
|
...constraints,
|
|
1466
1457
|
...capsule.acceptance,
|
|
1467
|
-
|
|
1458
|
+
capsule.first_slice_goal,
|
|
1468
1459
|
...capsule.first_slice_non_goals,
|
|
1469
1460
|
...capsule.implementation_surfaces,
|
|
1470
1461
|
...capsule.verification_commands,
|
|
1471
|
-
|
|
1462
|
+
capsule.why_this_slice_first,
|
|
1472
1463
|
],
|
|
1473
1464
|
),
|
|
1474
1465
|
goalText,
|
|
@@ -1479,32 +1470,6 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1479
1470
|
return finalizeContextProposal(proposal, projectName, deps);
|
|
1480
1471
|
}
|
|
1481
1472
|
|
|
1482
|
-
export function assessCookHandoffText(
|
|
1483
|
-
text: string,
|
|
1484
|
-
projectName: string,
|
|
1485
|
-
deps: ProposalParseDeps,
|
|
1486
|
-
options?: {
|
|
1487
|
-
messageId?: string;
|
|
1488
|
-
timestampMs?: number;
|
|
1489
|
-
context?: "explicit_preview" | "same_entry_synthesis";
|
|
1490
|
-
},
|
|
1491
|
-
): CookHandoffProposalAssessment {
|
|
1492
|
-
const capsules = parseCookHandoffCapsulesFromText(text, options?.messageId, options?.timestampMs, deps);
|
|
1493
|
-
for (let capsuleIndex = capsules.length - 1; capsuleIndex >= 0; capsuleIndex -= 1) {
|
|
1494
|
-
const capsule = capsules[capsuleIndex];
|
|
1495
|
-
const failures = cookHandoffStartabilityFailures(capsule, deps);
|
|
1496
|
-
if (failures.length > 0) {
|
|
1497
|
-
return {
|
|
1498
|
-
status: "fresh_but_not_startable",
|
|
1499
|
-
message: buildNonStartableCookHandoffMessage(failures, options?.context ?? "explicit_preview"),
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
const proposal = buildContextProposalFromCookHandoffCapsule(capsule, projectName, deps);
|
|
1503
|
-
if (proposal) return { status: "startable", proposal };
|
|
1504
|
-
}
|
|
1505
|
-
return { status: "none" };
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
1473
|
export function assessLatestCookHandoffProposal(
|
|
1509
1474
|
recentMessages: RecentSessionMessage[],
|
|
1510
1475
|
projectName: string,
|
|
@@ -1524,7 +1489,7 @@ export function assessLatestCookHandoffProposal(
|
|
|
1524
1489
|
if (failures.length > 0) {
|
|
1525
1490
|
return {
|
|
1526
1491
|
status: "fresh_but_not_startable",
|
|
1527
|
-
message: buildNonStartableCookHandoffMessage(failures
|
|
1492
|
+
message: buildNonStartableCookHandoffMessage(failures),
|
|
1528
1493
|
};
|
|
1529
1494
|
}
|
|
1530
1495
|
const proposal = buildContextProposalFromCookHandoffCapsule(capsule, projectName, deps);
|