@linimin/pi-letscook 0.1.67 → 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.
@@ -9,8 +9,6 @@ import { Container, matchesKey, SelectList, Text } from "@mariozechner/pi-tui";
9
9
  import { Type } from "typebox";
10
10
  import {
11
11
  autoContinueWorkflowIfNeeded,
12
- completionContinuationFingerprint,
13
- markQueuedDriverPromptInFlight,
14
12
  registerCookCommand,
15
13
  } from "./driver";
16
14
  import {
@@ -18,9 +16,7 @@ import {
18
16
  collectRecentDiscussionEntries,
19
17
  collectRecentSessionMessages,
20
18
  assessLatestCookHandoffProposal,
21
- deriveCookContextProposalFromRecentDiscussion,
22
19
  finalizeContextProposalAnalysis,
23
- hasStructuredContextProposalSignal,
24
20
  isWeakMissionAnchor,
25
21
  missionAnchorsLikelyEquivalent,
26
22
  missionAnchorsStrictlyEquivalent,
@@ -49,8 +45,7 @@ import {
49
45
  maybeWriteContextProposalSnapshot,
50
46
  } from "./prompt-surfaces";
51
47
  import { toolCallBlockReason } from "./policy-guards";
52
- import { analyzeContextProposalWithAgent, runCompletionRole } from "./role-runner";
53
- import { generateCookHandoffWithAgent } from "./role-runner";
48
+ import { analyzeContextProposalWithAgent, generateCookHandoffWithAgent, runCompletionRole } from "./role-runner";
54
49
  import {
55
50
  applyLiveRoleEvent,
56
51
  buildInlineRunningLines,
@@ -107,7 +102,6 @@ const RUBRIC_EVALUATION_ROLES = ["completion-reviewer", "completion-auditor", "c
107
102
  type RubricEvaluationRole = (typeof RUBRIC_EVALUATION_ROLES)[number];
108
103
 
109
104
  const liveRoleActivityByRoot = new Map<string, LiveRoleActivity>();
110
- const activatedCompletionRoutingRoots = new Set<string>();
111
105
  const LIVE_ROLE_HEARTBEAT_MS = 5_000;
112
106
  const COOK_HANDOFF_BLOCK_REGEX = /```cook_handoff\s*[\s\S]*?```/giu;
113
107
 
@@ -212,44 +206,76 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
212
206
 
213
207
  const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
214
208
  const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
215
- "/cook failed closed because the startup-plan step could not prepare a concrete workflow startup plan from the current task context. Clarify the mission, scope, acceptance, or verification intent in the main chat, then rerun /cook.";
209
+ "/cook failed closed because the primary-agent handoff step could not prepare a concrete startup handoff from the current task context. Clarify the mission, first slice, or verification intent in the main chat, then rerun /cook.";
216
210
 
217
211
  function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
218
212
  return asString(snapshot?.state?.continuation_policy) === "done";
219
213
  }
220
214
 
221
- function activateCompletionRoutingForRoot(root: string | undefined): void {
222
- if (!root) return;
223
- activatedCompletionRoutingRoots.add(path.resolve(root));
215
+ function activateCompletionRoutingForRoot(_root: string | undefined): void {
216
+ // Workflow-entry legitimacy is derived from canonical .agent state rather than in-memory routing activation.
217
+ }
218
+
219
+ function hasActiveWorkflowEntry(snapshot: CompletionStateSnapshot | undefined): boolean {
220
+ if (!snapshot) return false;
221
+ if (isWorkflowDone(snapshot)) return false;
222
+ return asString(snapshot.state?.workflow_entry_status) === "active" || isRecord(snapshot.startupBrief) || isRecord(snapshot.state?.advisory_startup_brief);
224
223
  }
225
224
 
226
225
  function hasCompletionRoutingActivation(snapshot: CompletionStateSnapshot | undefined): boolean {
227
226
  if (!snapshot) return false;
228
227
  if (roleFromEnv()) return true;
229
- return activatedCompletionRoutingRoots.has(path.resolve(snapshot.files.root));
228
+ return false;
230
229
  }
231
230
 
232
231
  function latestUserOrCustomTurnText(ctx: { sessionManager?: any }): string | undefined {
233
- return collectRecentDiscussionEntries(ctx as { sessionManager: any }, { isRecord, asString, isStaleContextError }, 1)[0]?.text;
232
+ const messages = collectRecentSessionMessages(ctx as { sessionManager: any }, { isRecord, asString, asNumber, isStaleContextError }, 4);
233
+ return messages.find((entry) => entry.role === "user" || entry.role === "custom")?.text;
234
+ }
235
+
236
+ function isCookCommandTurn(ctx: { sessionManager?: any }): boolean {
237
+ const latest = latestUserOrCustomTurnText(ctx);
238
+ if (!latest) return false;
239
+ return /^\/cook\b/.test(latest.trim());
240
+ }
241
+
242
+ function extractWorkflowSessionIdFromPrompt(text: string): string | undefined {
243
+ const match = text.match(/^- workflow_session_id:\s*(.+)$/m);
244
+ return match?.[1]?.trim() || undefined;
234
245
  }
235
246
 
236
- function isCompletionDriverPromptTurn(ctx: { sessionManager?: any }): boolean {
247
+ function isCompletionDriverPromptTurn(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
237
248
  const latest = latestUserOrCustomTurnText(ctx);
238
249
  if (!latest) return false;
239
- if (!/^\/skill:completion-protocol\b/.test(latest)) return false;
240
- return /(?:Start or continue the completion workflow for this repo\.|Resume the completion workflow from canonical state\.)/.test(latest);
250
+ const isLegacySkillPrompt = /^\/skill:completion-protocol\b/.test(latest);
251
+ const isWorkflowDriverPrompt = /^COMPLETION WORKFLOW DRIVER\b/m.test(latest);
252
+ if (!isLegacySkillPrompt && !isWorkflowDriverPrompt) return false;
253
+ if (!/(?:Start or continue the completion workflow for this repo\.|Resume the completion workflow from canonical state\.)/.test(latest)) return false;
254
+ const canonicalSessionId = asString(snapshot?.state?.workflow_session_id);
255
+ const promptSessionId = extractWorkflowSessionIdFromPrompt(latest);
256
+ if (canonicalSessionId && promptSessionId && canonicalSessionId !== promptSessionId) return false;
257
+ return true;
258
+ }
259
+
260
+ function isCompletionWorkflowSessionTurn(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
261
+ if (!(hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot))) return false;
262
+ return isCompletionDriverPromptTurn(snapshot, ctx) || isCookCommandTurn(ctx);
241
263
  }
242
264
 
243
265
  function shouldInjectCompletionWorkflowContext(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
244
- return hasCompletionRoutingActivation(snapshot) && isCompletionDriverPromptTurn(ctx);
266
+ return isCompletionWorkflowSessionTurn(snapshot, ctx);
245
267
  }
246
268
 
247
- function shouldInjectCookHandoffBoundary(event: { prompt?: string }, ctx: { sessionManager?: any }): boolean {
269
+ function shouldInjectCookHandoffBoundary(
270
+ event: { prompt?: string },
271
+ ctx: { sessionManager?: any },
272
+ snapshot?: CompletionStateSnapshot,
273
+ ): boolean {
248
274
  if (roleFromEnv()) return false;
249
- if (isCompletionDriverPromptTurn(ctx)) return false;
275
+ if (isCompletionWorkflowSessionTurn(snapshot, ctx)) return false;
250
276
  const prompt = typeof event.prompt === "string" ? event.prompt.trim() : "";
251
277
  if (!prompt) return false;
252
- if (prompt.startsWith("/")) return false;
278
+ if (prompt.startsWith("/") || /^COMPLETION WORKFLOW DRIVER\b/m.test(prompt)) return false;
253
279
  return true;
254
280
  }
255
281
 
@@ -421,23 +447,10 @@ async function deriveCookContextProposal(
421
447
  `latest verified slice: ${asString(snapshot.state?.latest_verified_slice) ?? "(none)"}`,
422
448
  `active slice goal: ${asString(snapshot.active?.goal) ?? "(none)"}`,
423
449
  `active slice why_now: ${asString(snapshot.active?.why_now) ?? "(none)"}`,
424
- `approved startup plan summary: ${asString(snapshot.startupPlan?.goal_text) ?? "(none)"}`,
425
450
  `verification goal: ${asString(snapshot.verificationEvidence?.goal) ?? "(none)"}`,
426
451
  `verification summary: ${asString(snapshot.verificationEvidence?.summary) ?? "(none)"}`,
427
452
  ]
428
453
  : [];
429
- const workflowContext = snapshot
430
- ? {
431
- currentMissionAnchor: asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? asString(snapshot.active?.mission_anchor),
432
- latestCompletedSlice: asString(snapshot.state?.latest_completed_slice),
433
- latestVerifiedSlice: asString(snapshot.state?.latest_verified_slice),
434
- activeSliceGoal: asString(snapshot.active?.goal),
435
- activeSliceWhyNow: asString(snapshot.active?.why_now),
436
- verificationGoal: asString(snapshot.verificationEvidence?.goal),
437
- verificationSummary: asString(snapshot.verificationEvidence?.summary),
438
- continuationPolicy: asString(snapshot.state?.continuation_policy),
439
- }
440
- : undefined;
441
454
  const raw = await generateCookHandoffWithAgent({
442
455
  ctx,
443
456
  projectName,
@@ -450,49 +463,20 @@ async function deriveCookContextProposal(
450
463
  getCtxHasUI,
451
464
  getCtxUi,
452
465
  });
453
- if (raw) {
454
- const generated = assessLatestCookHandoffProposal([
455
- { role: "assistant", text: raw, messageId: "generated-primary-agent-handoff", timestampMs: Date.now(), isCommand: false },
456
- ], projectName, {
457
- asString,
458
- asStringArray,
459
- assessMissionAnchor,
460
- normalizeMissionAnchorText,
461
- isWeakMissionAnchor,
462
- missionAnchorsStrictlyEquivalent,
463
- stripCodeBlocks,
464
- });
465
- if (generated.status === "startable") return { proposal: generated.proposal };
466
- if (generated.status === "fresh_but_not_startable") return { blockedFailureMessage: generated.message };
467
- }
468
- const canFallbackToDiscussionProposal =
469
- recentEntries.length >= 2 || recentEntries.some((entry) => hasStructuredContextProposalSignal(entry.text, stripCodeBlocks));
470
- if (canFallbackToDiscussionProposal) {
471
- const fromDiscussion = await deriveCookContextProposalFromRecentDiscussion(projectName, recentEntries, {
472
- asString,
473
- asStringArray,
474
- assessMissionAnchor,
475
- normalizeMissionAnchorText,
476
- isWeakMissionAnchor,
477
- missionAnchorsStrictlyEquivalent,
478
- stripCodeBlocks,
479
- analyzeContextProposal: async (candidateEntries) =>
480
- await analyzeContextProposalWithAgent({
481
- ctx,
482
- projectName,
483
- recentEntries: candidateEntries,
484
- workflowContextLines,
485
- liveRoleActivityByRoot,
486
- completionStatusKey: COMPLETION_STATUS_KEY,
487
- safeUiCall,
488
- getCtxCwd,
489
- getCtxHasUI,
490
- getCtxUi,
491
- }),
492
- workflowContext,
493
- });
494
- if (fromDiscussion) return { proposal: fromDiscussion };
495
- }
466
+ if (!raw) return {};
467
+ const generated = assessLatestCookHandoffProposal([
468
+ { role: "assistant", text: raw, messageId: "generated-primary-agent-handoff", timestampMs: Date.now(), isCommand: false },
469
+ ], projectName, {
470
+ asString,
471
+ asStringArray,
472
+ assessMissionAnchor,
473
+ normalizeMissionAnchorText,
474
+ isWeakMissionAnchor,
475
+ missionAnchorsStrictlyEquivalent,
476
+ stripCodeBlocks,
477
+ });
478
+ if (generated.status === "startable") return { proposal: generated.proposal };
479
+ if (generated.status === "fresh_but_not_startable") return { blockedFailureMessage: generated.message };
496
480
  return {};
497
481
  }
498
482
 
@@ -530,19 +514,13 @@ async function confirmContextProposal(
530
514
  async function scaffoldCompletionFiles(
531
515
  root: string,
532
516
  missionAnchor: string,
533
- options?: {
534
- analysis?: ContextProposalAnalysis;
535
- continuationReason?: string;
536
- advisoryStartupBrief?: JsonRecord;
537
- approvedStartupPlan?: JsonRecord;
538
- },
517
+ options?: { analysis?: ContextProposalAnalysis; continuationReason?: string; advisoryStartupBrief?: JsonRecord },
539
518
  ) {
540
519
  const routing = finalizeContextProposalAnalysis(options?.analysis, [missionAnchor]);
541
520
  return await scaffoldCompletionFilesOnDisk(root, missionAnchor, {
542
521
  analysis: { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile },
543
522
  continuationReason: options?.continuationReason,
544
523
  advisoryStartupBrief: options?.advisoryStartupBrief,
545
- approvedStartupPlan: options?.approvedStartupPlan,
546
524
  });
547
525
  }
548
526
 
@@ -705,20 +683,6 @@ function verificationEvidenceContext(snapshot: CompletionStateSnapshot) {
705
683
  };
706
684
  }
707
685
 
708
- function startupPlanContext(snapshot: CompletionStateSnapshot) {
709
- const startupPlan = snapshot.startupPlan;
710
- return {
711
- path: path.relative(snapshot.files.root, snapshot.files.startupPlanPath) || ".agent/startup-plan.json",
712
- status: startupPlan ? "present" : "missing",
713
- source: asString(startupPlan?.source),
714
- plannedSurfaces: asStringArray(startupPlan?.planned_surfaces),
715
- verificationIntent: asStringArray(startupPlan?.verification_intent),
716
- summary:
717
- asString(startupPlan?.goal_text) ??
718
- (startupPlan ? "Approved startup plan is present but its goal_text is missing." : "Approved startup plan is missing."),
719
- };
720
- }
721
-
722
686
  function buildEvaluationRoleContextLines(snapshot: CompletionStateSnapshot, role: RubricEvaluationRole): string[] {
723
687
  return buildExtractedEvaluationRoleContextLines(snapshot, role, {
724
688
  asString,
@@ -749,7 +713,6 @@ function composeSystemReminder(snapshot: CompletionStateSnapshot, sliceHistory:
749
713
  const exactActiveContract = activeCarriesExactHandoff(snapshot.active);
750
714
  const activeContractDrift = activeSliceContractDriftSummary(snapshot);
751
715
  const evidence = verificationEvidenceContext(snapshot);
752
- const startupPlan = startupPlanContext(snapshot);
753
716
  const activePriorityLine = activePriority !== undefined ? `Active slice priority: ${activePriority}` : undefined;
754
717
  const activeWhyNowLine = activeWhyNow ? `Active slice why_now: ${activeWhyNow}` : undefined;
755
718
  const implementationSurfacesLine =
@@ -779,7 +742,6 @@ function composeSystemReminder(snapshot: CompletionStateSnapshot, sliceHistory:
779
742
  implementationSurfacesLine,
780
743
  verificationCommandsLine,
781
744
  evidence,
782
- startupPlan,
783
745
  evaluationRoleReminderText: isRubricEvaluationRole(nextRole) ? buildEvaluationRoleReminderText(snapshot, nextRole) : undefined,
784
746
  });
785
747
  }
@@ -799,19 +761,17 @@ function buildPostCompactionDriverInstructions(snapshot: CompletionStateSnapshot
799
761
  const exactActiveContract = activeCarriesExactHandoff(snapshot.active);
800
762
  const activeContractDrift = activeSliceContractDriftSummary(snapshot);
801
763
  const evidence = verificationEvidenceContext(snapshot);
802
- const startupPlan = startupPlanContext(snapshot);
803
764
  const lines = [
804
765
  "POST-COMPACTION RECOVERY MODE is active.",
805
766
  `Compaction marker time: ${markerAt}`,
806
767
  "Treat the previous conversation as lossy continuity support only.",
807
- "Before taking any substantive action, 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 from disk.",
768
+ "Before taking any substantive action, 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 from disk.",
808
769
  `Canonical task_type is currently: ${taskType}`,
809
770
  `Canonical evaluation_profile is currently: ${evaluationProfile}`,
810
771
  `Canonical next mandatory role is currently: ${nextRole}`,
811
772
  `Canonical next mandatory action is currently: ${nextAction}`,
812
773
  `Canonical continuation policy is currently: ${continuation}`,
813
774
  `Canonical active slice is currently: ${activeSliceId}`,
814
- `Canonical approved startup plan is currently: ${startupPlan.path} (${startupPlan.status})`,
815
775
  `Canonical verification evidence artifact is currently: ${evidence.path} (${evidence.status})`,
816
776
  "Do not trust pre-compaction memory over canonical files.",
817
777
  "If the canonical state is ambiguous, inconsistent, missing, or stale after re-reading it, your first mandatory action is to dispatch completion-regrounder rather than guessing.",
@@ -826,10 +786,6 @@ function buildPostCompactionDriverInstructions(snapshot: CompletionStateSnapshot
826
786
  if (activeWhyNow) lines.push(`Canonical active-slice why_now is currently: ${activeWhyNow}`);
827
787
  if (implementationSurfaces.length > 0) lines.push(`Canonical implementation surfaces are currently: ${implementationSurfaces.join(", ")}`);
828
788
  if (verificationCommands.length > 0) lines.push(`Canonical verification commands are currently: ${verificationCommands.join(" | ")}`);
829
- if (startupPlan.source) lines.push(`Canonical approved startup plan source is currently: ${startupPlan.source}`);
830
- if (startupPlan.plannedSurfaces.length > 0) lines.push(`Canonical approved startup plan surfaces are currently: ${startupPlan.plannedSurfaces.join(" | ")}`);
831
- if (startupPlan.verificationIntent.length > 0) lines.push(`Canonical approved startup plan verification intent is currently: ${startupPlan.verificationIntent.join(" | ")}`);
832
- lines.push(`Canonical approved startup plan summary is currently: ${startupPlan.summary}`);
833
789
  if (evidence.subjectType) lines.push(`Canonical verification evidence subject is currently: ${evidence.subjectType}`);
834
790
  if (evidence.outcome) lines.push(`Canonical verification evidence outcome is currently: ${evidence.outcome}`);
835
791
  if (evidence.recordedAt) lines.push(`Canonical verification evidence recorded_at is currently: ${evidence.recordedAt}`);
@@ -920,7 +876,6 @@ function composeResumeCapsule(snapshot: CompletionStateSnapshot, sliceHistory: J
920
876
  const verificationCommands = asStringArray(snapshot.active?.verification_commands);
921
877
  const remainingBefore = asStringArray(snapshot.active?.remaining_contract_ids_before);
922
878
  const evidence = verificationEvidenceContext(snapshot);
923
- const startupPlan = startupPlanContext(snapshot);
924
879
  const implementationSurfacesLine =
925
880
  implementationSurfaces.length > 0 ? `- implementation_surfaces: ${implementationSurfaces.join(" | ")}` : undefined;
926
881
  const verificationCommandsLine =
@@ -941,7 +896,6 @@ function composeResumeCapsule(snapshot: CompletionStateSnapshot, sliceHistory: J
941
896
  activeSliceMatchesPlan: activeSliceMatchesPlan(snapshot),
942
897
  activeSliceContractDrift: activeSliceContractDriftSummary(snapshot),
943
898
  implementerHandoffSnapshot: handoffSnapshotState(snapshot.active),
944
- startupPlan,
945
899
  evidence,
946
900
  activeSlice: {
947
901
  sliceId: asString(snapshot.active?.slice_id) ?? asString(snapshot.activeSlice?.slice_id),
@@ -972,6 +926,7 @@ function completionKickoff(
972
926
  evaluationProfile: string,
973
927
  intent: "auto" | "continue" | "refocus" = "auto",
974
928
  missionAnchor?: string,
929
+ workflowSessionId?: string,
975
930
  ): string {
976
931
  const intentBlock =
977
932
  intent === "continue" && missionAnchor
@@ -979,11 +934,13 @@ function completionKickoff(
979
934
  : intent === "refocus" && missionAnchor
980
935
  ? `Updated canonical mission anchor:\n${missionAnchor}\n\nWorkflow intent:\n- The user explicitly refocused the workflow before this kickoff.\n- Re-read canonical .agent/** state and continue from the refocused mission.\n\n`
981
936
  : "";
982
- return `/skill:completion-protocol Start or continue the completion workflow for this repo.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\nUser goal:\n${goal}\n\n${intentBlock}Driver instructions:\n- Canonical truth is in .agent/**. Re-read .agent/state.json, .agent/startup-plan.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting when they exist.\n- Treat .agent/startup-plan.json as the approved startup plan captured at /cook. completion-regrounder should use it as planning input, then derive canonical slices from current repo truth.\n- If tracked completion contract files are missing or onboarding is required, invoke completion_role with role completion-bootstrapper.\n- Otherwise follow the mandatory dispatch rules from completion-protocol.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Use completion_role for all completion-* role work. Do not directly implement tracked product changes yourself.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
937
+ const sessionBlock = workflowSessionId ? `Workflow session:\n- workflow_session_id: ${workflowSessionId}\n\n` : "";
938
+ return `COMPLETION WORKFLOW DRIVER\nStart or continue the completion workflow for this repo.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\n${sessionBlock}User goal:\n${goal}\n\n${intentBlock}Driver instructions:\n- Canonical truth is in .agent/**. Re-read .agent/state.json, .agent/startup-brief.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting when they exist.\n- If tracked completion contract files are missing or onboarding is required, invoke completion_role with role completion-bootstrapper.\n- Otherwise follow the mandatory dispatch rules from completion-protocol.\n- Treat .agent/startup-brief.json as canonical intake, not as the canonical slice plan.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Use completion_role for all completion-* role work. Do not directly implement tracked product changes yourself.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
983
939
  }
984
940
 
985
- function completionResumePrompt(taskType: string, evaluationProfile: string): string {
986
- return `/skill:completion-protocol Resume the completion workflow from canonical state.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\nResume instructions:\n- Re-read .agent/state.json, .agent/startup-plan.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting.\n- Treat .agent/startup-plan.json as the approved workflow plan captured at /cook, then let completion-regrounder reconcile or rebuild canonical slices from repo truth when needed.\n- If canonical state is missing, invalid, contradictory, stale, or ambiguous, route to completion-regrounder first.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Continue from next_mandatory_role and next_mandatory_action.\n- Use completion_role for all completion-* role work.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
941
+ function completionResumePrompt(taskType: string, evaluationProfile: string, workflowSessionId?: string): string {
942
+ const sessionBlock = workflowSessionId ? `Workflow session:\n- workflow_session_id: ${workflowSessionId}\n\n` : "";
943
+ return `COMPLETION WORKFLOW DRIVER\nResume the completion workflow from canonical state.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\n${sessionBlock}Resume instructions:\n- Re-read .agent/state.json, .agent/startup-brief.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting.\n- If canonical state is missing, invalid, contradictory, stale, or ambiguous, route to completion-regrounder first.\n- Treat .agent/startup-brief.json as canonical intake, not as the canonical slice plan.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Continue from next_mandatory_role and next_mandatory_action.\n- Use completion_role for all completion-* role work.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
987
944
  }
988
945
 
989
946
  export default function completionExtension(pi: ExtensionAPI) {
@@ -999,7 +956,7 @@ export default function completionExtension(pi: ExtensionAPI) {
999
956
  structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
1000
957
  mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
1001
958
  cookCommandSpec: {
1002
- description: "/cook workflow: capture the approved startup plan into .agent, let completion-regrounder split canonical slices, or resume the current workflow from canonical state",
959
+ description: "/cook workflow: start or replace workflow only from an explicit primary-agent handoff, or resume the current workflow from canonical state",
1003
960
  },
1004
961
  buildContextProposalContinuationReason,
1005
962
  completionKickoff,
@@ -1034,7 +991,7 @@ export default function completionExtension(pi: ExtensionAPI) {
1034
991
  await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });
1035
992
  if (shouldTestAutoContinueOnSessionStart()) {
1036
993
  const snapshot = await loadCompletionSnapshot(getCtxCwd(ctx));
1037
- if (hasCompletionRoutingActivation(snapshot) && isCompletionDriverPromptTurn(ctx)) {
994
+ if (isCompletionWorkflowSessionTurn(snapshot, ctx)) {
1038
995
  await autoContinueWorkflowIfNeeded(pi, ctx, driverDeps);
1039
996
  }
1040
997
  }
@@ -1050,19 +1007,13 @@ export default function completionExtension(pi: ExtensionAPI) {
1050
1007
  await fsp.rm(snapshot.files.compactionMarkerPath, { force: true });
1051
1008
  }
1052
1009
  await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });
1053
- if (hasCompletionRoutingActivation(snapshot) && isCompletionDriverPromptTurn(ctx)) {
1010
+ if (isCompletionWorkflowSessionTurn(snapshot, ctx)) {
1054
1011
  await autoContinueWorkflowIfNeeded(pi, ctx, driverDeps);
1055
1012
  }
1056
1013
  });
1057
1014
 
1058
1015
  pi.on("before_agent_start", async (event, ctx) => {
1059
1016
  const loaded = await loadCompletionDataForReminder(getCtxCwd(ctx));
1060
- const driverPromptTurn = isCompletionDriverPromptTurn(ctx);
1061
- if (loaded && driverPromptTurn) {
1062
- const rootKey = completionRootKey(loaded.snapshot, getCtxCwd(ctx));
1063
- const fingerprint = completionContinuationFingerprint(loaded.snapshot);
1064
- if (fingerprint) markQueuedDriverPromptInFlight(rootKey, fingerprint);
1065
- }
1066
1017
  const systemPrompt = getSystemPromptSafe(ctx);
1067
1018
  if (!systemPrompt) return;
1068
1019
  if (loaded && shouldInjectCompletionWorkflowContext(loaded.snapshot, ctx)) {
@@ -1087,7 +1038,7 @@ export default function completionExtension(pi: ExtensionAPI) {
1087
1038
  systemPrompt: `${systemPrompt}\n\n${additions.join("\n\n")}`,
1088
1039
  };
1089
1040
  }
1090
- if (!shouldInjectCookHandoffBoundary(event, ctx)) return;
1041
+ if (!shouldInjectCookHandoffBoundary(event, ctx, loaded?.snapshot)) return;
1091
1042
  const handoffReminder = buildCookHandoffBoundaryReminder();
1092
1043
  maybeWriteTestSnapshot(completionTestCookHandoffReminderPath(), handoffReminder);
1093
1044
  return {
@@ -1130,7 +1081,7 @@ export default function completionExtension(pi: ExtensionAPI) {
1130
1081
  const snapshot = await loadCompletionSnapshot(cwd);
1131
1082
  const completionActive = Boolean(snapshot) && asString(snapshot?.state?.continuation_policy) !== "done";
1132
1083
  const root = snapshot?.files.root ?? findRepoRoot(cwd) ?? cwd;
1133
- const completionRoleDispatchAllowed = Boolean(role) || (hasCompletionRoutingActivation(snapshot) && isCompletionDriverPromptTurn(ctx));
1084
+ const completionRoleDispatchAllowed = Boolean(role) || isCompletionWorkflowSessionTurn(snapshot, ctx);
1134
1085
  const reason = toolCallBlockReason({
1135
1086
  toolName: event.toolName,
1136
1087
  input: isRecord(event.input) ? event.input : undefined,
@@ -1151,7 +1102,7 @@ export default function completionExtension(pi: ExtensionAPI) {
1151
1102
  "Use completion_role when driving the completion workflow and a mandatory completion role must act next.",
1152
1103
  "Use completion_role only for completion-bootstrapper, completion-regrounder, completion-implementer, completion-reviewer, completion-auditor, or completion-stop-judge.",
1153
1104
  "Do not use completion_role from inside a completion role; only the workflow driver may dispatch roles.",
1154
- "Do not call completion_role from ordinary chat; it is reserved for explicit /cook workflow driver turns.",
1105
+ "Do not call completion_role from ordinary chat; it is reserved for active /cook workflow sessions.",
1155
1106
  ],
1156
1107
  parameters: Type.Object({
1157
1108
  role: StringEnum(ROLE_NAMES, { description: "The completion role to invoke." }),
@@ -72,7 +72,7 @@ export function toolCallBlockReason(args: {
72
72
  }
73
73
 
74
74
  if (toolName === "completion_role" && !completionRoleDispatchAllowed) {
75
- return "completion_role may only be used from an explicit /cook workflow driver turn.";
75
+ return "completion_role may only be used from an active /cook workflow session.";
76
76
  }
77
77
 
78
78
  if (toolName === "edit" || toolName === "write") {