@linimin/pi-letscook 0.1.62 → 0.1.63

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/README.md CHANGED
@@ -34,8 +34,8 @@ Then run `/reload` in Pi.
34
34
  2. Run `/reload` in Pi.
35
35
  3. In the main chat, either implement directly with the agent or refine the concrete repo change you want.
36
36
  4. When you want workflow mode, run `/cook`.
37
- 5. Review the synthesized startup brief and choose **Start** or **Cancel**.
38
- 6. Later, run `/cook` again to resume from canonical state or confirm a synthesized replacement or next-round startup brief.
37
+ 5. Review the startup brief and choose **Start** or **Cancel**.
38
+ 6. Later, run `/cook` again to resume from canonical state or confirm a primary-agent-authored replacement or next-round handoff.
39
39
 
40
40
  ```text
41
41
  /cook
@@ -46,20 +46,20 @@ Then run `/reload` in Pi.
46
46
  | If you want to... | Do this |
47
47
  |---|---|
48
48
  | Implement directly without workflow | Ask in ordinary chat and let the agent modify the repo directly |
49
- | Start a tracked workflow | Discuss the concrete repo change in the main chat, then run `/cook` when you want workflow to begin |
49
+ | Start a tracked workflow | Ask the primary agent in ordinary chat to prepare the explicit `/cook` handoff, then run `/cook` |
50
50
  | Continue the current workflow | Run `/cook` |
51
- | Refocus or start the next round | Discuss the new concrete repo change in the main chat, then run `/cook` when you want a new startup brief synthesized |
51
+ | Refocus or start the next round | Ask the primary agent in ordinary chat to prepare the fresh explicit `/cook` handoff for the new slice, then run `/cook` |
52
52
 
53
53
  ## What `/cook` expects
54
54
 
55
- - enough recent main-chat discussion for `/cook` to synthesize a concrete startup brief when you explicitly invoke it
56
- - a mission that is concrete enough to anchor bounded repo work rather than planning-only discussion
55
+ - a fresh explicit primary-agent `cook_handoff` capsule for any new-workflow, next-round, or replacement startup
56
+ - a mission and first slice concrete enough for the primary agent to author the startup handoff directly
57
57
  - acceptance and verification intent that can support a truthful first workflow round
58
58
  - README/CHANGELOG updates still count as concrete repo changes
59
- - assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts still do not count unless they can be turned into a concrete startup brief
60
- - optional explicit `cook_handoff` capsules may still be consumed as a compatibility intake path, but they are no longer required for new-workflow or next-round entry
59
+ - assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts still do not count unless the primary agent turns them into an explicit `cook_handoff` capsule
60
+ - `/cook` does not synthesize startup from recent discussion when handoff data is missing; the primary agent must provide the handoff
61
61
 
62
- If `/cook` cannot derive a concrete startup brief, it fails closed, leaves canonical `.agent/**` state unchanged, and tells you to refine the mission, first slice, or verification intent in the main chat before rerunning `/cook`.
62
+ If no fresh explicit primary-agent handoff exists, `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to ask the primary agent in the main chat to emit a fresh `cook_handoff` capsule before rerunning `/cook`.
63
63
 
64
64
  If a fresh explicit handoff exists but is still workflow-worthy rather than implementation-startable, `/cook` also fails closed instead of silently treating that capsule as planning support or canonical workflow state.
65
65
 
@@ -69,16 +69,16 @@ If you pass inline arguments to `/cook`, it also fails closed and tells you to m
69
69
 
70
70
  Only explicit `/cook` enters workflow mode. Ordinary prompts stay in the main chat and go straight to the primary agent.
71
71
 
72
- Ordinary chat can still directly implement repo changes. `/cook` is for the cases where you want workflow control rather than just implementation help.
72
+ Ordinary chat can still directly implement repo changes. `/cook` is for the cases where you want workflow control rather than just implementation help, and the primary agent should prepare the handoff before you run it.
73
73
 
74
- When you explicitly run `/cook`, it synthesizes a startup brief from recent discussion using primary-agent-style context, then asks you to **Start** or **Cancel** before rewriting canonical workflow state.
74
+ When you explicitly run `/cook`, it should consume the explicit primary-agent handoff you already prepared in ordinary chat, then ask you to **Start** or **Cancel** before rewriting canonical workflow state.
75
75
 
76
- Optional explicit `/cook` capsules may still be used as compatibility startup intake, but they are not required for new-workflow or next-round entry.
76
+ Explicit `/cook` capsules are the required startup intake for new-workflow, next-round, and replacement entry.
77
77
 
78
78
  Important behavior:
79
79
  - `/cook` is an optional workflow boundary and manual entry point
80
- - startup and next-round entry stay confirm-first, deriving startup from explicit user `/cook` entry plus recent discussion when needed
81
- - active workflows resume from canonical `.agent/**` state unless `/cook` synthesizes or receives a concrete replacement mission
80
+ - startup and next-round entry stay confirm-first, but they start from explicit primary-agent handoff data rather than recent-discussion guessing
81
+ - active workflows resume from canonical `.agent/**` state unless a fresh explicit primary-agent handoff proposes a concrete replacement mission
82
82
  - explicit slash commands other than `/cook` continue normally in the main chat
83
83
  - ordinary main-chat discussion may clarify, propose, or directly implement repo changes without entering workflow mode
84
84
 
@@ -94,13 +94,13 @@ I want to add login redirect handling and tests.
94
94
 
95
95
  ## What happens when you run `/cook`
96
96
 
97
- `/cook` first checks for a fresh explicit primary-agent handoff capsule as a compatibility intake path. If none is present, `/cook` synthesizes a startup brief from recent discussion using primary-agent-style context. New-workflow entry and done-workflow next-round entry still fail closed when that synthesis is too weak or planning-only. When a workflow is already active and no concrete replacement mission is available, `/cook` resumes from canonical `.agent/**` state. None of this prevents ordinary-chat implementation when you choose not to enter workflow mode.
97
+ `/cook` first checks for a fresh explicit primary-agent handoff capsule. New-workflow entry, done-workflow next-round entry, and active-workflow replacement should use that handoff instead of guessing from recent discussion. If no fresh explicit handoff exists, `/cook` fails closed for startup/refocus and resumes canonical state only when continuing the existing workflow. None of this prevents ordinary-chat implementation when you choose not to enter workflow mode.
98
98
 
99
99
  | Repo state | What you'll see |
100
100
  |---|---|
101
- | No workflow yet | `/cook` synthesizes a startup brief from recent discussion and asks you to choose **Start** or **Cancel**. A fresh explicit handoff capsule may still be used if present. Weak, unreliable, stale, planning-only, or non-startable intake still fails closed. |
102
- | Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If `/cook` finds a different concrete replacement mission from a compatibility capsule or deferred synthesis, it shows a chooser first and only rewrites canonical state after you confirm the replacement. Ambiguous intake stays conservative. |
103
- | Previous workflow is `done` | `/cook` can synthesize the next implementation round from recent discussion behind **Start** or **Cancel**. Weak or planning-only next-round intake still fails closed. |
101
+ | No workflow yet | `/cook` consumes a fresh explicit primary-agent handoff and asks you to choose **Start** or **Cancel**. Missing, stale, planning-only, or non-startable handoffs fail closed. |
102
+ | Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If a fresh explicit primary-agent handoff points to a different concrete replacement mission, `/cook` shows a chooser first and only rewrites canonical state after you confirm the replacement. Ambiguous or missing replacement handoff stays conservative. |
103
+ | Previous workflow is `done` | `/cook` can start the next implementation round only from a fresh explicit primary-agent handoff behind **Start** or **Cancel**. Missing, weak, or planning-only next-round handoffs fail closed. |
104
104
 
105
105
  ## Confirmation and fail-closed behavior
106
106
 
@@ -38,7 +38,7 @@ type ContextProposalAlternate = {
38
38
  analysis: ContextProposalAnalysis;
39
39
  goalText: string;
40
40
  basisPreview: string;
41
- source: "session" | "analyst" | "handoff_capsule" | "deferred_primary_agent_handoff";
41
+ source: "session" | "analyst" | "handoff_capsule";
42
42
  };
43
43
 
44
44
  type ContextProposal = ContextProposalAlternate & {
@@ -60,7 +60,7 @@ type CookContextProposalResult = {
60
60
  blockedFailureMessage?: string;
61
61
  };
62
62
 
63
- function buildCookStartupDerivationFailureMessage(deps: CompletionDriverDeps, prefix?: string): string {
63
+ function buildCookStartupBriefRequiredMessage(deps: CompletionDriverDeps, prefix?: string): string {
64
64
  const requirement = deps.structuredDiscussionFailureDetail;
65
65
  return prefix ? `${prefix} ${requirement}` : requirement;
66
66
  }
@@ -72,10 +72,9 @@ type ActiveWorkflowProposalAssessment = {
72
72
  blockedFailureMessage?: string;
73
73
  reason:
74
74
  | "matching_mission"
75
- | "no_replacement_proposal"
76
- | "explicit_handoff_replacement"
77
- | "deferred_replacement"
78
- | "replacement_not_startable";
75
+ | "missing_explicit_handoff"
76
+ | "fresh_explicit_handoff"
77
+ | "fresh_explicit_handoff_not_startable";
79
78
  };
80
79
 
81
80
  type ExistingWorkflowChooserOptions = {
@@ -321,23 +320,23 @@ async function assessActiveWorkflowProposalRouting(
321
320
  ): Promise<ActiveWorkflowProposalAssessment> {
322
321
  const currentMission = currentMissionAnchor(snapshot);
323
322
  const projectName = path.basename(snapshot.files.root);
324
- const derived = await deps.deriveCookContextProposal(ctx, projectName);
325
- if (derived.blockedFailureMessage) {
323
+ const explicitHandoff = await deps.deriveCookStartupProposal(ctx, projectName);
324
+ if (explicitHandoff.blockedFailureMessage) {
326
325
  const assessment: ActiveWorkflowProposalAssessment = {
327
326
  action: "blocked",
328
327
  currentMissionAnchor: currentMission,
329
- blockedFailureMessage: derived.blockedFailureMessage,
330
- reason: "replacement_not_startable",
328
+ blockedFailureMessage: explicitHandoff.blockedFailureMessage,
329
+ reason: "fresh_explicit_handoff_not_startable",
331
330
  };
332
331
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
333
332
  return assessment;
334
333
  }
335
- const proposal = derived.proposal;
334
+ const proposal = explicitHandoff.proposal;
336
335
  if (!proposal) {
337
336
  const assessment: ActiveWorkflowProposalAssessment = {
338
337
  action: "continue",
339
338
  currentMissionAnchor: currentMission,
340
- reason: "no_replacement_proposal",
339
+ reason: "missing_explicit_handoff",
341
340
  };
342
341
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
343
342
  return assessment;
@@ -356,7 +355,7 @@ async function assessActiveWorkflowProposalRouting(
356
355
  action: "refocus",
357
356
  currentMissionAnchor: currentMission,
358
357
  proposal,
359
- reason: proposal.source === "handoff_capsule" ? "explicit_handoff_replacement" : "deferred_replacement",
358
+ reason: "fresh_explicit_handoff",
360
359
  };
361
360
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
362
361
  return assessment;
@@ -543,7 +542,7 @@ export async function runCookEntry(
543
542
  }
544
543
  const proposal = derived.proposal;
545
544
  if (!proposal) {
546
- deps.emitCommandText(ctx, buildCookStartupDerivationFailureMessage(deps), "info");
545
+ deps.emitCommandText(ctx, buildCookStartupBriefRequiredMessage(deps), "info");
547
546
  return;
548
547
  }
549
548
  const decision = await deps.confirmContextProposal(ctx, proposal, {
@@ -588,7 +587,7 @@ export async function runCookEntry(
588
587
  }
589
588
  const proposal = derived.proposal;
590
589
  if (!proposal) {
591
- deps.emitCommandText(ctx, buildCookStartupDerivationFailureMessage(deps, "The previous completion workflow is already done."), "info");
590
+ deps.emitCommandText(ctx, buildCookStartupBriefRequiredMessage(deps, "The previous completion workflow is already done."), "info");
592
591
  return;
593
592
  }
594
593
  const decision = await deps.confirmContextProposal(ctx, proposal, {
@@ -610,13 +609,7 @@ export async function runCookEntry(
610
609
  buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis }),
611
610
  );
612
611
  snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
613
- deps.emitCommandText(
614
- ctx,
615
- proposal.source === "handoff_capsule"
616
- ? `Started a new completion workflow round from explicit primary-agent handoff: ${decision.missionAnchor}`
617
- : `Started a new completion workflow round from deferred primary-agent handoff: ${decision.missionAnchor}`,
618
- "info",
619
- );
612
+ deps.emitCommandText(ctx, `Started a new completion workflow round from explicit primary-agent handoff: ${decision.missionAnchor}`, "info");
620
613
  } else {
621
614
  const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, deps);
622
615
  if (assessment.action === "blocked") {
@@ -627,29 +620,20 @@ export async function runCookEntry(
627
620
  await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
628
621
  return;
629
622
  }
630
- const explicitReplacement = assessment.reason === "explicit_handoff_replacement";
631
- const deferredReplacement = assessment.reason === "deferred_replacement";
623
+ const explicitReplacement = assessment.reason === "fresh_explicit_handoff";
632
624
  const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, deps, {
633
625
  intro: explicitReplacement
634
626
  ? "A fresh explicit primary-agent handoff proposes replacing the current workflow. Choose how /cook should proceed:"
635
- : deferredReplacement
636
- ? "A deferred primary-agent handoff synthesized from your recent discussion proposes replacing the current workflow. Choose how /cook should proceed:"
637
- : "A replacement workflow is ready. Choose how /cook should proceed:",
627
+ : "A replacement workflow is ready. Choose how /cook should proceed:",
638
628
  proposedMissionLabel: explicitReplacement
639
629
  ? "Proposed mission from explicit primary-agent handoff"
640
- : deferredReplacement
641
- ? "Proposed mission from deferred primary-agent handoff"
642
- : "Proposed mission",
630
+ : "Proposed mission",
643
631
  refocusChoiceLabel: explicitReplacement
644
632
  ? "Start new workflow from explicit primary-agent handoff\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
645
- : deferredReplacement
646
- ? "Start new workflow from deferred primary-agent handoff\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
647
- : "Start new workflow\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.",
633
+ : "Start new workflow\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.",
648
634
  alternateChoiceLabel: explicitReplacement
649
635
  ? "Start alternate workflow from explicit primary-agent handoff\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
650
- : deferredReplacement
651
- ? "Start alternate workflow from deferred primary-agent handoff\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
652
- : undefined,
636
+ : undefined,
653
637
  comparison: "strict",
654
638
  });
655
639
  if (!decision) {
@@ -662,11 +646,9 @@ export async function runCookEntry(
662
646
  }
663
647
  const selectedProposal = decision.proposal;
664
648
  const proposalDecision = await deps.confirmContextProposal(ctx, selectedProposal, {
665
- title: explicitReplacement
649
+ title: assessment.reason === "fresh_explicit_handoff"
666
650
  ? "Start the replacement workflow from this explicit startup brief?"
667
- : deferredReplacement
668
- ? "Start the replacement workflow from this deferred startup brief?"
669
- : "Start the replacement workflow from this startup brief?",
651
+ : "Start the replacement workflow from this startup brief?",
670
652
  });
671
653
  if (!proposalDecision) {
672
654
  deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal", deps), "info");
@@ -686,11 +668,9 @@ export async function runCookEntry(
686
668
  snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
687
669
  deps.emitCommandText(
688
670
  ctx,
689
- explicitReplacement
671
+ assessment.reason === "fresh_explicit_handoff"
690
672
  ? `Refocused completion mission from explicit primary-agent handoff to: ${proposalDecision.missionAnchor}`
691
- : deferredReplacement
692
- ? `Refocused completion mission from deferred primary-agent handoff to: ${proposalDecision.missionAnchor}`
693
- : `Refocused completion mission to: ${proposalDecision.missionAnchor}`,
673
+ : `Refocused completion mission to: ${proposalDecision.missionAnchor}`,
694
674
  "info",
695
675
  );
696
676
  }
@@ -135,10 +135,9 @@ type ActiveWorkflowProposalAssessment = {
135
135
  blockedFailureMessage?: string;
136
136
  reason:
137
137
  | "matching_mission"
138
- | "no_replacement_proposal"
139
- | "explicit_handoff_replacement"
140
- | "deferred_replacement"
141
- | "replacement_not_startable";
138
+ | "missing_explicit_handoff"
139
+ | "fresh_explicit_handoff"
140
+ | "fresh_explicit_handoff_not_startable";
142
141
  };
143
142
 
144
143
  function completionTestWorkflowActionOverride(): "continue" | "refocus" | "cancel" | undefined {
@@ -212,7 +211,7 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
212
211
 
213
212
  const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
214
213
  const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
215
- "/cook failed closed because it could not derive a concrete startup brief from recent discussion. Clarify the mission, first slice, or verification intent in the main chat, then rerun /cook.";
214
+ "/cook failed closed because starting workflow now requires a fresh explicit primary-agent handoff. Ask the primary agent in the main chat to emit a fresh ```cook_handoff``` capsule, then rerun /cook.";
216
215
 
217
216
  function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
218
217
  return asString(snapshot?.state?.continuation_policy) === "done";
@@ -375,72 +374,6 @@ async function promptContextProposalConfirmationAction(
375
374
  });
376
375
  }
377
376
 
378
- function stripCookHandoffBlocks(text: string): string {
379
- return text.replace(COOK_HANDOFF_BLOCK_REGEX, " ").replace(/\s+/g, " ").trim();
380
- }
381
-
382
- async function deriveCookRecentDiscussionProposal(
383
- ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
384
- projectName: string,
385
- ): Promise<ContextProposal | undefined> {
386
- const recentMessages = collectRecentSessionMessages(ctx, { isRecord, asString, asNumber, isStaleContextError });
387
- const recentEntries = recentMessages
388
- .filter((entry) => (entry.role === "user" || entry.role === "custom") && !entry.isCommand)
389
- .filter((entry) => !/```cook_handoff\b/i.test(entry.text))
390
- .slice(0, 8)
391
- .map((entry) => ({ role: entry.role, text: stripCookHandoffBlocks(entry.text) }))
392
- .filter((entry) => entry.text.length > 0);
393
- const snapshot = await loadCompletionSnapshot(getCtxCwd(ctx));
394
- const workflowContextLines = snapshot
395
- ? [
396
- `current mission anchor: ${asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? asString(snapshot.active?.mission_anchor) ?? "(none)"}`,
397
- `continuation policy: ${asString(snapshot.state?.continuation_policy) ?? "(none)"}`,
398
- `latest completed slice: ${asString(snapshot.state?.latest_completed_slice) ?? "(none)"}`,
399
- `latest verified slice: ${asString(snapshot.state?.latest_verified_slice) ?? "(none)"}`,
400
- `active slice goal: ${asString(snapshot.active?.goal) ?? "(none)"}`,
401
- `active slice why_now: ${asString(snapshot.active?.why_now) ?? "(none)"}`,
402
- `verification goal: ${asString(snapshot.verificationEvidence?.goal) ?? "(none)"}`,
403
- `verification summary: ${asString(snapshot.verificationEvidence?.summary) ?? "(none)"}`,
404
- ]
405
- : [];
406
- const proposal = await deriveCookContextProposalFromRecentDiscussion(projectName, recentEntries, {
407
- asString,
408
- asStringArray,
409
- workflowContext: snapshot
410
- ? {
411
- currentMissionAnchor:
412
- asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? asString(snapshot.active?.mission_anchor),
413
- latestCompletedSlice: asString(snapshot.state?.latest_completed_slice),
414
- latestVerifiedSlice: asString(snapshot.state?.latest_verified_slice),
415
- activeSliceGoal: asString(snapshot.active?.goal),
416
- activeSliceWhyNow: asString(snapshot.active?.why_now),
417
- verificationGoal: asString(snapshot.verificationEvidence?.goal),
418
- verificationSummary: asString(snapshot.verificationEvidence?.summary),
419
- continuationPolicy: asString(snapshot.state?.continuation_policy),
420
- }
421
- : undefined,
422
- analyzeContextProposal: async (entries) =>
423
- await analyzeContextProposalWithAgent({
424
- ctx,
425
- projectName,
426
- recentEntries: entries,
427
- workflowContextLines,
428
- liveRoleActivityByRoot,
429
- completionStatusKey: COMPLETION_STATUS_KEY,
430
- safeUiCall,
431
- getCtxCwd,
432
- getCtxHasUI,
433
- getCtxUi,
434
- }),
435
- assessMissionAnchor,
436
- isWeakMissionAnchor,
437
- missionAnchorsStrictlyEquivalent,
438
- normalizeMissionAnchorText,
439
- stripCodeBlocks,
440
- });
441
- return retagContextProposalSource(proposal, "deferred_primary_agent_handoff");
442
- }
443
-
444
377
  async function deriveCookStartupProposal(
445
378
  ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
446
379
  projectName: string,
@@ -459,7 +392,7 @@ async function deriveCookStartupProposal(
459
392
  if (explicitHandoff.status === "fresh_but_not_startable") {
460
393
  return { blockedFailureMessage: explicitHandoff.message };
461
394
  }
462
- return { proposal: await deriveCookRecentDiscussionProposal(ctx, projectName) };
395
+ return {};
463
396
  }
464
397
 
465
398
  async function deriveCookContextProposal(
@@ -942,7 +875,7 @@ export default function completionExtension(pi: ExtensionAPI) {
942
875
  structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
943
876
  mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
944
877
  cookCommandSpec: {
945
- description: "/cook workflow: optionally enter tracked workflow mode, synthesize a startup brief from explicit /cook entry, resume the current workflow from canonical state, or confirm a replacement mission",
878
+ description: "/cook workflow: start or replace workflow only from an explicit primary-agent handoff, or resume the current workflow from canonical state",
946
879
  },
947
880
  buildContextProposalContinuationReason,
948
881
  completionKickoff,
@@ -32,8 +32,9 @@ export function buildCookHandoffBoundaryReminder(): string {
32
32
  "/cook is optional workflow mode for resumability, review, audit, canonical .agent state, or deliberate multi-session control; it is not required just to edit repo files in ordinary chat.",
33
33
  "If the user wants direct implementation now, stay in ordinary chat and help directly instead of blocking on /cook.",
34
34
  "If the user asks follow-up questions or wants to keep refining scope, continue helping naturally in ordinary chat.",
35
- "If the user explicitly runs /cook, /cook will synthesize a startup brief from recent discussion using primary-agent-style context, then show Start/Cancel confirmation before canonical workflow state is rewritten.",
36
- "Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior.",
35
+ "If the user explicitly asks to enter /cook workflow, generate one fresh ```cook_handoff``` capsule in ordinary chat from the primary-agent view of the task, then tell the user to run /cook.",
36
+ "Do not expect /cook to infer or guess startup intent from recent discussion alone; /cook should consume the explicit primary-agent handoff instead.",
37
+ "Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior or explicitly asks to enter /cook workflow.",
37
38
  "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.",
38
39
  "When you continue in ordinary chat, do not pretend /cook already started and do not silently rewrite discussion into canonical workflow state.",
39
40
  ].join(" ");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linimin/pi-letscook",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -147,14 +147,15 @@ mkdir -p "$ROOT"
147
147
  cd "$ROOT"
148
148
  git init -q
149
149
 
150
- # No workflow yet: bare /cook should synthesize a deferred startup brief from recent discussion,
151
- # even when no explicit ordinary-chat handoff capsule exists.
150
+ # No workflow yet: bare /cook should fail closed without a fresh explicit primary-agent handoff,
151
+ # even when recent discussion is fully structured.
152
152
  SESSION_ZERO="$TMPDIR/session-zero.jsonl"
153
153
  DISCUSSION_ZERO=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\n- Suppress the widget while a completion role is active.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Update README to match the shipped behavior.\n- Keep observability regression coverage truthful.'
154
154
  DISCUSSION_SNAPSHOT_ZERO="$TMPDIR/context-proposal-structured-fallback.json"
155
155
  write_session "$SESSION_ZERO" "$ROOT" "$DISCUSSION_ZERO"
156
156
 
157
157
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
158
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
158
159
  PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO" \
159
160
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
160
161
  pi --session "$SESSION_ZERO" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-structured-fallback.out" 2>"$TMPDIR/pi-completion-context-proposal-structured-fallback.err"
@@ -166,13 +167,9 @@ from pathlib import Path
166
167
 
167
168
  output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
168
169
  snapshot = Path(sys.argv[3])
169
- assert Path('.agent').exists(), 'bare /cook should scaffold canonical state from structured recent discussion'
170
- assert snapshot.exists(), 'bare /cook should emit a startup proposal snapshot when recent discussion is concrete enough'
171
- proposal = json.loads(snapshot.read_text())
172
- brief = json.loads(Path('.agent/state.json').read_text())['advisory_startup_brief']
173
- assert proposal['source'] == 'deferred_primary_agent_handoff', 'structured startup should snapshot the deferred primary-agent handoff source'
174
- assert brief['source'] == 'deferred_primary_agent_handoff', 'structured startup should record the deferred primary-agent handoff source in advisory intake'
175
- assert 'Initialized completion control plane' in output, 'structured startup should initialize canonical workflow state'
170
+ assert not Path('.agent').exists(), 'missing explicit handoff should fail closed without writing canonical state'
171
+ assert not snapshot.exists(), 'missing explicit handoff should not emit a startup proposal snapshot'
172
+ assert 'fresh explicit primary-agent handoff' in output, 'missing explicit handoff should explain the explicit-handoff-only startup contract'
176
173
  PY
177
174
 
178
175
  rm -rf .agent
@@ -220,7 +217,7 @@ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
220
217
  snapshot = Path(sys.argv[3])
221
218
  assert not Path('.agent').exists(), 'user-authored faux handoff without supporting discussion should still fail closed without writing canonical state'
222
219
  assert not snapshot.exists(), 'user-authored faux handoff should not emit a startup proposal snapshot'
223
- assert 'could not derive a concrete startup brief from recent discussion' in output, 'user-authored faux handoff should fall back to the deferred-synthesis fail-closed message'
220
+ assert 'fresh explicit primary-agent handoff' in output, 'user-authored faux handoff should still explain the explicit-handoff requirement'
224
221
  PY
225
222
 
226
223
  # No workflow yet: malformed or invalid assistant handoff capsules must also fail closed.
@@ -241,7 +238,7 @@ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
241
238
  snapshot = Path(sys.argv[3])
242
239
  assert not Path('.agent').exists(), 'invalid assistant handoff without supporting discussion should fail closed without writing canonical state'
243
240
  assert not snapshot.exists(), 'invalid assistant handoff should not emit a startup proposal snapshot'
244
- assert 'could not derive a concrete startup brief from recent discussion' in output, 'invalid assistant handoff should explain the deferred-synthesis fail-closed contract'
241
+ assert 'fresh explicit primary-agent handoff' in output, 'invalid assistant handoff should still explain the explicit-handoff requirement'
245
242
  PY
246
243
 
247
244
  # No workflow yet: a fresh explicit primary-agent handoff should still bootstrap canonical startup state.
@@ -389,7 +386,7 @@ assert routing['mode'] == 'bare', 'active bare /cook resume regression should sn
389
386
  assert 'explicitGoal' not in routing, 'active bare /cook resume routing should not expose removed explicit-goal shim fields'
390
387
  assert 'explicitGoalProvided' not in routing, 'active bare /cook resume routing should not expose removed explicit-goal shim fields'
391
388
  assert routing['action'] == 'continue', 'active bare /cook should resume when no fresh explicit handoff exists'
392
- assert routing['reason'] == 'no_replacement_proposal', 'active bare /cook should explain that resume happened because no replacement mission was derived'
389
+ assert routing['reason'] == 'missing_explicit_handoff', 'active bare /cook should explain that resume happened because no fresh explicit handoff existed'
393
390
  assert routing['currentMissionAnchor'] == mission, 'resume routing should preserve the current mission anchor'
394
391
  assert routing['proposedMissionAnchor'] is None, 'resume routing should not derive a replacement mission from recent discussion'
395
392
  assert 'Resume the completion workflow from canonical state.' in resume, 'active bare /cook resume should still use the canonical resume prompt'
@@ -434,7 +431,7 @@ active = json.loads(Path('.agent/active-slice.json').read_text())
434
431
 
435
432
  assert routing['mode'] == 'bare', 'discussion-driven refocus removal should snapshot bare routing mode'
436
433
  assert routing['action'] == 'continue', 'bare /cook should resume instead of deriving a replacement workflow from recent discussion'
437
- assert routing['reason'] == 'no_replacement_proposal', 'discussion-driven refocus removal should explain that no replacement mission was derived'
434
+ assert routing['reason'] == 'missing_explicit_handoff', 'discussion-driven refocus removal should explain that no fresh explicit handoff existed'
438
435
  assert routing['currentMissionAnchor'] == mission, 'discussion-driven refocus removal should preserve the current mission anchor'
439
436
  assert routing['proposedMissionAnchor'] is None, 'discussion-driven refocus removal should not preserve a replacement mission from recent discussion'
440
437
  assert 'Resume the completion workflow from canonical state.' in resume, 'discussion-driven refocus removal should still queue the canonical resume prompt'
@@ -508,7 +505,7 @@ active = json.loads(Path('.agent/active-slice.json').read_text())
508
505
 
509
506
  assert routing['mode'] == 'bare', 'summary-only active bare /cook regression should snapshot bare routing mode'
510
507
  assert routing['action'] == 'continue', 'summary-only active bare /cook should resume rather than derive replacement startup'
511
- assert routing['reason'] == 'no_replacement_proposal', 'summary-only active bare /cook should explain that no replacement mission was derived'
508
+ assert routing['reason'] == 'missing_explicit_handoff', 'summary-only active bare /cook should explain that no fresh explicit handoff existed'
512
509
  assert routing['currentMissionAnchor'] == mission, 'summary-only active bare /cook should preserve the current mission anchor'
513
510
  assert routing['proposedMissionAnchor'] is None, 'summary-only active bare /cook should not derive a replacement mission from summary artifacts alone'
514
511
  assert 'Resume the completion workflow from canonical state.' in resume, 'summary-only active bare /cook should still resume the canonical workflow'
@@ -613,7 +610,7 @@ after = {
613
610
 
614
611
  assert routing['mode'] == 'bare', 'fresh non-startable explicit handoff should snapshot bare routing mode'
615
612
  assert routing['action'] == 'blocked', 'fresh non-startable explicit handoff should fail closed for active bare /cook'
616
- assert routing['reason'] == 'replacement_not_startable', 'fresh non-startable explicit handoff should keep the dedicated replacement fail-closed reason'
613
+ assert routing['reason'] == 'fresh_explicit_handoff_not_startable', 'fresh non-startable explicit handoff should keep the dedicated explicit-handoff fail-closed reason'
617
614
  assert 'fresh explicit primary-agent handoff exists' in routing['blockedFailureMessage'], 'fresh non-startable explicit handoff should surface the dedicated fail-closed message'
618
615
  assert 'acceptance is not anchored to concrete repo changes or verification' in routing['blockedFailureMessage'], 'fresh non-startable explicit handoff should explain why the capsule is not startable'
619
616
  assert not resume_path.exists(), 'fresh non-startable explicit handoff should not queue a resume prompt'
@@ -708,14 +705,15 @@ assert not snapshot.exists(), 'verification-evidence overlap suppression should
708
705
  assert '/cook failed closed' in output, 'verification-evidence overlap suppression should fail closed when the latest discussion only repeats verified work'
709
706
  PY
710
707
 
711
- # Completed workflow: bare /cook should synthesize the next round from discussion-only startup too,
712
- # even when no explicit ordinary-chat handoff capsule exists.
708
+ # Completed workflow: bare /cook should fail closed for next-round discussion-only startup too,
709
+ # even when the discussion is well structured.
713
710
  SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
714
711
  DISCUSSION_TWO_NORMALIZED=$'Mission: 開始實作這個方案\nScope:\n- Normalize bare /cook planning phrasing for the next workflow round.\n- Reset canonical state for the new implementation mission.\nConstraints:\n- Do not resume the completed workflow when the new round is clearly different.\nAcceptance:\n- Start a new round with the normalized mission anchor.'
715
712
  DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
716
713
  write_session "$SESSION_TWO_NORMALIZED" "$ROOT" "$DISCUSSION_TWO_NORMALIZED"
717
714
 
718
715
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
716
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
719
717
  PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" \
720
718
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
721
719
  pi --session "$SESSION_TWO_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-normalized.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-normalized.err"
@@ -730,12 +728,10 @@ snapshot = Path(sys.argv[3])
730
728
  previous = sys.argv[4]
731
729
  state = json.loads(Path('.agent/state.json').read_text())
732
730
 
733
- assert snapshot.exists(), 'done-workflow discussion-only startup should emit a proposal snapshot from deferred synthesis'
734
- proposal = json.loads(snapshot.read_text())
735
- assert state['mission_anchor'] != previous, 'done-workflow discussion-only startup should advance to the new mission anchor'
736
- assert state['continuation_policy'] == 'continue', 'done-workflow discussion-only startup should reopen workflow state'
737
- assert proposal['source'] == 'deferred_primary_agent_handoff', 'done-workflow discussion-only startup should snapshot the deferred handoff source'
738
- assert 'Started a new completion workflow round from deferred primary-agent handoff' in output, 'done-workflow discussion-only startup should report deferred next-round startup'
731
+ assert not snapshot.exists(), 'done-workflow discussion-only startup should not emit a proposal snapshot without a fresh explicit handoff'
732
+ assert state['mission_anchor'] == previous, 'done-workflow discussion-only startup should keep the completed mission anchor unchanged'
733
+ assert state['continuation_policy'] == 'done', 'done-workflow discussion-only startup should keep the workflow closed'
734
+ assert 'fresh explicit primary-agent handoff' in output, 'done-workflow discussion-only startup should explain the explicit-handoff-only entry contract'
739
735
  PY
740
736
 
741
737
  # Completed workflow: a fresh explicit primary-agent handoff should still start the next round.
@@ -954,8 +950,8 @@ after = {path.name: path.read_text() for path in tracked}
954
950
  assert before == after, 'done /cook inline-args rejection should leave canonical files unchanged'
955
951
  PY
956
952
 
957
- # Completed workflow again: model-assisted discussion analysis alone should be able to
958
- # synthesize the next round from explicit /cook entry.
953
+ # Completed workflow again: model-assisted discussion analysis alone should still fail closed
954
+ # without a fresh explicit primary-agent handoff.
959
955
  mark_done
960
956
 
961
957
  SESSION_FIVE="$TMPDIR/session-five.jsonl"
@@ -979,9 +975,9 @@ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
979
975
  snapshot = Path(sys.argv[3])
980
976
  state = json.loads(Path('.agent/state.json').read_text())
981
977
 
982
- assert snapshot.exists(), 'done-workflow analyst-only restart should emit a startup proposal snapshot'
983
- assert state['continuation_policy'] == 'continue', 'done-workflow analyst-only restart should reopen the workflow'
984
- assert 'deferred primary-agent handoff' in output, 'done-workflow analyst-only restart should report deferred startup'
978
+ assert not snapshot.exists(), 'done-workflow analyst-only restart should not emit a startup proposal snapshot'
979
+ assert state['continuation_policy'] == 'done', 'done-workflow analyst-only restart should keep the workflow closed'
980
+ assert 'fresh explicit primary-agent handoff' in output, 'done-workflow analyst-only restart should explain the explicit-handoff-only startup contract'
985
981
  PY
986
982
 
987
983
  # Custom confirmation UI: start should render proposal content separately from approval-only Start/Cancel actions.
@@ -1527,7 +1523,7 @@ output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
1527
1523
 
1528
1524
  assert not snapshot.exists(), 'stale handoff should not emit a startup proposal snapshot'
1529
1525
  assert not Path('.agent').exists(), 'stale handoff should fail closed without writing canonical state'
1530
- assert 'could not derive a concrete startup brief from recent discussion' in output, 'stale handoff should explain that deferred startup synthesis failed closed'
1526
+ assert 'fresh explicit primary-agent handoff' in output, 'stale handoff should explain that a fresh valid explicit handoff is required'
1531
1527
  PY
1532
1528
 
1533
1529
  # Negative handoff rationale: a non-startable capsule must not become the startup mission.
@@ -313,7 +313,7 @@ assert routing['mode'] == 'bare', 'supported refocus should use bare active-work
313
313
  assert 'explicitGoal' not in routing, 'supported bare refocus should not expose removed explicit-goal shim fields'
314
314
  assert 'explicitGoalProvided' not in routing, 'supported bare refocus should not expose removed explicit-goal shim fields'
315
315
  assert routing['action'] == 'refocus', 'supported bare /cook should classify as refocus when a fresh explicit handoff proposes a different mission'
316
- assert routing['reason'] == 'explicit_handoff_replacement', 'supported bare /cook should record the explicit-handoff replacement reason'
316
+ assert routing['reason'] == 'fresh_explicit_handoff', 'supported bare /cook should record the explicit-handoff replacement reason'
317
317
  assert routing['proposedMissionAnchor'] == new_anchor, 'explicit handoff routing snapshot should expose the replacement mission anchor'
318
318
  assert routing['proposalSource'] == 'handoff_capsule', 'explicit handoff routing snapshot should preserve the handoff source'
319
319
  PY
@@ -411,7 +411,7 @@ assert routing['mode'] == 'bare', 'bare /cook should snapshot bare active-workfl
411
411
  assert 'explicitGoal' not in routing, 'bare chooser routing should not expose removed explicit-goal shim fields'
412
412
  assert 'explicitGoalProvided' not in routing, 'bare chooser routing should not expose removed explicit-goal shim fields'
413
413
  assert routing['action'] == 'refocus', 'fresh explicit replacement handoff should classify active bare /cook as refocus'
414
- assert routing['reason'] == 'explicit_handoff_replacement', 'fresh explicit replacement handoff should record the explicit-handoff reason'
414
+ assert routing['reason'] == 'fresh_explicit_handoff', 'fresh explicit replacement handoff should record the explicit-handoff reason'
415
415
  assert routing['currentMissionAnchor'] == updated_mission, 'explicit-handoff routing should keep the current mission anchor until the user approves replacement'
416
416
  assert routing['proposedMissionAnchor'] == replacement_mission, 'explicit-handoff routing should expose the proposed replacement mission'
417
417
  assert routing['proposalSource'] == 'handoff_capsule', 'explicit-handoff routing should preserve the handoff source'
@@ -454,7 +454,7 @@ assert state['mission_anchor'] == updated_mission, 'final Start/Cancel cancel sh
454
454
  assert plan['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep plan.json unchanged'
455
455
  assert active['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep active-slice.json unchanged'
456
456
  assert routing['action'] == 'refocus', 'final Start/Cancel cancel should still come from an explicit-handoff refocus classification'
457
- assert routing['reason'] == 'explicit_handoff_replacement', 'final Start/Cancel cancel should preserve the explicit-handoff reason'
457
+ assert routing['reason'] == 'fresh_explicit_handoff', 'final Start/Cancel cancel should preserve the explicit-handoff reason'
458
458
  assert routing['currentMissionAnchor'] == updated_mission, 'final Start/Cancel cancel should keep the current mission anchor until the user approves replacement'
459
459
  assert proposal['mission'] == replacement_mission, 'final Start/Cancel cancel should still prepare the replacement proposal before rewriting state'
460
460
  assert proposal['source'] == 'handoff_capsule', 'final Start/Cancel cancel should preserve the explicit-handoff proposal source'
@@ -495,7 +495,7 @@ assert routing['mode'] == 'bare', 'accepted bare refocus should keep bare routin
495
495
  assert 'explicitGoal' not in routing, 'accepted bare refocus should not expose removed explicit-goal shim fields'
496
496
  assert 'explicitGoalProvided' not in routing, 'accepted bare refocus should not expose removed explicit-goal shim fields'
497
497
  assert routing['action'] == 'refocus', 'accepted bare refocus should keep the explicit-handoff refocus classification'
498
- assert routing['reason'] == 'explicit_handoff_replacement', 'accepted bare refocus should keep the explicit-handoff reason'
498
+ assert routing['reason'] == 'fresh_explicit_handoff', 'accepted bare refocus should keep the explicit-handoff reason'
499
499
  assert routing['currentMissionAnchor'] == 'Remove completion status line, keep widget.', 'accepted bare refocus should expose the original mission until Start is accepted'
500
500
  assert routing['proposalSource'] == 'handoff_capsule', 'accepted bare refocus should preserve the explicit-handoff source'
501
501
  assert new_anchor in mission_text, '.agent/mission.md did not update to the bare refocus mission anchor'
@@ -9,46 +9,43 @@ echo "[release-check] running control-plane validation, tracked .agent contract
9
9
  bash .agent/verify_completion_control_plane.sh
10
10
  git ls-files --error-unmatch .agent/README.md .agent/mission.md .agent/profile.json .agent/verify_completion_stop.sh .agent/verify_completion_control_plane.sh >/dev/null
11
11
 
12
- echo "[release-check] verifying public /cook parity and optional-workflow docs/help"
12
+ echo "[release-check] verifying public /cook parity and explicit-handoff docs/help"
13
13
  python3 - <<'PY'
14
14
  from pathlib import Path
15
15
 
16
16
  checks = {
17
17
  "README.md": [
18
18
  "You can still implement directly in ordinary chat when you do not need workflow state.",
19
- "Only explicit `/cook` enters workflow mode. Ordinary prompts stay in the main chat and go straight to the primary agent.",
20
- "Ordinary chat can still directly implement repo changes. `/cook` is for the cases where you want workflow control rather than just implementation help.",
21
- "- `/cook` is an optional workflow boundary and manual entry point",
22
- "- ordinary main-chat discussion may clarify, propose, or directly implement repo changes without entering workflow mode",
23
- "None of this prevents ordinary-chat implementation when you choose not to enter workflow mode.",
19
+ "When you explicitly run `/cook`, it should consume the explicit primary-agent handoff you already prepared in ordinary chat, then ask you to **Start** or **Cancel** before rewriting canonical workflow state.",
20
+ "Explicit `/cook` capsules are the required startup intake for new-workflow, next-round, and replacement entry.",
21
+ "`/cook` first checks for a fresh explicit primary-agent handoff capsule.",
22
+ "New-workflow entry, done-workflow next-round entry, and active-workflow replacement should use that handoff instead of guessing from recent discussion.",
24
23
  ],
25
24
  "CHANGELOG.md": [
26
- "made ordinary chat implementation-first again so the primary agent may directly edit repo files without requiring `/cook` when workflow state is unnecessary",
27
- "repositioned `/cook` as optional workflow mode for confirm-first startup, resumability, review/audit flow, and canonical `.agent/**` state rather than as a mandatory implementation boundary",
28
- "updated ordinary-chat boundary docs, reminders, and release-parity checks so they no longer tell the agent to block repo edits pending explicit `/cook`",
25
+ "made `/cook` stop inferring startup handoffs from recent discussion so workflow startup and replacement now require fresh explicit primary-agent `cook_handoff` data",
26
+ "clarified that when a user explicitly chooses `/cook`, the primary agent must author the handoff in ordinary chat and `/cook` must consume that handoff instead of guessing",
29
27
  ],
30
28
  "extensions/completion/prompt-surfaces.ts": [
31
- '"Ordinary chat may clarify requirements, discuss tradeoffs, refine scope, and directly implement requested repo changes, including multi-file work, when that is the most helpful response."',
32
- '"/cook is optional workflow mode for resumability, review, audit, canonical .agent state, or deliberate multi-session control; it is not required just to edit repo files in ordinary chat."',
33
- '"If the user wants direct implementation now, stay in ordinary chat and help directly instead of blocking on /cook."',
29
+ '"If the user explicitly asks to enter /cook workflow, generate one fresh ```cook_handoff``` capsule in ordinary chat from the primary-agent view of the task, then tell the user to run /cook."',
30
+ '"Do not expect /cook to infer or guess startup intent from recent discussion alone; /cook should consume the explicit primary-agent handoff instead."',
34
31
  ],
35
32
  "extensions/completion/index.ts": [
36
- '"/cook failed closed because it could not derive a concrete startup brief from recent discussion. Clarify the mission, first slice, or verification intent in the main chat, then rerun /cook."',
37
- 'description: "/cook workflow: optionally enter tracked workflow mode, synthesize a startup brief from explicit /cook entry, resume the current workflow from canonical state, or confirm a replacement mission"',
33
+ '"/cook failed closed because starting workflow now requires a fresh explicit primary-agent handoff. Ask the primary agent in the main chat to emit a fresh ```cook_handoff``` capsule, then rerun /cook."',
34
+ 'description: "/cook workflow: start or replace workflow only from an explicit primary-agent handoff, or resume the current workflow from canonical state"',
38
35
  ],
39
36
  }
40
37
 
41
38
  forbidden = {
42
39
  "README.md": [
43
- "mature long-running implementation still must not start before explicit `/cook`",
44
- "Ordinary chat remains advisory until you explicitly run `/cook`.",
40
+ "synthesizes a startup brief from recent discussion using primary-agent-style context",
41
+ "derive startup from explicit user `/cook` entry plus recent discussion when needed",
45
42
  ],
46
43
  "extensions/completion/prompt-surfaces.ts": [
47
- '"Before that explicit /cook entry, do not begin long-running product implementation in ordinary chat, do not edit tracked product files for that workflow-level task, and do not act as though /cook had already been invoked."',
44
+ '"If the user explicitly runs /cook, /cook will synthesize a startup brief from recent discussion using primary-agent-style context, then show Start/Cancel confirmation before canonical workflow state is rewritten."',
48
45
  ],
49
- "skills/cook-handoff-boundary/SKILL.md": [
50
- "Before that explicit `/cook` entry, the primary agent must stop short of long-running implementation for workflow-level tasks.",
51
- "not edit tracked product files for that workflow-level task in ordinary chat",
46
+ "extensions/completion/index.ts": [
47
+ 'description: "/cook workflow: optionally enter tracked workflow mode, synthesize a startup brief from explicit /cook entry, resume the current workflow from canonical state, or confirm a replacement mission"',
48
+ '"/cook failed closed because it could not derive a concrete startup brief from recent discussion. Clarify the mission, first slice, or verification intent in the main chat, then rerun /cook."',
52
49
  ],
53
50
  }
54
51
 
@@ -270,8 +270,8 @@ assert 'directly implement requested repo changes, including multi-file work' in
270
270
  assert 'Do not proactively tell the user to run /cook' in handoff_text, 'ordinary handoff reminder should keep ordinary chat neutral until explicit /cook entry'
271
271
  assert '/cook is optional workflow mode' in handoff_text, 'ordinary handoff reminder should position /cook as optional workflow mode'
272
272
  assert 'If the user wants direct implementation now, stay in ordinary chat and help directly instead of blocking on /cook.' in handoff_text, 'ordinary handoff reminder should avoid blocking implementation on /cook'
273
- assert 'Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior.' in handoff_text, 'ordinary handoff reminder should restrict preview capsules to explicit preview requests'
274
- assert 'startup brief from recent discussion using primary-agent-style context' in handoff_text, 'ordinary handoff reminder should describe deferred startup synthesis'
273
+ assert 'generate one fresh ```cook_handoff``` capsule in ordinary chat from the primary-agent view of the task' in handoff_text, 'ordinary handoff reminder should require primary-agent authored handoff when the user explicitly chooses /cook'
274
+ assert 'Do not expect /cook to infer or guess startup intent from recent discussion alone' in handoff_text, 'ordinary handoff reminder should forbid /cook-side guessing'
275
275
  assert 'do not silently rewrite discussion into canonical workflow state' in handoff_text, 'ordinary handoff reminder should preserve non-canonical ordinary-chat behavior'
276
276
  assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
277
277
  assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
@@ -301,7 +301,7 @@ assert f'- task_type: {expected_task_type}' in resume, 'resume prompt missing ca
301
301
  assert f'- evaluation_profile: {expected_eval_profile}' in resume, 'resume prompt missing canonical evaluation_profile'
302
302
  assert routing['mode'] == 'bare', 'active bare /cook should snapshot bare routing mode'
303
303
  assert routing['action'] == 'continue', 'no-discussion active bare /cook should resume from canonical state without a concrete replacement mission'
304
- assert routing['reason'] == 'no_replacement_proposal', 'no-discussion active bare /cook should explain that resume happened because no replacement mission was derived'
304
+ assert routing['reason'] == 'missing_explicit_handoff', 'no-discussion active bare /cook should explain that resume happened because no fresh explicit handoff existed'
305
305
  assert routing['currentMissionAnchor'] == state['mission_anchor'], 'resume routing snapshot should keep the current mission anchor'
306
306
  assert routing['proposedMissionAnchor'] is None, 'no-discussion active bare /cook should not propose a replacement mission'
307
307
  assert not chooser_path.exists(), 'active bare /cook resume should not open the chooser without a fresh explicit handoff'
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: cook-handoff-boundary
3
- description: Ordinary-chat contract for treating `/cook` as an optional workflow mode while still allowing direct repo implementation in main chat when workflow state is unnecessary.
3
+ description: Ordinary-chat contract for treating `/cook` as an optional workflow mode while still requiring primary-agent-authored handoff data whenever the user explicitly chooses workflow mode.
4
4
  ---
5
5
 
6
6
  # /cook Handoff Boundary
@@ -17,7 +17,7 @@ This skill governs the relationship between:
17
17
  - Ordinary chat may be used to clarify requirements, discuss tradeoffs, refine scope, and directly implement requested repo changes.
18
18
  - `/cook` is an explicit workflow entrypoint for users who want resumability, review, audit, or canonical `.agent/**` workflow state.
19
19
  - `/cook` is optional. It is not required just because the work spans multiple files or looks substantial.
20
- - Ordinary chat remains ordinary chat until the user explicitly runs `/cook`.
20
+ - Ordinary chat remains ordinary chat until the user explicitly chooses `/cook`.
21
21
 
22
22
  ## What Ordinary Chat May Do
23
23
 
@@ -52,30 +52,22 @@ But even in those cases:
52
52
  - do not frame `/cook` as mandatory for direct repo edits
53
53
  - continue helping directly in ordinary chat unless the user explicitly chooses workflow mode
54
54
 
55
- ## Required Behavior Before Explicit `/cook`
55
+ ## Required Behavior When The User Explicitly Chooses `/cook`
56
56
 
57
- Before the user explicitly runs `/cook`, the primary agent must:
57
+ If the user explicitly asks to enter `/cook` workflow mode, the primary agent must:
58
58
 
59
- - keep the interaction in ordinary chat
60
- - directly implement requested repo changes when appropriate instead of blocking on workflow mode
61
- - continue ordinary discussion naturally if the user keeps refining the task
62
- - avoid claiming that canonical workflow state already exists unless `/cook` actually started it
59
+ - generate the handoff information itself in ordinary chat
60
+ - emit exactly one fresh `cook_handoff` capsule that captures the intended startup slice from the primary-agent view of the task
61
+ - tell the user to run `/cook` only after that explicit handoff exists
62
+ - not rely on `/cook` to infer, summarize, or guess the startup slice from recent discussion alone
63
63
 
64
- ## Deferred Handoff Model
64
+ In other words:
65
65
 
66
- When the user explicitly runs `/cook`:
66
+ - primary agent authors the handoff
67
+ - `/cook` consumes and confirms that handoff
68
+ - `/cook` must not invent the mission from transcript guessing
67
69
 
68
- - `/cook` synthesizes a startup brief from recent discussion using primary-agent-style context
69
- - `/cook` shows Start / Cancel confirmation before canonical workflow state is rewritten
70
- - that synthesized startup brief is advisory intake only until the user confirms startup
71
-
72
- This means the primary agent does not need to proactively attach startup capsules during ordinary chat just because the task looks ready.
73
-
74
- ## Optional Preview Behavior
75
-
76
- Only if the user explicitly asks for a preview startup brief or handoff capsule in ordinary chat may the primary agent provide one.
77
-
78
- Optional preview capsule format:
70
+ ## Required Capsule Format
79
71
 
80
72
  ````text
81
73
  ```cook_handoff
@@ -107,14 +99,12 @@ Optional preview capsule format:
107
99
  Notes:
108
100
 
109
101
  - `constraints` may be replaced or supplemented by `non_goals` when clearer.
110
- - `first_slice_goal`, `first_slice_non_goals`, `implementation_surfaces`, `verification_commands`, and `why_this_slice_first` are required only for an implementation-ready preview capsule.
111
- - Any preview capsule is startup intake for `/cook` only. It is not canonical `.agent/**` state, not active-slice state, and not a second repo contract source.
102
+ - `first_slice_goal`, `first_slice_non_goals`, `implementation_surfaces`, `verification_commands`, and `why_this_slice_first` are required for an implementation-ready handoff.
103
+ - Any capsule is startup intake for `/cook` only. It is not canonical `.agent/**` state, not active-slice state, and not a second repo contract source.
112
104
 
113
- Suggested wording:
105
+ Suggested wording when the user chooses workflow mode:
114
106
 
115
- > We can continue directly in ordinary chat if you want. If you prefer resumable workflow state, explicit review flow, or a confirm-first startup gate, you can run `/cook` and it will synthesize a startup brief from our recent discussion before workflow begins.
116
-
117
- A short recap may include mission, scope, or acceptance, but that recap must not be presented as canonical plan state.
107
+ > Got it since you want `/cook`, I’ll first prepare the explicit startup handoff here from the current task context. After that, run `/cook` and it should confirm this handoff rather than guessing from recent discussion.
118
108
 
119
109
  ## Forbidden Behaviors
120
110
 
@@ -125,9 +115,15 @@ Before the user explicitly runs `/cook`, the primary agent must not:
125
115
  - claim canonical `.agent/**` startup state exists when it does not
126
116
  - refuse ordinary-chat implementation solely because `/cook` would also be possible
127
117
 
118
+ When the user does explicitly choose `/cook`, the system must not:
119
+
120
+ - let `/cook` invent the startup mission from recent discussion alone
121
+ - let `/cook` replace missing primary-agent handoff data with transcript guessing
122
+ - let `/cook` reopen or refocus workflow from guessed intent when no fresh explicit primary-agent handoff exists
123
+
128
124
  ## Relationship To `completion-protocol`
129
125
 
130
- This skill is only about pre-`/cook` ordinary-chat behavior.
126
+ This skill is only about pre-`/cook` ordinary-chat behavior and explicit handoff preparation.
131
127
 
132
128
  After the user explicitly enters `/cook`, the separate `completion-protocol` skill governs:
133
129