@linimin/pi-letscook 0.1.68 → 0.1.69
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 +9 -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 +17 -158
- package/extensions/completion/proposal.ts +26 -61
- package/extensions/completion/role-runner.ts +10 -15
- 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/refocus-test.sh +459 -185
- package/scripts/release-check.sh +14 -15
- package/scripts/role-runner-contract-test.sh +2 -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),
|
|
@@ -529,15 +417,6 @@ type CompletionVerificationEvidenceSummary = {
|
|
|
529
417
|
summary: string;
|
|
530
418
|
};
|
|
531
419
|
|
|
532
|
-
type CompletionStartupPlanSummary = {
|
|
533
|
-
path: string;
|
|
534
|
-
status: string;
|
|
535
|
-
source?: string;
|
|
536
|
-
plannedSurfaces: string[];
|
|
537
|
-
verificationIntent: string[];
|
|
538
|
-
summary: string;
|
|
539
|
-
};
|
|
540
|
-
|
|
541
420
|
export function buildSystemReminder(args: {
|
|
542
421
|
missionAnchor?: string;
|
|
543
422
|
taskType?: string;
|
|
@@ -561,12 +440,11 @@ export function buildSystemReminder(args: {
|
|
|
561
440
|
implementationSurfacesLine?: string;
|
|
562
441
|
verificationCommandsLine?: string;
|
|
563
442
|
evidence: CompletionVerificationEvidenceSummary;
|
|
564
|
-
startupPlan: CompletionStartupPlanSummary;
|
|
565
443
|
evaluationRoleReminderText?: string;
|
|
566
444
|
}): string {
|
|
567
445
|
const lines = [
|
|
568
446
|
"Completion workflow detected.",
|
|
569
|
-
"Canonical truth lives in .agent/state.json, .agent/
|
|
447
|
+
"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
448
|
`Mission anchor: ${args.missionAnchor ?? "(unknown)"}`,
|
|
571
449
|
`Task type: ${args.taskType ?? "(missing)"}`,
|
|
572
450
|
`Evaluation profile: ${args.evaluationProfile ?? "(missing)"}`,
|
|
@@ -583,7 +461,6 @@ export function buildSystemReminder(args: {
|
|
|
583
461
|
"Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.",
|
|
584
462
|
"If canonical state is stale, invalid, ambiguous, or missing, route to completion-regrounder.",
|
|
585
463
|
"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
464
|
];
|
|
588
465
|
if (args.exactActiveContract) {
|
|
589
466
|
lines.push("Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.");
|
|
@@ -597,14 +474,6 @@ export function buildSystemReminder(args: {
|
|
|
597
474
|
else if (args.implementationSurfaces.length > 0) lines.push(`Active implementation surfaces: ${args.implementationSurfaces.join(", ")}`);
|
|
598
475
|
if (args.verificationCommandsLine) lines.push(args.verificationCommandsLine);
|
|
599
476
|
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
477
|
lines.push(`Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})`);
|
|
609
478
|
if (args.evidence.subjectType) lines.push(`Verification evidence subject: ${args.evidence.subjectType}`);
|
|
610
479
|
if (args.evidence.outcome) lines.push(`Verification evidence outcome: ${args.evidence.outcome}`);
|
|
@@ -633,7 +502,6 @@ export function buildResumeCapsule(args: {
|
|
|
633
502
|
activeSliceMatchesPlan: "yes" | "no" | "unknown";
|
|
634
503
|
activeSliceContractDrift: string;
|
|
635
504
|
implementerHandoffSnapshot: "present" | "missing_or_unclear";
|
|
636
|
-
startupPlan: CompletionStartupPlanSummary;
|
|
637
505
|
evidence: CompletionVerificationEvidenceSummary;
|
|
638
506
|
activeSlice: {
|
|
639
507
|
sliceId?: string;
|
|
@@ -676,14 +544,6 @@ export function buildResumeCapsule(args: {
|
|
|
676
544
|
`implementer_handoff_snapshot: ${args.implementerHandoffSnapshot}`,
|
|
677
545
|
`history_counts: reviewed=${args.history.reviewed}, audited=${args.history.audited}, accepted=${args.history.accepted}, reopened=${args.history.reopened}, judgments=${args.history.judgments}`,
|
|
678
546
|
"",
|
|
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
547
|
"verification_evidence:",
|
|
688
548
|
`- path: ${args.evidence.path}`,
|
|
689
549
|
`- status: ${args.evidence.status}`,
|
|
@@ -731,9 +591,8 @@ export function buildResumeCapsule(args: {
|
|
|
731
591
|
"- Treat this block as continuity support derived from canonical .agent state.",
|
|
732
592
|
"- 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
593
|
"- 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
594
|
"- 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/
|
|
595
|
+
"- 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
596
|
"- Invoke completion-regrounder before continuing when requires_reground is true or unknown.",
|
|
738
597
|
"- Invoke completion-regrounder before continuing when next_mandatory_role or next_mandatory_action is unknown or ambiguous.",
|
|
739
598
|
"- 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);
|
|
@@ -97,18 +97,14 @@ const STARTUP_ANALYST_ROLE = "cook-proposal-analyst";
|
|
|
97
97
|
const ANALYST_HEARTBEAT_MS = 5_000;
|
|
98
98
|
|
|
99
99
|
const PRIMARY_AGENT_HANDOFF_SYSTEM_PROMPT = [
|
|
100
|
-
"You are the primary agent preparing an explicit /cook
|
|
101
|
-
"Return either exactly one fenced ```cook_handoff JSON block or one brief plain sentence explaining why no concrete
|
|
102
|
-
"If you can prepare a
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"If a bounded first slice, likely implementation surfaces, or likely verification commands are already obvious, include first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, and why_this_slice_first as optional hints only. They are not required when the overall startup plan is already concrete enough to begin workflow planning.",
|
|
106
|
-
"Prefer the latest user-authored task context plus canonical workflow context over older assistant-authored previews or stale planning text.",
|
|
107
|
-
"Do not directly reuse an old preview capsule as-is; either synthesize a fresh startup plan from the current task context or return a brief plain sentence saying no concrete startup plan should replace canonical state yet.",
|
|
108
|
-
"If canonical workflow context already exists and the latest discussion does not clearly ask to replace the mission or start the next round, return a brief plain sentence instead of inventing a replacement startup plan.",
|
|
109
|
-
"Do not make /cook infer or rediscover the mission later; author the startup plan now from the primary-agent view of the task.",
|
|
100
|
+
"You are the primary agent preparing an explicit /cook handoff after the user already chose workflow mode.",
|
|
101
|
+
"Return either exactly one fenced ```cook_handoff JSON block or one brief plain sentence explaining why no concrete handoff can be prepared.",
|
|
102
|
+
"If you can prepare a handoff, the JSON must use kind cook_handoff, source primary_agent, and handoff_kind implementation_workflow_handoff.",
|
|
103
|
+
"When the user has clearly accepted a concrete assistant-proposed slice, carry that slice forward into the handoff instead of broadening or re-guessing the mission.",
|
|
104
|
+
"Do not make /cook infer or rediscover the mission from recent discussion later; author the handoff now from the primary-agent view of the task.",
|
|
110
105
|
"Do not emit markdown commentary before or after the capsule.",
|
|
111
|
-
"If the task is not concrete enough for workflow
|
|
106
|
+
"If the task is not concrete enough for implementation workflow, do not invent the slice.",
|
|
107
|
+
"A valid implementation-ready handoff must include mission, scope, constraints or non_goals, acceptance, risks, notes, first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, and why_this_slice_first.",
|
|
112
108
|
].join(" ");
|
|
113
109
|
const PRIMARY_AGENT_HANDOFF_ROLE = "cook-primary-agent-handoff";
|
|
114
110
|
|
|
@@ -346,8 +342,7 @@ function buildPrimaryAgentHandoffPrompt(projectName: string, recentEntries: Rece
|
|
|
346
342
|
lines.push(
|
|
347
343
|
"",
|
|
348
344
|
"Task:",
|
|
349
|
-
"The user explicitly invoked /cook. Prepare the primary-agent
|
|
350
|
-
"If the latest discussion does not justify a concrete new startup plan, return a brief plain sentence instead of speculative JSON.",
|
|
345
|
+
"The user explicitly invoked /cook. Prepare the primary-agent handoff that /cook should consume immediately for Start/Cancel confirmation.",
|
|
351
346
|
);
|
|
352
347
|
return lines.join("\n");
|
|
353
348
|
}
|
|
@@ -364,7 +359,7 @@ async function runPrimaryAgentHandoffSubprocess(params: GenerateCookHandoffWithA
|
|
|
364
359
|
const invocation = getPiInvocation(args);
|
|
365
360
|
const liveActivity = createLiveRoleActivity(PRIMARY_AGENT_HANDOFF_ROLE);
|
|
366
361
|
liveActivity.progress = "Preparing primary-agent /cook handoff";
|
|
367
|
-
liveActivity.currentAction = "Authoring explicit startup
|
|
362
|
+
liveActivity.currentAction = "Authoring explicit startup handoff from current task context";
|
|
368
363
|
liveActivity.assistantSummary = liveActivity.progress;
|
|
369
364
|
try {
|
|
370
365
|
const output = await new Promise<string | undefined>((resolve) => {
|
|
@@ -414,7 +409,7 @@ export async function generateCookHandoffWithAgent(params: GenerateCookHandoffWi
|
|
|
414
409
|
try {
|
|
415
410
|
return await runPrimaryAgentHandoffSubprocess(params);
|
|
416
411
|
} catch (error) {
|
|
417
|
-
console.warn("[completion] primary-agent
|
|
412
|
+
console.warn("[completion] primary-agent handoff generation failed", error);
|
|
418
413
|
return undefined;
|
|
419
414
|
}
|
|
420
415
|
}
|