@linimin/pi-letscook 0.1.61 → 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/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.1.62
4
+
5
+ ### Changed
6
+
7
+ - made ordinary chat implementation-first again so the primary agent may directly edit repo files without requiring `/cook` when workflow state is unnecessary
8
+ - 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
9
+ - updated ordinary-chat boundary docs, reminders, and release-parity checks so they no longer tell the agent to block repo edits pending explicit `/cook`
4
10
 
5
11
  ## 0.1.61
6
12
 
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @linimin/pi-letscook
2
2
 
3
- `/cook` turns main-chat discussion about concrete repo changes into a resumable repo workflow stored in repo-local `.agent/**` state.
3
+ `/cook` is optional workflow mode for turning main-chat discussion about concrete repo changes into a resumable repo workflow stored in repo-local `.agent/**` state.
4
4
 
5
- `/cook` is the explicit workflow boundary for starting, continuing, refocusing, or beginning the next round of long-running repo work.
5
+ You can still implement directly in ordinary chat when you do not need workflow state. Use `/cook` when you want confirm-first startup, resumability, review/audit flow, or canonical `.agent/**` control.
6
6
 
7
7
  ## Use it when
8
8
 
@@ -10,10 +10,12 @@
10
10
  - you want one mission tracked in repo state instead of chat memory
11
11
  - you want clear continue / refocus / next-round behavior
12
12
  - you want review, audit, and verification tied to the repo
13
+ - you want a confirm-first workflow boundary before canonical state is written
13
14
 
14
15
  ## Skip it when
15
16
 
16
17
  - you only need a one-off answer
18
+ - you want the agent to implement directly in ordinary chat
17
19
  - you are brainstorming
18
20
  - you are writing planning docs but are not ready to start concrete repo changes
19
21
 
@@ -30,10 +32,10 @@ Then run `/reload` in Pi.
30
32
  1. Install the package:
31
33
  `pi install npm:@linimin/pi-letscook`
32
34
  2. Run `/reload` in Pi.
33
- 3. In the main chat, describe and refine the concrete repo change you want.
34
- 4. When you want to enter workflow, run `/cook`.
35
- 5. Review the synthesized startup brief and choose **Start** or **Cancel**.
36
- 6. Later, run `/cook` again to resume from canonical state or confirm a synthesized replacement or next-round startup brief.
35
+ 3. In the main chat, either implement directly with the agent or refine the concrete repo change you want.
36
+ 4. When you want workflow mode, run `/cook`.
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.
37
39
 
38
40
  ```text
39
41
  /cook
@@ -43,20 +45,21 @@ Then run `/reload` in Pi.
43
45
 
44
46
  | If you want to... | Do this |
45
47
  |---|---|
46
- | Start a long-running task | Discuss the concrete repo change in the main chat, then run `/cook` when you want workflow to begin |
48
+ | Implement directly without workflow | Ask in ordinary chat and let the agent modify the repo directly |
49
+ | Start a tracked workflow | Ask the primary agent in ordinary chat to prepare the explicit `/cook` handoff, then run `/cook` |
47
50
  | Continue the current workflow | Run `/cook` |
48
- | 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` |
49
52
 
50
53
  ## What `/cook` expects
51
54
 
52
- - enough recent main-chat discussion for `/cook` to synthesize a concrete startup brief when you explicitly invoke it
53
- - 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
54
57
  - acceptance and verification intent that can support a truthful first workflow round
55
58
  - README/CHANGELOG updates still count as concrete repo changes
56
- - 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
57
- - 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
58
61
 
59
- 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`.
60
63
 
61
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.
62
65
 
@@ -64,20 +67,20 @@ If you pass inline arguments to `/cook`, it also fails closed and tells you to m
64
67
 
65
68
  ## Workflow entry
66
69
 
67
- Only explicit `/cook` enters the workflow. Ordinary prompts stay in the main chat and go straight to the primary agent.
70
+ Only explicit `/cook` enters workflow mode. Ordinary prompts stay in the main chat and go straight to the primary agent.
68
71
 
69
- If a task has clearly matured into completion-workflow scope, the primary agent should still avoid starting long-running implementation directly in ordinary chat.
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.
70
73
 
71
- Ordinary chat remains advisory until you explicitly run `/cook`. At that point `/cook` 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.
72
75
 
73
- Optional explicit `/cook` capsules may still be used as compatibility startup intake, but they are no longer the default path and 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.
74
77
 
75
78
  Important behavior:
76
- - `/cook` is the canonical workflow boundary and manual entry point
77
- - startup and next-round entry stay confirm-first, but they now derive startup from explicit user `/cook` entry plus recent discussion when needed
78
- - active workflows resume from canonical `.agent/**` state unless `/cook` synthesizes or receives a concrete replacement mission
79
+ - `/cook` is an optional workflow boundary and manual entry point
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
79
82
  - explicit slash commands other than `/cook` continue normally in the main chat
80
- - ordinary main-chat discussion may clarify or propose, but mature long-running implementation still must not start before explicit `/cook`
83
+ - ordinary main-chat discussion may clarify, propose, or directly implement repo changes without entering workflow mode
81
84
 
82
85
  ## Typical examples
83
86
 
@@ -91,13 +94,13 @@ I want to add login redirect handling and tests.
91
94
 
92
95
  ## What happens when you run `/cook`
93
96
 
94
- `/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.
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.
95
98
 
96
99
  | Repo state | What you'll see |
97
100
  |---|---|
98
- | 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. |
99
- | 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. |
100
- | 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. |
101
104
 
102
105
  ## Confirmation and fail-closed behavior
103
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: synthesize a startup brief when the user explicitly enters /cook, resume the current workflow from canonical state, or confirm a replacement mission from explicit /cook entry",
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,
@@ -26,17 +26,17 @@ export type AdvisoryStartupBrief = {
26
26
 
27
27
  export function buildCookHandoffBoundaryReminder(): string {
28
28
  return [
29
- "You are still in ordinary main chat before any explicit /cook workflow entry.",
30
- "Use ordinary chat to clarify requirements, discuss tradeoffs, propose implementation approaches, and refine scope naturally.",
31
- "/cook is the only explicit entrypoint into long-running completion workflow.",
29
+ "You are in ordinary main chat unless the user explicitly runs /cook.",
30
+ "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
31
  "Do not proactively tell the user to run /cook just because a task looks workflow-worthy, and do not emit a ```cook_handoff``` capsule by default in ordinary chat.",
33
- "Even when the task has matured into workflow-level implementation work, ordinary chat remains ordinary chat until the user explicitly runs /cook.",
34
- "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.",
35
- "If the user asks follow-up questions or wants to keep refining scope, continue helping in ordinary chat instead of steering them into workflow mode.",
36
- "Once 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.",
37
- "Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior.",
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.",
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 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.",
38
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.",
39
- "If the task is still ordinary Q&A, lightweight brainstorming, or a tiny one-off fix, continue normally without forcing /cook.",
39
+ "When you continue in ordinary chat, do not pretend /cook already started and do not silently rewrite discussion into canonical workflow state.",
40
40
  ].join(" ");
41
41
  }
42
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linimin/pi-letscook",
3
- "version": "0.1.61",
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,47 +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 explicit-entry 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
- "`/cook` is the explicit workflow boundary for starting, continuing, refocusing, or beginning the next round of long-running repo work.",
19
- "Only explicit `/cook` enters the workflow. Ordinary prompts stay in the main chat and go straight to the primary agent.",
20
- "Ordinary chat remains advisory until you explicitly run `/cook`. At that point `/cook` synthesizes a startup brief from recent discussion using primary-agent-style context, then asks you to **Start** or **Cancel** before rewriting canonical workflow state.",
21
- "- startup and next-round entry stay confirm-first, but they now derive startup from explicit user `/cook` entry plus recent discussion when needed",
22
- "- active workflows resume from canonical `.agent/**` state unless `/cook` synthesizes or receives a concrete replacement mission",
23
- "`/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.",
24
- "when a concrete replacement mission suggests replacing an active workflow, `/cook` shows a chooser before any canonical state rewrite",
18
+ "You can still implement directly in ordinary chat when you do not need workflow state.",
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.",
25
23
  ],
26
24
  "CHANGELOG.md": [
27
- "removed proactive primary-agent `/cook` prompting and default ordinary-chat `cook_handoff` emission so main chat stays advisory until the user explicitly runs `/cook`",
28
- "changed bare `/cook` startup and done-workflow next-round entry to synthesize a deferred primary-agent startup brief from recent discussion instead of requiring a pre-authored explicit handoff capsule",
29
- "updated public parity and shipped package contents so the tracked `.agent` contract files are included in package tarballs and packaged smoke/release verification can scaffold canonical state truthfully",
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",
30
27
  ],
31
28
  "extensions/completion/prompt-surfaces.ts": [
32
- '"/cook is the only explicit entrypoint into long-running completion workflow."',
33
- '"Do not proactively tell the user to run /cook just because a task looks workflow-worthy, and do not emit a ```cook_handoff``` capsule by default in ordinary chat."',
34
- '"Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior."',
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."',
35
31
  ],
36
32
  "extensions/completion/index.ts": [
37
- '"/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."',
38
- 'description: "/cook workflow: synthesize a startup brief when the user explicitly enters /cook, resume the current workflow from canonical state, or confirm a replacement mission from explicit /cook entry"',
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"',
39
35
  ],
40
36
  }
41
37
 
42
38
  forbidden = {
43
39
  "README.md": [
44
- "wait for a fresh primary-agent handoff",
45
- "require a fresh valid explicit primary-agent handoff",
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",
46
42
  ],
47
43
  "extensions/completion/prompt-surfaces.ts": [
48
- '"When handing off, explain that /cook can start a new workflow or next round only from a fresh valid explicit primary-agent handoff capsule; otherwise it fails closed, while already-active workflows resume from canonical .agent state unless a fresh valid explicit handoff proposes replacement."',
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."',
49
45
  ],
50
46
  "extensions/completion/index.ts": [
51
- 'description: "/cook workflow: start a new workflow or next round only from a fresh explicit primary-agent handoff, resume the current workflow from canonical state, or confirm an explicit replacement from the explicit /cook command"',
52
- '"/cook failed closed because new-workflow startup now requires a fresh valid explicit primary-agent handoff from the immediately preceding ordinary-chat turn; recent discussion alone no longer starts a workflow. Ask the primary agent to hand off explicitly in the main chat, then rerun /cook."',
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."',
53
49
  ],
54
50
  }
55
51
 
@@ -265,14 +265,14 @@ auto_resume = Path(sys.argv[5])
265
265
  assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from canonical state'
266
266
  assert handoff.exists(), 'ordinary non-/cook turn should inject the /cook handoff boundary reminder'
267
267
  handoff_text = handoff.read_text()
268
- assert '/cook is the only explicit entrypoint into long-running completion workflow.' in handoff_text, 'ordinary handoff reminder should preserve the explicit /cook workflow boundary'
268
+ assert 'ordinary main chat unless the user explicitly runs /cook' in handoff_text, 'ordinary handoff reminder should preserve explicit /cook workflow entry'
269
+ assert 'directly implement requested repo changes, including multi-file work' in handoff_text, 'ordinary handoff reminder should allow direct ordinary-chat implementation'
269
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'
270
- assert 'do not emit a ```cook_handoff``` capsule by default in ordinary chat' in handoff_text, 'ordinary handoff reminder should stop proactive capsule emission'
271
- assert 'ordinary chat remains ordinary chat until the user explicitly runs /cook' in handoff_text, 'ordinary handoff reminder should preserve the explicit /cook boundary'
272
- assert 'do not begin long-running product implementation in ordinary chat' in handoff_text, 'ordinary handoff reminder should still block workflow-level implementation before /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'
275
- assert 'Start/Cancel confirmation before canonical workflow state is rewritten' in handoff_text, 'ordinary handoff reminder should preserve confirm-first startup'
271
+ assert '/cook is optional workflow mode' in handoff_text, 'ordinary handoff reminder should position /cook as optional workflow mode'
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 '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
+ 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'
278
278
  PY
@@ -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,23 +1,23 @@
1
1
  ---
2
2
  name: cook-handoff-boundary
3
- description: Ordinary-chat boundary contract for keeping main chat advisory until the user explicitly enters `/cook`, while preventing long-running implementation from starting before that explicit workflow entry.
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
7
7
 
8
8
  Load or summarize this contract when the primary agent is operating in ordinary main chat before the user has explicitly entered `/cook`.
9
9
 
10
- This skill governs the boundary between:
10
+ This skill governs the relationship between:
11
11
 
12
- - ordinary main-chat discussion, clarification, and proposal work
13
- - explicit transition into long-running completion workflow through `/cook`
12
+ - ordinary main-chat discussion and direct implementation
13
+ - optional transition into long-running completion workflow through `/cook`
14
14
 
15
15
  ## Core Contract
16
16
 
17
- - Ordinary chat may be used to clarify requirements, discuss tradeoffs, propose implementation approaches, and refine scope.
18
- - `/cook` is the only explicit entrypoint into long-running completion workflow.
19
- - Ordinary chat remains ordinary chat until the user explicitly runs `/cook`.
20
- - Before that explicit `/cook` entry, the primary agent must stop short of long-running implementation for workflow-level tasks.
17
+ - Ordinary chat may be used to clarify requirements, discuss tradeoffs, refine scope, and directly implement requested repo changes.
18
+ - `/cook` is an explicit workflow entrypoint for users who want resumability, review, audit, or canonical `.agent/**` workflow state.
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 chooses `/cook`.
21
21
 
22
22
  ## What Ordinary Chat May Do
23
23
 
@@ -27,53 +27,47 @@ The primary agent may:
27
27
  - discuss tradeoffs
28
28
  - refine scope and constraints
29
29
  - summarize likely mission, acceptance, or risks
30
- - help the user determine whether the work seems large enough for `/cook`
30
+ - directly edit repo files when that is the most helpful response
31
+ - complete multi-file implementation in ordinary chat when workflow state is unnecessary
31
32
 
32
33
  The primary agent should not:
33
34
 
34
- - proactively tell the user to run `/cook`
35
+ - proactively tell the user to run `/cook` just because the task looks workflow-worthy
35
36
  - proactively emit a `cook_handoff` capsule by default
36
- - act as though workflow has already started
37
- - rewrite ordinary-chat discussion into canonical workflow state
37
+ - act as though workflow has already started when it has not
38
+ - silently rewrite ordinary-chat discussion into canonical workflow state
38
39
 
39
- ## When Work Looks Workflow-Worthy
40
+ ## When `/cook` Is Helpful
40
41
 
41
- The primary agent should treat work as workflow-worthy when one or more of the following are true:
42
+ The primary agent may mention `/cook` as an optional tool when it would genuinely help, for example when:
42
43
 
43
- - the task spans multiple files, steps, or verification surfaces
44
- - the next natural step would be bounded repo implementation rather than more explanation
45
- - the work needs resumability, review, audit, or canonical workflow state
46
- - the task is better treated as a long-running repo mission than a one-off answer or tiny fix
44
+ - the work should be resumable across sessions
45
+ - the user wants a tracked mission in canonical `.agent/**` state
46
+ - the task benefits from explicit review / audit / stop-wave flow
47
+ - the user wants a confirm-first workflow boundary before a long-running effort
47
48
 
48
- Even then, the boundary remains:
49
+ But even in those cases:
49
50
 
50
- - ordinary chat can still keep refining the task
51
- - only explicit `/cook` starts workflow
51
+ - do not force `/cook`
52
+ - do not frame `/cook` as mandatory for direct repo edits
53
+ - continue helping directly in ordinary chat unless the user explicitly chooses workflow mode
52
54
 
53
- ## Required Behavior Before Explicit `/cook`
55
+ ## Required Behavior When The User Explicitly Chooses `/cook`
54
56
 
55
- When a task has matured into workflow-level work, the primary agent must:
57
+ If the user explicitly asks to enter `/cook` workflow mode, the primary agent must:
56
58
 
57
- - stop before long-running implementation
58
- - not edit tracked product files for that workflow-level task in ordinary chat
59
- - continue ordinary discussion naturally if the user keeps asking questions or refining scope
60
- - wait for the user to explicitly run `/cook` before treating the conversation as workflow startup
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
61
63
 
62
- ## Deferred Handoff Model
64
+ In other words:
63
65
 
64
- 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
65
69
 
66
- - `/cook` will synthesize a startup brief from recent discussion using primary-agent-style context
67
- - `/cook` will show Start / Cancel confirmation before canonical workflow state is rewritten
68
- - that synthesized startup brief is advisory intake only, not canonical `.agent/**` state by itself
69
-
70
- This means the primary agent does **not** need to proactively attach startup capsules during ordinary chat just because the task looks ready.
71
-
72
- ## Optional Preview Behavior
73
-
74
- Only if the user explicitly asks for a preview startup brief or handoff capsule in ordinary chat may the primary agent provide one.
75
-
76
- Optional preview capsule format:
70
+ ## Required Capsule Format
77
71
 
78
72
  ````text
79
73
  ```cook_handoff
@@ -105,28 +99,31 @@ Optional preview capsule format:
105
99
  Notes:
106
100
 
107
101
  - `constraints` may be replaced or supplemented by `non_goals` when clearer.
108
- - `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.
109
- - 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.
110
104
 
111
- Suggested wording:
105
+ Suggested wording when the user chooses workflow mode:
112
106
 
113
- > We are still in ordinary chat until you explicitly run `/cook`. If you want, we can keep refining the first slice here. When you do run `/cook`, it will synthesize a startup brief from our recent discussion and show Start / Cancel before workflow begins.
114
-
115
- 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.
116
108
 
117
109
  ## Forbidden Behaviors
118
110
 
119
111
  Before the user explicitly runs `/cook`, the primary agent must not:
120
112
 
121
- - directly begin long-running implementation in ordinary chat
122
- - modify tracked product files as part of that workflow-level task
123
- - act as though `/cook` had already been invoked
113
+ - pretend `/cook` has already been invoked
124
114
  - silently rewrite ordinary-chat discussion into active workflow state
125
- - refuse ordinary-chat clarification only because `/cook` would now be appropriate
115
+ - claim canonical `.agent/**` startup state exists when it does not
116
+ - refuse ordinary-chat implementation solely because `/cook` would also be possible
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
126
123
 
127
124
  ## Relationship To `completion-protocol`
128
125
 
129
- This skill is only about pre-`/cook` ordinary-chat boundary behavior.
126
+ This skill is only about pre-`/cook` ordinary-chat behavior and explicit handoff preparation.
130
127
 
131
128
  After the user explicitly enters `/cook`, the separate `completion-protocol` skill governs:
132
129