@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.
@@ -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 startup-plan synthesis step from the current task context, show Start/Cancel confirmation in the same /cook entry, and only write the approved plan into .agent after Start.",
57
- "Do not expect /cook to infer or guess startup intent from recent discussion alone, and do not expect /cook to directly reuse an old preview capsule; /cook should always synthesize the startup plan fresh in the same entry from current task context.",
58
- "Only provide a preview startup plan or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior.",
59
- "Any preview capsule is advisory only until /cook reruns same-entry primary-agent startup-plan synthesis: do not present it as canonical .agent state, an active slice, or a persistent repo contract.",
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 captured for the approved startup plan."];
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: startupPlanSourceForProposal(args.proposal.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 plan.";
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 plan and let /cook write or refocus canonical workflow state.",
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 plan (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.",
332
- proposalHeading: "Startup plan",
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
- export function contextProposalAnalystProgressLines(
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
- "This step only prepares a proposal for confirmation.",
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/startup-plan.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json.",
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/startup-plan.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.",
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?: string;
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?: string;
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
- if (capsule.first_slice_goal) {
1367
- const firstSliceGoal = deps.normalizeMissionAnchorText(capsule.first_slice_goal);
1368
- if (!firstSliceGoal || deps.isWeakMissionAnchor(firstSliceGoal) || COOK_HANDOFF_NEGATIVE_MISSION_REGEX.test(firstSliceGoal)) {
1369
- failures.push("first_slice_goal is not a useful sequencing hint");
1370
- } else if (hasExplicitPlanningOnlyDeliverable([capsule.first_slice_goal]) || hasClearNoImplementationSignal([capsule.first_slice_goal])) {
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
- failures: string[],
1379
- context: "explicit_preview" | "same_entry_synthesis" = "explicit_preview",
1380
- ): string {
1381
- return context === "same_entry_synthesis"
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
- ...(capsule.first_slice_goal ? [`First slice goal: ${capsule.first_slice_goal}`] : []),
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
- ...(capsule.why_this_slice_first ? [`Why this slice first: ${capsule.why_this_slice_first}`] : []),
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
- ...(capsule.first_slice_goal ? [capsule.first_slice_goal] : []),
1458
+ capsule.first_slice_goal,
1468
1459
  ...capsule.first_slice_non_goals,
1469
1460
  ...capsule.implementation_surfaces,
1470
1461
  ...capsule.verification_commands,
1471
- ...(capsule.why_this_slice_first ? [capsule.why_this_slice_first] : []),
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, "explicit_preview"),
1492
+ message: buildNonStartableCookHandoffMessage(failures),
1528
1493
  };
1529
1494
  }
1530
1495
  const proposal = buildContextProposalFromCookHandoffCapsule(capsule, projectName, deps);