@linimin/pi-letscook 0.1.67 → 0.1.68

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.
@@ -90,6 +90,22 @@ with session_path.open('w', encoding='utf-8') as fh:
90
90
  PY
91
91
  }
92
92
 
93
+ bootstrap_workflow() {
94
+ local repo_root="$1"
95
+ local session_path="$2"
96
+ local discussion="$3"
97
+ local generated_handoff="$4"
98
+ mkdir -p "$repo_root"
99
+ cd "$repo_root"
100
+ git init -q
101
+ write_session "$session_path" "$repo_root" "$discussion"
102
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
103
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$generated_handoff" \
104
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
105
+ pi --session "$session_path" -e "$PKG_ROOT" -p "/cook" \
106
+ >"$TMPDIR/bootstrap.out" 2>"$TMPDIR/bootstrap.err"
107
+ }
108
+
93
109
  mark_done() {
94
110
  python3 - <<'PY'
95
111
  import json
@@ -142,25 +158,29 @@ active_path.write_text(json.dumps(active, indent=2) + '\n')
142
158
  PY
143
159
  }
144
160
 
145
- ROOT="$TMPDIR/repo"
146
- mkdir -p "$ROOT"
147
- cd "$ROOT"
148
- git init -q
161
+ snapshot_tracked() {
162
+ local baseline_path="$1"
163
+ python3 - "$baseline_path" <<'PY'
164
+ import json
165
+ import sys
166
+ from pathlib import Path
167
+
168
+ tracked = [
169
+ Path('.agent/mission.md'),
170
+ Path('.agent/profile.json'),
171
+ Path('.agent/state.json'),
172
+ Path('.agent/startup-plan.json'),
173
+ Path('.agent/startup-plan.md'),
174
+ Path('.agent/plan.json'),
175
+ Path('.agent/active-slice.json'),
176
+ Path('.agent/verification-evidence.json'),
177
+ ]
178
+ Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
179
+ PY
180
+ }
149
181
 
150
- # No workflow yet: bare /cook should be able to generate a primary-agent startup plan in the same entry,
151
- # then continue directly to startup confirmation.
152
- SESSION_ZERO="$TMPDIR/session-zero.jsonl"
153
- DISCUSSION_ZERO=$'Mission: Remove the completion status line while keeping the completion widget.
154
- Scope:
155
- - Keep the non-running completion widget.
156
- - Suppress the widget while a completion role is active.
157
- Constraints:
158
- - Do not reintroduce any other completion status surface.
159
- Acceptance:
160
- - Update README to match the shipped behavior.
161
- - Keep observability regression coverage truthful.'
162
- DISCUSSION_SNAPSHOT_ZERO="$TMPDIR/context-proposal-structured-fallback.json"
163
- GENERATED_HANDOFF_ZERO="$(python3 - <<'PY'
182
+ STARTUP_DISCUSSION=$'Remove the completion status line while keeping the completion widget and keep the startup confirm-first.'
183
+ STARTUP_HANDOFF="$(python3 - <<'PY'
164
184
  import json
165
185
  capsule = {
166
186
  "kind": "cook_handoff",
@@ -171,1521 +191,382 @@ capsule = {
171
191
  "Suppress the widget while a completion role is active."
172
192
  ],
173
193
  "constraints": [
174
- "Do not reintroduce any other completion status surface."
194
+ "Do not reintroduce another completion status surface."
175
195
  ],
176
196
  "acceptance": [
177
197
  "Update README to match the shipped behavior.",
178
198
  "Keep observability regression coverage truthful."
179
199
  ],
180
200
  "risks": [],
181
- "notes": ["Generated by the primary-agent startup-plan step triggered from /cook."],
201
+ "notes": ["Generated by same-entry primary-agent startup-plan synthesis."],
182
202
  "handoff_kind": "implementation_workflow_handoff",
183
- "first_slice_goal": "Remove the completion status line while preserving the widget behavior.",
184
- "first_slice_non_goals": ["Do not reintroduce another status surface."],
203
+ "first_slice_goal": "Remove the completion status line while preserving widget behavior.",
204
+ "first_slice_non_goals": ["Do not add a replacement status surface."],
185
205
  "implementation_surfaces": ["extensions/completion/index.ts", "README.md"],
186
206
  "verification_commands": ["npm run smoke-test"],
187
- "why_this_slice_first": "This slice is already concrete and bounded enough to start workflow safely.",
207
+ "why_this_slice_first": "The startup mission is already concrete and bounded enough to begin workflow planning.",
188
208
  "task_type": "completion-workflow",
189
209
  "evaluation_profile": "completion-rubric-v1",
190
- "why_cook_now": "The user explicitly chose workflow mode for this bounded implementation slice."
210
+ "why_cook_now": "The user explicitly chose workflow mode for this bounded implementation mission."
191
211
  }
192
212
  print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
193
213
  PY
194
214
  )"
195
- write_session "$SESSION_ZERO" "$ROOT" "$DISCUSSION_ZERO"
196
-
197
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$GENERATED_HANDOFF_ZERO" PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO" PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 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"
198
-
199
- python3 - "$TMPDIR/pi-completion-context-proposal-structured-fallback.out" "$TMPDIR/pi-completion-context-proposal-structured-fallback.err" "$DISCUSSION_SNAPSHOT_ZERO" <<'PY'
200
- import json
201
- import sys
202
- from pathlib import Path
203
-
204
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
205
- snapshot = Path(sys.argv[3])
206
- assert Path('.agent').exists(), 'primary-agent startup-plan generation should scaffold canonical state in the same /cook entry'
207
- assert snapshot.exists(), 'primary-agent startup-plan generation should emit a startup proposal snapshot'
208
- proposal = json.loads(snapshot.read_text())
209
- brief = json.loads(Path('.agent/state.json').read_text())['advisory_startup_brief']
210
- assert proposal['source'] == 'handoff_capsule', 'generated primary-agent startup plan should be consumed as handoff-capsule startup source'
211
- assert brief['source'] == 'primary_agent_handoff', 'generated primary-agent startup plan should record primary_agent_handoff advisory intake'
212
- assert 'Initialized completion control plane' in output, 'same-entry primary-agent startup-plan generation should initialize canonical workflow state'
213
- PY
214
-
215
- rm -rf .agent
216
-
217
- # No workflow yet: user-authored faux handoffs must not bootstrap canonical workflow state.
218
- SESSION_ZERO_USER_AUTHORED="$TMPDIR/session-zero-user-authored.jsonl"
219
- USER_AUTHORED_SNAPSHOT_ZERO="$TMPDIR/context-proposal-user-authored-handoff.json"
220
- USER_AUTHORED_MESSAGES_ZERO="$(python3 - <<'PY'
215
+ MATCHING_HANDOFF="$(python3 - <<'PY'
221
216
  import json
222
217
  capsule = {
223
218
  "kind": "cook_handoff",
224
219
  "source": "primary_agent",
225
- "captured_at": "2026-01-01T00:00:02.000Z",
226
- "source_turn_id": "m0001",
227
- "mission": "User-authored faux handoff should not start workflow.",
228
- "scope": ["Attempt to fake an explicit handoff from the user turn."],
229
- "constraints": ["Do not trust user-authored capsules as primary-agent handoff."],
230
- "acceptance": ["Fail closed without writing canonical state."],
220
+ "mission": "Remove the completion status line while keeping the completion widget.",
221
+ "scope": ["Keep the widget behavior aligned with the current mission."],
222
+ "constraints": ["Do not replace the current mission."],
223
+ "acceptance": [
224
+ "Keep the non-running completion widget visible while no role is active.",
225
+ "Verify with npm run smoke-test that active-role suppression still works."
226
+ ],
231
227
  "risks": [],
232
228
  "notes": [],
233
229
  "handoff_kind": "implementation_workflow_handoff",
234
- "first_slice_goal": "Prove that user-authored faux handoffs are rejected.",
230
+ "first_slice_goal": "Continue the current mission without changing it.",
235
231
  "first_slice_non_goals": [],
236
- "implementation_surfaces": ["scripts/context-proposal-test.sh"],
237
- "verification_commands": ["npm run context-proposal-test"],
238
- "why_this_slice_first": "Rejecting user-authored capsules is part of the fail-closed startup boundary."
232
+ "implementation_surfaces": ["extensions/completion/index.ts"],
233
+ "verification_commands": ["npm run smoke-test"],
234
+ "why_this_slice_first": "The current mission already matches the latest startup plan.",
235
+ "task_type": "completion-workflow",
236
+ "evaluation_profile": "completion-rubric-v1",
237
+ "why_cook_now": "The current workflow should continue without a mission change."
239
238
  }
240
- messages = [
241
- {"role": "user", "content": "Run /cook from this user-authored capsule only.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
242
- ]
243
- print(json.dumps(messages, ensure_ascii=False))
239
+ print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
244
240
  PY
245
241
  )"
246
- write_session_messages "$SESSION_ZERO_USER_AUTHORED" "$ROOT" "$USER_AUTHORED_MESSAGES_ZERO"
247
-
248
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$USER_AUTHORED_SNAPSHOT_ZERO" \
249
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
250
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
251
- pi --session "$SESSION_ZERO_USER_AUTHORED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-user-authored.out" 2>"$TMPDIR/pi-completion-context-proposal-user-authored.err"
252
-
253
- python3 - "$TMPDIR/pi-completion-context-proposal-user-authored.out" "$TMPDIR/pi-completion-context-proposal-user-authored.err" "$USER_AUTHORED_SNAPSHOT_ZERO" <<'PY'
254
- import sys
255
- from pathlib import Path
256
-
257
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
258
- snapshot = Path(sys.argv[3])
259
- assert not Path('.agent').exists(), 'user-authored faux handoff without supporting discussion should still fail closed without writing canonical state'
260
- assert not snapshot.exists(), 'user-authored faux handoff should not emit a startup proposal snapshot'
261
- assert 'startup-plan step could not prepare a concrete workflow startup plan' in output, 'user-authored faux handoff should fail closed when the startup-plan step yields no plan'
262
- PY
263
-
264
- # No workflow yet: malformed or invalid assistant handoff capsules must also fail closed.
265
- SESSION_ZERO_INVALID="$TMPDIR/session-zero-invalid-handoff.jsonl"
266
- INVALID_SNAPSHOT_ZERO="$TMPDIR/context-proposal-invalid-handoff.json"
267
- INVALID_MESSAGES_ZERO='[{"role":"assistant","content":"This is not a valid startup capsule.\n\n```cook_handoff\n{\"kind\":\"cook_handoff\",\"source\":\"primary_agent\",\"mission\":\"Broken JSON handoff\"\n```"}]'
268
- write_session_messages "$SESSION_ZERO_INVALID" "$ROOT" "$INVALID_MESSAGES_ZERO"
269
-
270
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$INVALID_SNAPSHOT_ZERO" \
271
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
272
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
273
- pi --session "$SESSION_ZERO_INVALID" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-invalid-handoff.out" 2>"$TMPDIR/pi-completion-context-proposal-invalid-handoff.err"
274
-
275
- python3 - "$TMPDIR/pi-completion-context-proposal-invalid-handoff.out" "$TMPDIR/pi-completion-context-proposal-invalid-handoff.err" "$INVALID_SNAPSHOT_ZERO" <<'PY'
276
- import sys
277
- from pathlib import Path
278
-
279
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
280
- snapshot = Path(sys.argv[3])
281
- assert not Path('.agent').exists(), 'invalid assistant handoff without supporting discussion should fail closed without writing canonical state'
282
- assert not snapshot.exists(), 'invalid assistant handoff should not emit a startup proposal snapshot'
283
- assert 'startup-plan step could not prepare a concrete workflow startup plan' in output, 'invalid assistant handoff should fail closed when no valid startup plan can be prepared'
242
+ NON_STARTABLE_HANDOFF="$(python3 - <<'PY'
243
+ import json
244
+ capsule = {
245
+ "kind": "cook_handoff",
246
+ "source": "primary_agent",
247
+ "mission": "Maybe think about workflow status later.",
248
+ "scope": ["Consider status changes eventually."],
249
+ "constraints": ["Do not commit to concrete repo work yet."],
250
+ "acceptance": ["Discuss possible approaches."],
251
+ "risks": [],
252
+ "notes": [],
253
+ "handoff_kind": "implementation_workflow_handoff",
254
+ "task_type": "completion-workflow",
255
+ "evaluation_profile": "completion-rubric-v1"
256
+ }
257
+ print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
284
258
  PY
285
-
286
- # No workflow yet: a fresh explicit primary-agent startup-plan preview should still bootstrap canonical startup state.
287
- SESSION_ONE="$TMPDIR/session-one.jsonl"
288
- HANDOFF_SNAPSHOT_ONE="$TMPDIR/context-proposal-explicit-startup.json"
289
- HANDOFF_MESSAGES_ONE="$(python3 - <<'PY'
259
+ )"
260
+ NEXT_ROUND_HANDOFF="$(python3 - <<'PY'
290
261
  import json
291
262
  capsule = {
292
263
  "kind": "cook_handoff",
293
264
  "source": "primary_agent",
294
- "captured_at": "2026-01-01T00:00:02.000Z",
295
- "source_turn_id": "m0002",
296
- "mission": "Remove the completion status line while keeping the completion widget.",
265
+ "mission": "Start the next workflow round for widget follow-up docs.",
297
266
  "scope": [
298
- "Keep the non-running completion widget.",
299
- "Suppress the widget while a completion role is active."
300
- ],
301
- "constraints": [
302
- "Do not reintroduce any other completion status surface."
267
+ "Document the widget-only status behavior for the next round.",
268
+ "Keep the next round confirm-first."
303
269
  ],
270
+ "constraints": ["Do not reopen the previous mission."],
304
271
  "acceptance": [
305
- "Update README to match the shipped behavior.",
306
- "Keep observability regression coverage truthful."
307
- ],
308
- "risks": [
309
- "Stale widget-removal discussion could broaden the startup plan if the handoff is ignored."
310
- ],
311
- "notes": [
312
- "Keep the startup plan aligned with the explicit primary-agent plan."
272
+ "Record the next round as a new canonical mission.",
273
+ "Keep startup-plan persistence truthful for the next round."
313
274
  ],
275
+ "risks": [],
276
+ "notes": [],
314
277
  "handoff_kind": "implementation_workflow_handoff",
315
- "first_slice_goal": "Land the completion-status removal and keep the completion widget coverage truthful.",
316
- "first_slice_non_goals": [
317
- "Do not reintroduce any other completion status surface."
318
- ],
319
- "implementation_surfaces": [
320
- "extensions/completion/index.ts",
321
- "scripts/context-proposal-test.sh"
322
- ],
323
- "verification_commands": [
324
- "npm run context-proposal-test"
325
- ],
326
- "why_this_slice_first": "The startup boundary regression is already bounded enough to implement safely.",
278
+ "first_slice_goal": "Reset canonical state for the next widget-docs mission.",
279
+ "first_slice_non_goals": [],
280
+ "implementation_surfaces": ["README.md", ".agent/startup-plan.json"],
281
+ "verification_commands": ["npm run context-proposal-test"],
282
+ "why_this_slice_first": "The next round must be anchored before later slice derivation.",
327
283
  "task_type": "completion-workflow",
328
284
  "evaluation_profile": "completion-rubric-v1",
329
- "why_cook_now": "The explicit startup plan is concrete and ready for repo changes."
285
+ "why_cook_now": "The previous workflow is done and the next round is concrete enough to start."
330
286
  }
331
- messages = [
332
- {"role": "user", "content": "Please think through the completion widget startup boundary and tell me when it is ready for /cook."},
333
- {"role": "assistant", "content": "This task is now ready for /cook. Run /cook to confirm the startup plan.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
334
- ]
335
- print(json.dumps(messages, ensure_ascii=False))
287
+ print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
336
288
  PY
337
289
  )"
338
- write_session_messages "$SESSION_ONE" "$ROOT" "$HANDOFF_MESSAGES_ONE"
339
290
 
291
+ # Case 1: startup succeeds only through same-entry primary-agent synthesis.
292
+ ROOT_ONE="$TMPDIR/repo-one"
293
+ SESSION_ONE="$TMPDIR/session-one.jsonl"
294
+ PROPOSAL_ONE="$TMPDIR/proposal-one.json"
295
+ mkdir -p "$ROOT_ONE"
296
+ cd "$ROOT_ONE"
297
+ git init -q
298
+ write_session "$SESSION_ONE" "$ROOT_ONE" "$STARTUP_DISCUSSION"
340
299
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
341
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_ONE" \
300
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$STARTUP_HANDOFF" \
301
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_ONE" \
342
302
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
343
- pi --session "$SESSION_ONE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-explicit-startup.out" 2>"$TMPDIR/pi-completion-context-proposal-explicit-startup.err"
303
+ pi --session "$SESSION_ONE" -e "$PKG_ROOT" -p "/cook" \
304
+ >"$TMPDIR/startup-success.out" 2>"$TMPDIR/startup-success.err"
344
305
 
345
- python3 - "$HANDOFF_SNAPSHOT_ONE" <<'PY'
306
+ python3 - "$PROPOSAL_ONE" "$TMPDIR/startup-success.out" "$TMPDIR/startup-success.err" <<'PY'
346
307
  import json
347
308
  import sys
348
309
  from pathlib import Path
349
310
 
350
- mission = 'Remove the completion status line while keeping the completion widget.'
351
- expected_task_type = 'completion-workflow'
352
- expected_eval_profile = 'completion-rubric-v1'
353
- mission_text = Path('.agent/mission.md').read_text()
354
- profile = json.loads(Path('.agent/profile.json').read_text())
355
- state = json.loads(Path('.agent/state.json').read_text())
356
- plan = json.loads(Path('.agent/plan.json').read_text())
357
- active = json.loads(Path('.agent/active-slice.json').read_text())
358
311
  proposal = json.loads(Path(sys.argv[1]).read_text())
359
-
360
- assert mission in mission_text, '.agent/mission.md did not record the explicit-handoff mission anchor'
361
- assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after explicit-handoff bootstrap'
362
- assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after explicit-handoff bootstrap'
363
- assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after explicit-handoff bootstrap'
364
- assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after explicit-handoff bootstrap'
365
- assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after explicit-handoff bootstrap'
366
- assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after explicit-handoff bootstrap'
367
- assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after explicit-handoff bootstrap'
368
- assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after explicit-handoff bootstrap'
369
- assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after explicit-handoff bootstrap'
370
- assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after explicit-handoff bootstrap'
371
- assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after explicit-handoff bootstrap'
372
- brief = state['advisory_startup_brief']
373
- assert brief['kind'] == 'startup_brief', 'state.json should preserve the confirmed startup brief as advisory intake'
374
- assert brief['source'] == 'primary_agent_handoff', 'explicit startup should record the handoff source in advisory intake'
375
- assert brief['mission'] == mission, 'advisory startup brief mission should match the accepted mission anchor'
376
- assert brief['scope'] == ['Keep the non-running completion widget.', 'Suppress the widget while a completion role is active.'], 'advisory startup brief should preserve scope items separately from canonical planning state'
377
- assert brief['constraints'] == ['Do not reintroduce any other completion status surface.'], 'advisory startup brief should preserve constraints separately from canonical planning state'
378
- assert brief['acceptance'] == ['Update README to match the shipped behavior.', 'Keep observability regression coverage truthful.'], 'advisory startup brief should preserve acceptance separately from canonical planning state'
379
- assert brief['risks'] == ['Stale widget-removal discussion could broaden the startup plan if the handoff is ignored.'], 'advisory startup brief should preserve handoff risks'
380
- assert 'First slice goal: Land the completion-status removal and keep the completion widget coverage truthful.' in brief['notes'], 'advisory startup brief should preserve first_slice_goal in notes'
381
- assert 'Verification commands: npm run context-proposal-test' in brief['notes'], 'advisory startup brief should preserve verification_commands in notes'
382
- assert plan['candidate_slices'] == [], 'startup plan summary should remain advisory intake only until regrounder owns plan selection'
383
- assert active['status'] == 'idle', 'startup plan summary should not become the active-slice source before regrounder runs'
384
- assert proposal['mission'] == mission, 'explicit startup proposal snapshot should keep the handoff mission anchor'
385
- assert proposal['source'] == 'handoff_capsule', 'explicit startup proposal snapshot should expose the handoff capsule source'
386
- assert proposal['analysis']['taskType'] == expected_task_type, 'explicit startup proposal snapshot should expose task_type hints separately'
387
- assert proposal['analysis']['evaluationProfile'] == expected_eval_profile, 'explicit startup proposal snapshot should expose evaluation_profile hints separately'
388
- assert state['current_phase'] == 'reground', 'state.json current_phase should start at reground after explicit-handoff bootstrap'
389
- assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should start at completion-regrounder after explicit-handoff bootstrap'
390
- assert state['continuation_reason'].startswith('User started workflow via /cook:'), 'initial startup should record the accepted startup routing in continuation_reason'
391
- assert 'task_type=completion-workflow' in state['continuation_reason'], 'initial startup should persist the selected task_type in continuation_reason'
392
- assert 'evaluation_profile=completion-rubric-v1' in state['continuation_reason'], 'initial startup should persist the selected evaluation_profile in continuation_reason'
393
- PY
394
-
395
- # Active workflow: bare /cook should resume from canonical state when no fresh explicit handoff exists,
396
- # even if recent discussion restates the current mission in a structured way.
397
- SESSION_ONE_CONTINUE="$TMPDIR/session-one-continue.jsonl"
398
- DISCUSSION_ONE_CONTINUE=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the current mission focused on the non-running completion widget.\nConstraints:\n- Do not start a different workflow from this discussion.\nAcceptance:\n- Resume the current workflow from canonical state without rewriting it.'
399
- CONTINUE_ROUTING_ONE="$TMPDIR/active-continue-routing.json"
400
- CONTINUE_RESUME_PROMPT_ONE="$TMPDIR/active-continue-resume.txt"
401
- CONTINUE_CHOOSER_ONE="$TMPDIR/unexpected-active-continue-chooser.json"
402
- CONTINUE_PROPOSAL_ONE="$TMPDIR/unexpected-active-continue-proposal.json"
403
- write_session "$SESSION_ONE_CONTINUE" "$ROOT" "$DISCUSSION_ONE_CONTINUE"
404
-
405
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
406
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
407
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$CONTINUE_ROUTING_ONE" \
408
- PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$CONTINUE_RESUME_PROMPT_ONE" \
409
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CONTINUE_CHOOSER_ONE" \
410
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$CONTINUE_PROPOSAL_ONE" \
411
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
412
- pi --session "$SESSION_ONE_CONTINUE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-continue.out" 2>"$TMPDIR/pi-completion-context-proposal-active-continue.err"
413
-
414
- python3 - "$CONTINUE_ROUTING_ONE" "$CONTINUE_RESUME_PROMPT_ONE" "$CONTINUE_CHOOSER_ONE" "$CONTINUE_PROPOSAL_ONE" <<'PY'
415
- import json
416
- import sys
417
- from pathlib import Path
418
-
419
- mission = 'Remove the completion status line while keeping the completion widget.'
420
- routing = json.loads(Path(sys.argv[1]).read_text())
421
- resume = Path(sys.argv[2]).read_text()
422
- chooser_path = Path(sys.argv[3])
423
- proposal_path = Path(sys.argv[4])
424
- state = json.loads(Path('.agent/state.json').read_text())
425
- plan = json.loads(Path('.agent/plan.json').read_text())
426
- active = json.loads(Path('.agent/active-slice.json').read_text())
427
-
428
- assert routing['mode'] == 'bare', 'active bare /cook resume regression should snapshot bare routing mode'
429
- assert 'explicitGoal' not in routing, 'active bare /cook resume routing should not expose removed explicit-goal shim fields'
430
- assert 'explicitGoalProvided' not in routing, 'active bare /cook resume routing should not expose removed explicit-goal shim fields'
431
- assert routing['action'] == 'continue', 'active bare /cook should resume when no fresh explicit handoff exists'
432
- assert routing['reason'] == 'missing_explicit_handoff', 'active bare /cook should explain that resume happened because no fresh explicit handoff existed'
433
- assert routing['currentMissionAnchor'] == mission, 'resume routing should preserve the current mission anchor'
434
- assert routing['proposedMissionAnchor'] is None, 'resume routing should not derive a replacement mission from recent discussion'
435
- assert 'Resume the completion workflow from canonical state.' in resume, 'active bare /cook resume should still use the canonical resume prompt'
436
- assert not chooser_path.exists(), 'active bare /cook resume should not open the replacement chooser without a fresh explicit handoff'
437
- assert not proposal_path.exists(), 'active bare /cook resume should not open replacement proposal confirmation without a fresh explicit handoff'
438
- assert state['mission_anchor'] == mission, 'active bare /cook resume should keep state.json unchanged'
439
- assert plan['mission_anchor'] == mission, 'active bare /cook resume should keep plan.json unchanged'
440
- assert active['mission_anchor'] == mission, 'active bare /cook resume should keep active-slice.json unchanged'
441
- PY
442
-
443
- # Active workflow: even strongly different recent discussion should no longer open chooser/refocus startup
444
- # when no fresh valid explicit handoff is present.
445
- SESSION_ONE_DISCUSSION_REFOCUS="$TMPDIR/session-one-discussion-refocus.jsonl"
446
- DISCUSSION_ONE_DISCUSSION_REFOCUS=$'Mission: Normalize bare /cook planning phrasing into implementation-result missions.\nScope:\n- Replace the current workflow from recent discussion only.\n- Keep the approval-only Start/Cancel gate before rewriting canonical state.\nConstraints:\n- Do not require a fresh explicit handoff.\nAcceptance:\n- Rewrite canonical state from recent discussion.'
447
- DISCUSSION_REFOCUS_ROUTING_ONE="$TMPDIR/active-discussion-refocus-routing.json"
448
- DISCUSSION_REFOCUS_RESUME_ONE="$TMPDIR/active-discussion-refocus-resume.txt"
449
- DISCUSSION_REFOCUS_CHOOSER_ONE="$TMPDIR/unexpected-active-discussion-refocus-chooser.json"
450
- DISCUSSION_REFOCUS_PROPOSAL_ONE="$TMPDIR/unexpected-active-discussion-refocus-proposal.json"
451
- write_session "$SESSION_ONE_DISCUSSION_REFOCUS" "$ROOT" "$DISCUSSION_ONE_DISCUSSION_REFOCUS"
452
-
453
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
454
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
455
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$DISCUSSION_REFOCUS_ROUTING_ONE" \
456
- PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$DISCUSSION_REFOCUS_RESUME_ONE" \
457
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$DISCUSSION_REFOCUS_CHOOSER_ONE" \
458
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_REFOCUS_PROPOSAL_ONE" \
459
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
460
- pi --session "$SESSION_ONE_DISCUSSION_REFOCUS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-discussion-refocus.out" 2>"$TMPDIR/pi-completion-context-proposal-active-discussion-refocus.err"
461
-
462
- python3 - "$DISCUSSION_REFOCUS_ROUTING_ONE" "$DISCUSSION_REFOCUS_RESUME_ONE" "$DISCUSSION_REFOCUS_CHOOSER_ONE" "$DISCUSSION_REFOCUS_PROPOSAL_ONE" <<'PY'
463
- import json
464
- import sys
465
- from pathlib import Path
466
-
467
- mission = 'Remove the completion status line while keeping the completion widget.'
468
- routing = json.loads(Path(sys.argv[1]).read_text())
469
- resume = Path(sys.argv[2]).read_text()
470
- chooser_path = Path(sys.argv[3])
471
- proposal_path = Path(sys.argv[4])
472
- state = json.loads(Path('.agent/state.json').read_text())
473
- plan = json.loads(Path('.agent/plan.json').read_text())
474
- active = json.loads(Path('.agent/active-slice.json').read_text())
475
-
476
- assert routing['mode'] == 'bare', 'discussion-driven refocus removal should snapshot bare routing mode'
477
- assert routing['action'] == 'continue', 'bare /cook should resume instead of deriving a replacement workflow from recent discussion'
478
- assert routing['reason'] == 'missing_explicit_handoff', 'discussion-driven refocus removal should explain that no fresh explicit handoff existed'
479
- assert routing['currentMissionAnchor'] == mission, 'discussion-driven refocus removal should preserve the current mission anchor'
480
- assert routing['proposedMissionAnchor'] is None, 'discussion-driven refocus removal should not preserve a replacement mission from recent discussion'
481
- assert 'Resume the completion workflow from canonical state.' in resume, 'discussion-driven refocus removal should still queue the canonical resume prompt'
482
- assert not chooser_path.exists(), 'discussion-driven refocus removal should not open the chooser'
483
- assert not proposal_path.exists(), 'discussion-driven refocus removal should not open final proposal confirmation'
484
- assert state['mission_anchor'] == mission, 'discussion-driven refocus removal should keep state.json unchanged'
485
- assert plan['mission_anchor'] == mission, 'discussion-driven refocus removal should keep plan.json unchanged'
486
- assert active['mission_anchor'] == mission, 'discussion-driven refocus removal should keep active-slice.json unchanged'
487
- PY
488
-
489
- # Active workflow: summary-only replacement artifacts should also resume the current workflow when no fresh
490
- # explicit handoff exists.
491
- SESSION_ONE_SUMMARY_ONLY="$TMPDIR/session-one-summary-only.jsonl"
492
- SUMMARY_ROUTING_ONE="$TMPDIR/active-summary-only-routing.json"
493
- SUMMARY_RESUME_PROMPT_ONE="$TMPDIR/active-summary-only-resume.txt"
494
- SUMMARY_CHOOSER_ONE="$TMPDIR/unexpected-active-summary-only-chooser.json"
495
- SUMMARY_PROPOSAL_ONE="$TMPDIR/unexpected-active-summary-only-proposal.json"
496
- python3 - "$SESSION_ONE_SUMMARY_ONLY" "$ROOT" <<'PY'
497
- import json
498
- import sys
499
- from pathlib import Path
500
-
501
- session_path = Path(sys.argv[1])
502
- cwd = sys.argv[2]
503
- session_path.parent.mkdir(parents=True, exist_ok=True)
504
- entries = [
505
- {
506
- "type": "session",
507
- "version": 3,
508
- "id": "11111111-1111-4111-8111-111111111111",
509
- "timestamp": "2026-01-01T00:00:00.000Z",
510
- "cwd": cwd,
511
- },
512
- {
513
- "type": "message",
514
- "id": "c3d4e5f6",
515
- "parentId": None,
516
- "timestamp": "2026-01-01T00:00:03.000Z",
517
- "message": {
518
- "role": "branchSummary",
519
- "summary": "Mission: Replace the current workflow from the completed plan summary.\nScope:\n- Refocus to a different mission from this summary artifact alone.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Rewrite canonical state from the summary without new user discussion.",
520
- },
521
- },
522
- ]
523
- with session_path.open('w', encoding='utf-8') as fh:
524
- for entry in entries:
525
- fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
526
- PY
527
-
528
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
529
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
530
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$SUMMARY_ROUTING_ONE" \
531
- PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$SUMMARY_RESUME_PROMPT_ONE" \
532
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$SUMMARY_CHOOSER_ONE" \
533
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$SUMMARY_PROPOSAL_ONE" \
534
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
535
- pi --session "$SESSION_ONE_SUMMARY_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-summary-only.out" 2>"$TMPDIR/pi-completion-context-proposal-active-summary-only.err"
536
-
537
- python3 - "$SUMMARY_ROUTING_ONE" "$SUMMARY_RESUME_PROMPT_ONE" "$SUMMARY_CHOOSER_ONE" "$SUMMARY_PROPOSAL_ONE" <<'PY'
538
- import json
539
- import sys
540
- from pathlib import Path
541
-
542
- mission = 'Remove the completion status line while keeping the completion widget.'
543
- routing = json.loads(Path(sys.argv[1]).read_text())
544
- resume = Path(sys.argv[2]).read_text()
545
- chooser_path = Path(sys.argv[3])
546
- proposal_path = Path(sys.argv[4])
312
+ output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
547
313
  state = json.loads(Path('.agent/state.json').read_text())
548
- plan = json.loads(Path('.agent/plan.json').read_text())
549
- active = json.loads(Path('.agent/active-slice.json').read_text())
314
+ startup_plan = json.loads(Path('.agent/startup-plan.json').read_text())
550
315
 
551
- assert routing['mode'] == 'bare', 'summary-only active bare /cook regression should snapshot bare routing mode'
552
- assert routing['action'] == 'continue', 'summary-only active bare /cook should resume rather than derive replacement startup'
553
- assert routing['reason'] == 'missing_explicit_handoff', 'summary-only active bare /cook should explain that no fresh explicit handoff existed'
554
- assert routing['currentMissionAnchor'] == mission, 'summary-only active bare /cook should preserve the current mission anchor'
555
- assert routing['proposedMissionAnchor'] is None, 'summary-only active bare /cook should not derive a replacement mission from summary artifacts alone'
556
- assert 'Resume the completion workflow from canonical state.' in resume, 'summary-only active bare /cook should still resume the canonical workflow'
557
- assert not chooser_path.exists(), 'summary-only active bare /cook should not open the refocus chooser'
558
- assert not proposal_path.exists(), 'summary-only active bare /cook should not open replacement proposal confirmation'
559
- assert state['mission_anchor'] == mission, 'summary-only active bare /cook should keep state.json unchanged'
560
- assert plan['mission_anchor'] == mission, 'summary-only active bare /cook should keep plan.json unchanged'
561
- assert active['mission_anchor'] == mission, 'summary-only active bare /cook should keep active-slice.json unchanged'
316
+ assert Path('.agent').exists(), 'same-entry primary-agent synthesis should scaffold canonical workflow state'
317
+ assert proposal['source'] == 'deferred_primary_agent_handoff', proposal
318
+ assert state['advisory_startup_brief']['source'] == 'deferred_primary_agent_handoff', state
319
+ assert startup_plan['source'] == 'deferred_primary_agent_handoff', startup_plan
320
+ assert 'Initialized completion control plane' in output, output
562
321
  PY
563
322
 
564
- # Active workflow: a fresh explicit startup-plan preview that is still too weak should fail closed
565
- # without rewriting canonical state.
566
- SESSION_ONE_NON_STARTABLE_ACTIVE="$TMPDIR/session-one-non-startable-active.jsonl"
567
- NON_STARTABLE_ACTIVE_ROUTING="$TMPDIR/active-non-startable-routing.json"
568
- NON_STARTABLE_ACTIVE_RESUME="$TMPDIR/unexpected-active-non-startable-resume.txt"
569
- NON_STARTABLE_ACTIVE_CHOOSER="$TMPDIR/unexpected-active-non-startable-chooser.json"
570
- NON_STARTABLE_ACTIVE_PROPOSAL="$TMPDIR/unexpected-active-non-startable-proposal.json"
571
- NON_STARTABLE_ACTIVE_MESSAGES="$(python3 - <<'PY'
323
+ # Case 2: assistant preview alone is ignored when same-entry synthesis is unavailable.
324
+ ROOT_TWO="$TMPDIR/repo-two"
325
+ SESSION_TWO="$TMPDIR/session-two.jsonl"
326
+ PROPOSAL_TWO="$TMPDIR/proposal-two.json"
327
+ mkdir -p "$ROOT_TWO"
328
+ cd "$ROOT_TWO"
329
+ git init -q
330
+ PREVIEW_MESSAGES="$(python3 - <<'PY'
572
331
  import json
573
332
  capsule = {
574
333
  "kind": "cook_handoff",
575
334
  "source": "primary_agent",
576
335
  "captured_at": "2026-01-01T00:00:02.000Z",
577
336
  "source_turn_id": "m0002",
578
- "mission": "Replace the current widget mission from a vague explicit handoff.",
579
- "scope": [
580
- "Replace the active workflow from a fresh explicit startup-plan preview."
581
- ],
582
- "constraints": [
583
- "Do not rely on recent discussion to fill in missing implementation details."
584
- ],
585
- "acceptance": [
586
- "Current behavior stays understandable."
587
- ],
337
+ "mission": "Assistant preview should not start workflow by itself.",
338
+ "scope": ["Do not trust the old preview as approval-ready startup state."],
339
+ "constraints": ["Require same-entry synthesis."],
340
+ "acceptance": ["Fail closed without canonical state."],
588
341
  "risks": [],
589
- "notes": [
590
- "This capsule is intentionally non-startable for active-workflow fail-closed coverage."
591
- ],
342
+ "notes": [],
592
343
  "handoff_kind": "implementation_workflow_handoff",
593
- "first_slice_goal": "Attempt to replace the active workflow from a vague capsule.",
344
+ "first_slice_goal": "Prove preview-only startup is ignored.",
594
345
  "first_slice_non_goals": [],
595
- "implementation_surfaces": [
596
- "extensions/completion/driver.ts"
597
- ],
598
- "verification_commands": [
599
- "npm run context-proposal-test"
600
- ],
601
- "why_this_slice_first": "Active-workflow replacement should fail closed when the capsule is fresh but not startable.",
602
- "task_type": "completion-workflow",
603
- "evaluation_profile": "completion-rubric-v1"
346
+ "implementation_surfaces": ["scripts/context-proposal-test.sh"],
347
+ "verification_commands": ["npm run context-proposal-test"],
348
+ "why_this_slice_first": "Preview-only startup should no longer bootstrap workflow by itself."
604
349
  }
605
350
  messages = [
606
- {"role": "user", "content": "We may need a different active workflow, but only if there is a fresh explicit startup-plan preview."},
607
- {"role": "assistant", "content": "Only use this capsule if it is concrete enough.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
351
+ {"role": "user", "content": "Should this preview be enough to start workflow on its own?"},
352
+ {"role": "assistant", "content": "Preview only.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
608
353
  ]
609
354
  print(json.dumps(messages, ensure_ascii=False))
610
355
  PY
611
356
  )"
612
- write_session_messages "$SESSION_ONE_NON_STARTABLE_ACTIVE" "$ROOT" "$NON_STARTABLE_ACTIVE_MESSAGES"
357
+ write_session_messages "$SESSION_TWO" "$ROOT_TWO" "$PREVIEW_MESSAGES"
358
+ PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
359
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_TWO" \
360
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
361
+ pi --session "$SESSION_TWO" -e "$PKG_ROOT" -p "/cook" \
362
+ >"$TMPDIR/preview-ignored.out" 2>"$TMPDIR/preview-ignored.err"
613
363
 
614
- python3 - "$TMPDIR/active-non-startable-before.json" <<'PY'
615
- import json
364
+ python3 - "$PROPOSAL_TWO" "$TMPDIR/preview-ignored.out" "$TMPDIR/preview-ignored.err" <<'PY'
616
365
  import sys
617
366
  from pathlib import Path
618
- tracked = {
619
- 'mission.md': Path('.agent/mission.md').read_text(),
620
- 'profile.json': Path('.agent/profile.json').read_text(),
621
- 'state.json': Path('.agent/state.json').read_text(),
622
- 'plan.json': Path('.agent/plan.json').read_text(),
623
- 'active-slice.json': Path('.agent/active-slice.json').read_text(),
624
- 'verification-evidence.json': Path('.agent/verification-evidence.json').read_text(),
625
- }
626
- Path(sys.argv[1]).write_text(json.dumps(tracked, indent=2) + '\n')
367
+
368
+ proposal = Path(sys.argv[1])
369
+ output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
370
+
371
+ assert not Path('.agent').exists(), 'preview-only startup must fail closed without same-entry synthesis'
372
+ assert not proposal.exists(), 'preview-only startup must not emit a proposal snapshot'
373
+ assert '/cook failed closed because the startup-plan step could not prepare a concrete workflow startup plan from the current task context.' in output, output
627
374
  PY
628
375
 
629
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$NON_STARTABLE_ACTIVE_ROUTING" \
630
- PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$NON_STARTABLE_ACTIVE_RESUME" \
631
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$NON_STARTABLE_ACTIVE_CHOOSER" \
632
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$NON_STARTABLE_ACTIVE_PROPOSAL" \
376
+ # Case 3: structured discussion alone no longer falls back when synthesis is unavailable.
377
+ ROOT_THREE="$TMPDIR/repo-three"
378
+ SESSION_THREE="$TMPDIR/session-three.jsonl"
379
+ PROPOSAL_THREE="$TMPDIR/proposal-three.json"
380
+ mkdir -p "$ROOT_THREE"
381
+ cd "$ROOT_THREE"
382
+ git init -q
383
+ DISCUSSION_THREE=$'Mission: Rewrite startup flow from structured discussion only.\nScope:\n- Do not run same-entry synthesis.\nAcceptance:\n- Pretend transcript inference is enough.'
384
+ write_session "$SESSION_THREE" "$ROOT_THREE" "$DISCUSSION_THREE"
385
+ PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
386
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_THREE" \
633
387
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
634
- pi --session "$SESSION_ONE_NON_STARTABLE_ACTIVE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-non-startable.out" 2>"$TMPDIR/pi-completion-context-proposal-active-non-startable.err"
388
+ pi --session "$SESSION_THREE" -e "$PKG_ROOT" -p "/cook" \
389
+ >"$TMPDIR/discussion-ignored.out" 2>"$TMPDIR/discussion-ignored.err"
635
390
 
636
- python3 - "$NON_STARTABLE_ACTIVE_ROUTING" "$NON_STARTABLE_ACTIVE_RESUME" "$NON_STARTABLE_ACTIVE_CHOOSER" "$NON_STARTABLE_ACTIVE_PROPOSAL" "$TMPDIR/pi-completion-context-proposal-active-non-startable.out" "$TMPDIR/pi-completion-context-proposal-active-non-startable.err" "$TMPDIR/active-non-startable-before.json" <<'PY'
637
- import json
391
+ python3 - "$PROPOSAL_THREE" "$TMPDIR/discussion-ignored.out" "$TMPDIR/discussion-ignored.err" <<'PY'
638
392
  import sys
639
393
  from pathlib import Path
640
394
 
641
- routing = json.loads(Path(sys.argv[1]).read_text())
642
- resume_path = Path(sys.argv[2])
643
- chooser_path = Path(sys.argv[3])
644
- proposal_path = Path(sys.argv[4])
645
- output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
646
- before = json.loads(Path(sys.argv[7]).read_text())
647
- after = {
648
- 'mission.md': Path('.agent/mission.md').read_text(),
649
- 'profile.json': Path('.agent/profile.json').read_text(),
650
- 'state.json': Path('.agent/state.json').read_text(),
651
- 'plan.json': Path('.agent/plan.json').read_text(),
652
- 'active-slice.json': Path('.agent/active-slice.json').read_text(),
653
- 'verification-evidence.json': Path('.agent/verification-evidence.json').read_text(),
654
- }
395
+ proposal = Path(sys.argv[1])
396
+ output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
655
397
 
656
- assert routing['mode'] == 'bare', 'fresh non-startable explicit handoff should snapshot bare routing mode'
657
- assert routing['action'] == 'blocked', 'fresh non-startable explicit handoff should fail closed for active bare /cook'
658
- assert routing['reason'] == 'fresh_explicit_handoff_not_startable', 'fresh non-startable explicit handoff should keep the dedicated explicit-handoff fail-closed reason'
659
- assert 'fresh explicit primary-agent startup plan exists' in routing['blockedFailureMessage'], 'fresh non-startable explicit handoff should surface the dedicated fail-closed message'
660
- 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'
661
- assert not resume_path.exists(), 'fresh non-startable explicit handoff should not queue a resume prompt'
662
- assert not chooser_path.exists(), 'fresh non-startable explicit handoff should not open the replacement chooser'
663
- assert not proposal_path.exists(), 'fresh non-startable explicit handoff should not open final proposal confirmation'
664
- assert 'fresh explicit primary-agent startup plan exists' in output, 'fresh non-startable explicit handoff should explain that the explicit capsule blocked active-workflow replacement'
665
- assert before == after, 'fresh non-startable explicit handoff should leave canonical state unchanged'
398
+ assert not Path('.agent').exists(), 'structured discussion alone must fail closed without same-entry synthesis'
399
+ assert not proposal.exists(), 'structured discussion alone must not emit a proposal snapshot'
400
+ assert '/cook failed closed because the startup-plan step could not prepare a concrete workflow startup plan from the current task context.' in output, output
666
401
  PY
667
402
 
668
- # Completed workflow: bare /cook should suppress proposals that simply restate the completed mission
669
- # without a clear reopen or next-round signal.
670
- mark_done
403
+ # Case 4: non-startable synthesized startup plans fail closed with the dedicated same-entry message.
404
+ ROOT_FOUR="$TMPDIR/repo-four"
405
+ SESSION_FOUR="$TMPDIR/session-four.jsonl"
406
+ PROPOSAL_FOUR="$TMPDIR/proposal-four.json"
407
+ mkdir -p "$ROOT_FOUR"
408
+ cd "$ROOT_FOUR"
409
+ git init -q
410
+ write_session "$SESSION_FOUR" "$ROOT_FOUR" "$STARTUP_DISCUSSION"
411
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$NON_STARTABLE_HANDOFF" \
412
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_FOUR" \
413
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
414
+ pi --session "$SESSION_FOUR" -e "$PKG_ROOT" -p "/cook" \
415
+ >"$TMPDIR/non-startable-startup.out" 2>"$TMPDIR/non-startable-startup.err"
671
416
 
672
- SESSION_TWO_COMPLETED_SUPPRESS="$TMPDIR/session-two-completed-suppress.jsonl"
673
- CURRENT_DONE_MISSION="$(python3 - <<'PY'
674
- import json
417
+ python3 - "$PROPOSAL_FOUR" "$TMPDIR/non-startable-startup.out" "$TMPDIR/non-startable-startup.err" <<'PY'
418
+ import sys
675
419
  from pathlib import Path
676
- print(json.loads(Path('.agent/state.json').read_text())['mission_anchor'])
677
- PY
678
- )"
679
- DISCUSSION_TWO_COMPLETED_SUPPRESS="Mission: ${CURRENT_DONE_MISSION}
680
- Scope:
681
- - Keep the current completed mission exactly as-is.
682
- Constraints:
683
- - Do not start a different workflow from this discussion.
684
- Acceptance:
685
- - Keep the finished mission closed and unchanged."
686
- DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS="$TMPDIR/context-proposal-next-round-completed-suppress.json"
687
- write_session "$SESSION_TWO_COMPLETED_SUPPRESS" "$ROOT" "$DISCUSSION_TWO_COMPLETED_SUPPRESS"
688
420
 
689
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
690
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
691
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
692
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS" \
421
+ proposal = Path(sys.argv[1])
422
+ output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
423
+
424
+ assert not Path('.agent').exists(), 'non-startable synthesized startup must fail closed'
425
+ assert not proposal.exists(), 'non-startable synthesized startup must not emit a proposal snapshot'
426
+ assert 'same-entry primary-agent startup-plan synthesis step returned a startup plan that is still not concrete enough' in output, output
427
+ assert 'acceptance is not anchored to concrete repo changes or verification' in output, output
428
+ PY
429
+
430
+ # Case 5: active workflow resumes when same-entry synthesis matches the current mission.
431
+ ROOT_FIVE="$TMPDIR/repo-five"
432
+ SESSION_FIVE_BOOTSTRAP="$TMPDIR/session-five-bootstrap.jsonl"
433
+ SESSION_FIVE_MATCHING="$TMPDIR/session-five-matching.jsonl"
434
+ ROUTING_FIVE="$TMPDIR/routing-five.json"
435
+ CHOOSER_FIVE="$TMPDIR/chooser-five.json"
436
+ BASELINE_FIVE="$TMPDIR/baseline-five.json"
437
+ bootstrap_workflow "$ROOT_FIVE" "$SESSION_FIVE_BOOTSTRAP" "$STARTUP_DISCUSSION" "$STARTUP_HANDOFF"
438
+ cd "$ROOT_FIVE"
439
+ snapshot_tracked "$BASELINE_FIVE"
440
+ write_session "$SESSION_FIVE_MATCHING" "$ROOT_FIVE" $'Continue the widget mission and keep the workflow aligned.'
441
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$MATCHING_HANDOFF" \
442
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ROUTING_FIVE" \
443
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CHOOSER_FIVE" \
693
444
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
694
- pi --session "$SESSION_TWO_COMPLETED_SUPPRESS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.err"
445
+ pi --session "$SESSION_FIVE_MATCHING" -e "$PKG_ROOT" -p "/cook" \
446
+ >"$TMPDIR/matching-active.out" 2>"$TMPDIR/matching-active.err"
695
447
 
696
- python3 - "$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.out" "$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.err" "$DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS" "$CURRENT_DONE_MISSION" <<'PY'
448
+ python3 - "$ROUTING_FIVE" "$CHOOSER_FIVE" "$BASELINE_FIVE" <<'PY'
697
449
  import json
698
450
  import sys
699
451
  from pathlib import Path
700
452
 
701
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
702
- snapshot = Path(sys.argv[3])
703
- expected = sys.argv[4]
704
- state = json.loads(Path('.agent/state.json').read_text())
705
-
706
- assert state['mission_anchor'] == expected, 'completed-topic suppression should keep the done workflow mission anchor unchanged'
707
- assert state['continuation_policy'] == 'done', 'completed-topic suppression should keep the workflow closed'
708
- assert not snapshot.exists(), 'completed-topic suppression should not emit a proposal snapshot when the latest discussion only repeats finished work'
709
- assert 'startup-plan step could not prepare a concrete workflow startup plan' in output, 'completed-topic suppression should fail closed when no concrete startup plan can be prepared'
710
- PY
453
+ routing = json.loads(Path(sys.argv[1]).read_text())
454
+ chooser = Path(sys.argv[2])
455
+ before = json.loads(Path(sys.argv[3]).read_text())
456
+ tracked = [
457
+ Path('.agent/mission.md'),
458
+ Path('.agent/profile.json'),
459
+ Path('.agent/state.json'),
460
+ Path('.agent/startup-plan.json'),
461
+ Path('.agent/startup-plan.md'),
462
+ Path('.agent/plan.json'),
463
+ Path('.agent/active-slice.json'),
464
+ Path('.agent/verification-evidence.json'),
465
+ ]
466
+ after = {path.name: path.read_text() for path in tracked}
467
+ state = json.loads(after['state.json'])
468
+
469
+ assert routing['action'] == 'continue', routing
470
+ assert routing['reason'] == 'matching_generated_startup_plan', routing
471
+ assert routing['currentMissionAnchor'] == state['mission_anchor'], routing
472
+ assert routing['proposedMissionAnchor'] == state['mission_anchor'], routing
473
+ assert not chooser.exists(), 'matching synthesized mission should not open the chooser'
474
+ assert before == after, 'matching synthesized mission should leave canonical state unchanged before resume'
475
+ PY
476
+
477
+ # Case 6: non-startable synthesized active replacement blocks without rewriting canonical state.
478
+ ROOT_SIX="$TMPDIR/repo-six"
479
+ SESSION_SIX_BOOTSTRAP="$TMPDIR/session-six-bootstrap.jsonl"
480
+ SESSION_SIX_BLOCKED="$TMPDIR/session-six-blocked.jsonl"
481
+ ROUTING_SIX="$TMPDIR/routing-six.json"
482
+ CHOOSER_SIX="$TMPDIR/chooser-six.json"
483
+ PROPOSAL_SIX="$TMPDIR/proposal-six.json"
484
+ BASELINE_SIX="$TMPDIR/baseline-six.json"
485
+ bootstrap_workflow "$ROOT_SIX" "$SESSION_SIX_BOOTSTRAP" "$STARTUP_DISCUSSION" "$STARTUP_HANDOFF"
486
+ cd "$ROOT_SIX"
487
+ snapshot_tracked "$BASELINE_SIX"
488
+ write_session "$SESSION_SIX_BLOCKED" "$ROOT_SIX" $'Maybe replace the current mission, but the new intent is still vague.'
489
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$NON_STARTABLE_HANDOFF" \
490
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ROUTING_SIX" \
491
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CHOOSER_SIX" \
492
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_SIX" \
493
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
494
+ pi --session "$SESSION_SIX_BLOCKED" -e "$PKG_ROOT" -p "/cook" \
495
+ >"$TMPDIR/blocked-active.out" 2>"$TMPDIR/blocked-active.err"
711
496
 
712
- # Completed workflow: bare /cook should also suppress proposals that merely restate canonical
713
- # verification evidence for already verified work.
714
- python3 - <<'PY'
497
+ python3 - "$ROUTING_SIX" "$CHOOSER_SIX" "$PROPOSAL_SIX" "$BASELINE_SIX" "$TMPDIR/blocked-active.out" "$TMPDIR/blocked-active.err" <<'PY'
715
498
  import json
499
+ import sys
716
500
  from pathlib import Path
717
501
 
718
- state = json.loads(Path('.agent/state.json').read_text())
719
- state['latest_verified_slice'] = 'verified-logout-redirect'
720
- Path('.agent/state.json').write_text(json.dumps(state, indent=2) + '\n')
721
-
722
- evidence = json.loads(Path('.agent/verification-evidence.json').read_text())
723
- evidence.update({
724
- 'subject_type': 'selected_slice',
725
- 'slice_id': 'verified-logout-redirect',
726
- 'goal': 'Add logout redirect regression coverage.',
727
- 'summary': 'Verified logout redirect regression coverage already matches the selected slice and current HEAD.',
728
- 'outcome': 'pass',
729
- })
730
- Path('.agent/verification-evidence.json').write_text(json.dumps(evidence, indent=2) + '\n')
731
- PY
732
-
733
- SESSION_TWO_VERIFIED_SUPPRESS="$TMPDIR/session-two-verified-suppress.jsonl"
734
- DISCUSSION_TWO_VERIFIED_SUPPRESS=$'Mission: Add logout redirect regression coverage.\nScope:\n- Add coverage for logout redirect behavior.\nConstraints:\n- Do not change the verified logout redirect work.\nAcceptance:\n- Keep the verified logout redirect regression coverage unchanged.'
735
- DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS="$TMPDIR/context-proposal-next-round-verified-suppress.json"
736
- write_session "$SESSION_TWO_VERIFIED_SUPPRESS" "$ROOT" "$DISCUSSION_TWO_VERIFIED_SUPPRESS"
502
+ routing = json.loads(Path(sys.argv[1]).read_text())
503
+ chooser = Path(sys.argv[2])
504
+ proposal = Path(sys.argv[3])
505
+ before = json.loads(Path(sys.argv[4]).read_text())
506
+ output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
507
+ tracked = [
508
+ Path('.agent/mission.md'),
509
+ Path('.agent/profile.json'),
510
+ Path('.agent/state.json'),
511
+ Path('.agent/startup-plan.json'),
512
+ Path('.agent/startup-plan.md'),
513
+ Path('.agent/plan.json'),
514
+ Path('.agent/active-slice.json'),
515
+ Path('.agent/verification-evidence.json'),
516
+ ]
517
+ after = {path.name: path.read_text() for path in tracked}
737
518
 
519
+ assert routing['action'] == 'blocked', routing
520
+ assert routing['reason'] == 'generated_startup_plan_not_startable', routing
521
+ assert 'same-entry primary-agent startup-plan synthesis step returned a startup plan that is still not concrete enough' in routing['blockedFailureMessage'], routing
522
+ assert not chooser.exists(), 'blocked replacement should not open the chooser'
523
+ assert not proposal.exists(), 'blocked replacement should not open final proposal confirmation'
524
+ assert before == after, 'blocked replacement should leave canonical state unchanged'
525
+ assert 'same-entry primary-agent startup-plan synthesis step returned a startup plan that is still not concrete enough' in output, output
526
+ PY
527
+
528
+ # Case 7: done workflow starts the next round from same-entry synthesis.
529
+ ROOT_SEVEN="$TMPDIR/repo-seven"
530
+ SESSION_SEVEN_BOOTSTRAP="$TMPDIR/session-seven-bootstrap.jsonl"
531
+ SESSION_SEVEN_NEXT="$TMPDIR/session-seven-next.jsonl"
532
+ PROPOSAL_SEVEN="$TMPDIR/proposal-seven.json"
533
+ bootstrap_workflow "$ROOT_SEVEN" "$SESSION_SEVEN_BOOTSTRAP" "$STARTUP_DISCUSSION" "$STARTUP_HANDOFF"
534
+ cd "$ROOT_SEVEN"
535
+ mark_done
536
+ write_session "$SESSION_SEVEN_NEXT" "$ROOT_SEVEN" $'Start the next workflow round for widget follow-up docs.'
738
537
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
739
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
740
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
741
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS" \
538
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$NEXT_ROUND_HANDOFF" \
539
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_SEVEN" \
742
540
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
743
- pi --session "$SESSION_TWO_VERIFIED_SUPPRESS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.err"
541
+ pi --session "$SESSION_SEVEN_NEXT" -e "$PKG_ROOT" -p "/cook" \
542
+ >"$TMPDIR/next-round.out" 2>"$TMPDIR/next-round.err"
744
543
 
745
- python3 - "$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.out" "$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.err" "$DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS" <<'PY'
544
+ python3 - "$PROPOSAL_SEVEN" "$TMPDIR/next-round.out" "$TMPDIR/next-round.err" <<'PY'
545
+ import json
746
546
  import sys
747
547
  from pathlib import Path
748
548
 
749
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
750
- snapshot = Path(sys.argv[3])
751
- assert not snapshot.exists(), 'verification-evidence overlap suppression should not emit a proposal snapshot for already verified work'
752
- assert 'startup-plan step could not prepare a concrete workflow startup plan' in output, 'verification-evidence overlap suppression should fail closed when no concrete startup plan can be prepared'
753
- PY
754
-
755
- # Completed workflow: bare /cook should fail closed for next-round discussion-only startup too,
756
- # even when the discussion is well structured.
757
- SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
758
- 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.'
759
- DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
760
- write_session "$SESSION_TWO_NORMALIZED" "$ROOT" "$DISCUSSION_TWO_NORMALIZED"
761
-
762
- GENERATED_HANDOFF_TWO_NORMALIZED="$(python3 - <<'PY'
763
- import json
764
- capsule = {
765
- "kind": "cook_handoff",
766
- "source": "primary_agent",
767
- "mission": "Start the next workflow round with a normalized implementation mission.",
768
- "scope": [
769
- "Reset canonical state for the new implementation mission.",
770
- "Keep the next round distinct from the completed workflow."
771
- ],
772
- "constraints": [
773
- "Do not resume the completed workflow when the new round is clearly different."
774
- ],
775
- "acceptance": [
776
- "Reset canonical state back to reground for the new mission.",
777
- "Preserve the tracked completion control-plane files."
778
- ],
779
- "risks": [],
780
- "notes": ["Generated by the primary-agent startup-plan step triggered from /cook."],
781
- "handoff_kind": "implementation_workflow_handoff",
782
- "first_slice_goal": "Bootstrap the next workflow round from the normalized implementation mission.",
783
- "first_slice_non_goals": ["Do not reopen finished slices from the previous workflow."],
784
- "implementation_surfaces": ["extensions/completion/driver.ts", "scripts/context-proposal-test.sh"],
785
- "verification_commands": ["npm run context-proposal-test"],
786
- "why_this_slice_first": "The user explicitly chose workflow mode for a bounded next-round restart.",
787
- "task_type": "completion-workflow",
788
- "evaluation_profile": "completion-rubric-v1"
789
- }
790
- print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
791
- PY
792
- )"
793
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$GENERATED_HANDOFF_TWO_NORMALIZED" PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 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"
794
-
795
- python3 - "$TMPDIR/pi-completion-context-proposal-next-round-normalized.out" "$TMPDIR/pi-completion-context-proposal-next-round-normalized.err" "$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" "$CURRENT_DONE_MISSION" <<'PY'
796
- import json
797
- import sys
798
- from pathlib import Path
799
-
800
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
801
- snapshot = Path(sys.argv[3])
802
- previous = sys.argv[4]
803
- state = json.loads(Path('.agent/state.json').read_text())
804
- if snapshot.exists():
805
- proposal = json.loads(snapshot.read_text())
806
- assert proposal['source'] == 'handoff_capsule', 'done-workflow generated startup should snapshot the primary-agent startup-plan source'
807
- assert state['mission_anchor'] != previous, 'done-workflow discussion-only startup should advance to the new mission anchor'
808
- assert state['continuation_policy'] == 'continue', 'done-workflow discussion-only startup should reopen workflow state'
809
- assert 'Started a new completion workflow round and recorded the approved startup plan' in output, 'done-workflow generated startup should report approved startup-plan capture'
810
- PY
811
-
812
- # Completed workflow: a fresh explicit primary-agent startup-plan preview should still start the next round.
813
- mark_done
814
-
815
- SESSION_TWO="$TMPDIR/session-two.jsonl"
816
- DISCUSSION_SNAPSHOT_TWO="$TMPDIR/context-proposal-next-round-explicit-handoff.json"
817
- HANDOFF_MESSAGES_TWO="$(python3 - <<'PY'
818
- import json
819
- capsule = {
820
- "kind": "cook_handoff",
821
- "source": "primary_agent",
822
- "captured_at": "2026-01-01T00:00:02.000Z",
823
- "source_turn_id": "m0002",
824
- "mission": "Ship the next workflow round from a fresh explicit startup-plan preview.",
825
- "scope": [
826
- "Reset canonical state back to reground for the fresh mission.",
827
- "Preserve the tracked completion control-plane files."
828
- ],
829
- "constraints": [
830
- "Do not resume the completed workflow when the new round is clearly different."
831
- ],
832
- "acceptance": [
833
- "Reset canonical state back to reground for the new mission.",
834
- "Preserve the tracked completion control-plane files."
835
- ],
836
- "risks": [
837
- "Done-state history could override the fresh mission if the explicit handoff is ignored."
838
- ],
839
- "notes": [
840
- "This next round must come from the fresh explicit startup-plan preview rather than recent discussion."
841
- ],
842
- "handoff_kind": "implementation_workflow_handoff",
843
- "first_slice_goal": "Start the next round from the fresh explicit startup-plan preview and preserve canonical control-plane files.",
844
- "first_slice_non_goals": [
845
- "Do not resume the completed workflow when the new round is clearly different."
846
- ],
847
- "implementation_surfaces": [
848
- "extensions/completion/driver.ts",
849
- "scripts/context-proposal-test.sh"
850
- ],
851
- "verification_commands": [
852
- "npm run context-proposal-test"
853
- ],
854
- "why_this_slice_first": "The fresh explicit startup-plan preview is the smallest truthful next-round startup after the previous workflow closed.",
855
- "task_type": "completion-workflow",
856
- "evaluation_profile": "completion-rubric-v1",
857
- "why_cook_now": "A new implementation-ready mission was identified after the previous round closed."
858
- }
859
- messages = [
860
- {"role": "user", "content": "The previous round is done, but there is a fresh next round ready for /cook."},
861
- {"role": "assistant", "content": "The next round is ready for /cook. Run /cook to confirm this fresh implementation mission.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
862
- ]
863
- print(json.dumps(messages, ensure_ascii=False))
864
- PY
865
- )"
866
- write_session_messages "$SESSION_TWO" "$ROOT" "$HANDOFF_MESSAGES_TWO"
867
-
868
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
869
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO" \
870
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
871
- pi --session "$SESSION_TWO" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round.err"
872
-
873
- python3 - "$DISCUSSION_SNAPSHOT_TWO" <<'PY'
874
- import json
875
- import sys
876
- from pathlib import Path
877
-
878
- mission = 'Ship the next workflow round from a fresh explicit startup-plan preview.'
879
- expected_task_type = 'completion-workflow'
880
- expected_eval_profile = 'completion-rubric-v1'
881
- mission_text = Path('.agent/mission.md').read_text()
882
- profile = json.loads(Path('.agent/profile.json').read_text())
883
- state = json.loads(Path('.agent/state.json').read_text())
884
- plan = json.loads(Path('.agent/plan.json').read_text())
885
- active = json.loads(Path('.agent/active-slice.json').read_text())
886
549
  proposal = json.loads(Path(sys.argv[1]).read_text())
887
-
888
- assert mission in mission_text, '.agent/mission.md did not update to the next-round explicit-handoff mission anchor'
889
- assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after next-round explicit handoff startup'
890
- assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after next-round explicit handoff startup'
891
- assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after starting the next workflow round from explicit handoff'
892
- assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after starting the next workflow round from explicit handoff'
893
- assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after starting the next workflow round from explicit handoff'
894
- assert state['advisory_startup_brief']['mission'] == mission, 'next-round explicit startup plan should preserve the confirmed startup plan as advisory intake'
895
- assert state['advisory_startup_brief']['source'] == 'primary_agent_handoff', 'next-round explicit handoff should preserve the handoff advisory source'
896
- assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after starting the next workflow round from explicit handoff'
897
- assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after starting the next workflow round from explicit handoff'
898
- assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after starting the next workflow round from explicit handoff'
899
- assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after starting the next workflow round from explicit handoff'
900
- assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after starting the next workflow round from explicit handoff'
901
- assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after starting the next workflow round from explicit handoff'
902
- assert proposal['mission'] == mission, 'next-round explicit handoff proposal snapshot should preserve the handoff mission anchor'
903
- assert proposal['source'] == 'handoff_capsule', 'next-round explicit handoff proposal snapshot should record the handoff capsule source'
904
- assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground for the next workflow round'
905
- assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue for the next workflow round'
906
- assert state['requires_reground'] is True, 'requires_reground should reset to true for the next workflow round'
907
- assert state['project_done'] is False, 'project_done should reset to false for the next workflow round'
908
- assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should reset to completion-regrounder for the next workflow round'
909
- assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the next-round refocus'
910
- assert 'task_type=completion-workflow' in state['continuation_reason'], 'next-round refocus should persist the selected task_type'
911
- assert 'evaluation_profile=completion-rubric-v1' in state['continuation_reason'], 'next-round refocus should persist the selected evaluation_profile'
912
- assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_refocus for the next workflow round'
913
- assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
914
- PY
915
-
916
- # Active workflow: inline `/cook` arguments should fail closed immediately and leave canonical state unchanged.
917
- ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
918
- ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
919
- ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
920
- ACTIVE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-active-inline-before.json"
921
- python3 - "$ACTIVE_INLINE_REJECTION_BASELINE" <<'PY'
922
- import json
923
- import sys
924
- from pathlib import Path
925
-
926
- tracked = [
927
- Path('.agent/mission.md'),
928
- Path('.agent/profile.json'),
929
- Path('.agent/state.json'),
930
- Path('.agent/plan.json'),
931
- Path('.agent/active-slice.json'),
932
- Path('.agent/verification-evidence.json'),
933
- ]
934
- Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
935
- PY
936
-
937
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$ACTIVE_INLINE_REJECTION_PROPOSAL" \
938
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ACTIVE_INLINE_REJECTION_ROUTING" \
939
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$ACTIVE_INLINE_REJECTION_CHOOSER" \
940
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
941
- pi -e "$PKG_ROOT" -p "/cook Replacement mission for the active workflow" >"$TMPDIR/pi-completion-context-proposal-active-inline-arg.out" 2>"$TMPDIR/pi-completion-context-proposal-active-inline-arg.err"
942
-
943
- python3 - "$TMPDIR/pi-completion-context-proposal-active-inline-arg.out" "$TMPDIR/pi-completion-context-proposal-active-inline-arg.err" "$ACTIVE_INLINE_REJECTION_ROUTING" "$ACTIVE_INLINE_REJECTION_PROPOSAL" "$ACTIVE_INLINE_REJECTION_CHOOSER" "$ACTIVE_INLINE_REJECTION_BASELINE" <<'PY'
944
- import json
945
- import sys
946
- from pathlib import Path
947
-
948
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
949
- routing = Path(sys.argv[3])
950
- proposal = Path(sys.argv[4])
951
- chooser = Path(sys.argv[5])
952
- before = json.loads(Path(sys.argv[6]).read_text())
953
- tracked = [
954
- Path('.agent/mission.md'),
955
- Path('.agent/profile.json'),
956
- Path('.agent/state.json'),
957
- Path('.agent/plan.json'),
958
- Path('.agent/active-slice.json'),
959
- Path('.agent/verification-evidence.json'),
960
- ]
961
-
962
- assert not routing.exists(), 'active /cook inline-args rejection should not run active-workflow routing'
963
- assert not proposal.exists(), 'active /cook inline-args rejection should not emit a replacement startup-plan proposal'
964
- assert not chooser.exists(), 'active /cook inline-args rejection should not open the existing-workflow chooser'
965
- assert '/cook no longer accepts inline arguments.' in output, 'active /cook inline-args rejection should explain the bare-only entry contract'
966
- after = {path.name: path.read_text() for path in tracked}
967
- assert before == after, 'active /cook inline-args rejection should leave canonical files unchanged'
968
- PY
969
-
970
- # Completed workflow: inline `/cook` arguments should also fail closed before any next-round proposal derivation.
971
- mark_done
972
-
973
- DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
974
- DONE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-done-inline-arg-proposal.json"
975
- DONE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-done-inline-arg-chooser.json"
976
- DONE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-done-inline-before.json"
977
- python3 - "$DONE_INLINE_REJECTION_BASELINE" <<'PY'
978
- import json
979
- import sys
980
- from pathlib import Path
981
-
982
- tracked = [
983
- Path('.agent/mission.md'),
984
- Path('.agent/profile.json'),
985
- Path('.agent/state.json'),
986
- Path('.agent/plan.json'),
987
- Path('.agent/active-slice.json'),
988
- Path('.agent/verification-evidence.json'),
989
- ]
990
- Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
991
- PY
992
-
993
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DONE_INLINE_REJECTION_PROPOSAL" \
994
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$DONE_INLINE_REJECTION_ROUTING" \
995
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$DONE_INLINE_REJECTION_CHOOSER" \
996
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
997
- pi -e "$PKG_ROOT" -p "/cook done-workflow replacement mission" >"$TMPDIR/pi-completion-context-proposal-done-inline-arg.out" 2>"$TMPDIR/pi-completion-context-proposal-done-inline-arg.err"
998
-
999
- python3 - "$TMPDIR/pi-completion-context-proposal-done-inline-arg.out" "$TMPDIR/pi-completion-context-proposal-done-inline-arg.err" "$DONE_INLINE_REJECTION_ROUTING" "$DONE_INLINE_REJECTION_PROPOSAL" "$DONE_INLINE_REJECTION_CHOOSER" "$DONE_INLINE_REJECTION_BASELINE" <<'PY'
1000
- import json
1001
- import sys
1002
- from pathlib import Path
1003
-
1004
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
1005
- routing = Path(sys.argv[3])
1006
- proposal = Path(sys.argv[4])
1007
- chooser = Path(sys.argv[5])
1008
- before = json.loads(Path(sys.argv[6]).read_text())
1009
- tracked = [
1010
- Path('.agent/mission.md'),
1011
- Path('.agent/profile.json'),
1012
- Path('.agent/state.json'),
1013
- Path('.agent/plan.json'),
1014
- Path('.agent/active-slice.json'),
1015
- Path('.agent/verification-evidence.json'),
1016
- ]
1017
- state_before = json.loads(before['state.json'])
1018
- assert state_before['current_phase'] == 'done', 'done /cook inline-args rejection should start from a completed workflow'
1019
- assert state_before['project_done'] is True, 'done /cook inline-args rejection should start from project_done=true'
1020
- assert not routing.exists(), 'done /cook inline-args rejection should not run active-workflow routing while starting the next round'
1021
- assert not proposal.exists(), 'done /cook inline-args rejection should not emit a next-round startup-plan proposal'
1022
- assert not chooser.exists(), 'done /cook inline-args rejection should not open the existing-workflow chooser when starting the next round'
1023
- assert '/cook no longer accepts inline arguments.' in output, 'done /cook inline-args rejection should explain the bare-only entry contract'
1024
- after = {path.name: path.read_text() for path in tracked}
1025
- assert before == after, 'done /cook inline-args rejection should leave canonical files unchanged'
1026
- PY
1027
-
1028
- # Completed workflow again: model-assisted discussion analysis alone should still fail closed
1029
- # without a fresh explicit primary-agent startup-plan preview.
1030
- mark_done
1031
-
1032
- SESSION_FIVE="$TMPDIR/session-five.jsonl"
1033
- DISCUSSION_FIVE=$'I do not want to rewrite the parser. The safer path is to let /cook analyze the discussion first, keep the discussion-derived mission anchored once it is clear, and ignore stale scope that drifted in from earlier turns. We should still prove it with a regression test before writing canonical state.'
1034
- ANALYST_OUTPUT_FIVE='{"mission":"Use a proposal analyst to summarize natural discussion before /cook writes canonical state.","scope":["Keep the discussion-derived mission anchored once it is clear.","Drop stale scope from earlier turns."],"constraints":["Do not rewrite the parser."],"acceptance":["Add a regression test."],"confidence":0.91,"possible_noise":["old unrelated scope"]}'
1035
- DISCUSSION_SNAPSHOT_FIVE="$TMPDIR/context-proposal-analyst-restart-rejected.json"
1036
- write_session "$SESSION_FIVE" "$ROOT" "$DISCUSSION_FIVE"
1037
-
1038
- GENERATED_HANDOFF_FIVE="$(python3 - <<'PY'
1039
- import json
1040
- capsule = {
1041
- "kind": "cook_handoff",
1042
- "source": "primary_agent",
1043
- "mission": "Use the analyst-backed parser follow-up as the next workflow round.",
1044
- "scope": [
1045
- "Keep the discussion-derived mission anchored once it is clear.",
1046
- "Drop stale scope from earlier turns."
1047
- ],
1048
- "constraints": [
1049
- "Do not rewrite the parser."
1050
- ],
1051
- "acceptance": [
1052
- "Add a regression test."
1053
- ],
1054
- "risks": [],
1055
- "notes": ["Generated by the primary-agent startup-plan step triggered from /cook."],
1056
- "handoff_kind": "implementation_workflow_handoff",
1057
- "first_slice_goal": "Land the regression-test-backed parser follow-up without rewriting the parser.",
1058
- "first_slice_non_goals": ["Do not broaden the mission with stale scope."],
1059
- "implementation_surfaces": ["scripts/context-proposal-test.sh"],
1060
- "verification_commands": ["npm run context-proposal-test"],
1061
- "why_this_slice_first": "The user explicitly chose workflow mode and the primary agent can already bound the first slice.",
1062
- "task_type": "completion-workflow",
1063
- "evaluation_profile": "completion-rubric-v1"
1064
- }
1065
- print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
1066
- PY
1067
- )"
1068
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_FIVE" PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$GENERATED_HANDOFF_FIVE" PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_FIVE" PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 pi --session "$SESSION_FIVE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-analyst.out" 2>"$TMPDIR/pi-completion-context-proposal-analyst.err"
1069
-
1070
- python3 - "$TMPDIR/pi-completion-context-proposal-analyst.out" "$TMPDIR/pi-completion-context-proposal-analyst.err" "$DISCUSSION_SNAPSHOT_FIVE" <<'PY'
1071
- import json
1072
- import sys
1073
- from pathlib import Path
1074
-
1075
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
1076
- snapshot = Path(sys.argv[3])
1077
- state = json.loads(Path('.agent/state.json').read_text())
1078
-
1079
- if snapshot.exists():
1080
- pass
1081
- assert state['continuation_policy'] == 'continue', 'done-workflow analyst-backed primary-agent startup plan should reopen the workflow'
1082
- assert 'Started a new completion workflow round and recorded the approved startup plan' in output, 'done-workflow analyst-backed startup should report approved startup-plan capture'
1083
- PY
1084
-
1085
- # Custom confirmation UI: start should render proposal content separately from approval-only Start/Cancel actions.
1086
- UI_ROOT_START="$TMPDIR/ui-root-start"
1087
- mkdir -p "$UI_ROOT_START"
1088
- cd "$UI_ROOT_START"
1089
- git init -q
1090
-
1091
- UI_SESSION_START="$TMPDIR/ui-session-start.jsonl"
1092
- UI_SNAPSHOT_START="$TMPDIR/context-proposal-ui-start.json"
1093
- UI_MESSAGES_START="$(python3 - <<'PY'
1094
- import json
1095
- capsule = {
1096
- "kind": "cook_handoff",
1097
- "source": "primary_agent",
1098
- "captured_at": "2026-01-01T00:00:02.000Z",
1099
- "source_turn_id": "m0002",
1100
- "mission": "Replace the crowded selector with a clearer action layout.",
1101
- "scope": ["Separate proposal text from actions."],
1102
- "constraints": ["Preserve approval-only Start/Cancel behavior."],
1103
- "acceptance": ["Add regression coverage."],
1104
- "risks": ["Bundling critique into the action list would make the confirmation harder to scan."],
1105
- "notes": ["Keep critique details separate from the approval-only proposal summary.", "Possible noise: old selector wording"],
1106
- "handoff_kind": "implementation_workflow_handoff",
1107
- "first_slice_goal": "Separate the proposal text from the approval-only Start/Cancel actions.",
1108
- "first_slice_non_goals": [],
1109
- "implementation_surfaces": ["extensions/completion/prompt-surfaces.ts"],
1110
- "verification_commands": ["npm run context-proposal-test"],
1111
- "why_this_slice_first": "The confirmation layout regression is small and directly testable.",
1112
- "task_type": "completion-workflow",
1113
- "evaluation_profile": "completion-rubric-v1",
1114
- "why_cook_now": "The explicit handoff is concrete enough to exercise the startup confirmation UI."
1115
- }
1116
- messages = [
1117
- {"role": "user", "content": "Prepare the confirmation-layout work and tell me when it is ready for /cook."},
1118
- {"role": "assistant", "content": "The confirmation-layout work is ready for /cook.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1119
- ]
1120
- print(json.dumps(messages, ensure_ascii=False))
1121
- PY
1122
- )"
1123
- write_session_messages "$UI_SESSION_START" "$UI_ROOT_START" "$UI_MESSAGES_START"
1124
-
1125
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_ACTION=start \
1126
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_PATH="$UI_SNAPSHOT_START" \
1127
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1128
- pi --session "$UI_SESSION_START" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-ui-start.out" 2>"$TMPDIR/pi-completion-context-proposal-ui-start.err"
1129
-
1130
- python3 - "$UI_SNAPSHOT_START" <<'PY'
1131
- import json
1132
- import sys
1133
- from pathlib import Path
1134
-
1135
- snapshot = json.loads(Path(sys.argv[1]).read_text())
1136
- state = json.loads(Path('.agent/state.json').read_text())
1137
-
1138
- assert snapshot['proposalHeading'] == 'Startup plan', 'custom confirmation snapshot should expose a dedicated startup-plan section'
1139
- assert snapshot['critiqueHeading'] == 'Notes and risks', 'custom confirmation snapshot should expose notes separately from the startup-plan body'
1140
- assert snapshot['routingHeading'] == 'Routing recommendations', 'custom confirmation snapshot should expose routing recommendations separately from the proposal body'
1141
- assert 'approval-only' in snapshot['intro'], 'custom confirmation intro should explain the approval-only gate'
1142
- assert state['task_type'] == 'completion-workflow', 'start action should preserve canonical task_type'
1143
- assert state['evaluation_profile'] == 'completion-rubric-v1', 'start action should preserve canonical evaluation_profile'
1144
- assert 'Mission\nReplace the crowded selector with a clearer action layout.' in snapshot['proposalBody'], 'proposal body should be captured separately from the action list'
1145
- assert 'Keep critique details separate from the approval-only proposal summary.' not in snapshot['proposalBody'], 'critique notes should not be embedded in the startup-plan body'
1146
- assert 'Critique\n- Keep critique details separate from the approval-only proposal summary.' in snapshot['critiqueBody'], 'notes section should render accepted critique notes separately'
1147
- assert 'Risks\n- Bundling critique into the action list would make the confirmation harder to scan.' in snapshot['critiqueBody'], 'critique section should render risk notes separately'
1148
- assert '- Possible noise: old selector wording' in snapshot['critiqueBody'], 'critique section should preserve additional operator notes separately from the startup-plan body'
1149
- assert '- task_type: completion-workflow' in snapshot['routingBody'], 'routing section should render the recommended task_type'
1150
- assert '- evaluation_profile: completion-rubric-v1' in snapshot['routingBody'], 'routing section should render the recommended evaluation_profile'
1151
- assert [action['id'] for action in snapshot['actions']] == ['start', 'cancel'], 'custom confirmation actions should stay Start/Cancel only'
1152
- assert [action['label'] for action in snapshot['actions']] == ['Start', 'Cancel'], 'custom confirmation action labels should be concise'
1153
- assert 'Discuss changes in the main chat and rerun /cook.' in snapshot['actions'][1]['description'], 'cancel action should redirect users back to the main chat and rerun /cook'
1154
- for action in snapshot['actions']:
1155
- assert 'Replace the crowded selector with a clearer action layout.' not in action['label'], 'proposal mission should not be embedded in action labels'
1156
- assert 'Separate proposal text from actions.' not in action['description'], 'proposal scope should not be embedded in action descriptions'
1157
- assert state['mission_anchor'] == 'Replace the crowded selector with a clearer action layout.', 'start action should still accept the proposed mission'
1158
- assert state['advisory_startup_brief']['mission'] == 'Replace the crowded selector with a clearer action layout.', 'start action should preserve the confirmed startup plan canonically'
1159
- assert state['continuation_reason'].startswith('User started workflow via /cook:'), 'start action should persist the startup routing outcome in continuation_reason'
1160
- assert 'Keep critique details separate from the approval-only proposal summary.' in state['continuation_reason'], 'start action should persist the accepted critique outcome canonically'
1161
- PY
1162
-
1163
- # Custom confirmation UI: cancel should exit without writing canonical state and should tell the user
1164
- # to discuss changes in the main chat before rerunning /cook.
1165
- UI_ROOT_CANCEL="$TMPDIR/ui-root-cancel"
1166
- mkdir -p "$UI_ROOT_CANCEL"
1167
- cd "$UI_ROOT_CANCEL"
1168
- git init -q
1169
-
1170
- UI_SESSION_CANCEL="$TMPDIR/ui-session-cancel.jsonl"
1171
- UI_SNAPSHOT_CANCEL="$TMPDIR/context-proposal-ui-cancel.json"
1172
- UI_MESSAGES_CANCEL="$(python3 - <<'PY'
1173
- import json
1174
- capsule = {
1175
- "kind": "cook_handoff",
1176
- "source": "primary_agent",
1177
- "captured_at": "2026-01-01T00:00:02.000Z",
1178
- "source_turn_id": "m0002",
1179
- "mission": "Cancel from the custom confirmation UI without writing state.",
1180
- "scope": ["Show the proposal separately from the approval-only actions."],
1181
- "constraints": ["Keep cancellation side-effect free."],
1182
- "acceptance": ["Add regression coverage proving cancel leaves .agent absent."],
1183
- "risks": [],
1184
- "notes": [],
1185
- "handoff_kind": "implementation_workflow_handoff",
1186
- "first_slice_goal": "Exercise the cancel path without writing canonical state.",
1187
- "first_slice_non_goals": [],
1188
- "implementation_surfaces": ["extensions/completion/prompt-surfaces.ts"],
1189
- "verification_commands": ["npm run context-proposal-test"],
1190
- "why_this_slice_first": "The cancel path is a direct regression around the startup confirmation UI.",
1191
- "task_type": "completion-workflow",
1192
- "evaluation_profile": "completion-rubric-v1",
1193
- "why_cook_now": "The explicit handoff is concrete enough to exercise the cancel confirmation UI."
1194
- }
1195
- messages = [
1196
- {"role": "user", "content": "Prepare the cancel-path confirmation work and tell me when it is ready for /cook."},
1197
- {"role": "assistant", "content": "The cancel-path confirmation work is ready for /cook.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1198
- ]
1199
- print(json.dumps(messages, ensure_ascii=False))
1200
- PY
1201
- )"
1202
- write_session_messages "$UI_SESSION_CANCEL" "$UI_ROOT_CANCEL" "$UI_MESSAGES_CANCEL"
1203
-
1204
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_ACTION=cancel \
1205
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_PATH="$UI_SNAPSHOT_CANCEL" \
1206
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1207
- pi --session "$UI_SESSION_CANCEL" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-ui-cancel.out" 2>"$TMPDIR/pi-completion-context-proposal-ui-cancel.err"
1208
-
1209
- python3 - "$UI_SNAPSHOT_CANCEL" "$TMPDIR/pi-completion-context-proposal-ui-cancel.out" "$TMPDIR/pi-completion-context-proposal-ui-cancel.err" <<'PY'
1210
- import json
1211
- import sys
1212
- from pathlib import Path
1213
-
1214
- snapshot = json.loads(Path(sys.argv[1]).read_text())
1215
550
  output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
1216
- assert 'approval-only' in snapshot['intro'], 'cancel snapshot should keep the approval-only intro'
1217
- assert [action['id'] for action in snapshot['actions']] == ['start', 'cancel'], 'cancel snapshot should expose Start/Cancel actions only'
1218
- assert [action['label'] for action in snapshot['actions']] == ['Start', 'Cancel'], 'cancel snapshot should keep concise Start/Cancel labels'
1219
- assert 'Discuss changes in the main chat and rerun /cook.' in snapshot['actions'][1]['description'], 'cancel action copy should redirect users back to the main chat and rerun /cook'
1220
- assert 'Discuss changes in the main chat and rerun /cook.' in output, 'cancel command output should redirect users back to the main chat and rerun /cook'
1221
- assert not Path('.agent').exists(), 'cancel action should not write canonical workflow state'
1222
- PY
1223
-
1224
- # Explicit primary-agent startup-plan preview: /cook should prefer the structured handoff capsule over broad context re-inference.
1225
- HANDOFF_ROOT_START="$TMPDIR/handoff-root-start"
1226
- mkdir -p "$HANDOFF_ROOT_START"
1227
- cd "$HANDOFF_ROOT_START"
1228
- git init -q
1229
-
1230
- HANDOFF_SESSION_START="$TMPDIR/handoff-session-start.jsonl"
1231
- HANDOFF_SNAPSHOT_START="$TMPDIR/handoff-proposal-start.json"
1232
- HANDOFF_MESSAGES_START="$(python3 - <<'PY'
1233
- import json
1234
- capsule = {
1235
- "kind": "cook_handoff",
1236
- "source": "primary_agent",
1237
- "captured_at": "2026-01-01T00:00:02.000Z",
1238
- "source_turn_id": "m0002",
1239
- "mission": "Fix login redirect callback behavior.",
1240
- "scope": [
1241
- "Update the callback redirect decision logic.",
1242
- "Preserve the broader auth flow."
1243
- ],
1244
- "constraints": [
1245
- "Do not refactor the broader auth flow."
1246
- ],
1247
- "acceptance": [
1248
- "Add a regression test for returning to the requested page."
1249
- ],
1250
- "risks": [
1251
- "Stale auth discussion could broaden the startup brief if the handoff is ignored."
1252
- ],
1253
- "notes": [
1254
- "Keep the startup plan aligned with the explicit primary-agent plan."
1255
- ],
1256
- "handoff_kind": "implementation_workflow_handoff",
1257
- "first_slice_goal": "Land the redirect callback fix and its regression coverage.",
1258
- "first_slice_non_goals": [
1259
- "Do not refactor the broader auth flow."
1260
- ],
1261
- "implementation_surfaces": [
1262
- "src/auth/redirect.ts",
1263
- "tests/auth/redirect.spec.ts"
1264
- ],
1265
- "verification_commands": [
1266
- "npm test -- redirect.spec.ts"
1267
- ],
1268
- "why_this_slice_first": "The redirect callback bug is already bounded enough to start implementation safely.",
1269
- "task_type": "completion-workflow",
1270
- "evaluation_profile": "completion-rubric-v1",
1271
- "why_cook_now": "The implementation plan is concrete and ready for repo changes."
1272
- }
1273
- messages = [
1274
- {"role": "user", "content": "Please think through the login redirect fix and tell me when it is ready for /cook."},
1275
- {"role": "assistant", "content": "This task is now ready for /cook. Run /cook to confirm the startup plan.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1276
- ]
1277
- print(json.dumps(messages, ensure_ascii=False))
1278
- PY
1279
- )"
1280
- write_session_messages "$HANDOFF_SESSION_START" "$HANDOFF_ROOT_START" "$HANDOFF_MESSAGES_START"
1281
-
1282
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
1283
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_START" \
1284
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1285
- pi --session "$HANDOFF_SESSION_START" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-start.out" 2>"$TMPDIR/pi-completion-handoff-start.err"
1286
-
1287
- python3 - "$HANDOFF_SNAPSHOT_START" <<'PY'
1288
- import json
1289
- import sys
1290
- from pathlib import Path
1291
-
1292
- snapshot = json.loads(Path(sys.argv[1]).read_text())
1293
- state = json.loads(Path('.agent/state.json').read_text())
1294
-
1295
- assert snapshot['source'] == 'handoff_capsule', 'explicit handoff startup should snapshot the handoff capsule as the proposal source'
1296
- assert snapshot['mission'] == 'Fix login redirect callback behavior.', 'explicit handoff startup should preserve the primary-agent mission'
1297
- assert state['mission_anchor'] == 'Fix login redirect callback behavior.', 'explicit handoff startup should use the handoff mission as canonical mission_anchor'
1298
- assert state['advisory_startup_brief']['source'] == 'primary_agent_handoff', 'explicit handoff startup should preserve the advisory intake source'
1299
- assert state['advisory_startup_brief']['risks'] == ['Stale auth discussion could broaden the startup brief if the handoff is ignored.'], 'explicit startup-plan preview should preserve startup-plan risks'
1300
- assert 'First slice goal: Land the redirect callback fix and its regression coverage.' in state['advisory_startup_brief']['notes'], 'explicit handoff startup should preserve first_slice_goal in advisory notes'
1301
- assert 'First slice non-goals: Do not refactor the broader auth flow.' in state['advisory_startup_brief']['notes'], 'explicit handoff startup should preserve first_slice_non_goals in advisory notes'
1302
- assert 'Implementation surfaces: src/auth/redirect.ts | tests/auth/redirect.spec.ts' in state['advisory_startup_brief']['notes'], 'explicit handoff startup should preserve implementation_surfaces in advisory notes'
1303
- assert 'Verification commands: npm test -- redirect.spec.ts' in state['advisory_startup_brief']['notes'], 'explicit handoff startup should preserve verification_commands in advisory notes'
1304
- assert 'Why this slice first: The redirect callback bug is already bounded enough to start implementation safely.' in state['advisory_startup_brief']['notes'], 'explicit handoff startup should preserve why_this_slice_first in advisory notes'
1305
- assert 'Primary-agent /cook handoff rationale: The implementation plan is concrete and ready for repo changes.' in state['advisory_startup_brief']['notes'], 'explicit handoff startup should preserve why_cook_now as notes'
1306
- PY
1307
-
1308
- # Fresh but non-startable explicit handoff: /cook should fail closed instead of falling back
1309
- # to a broad recent-discussion startup plan when the explicit capsule is still too vague.
1310
- HANDOFF_ROOT_VAGUE="$TMPDIR/handoff-root-vague"
1311
- mkdir -p "$HANDOFF_ROOT_VAGUE"
1312
- cd "$HANDOFF_ROOT_VAGUE"
1313
- git init -q
1314
-
1315
- HANDOFF_SESSION_VAGUE="$TMPDIR/handoff-session-vague.jsonl"
1316
- HANDOFF_SNAPSHOT_VAGUE="$TMPDIR/handoff-proposal-vague.json"
1317
- HANDOFF_MESSAGES_VAGUE="$(python3 - <<'PY'
1318
- import json
1319
- capsule = {
1320
- "kind": "cook_handoff",
1321
- "source": "primary_agent",
1322
- "captured_at": "2026-01-01T00:00:02.000Z",
1323
- "source_turn_id": "m0002",
1324
- "mission": "Fix login redirect callback behavior.",
1325
- "scope": [
1326
- "Update the callback redirect decision logic."
1327
- ],
1328
- "constraints": [
1329
- "Do not refactor the broader auth flow."
1330
- ],
1331
- "acceptance": [
1332
- "Confirm the final implementation breakdown before coding."
1333
- ],
1334
- "risks": [
1335
- "Broad recent context could be reused if the vague explicit handoff is ignored."
1336
- ],
1337
- "notes": [
1338
- "This handoff is still too vague to start implementation directly."
1339
- ],
1340
- "handoff_kind": "implementation_workflow_handoff",
1341
- "first_slice_goal": "Patch the callback redirect decision logic.",
1342
- "first_slice_non_goals": [
1343
- "Do not refactor the broader auth flow."
1344
- ],
1345
- "implementation_surfaces": [],
1346
- "verification_commands": [],
1347
- "why_this_slice_first": "The callback redirect path is the likely first slice, but the handoff still lacks execution detail.",
1348
- "task_type": "completion-workflow",
1349
- "evaluation_profile": "completion-rubric-v1",
1350
- "why_cook_now": "The task is workflow-worthy, but the implementation slice is not concrete enough yet."
1351
- }
1352
- recent_discussion = "Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic.\nConstraints:\n- Do not refactor the broader auth flow.\nAcceptance:\n- Add a regression test for returning to the requested page."
1353
- messages = [
1354
- {"role": "user", "content": recent_discussion},
1355
- {"role": "assistant", "content": "This follow-up might soon be ready for /cook.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1356
- ]
1357
- print(json.dumps(messages, ensure_ascii=False))
1358
- PY
1359
- )"
1360
- write_session_messages "$HANDOFF_SESSION_VAGUE" "$HANDOFF_ROOT_VAGUE" "$HANDOFF_MESSAGES_VAGUE"
1361
-
1362
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
1363
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_VAGUE" \
1364
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1365
- pi --session "$HANDOFF_SESSION_VAGUE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-vague.out" 2>"$TMPDIR/pi-completion-handoff-vague.err"
1366
-
1367
- python3 - "$HANDOFF_SNAPSHOT_VAGUE" "$TMPDIR/pi-completion-handoff-vague.out" "$TMPDIR/pi-completion-handoff-vague.err" <<'PY'
1368
- import sys
1369
- from pathlib import Path
1370
-
1371
- snapshot = Path(sys.argv[1])
1372
- output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
1373
-
1374
- assert not snapshot.exists(), 'fresh non-startable handoff should not emit a startup proposal snapshot'
1375
- assert not Path('.agent').exists(), 'fresh non-startable handoff should fail closed without writing canonical state'
1376
- assert 'fresh explicit primary-agent startup plan exists' in output, 'fresh non-startable handoff should explain that the explicit capsule blocked startup'
1377
- assert 'acceptance is not anchored to concrete repo changes or verification' in output, 'fresh non-startable handoff should explain the workflow-only acceptance failure'
1378
- assert 'verification intent' in output, 'fresh non-startable handoff should explain the plan-level verification requirement'
1379
- PY
1380
-
1381
- # Fresh explicit startup-plan preview with sequencing hints but vague acceptance: /cook should still fail closed
1382
- # with the dedicated explicit-handoff message instead of bootstrapping canonical state.
1383
- HANDOFF_ROOT_VAGUE_ACCEPTANCE="$TMPDIR/handoff-root-vague-acceptance"
1384
- mkdir -p "$HANDOFF_ROOT_VAGUE_ACCEPTANCE"
1385
- cd "$HANDOFF_ROOT_VAGUE_ACCEPTANCE"
1386
- git init -q
1387
-
1388
- HANDOFF_SESSION_VAGUE_ACCEPTANCE="$TMPDIR/handoff-session-vague-acceptance.jsonl"
1389
- HANDOFF_SNAPSHOT_VAGUE_ACCEPTANCE="$TMPDIR/handoff-proposal-vague-acceptance.json"
1390
- HANDOFF_MESSAGES_VAGUE_ACCEPTANCE="$(python3 - <<'PY'
1391
- import json
1392
- capsule = {
1393
- "kind": "cook_handoff",
1394
- "source": "primary_agent",
1395
- "captured_at": "2026-01-01T00:00:02.000Z",
1396
- "source_turn_id": "m0002",
1397
- "mission": "Fix login redirect callback behavior.",
1398
- "scope": [
1399
- "Update the callback redirect decision logic.",
1400
- "Preserve the broader auth flow."
1401
- ],
1402
- "constraints": [
1403
- "Do not refactor the broader auth flow."
1404
- ],
1405
- "acceptance": [
1406
- "Current behavior stays understandable."
1407
- ],
1408
- "risks": [
1409
- "Broad recent context could be reused if the vague explicit handoff is ignored."
1410
- ],
1411
- "notes": [
1412
- "This handoff includes first-slice fields but still lacks concrete acceptance."
1413
- ],
1414
- "handoff_kind": "implementation_workflow_handoff",
1415
- "first_slice_goal": "Land the redirect callback fix and its regression coverage.",
1416
- "first_slice_non_goals": [
1417
- "Do not refactor the broader auth flow."
1418
- ],
1419
- "implementation_surfaces": [
1420
- "src/auth/redirect.ts",
1421
- "tests/auth/redirect.spec.ts"
1422
- ],
1423
- "verification_commands": [
1424
- "npm test -- redirect.spec.ts"
1425
- ],
1426
- "why_this_slice_first": "The redirect callback bug is already bounded enough to start implementation safely once acceptance is concrete.",
1427
- "task_type": "completion-workflow",
1428
- "evaluation_profile": "completion-rubric-v1",
1429
- "why_cook_now": "The task is workflow-worthy, but the acceptance still needs concrete repo-change detail."
1430
- }
1431
- recent_discussion = "Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic.\nConstraints:\n- Do not refactor the broader auth flow.\nAcceptance:\n- Add a regression test for returning to the requested page."
1432
- messages = [
1433
- {"role": "user", "content": recent_discussion},
1434
- {"role": "assistant", "content": "This follow-up might soon be ready for /cook.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1435
- ]
1436
- print(json.dumps(messages, ensure_ascii=False))
1437
- PY
1438
- )"
1439
- write_session_messages "$HANDOFF_SESSION_VAGUE_ACCEPTANCE" "$HANDOFF_ROOT_VAGUE_ACCEPTANCE" "$HANDOFF_MESSAGES_VAGUE_ACCEPTANCE"
1440
-
1441
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
1442
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_VAGUE_ACCEPTANCE" \
1443
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1444
- pi --session "$HANDOFF_SESSION_VAGUE_ACCEPTANCE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-vague-acceptance.out" 2>"$TMPDIR/pi-completion-handoff-vague-acceptance.err"
1445
-
1446
- python3 - "$HANDOFF_SNAPSHOT_VAGUE_ACCEPTANCE" "$TMPDIR/pi-completion-handoff-vague-acceptance.out" "$TMPDIR/pi-completion-handoff-vague-acceptance.err" <<'PY'
1447
- import sys
1448
- from pathlib import Path
1449
-
1450
- snapshot = Path(sys.argv[1])
1451
- output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
1452
-
1453
- assert not snapshot.exists(), 'fresh explicit startup-plan preview with vague acceptance should not emit a startup proposal snapshot'
1454
- assert not Path('.agent').exists(), 'fresh explicit startup-plan preview with vague acceptance should fail closed without writing canonical state'
1455
- assert 'fresh explicit primary-agent startup plan exists' in output, 'fresh explicit startup-plan preview with vague acceptance should use the dedicated explicit-startup-plan fail-closed message'
1456
- assert 'acceptance is not anchored to concrete repo changes or verification' in output, 'fresh explicit startup-plan preview with vague acceptance should explain the vague acceptance failure'
1457
- PY
1458
-
1459
- # Done workflow + fresh startup-plan preview: the fresh explicit startup-plan preview should override done-state suppression and start the new round.
1460
- HANDOFF_ROOT_DONE="$TMPDIR/handoff-root-done"
1461
- mkdir -p "$HANDOFF_ROOT_DONE"
1462
- cd "$HANDOFF_ROOT_DONE"
1463
- git init -q
1464
-
1465
- DONE_SEED_SESSION="$TMPDIR/handoff-done-seed-session.jsonl"
1466
- DONE_SEED_MESSAGES="$(python3 - <<'PY'
1467
- import json
1468
- capsule = {
1469
- "kind": "cook_handoff",
1470
- "source": "primary_agent",
1471
- "captured_at": "2026-01-01T00:00:02.000Z",
1472
- "source_turn_id": "m0002",
1473
- "mission": "Seed a finished workflow before testing fresh handoff priority.",
1474
- "scope": ["Create canonical workflow state."],
1475
- "constraints": ["Keep the seed minimal."],
1476
- "acceptance": ["Add regression coverage for marking the seeded workflow done before the next step."],
1477
- "risks": [],
1478
- "notes": [],
1479
- "handoff_kind": "implementation_workflow_handoff",
1480
- "first_slice_goal": "Bootstrap the done-workflow seed fixture from an explicit handoff.",
1481
- "first_slice_non_goals": [],
1482
- "implementation_surfaces": ["scripts/context-proposal-test.sh"],
1483
- "verification_commands": ["npm run context-proposal-test"],
1484
- "why_this_slice_first": "The done-workflow handoff test needs canonical state before it can be marked done."
1485
- }
1486
- messages = [
1487
- {"role": "user", "content": "Prepare the done-workflow seed fixture and tell me when it is ready for /cook."},
1488
- {"role": "assistant", "content": "The done-workflow seed fixture is ready for /cook.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1489
- ]
1490
- print(json.dumps(messages, ensure_ascii=False))
1491
- PY
1492
- )"
1493
- write_session_messages "$DONE_SEED_SESSION" "$HANDOFF_ROOT_DONE" "$DONE_SEED_MESSAGES"
1494
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
1495
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1496
- pi --session "$DONE_SEED_SESSION" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-done-seed.out" 2>"$TMPDIR/pi-completion-handoff-done-seed.err"
1497
- mark_done
1498
-
1499
- HANDOFF_SESSION_DONE="$TMPDIR/handoff-session-done.jsonl"
1500
- HANDOFF_SNAPSHOT_DONE="$TMPDIR/handoff-proposal-done.json"
1501
- HANDOFF_MESSAGES_DONE="$(python3 - <<'PY'
1502
- import json
1503
- capsule = {
1504
- "kind": "cook_handoff",
1505
- "source": "primary_agent",
1506
- "captured_at": "2026-01-01T00:00:02.000Z",
1507
- "source_turn_id": "m0002",
1508
- "mission": "Reopen the login redirect work for the callback edge case.",
1509
- "scope": [
1510
- "Handle the callback edge case in the redirect logic.",
1511
- "Keep the finished workflow as historical context only."
1512
- ],
1513
- "constraints": [
1514
- "Do not turn done-state suppression into the startup mission."
1515
- ],
1516
- "acceptance": [
1517
- "Add a regression test for the callback edge case."
1518
- ],
1519
- "risks": [
1520
- "Done-state context could override the new mission if the handoff is ignored."
1521
- ],
1522
- "notes": [
1523
- "This is a fresh implementation round, not a summary of the finished workflow."
1524
- ],
1525
- "handoff_kind": "implementation_workflow_handoff",
1526
- "first_slice_goal": "Patch the callback edge case and cover it with a focused regression test.",
1527
- "first_slice_non_goals": [
1528
- "Do not turn done-state suppression into the startup mission."
1529
- ],
1530
- "implementation_surfaces": [
1531
- "src/auth/redirect.ts",
1532
- "tests/auth/redirect-edge.spec.ts"
1533
- ],
1534
- "verification_commands": [
1535
- "npm test -- redirect-edge.spec.ts"
1536
- ],
1537
- "why_this_slice_first": "The new callback edge case is the smallest fresh implementation slice after the prior round closed.",
1538
- "task_type": "completion-workflow",
1539
- "evaluation_profile": "completion-rubric-v1",
1540
- "why_cook_now": "A new implementation-ready edge case was identified after the previous round closed."
1541
- }
1542
- messages = [
1543
- {"role": "user", "content": "The previous round is done, but there is a fresh callback edge case to implement."},
1544
- {"role": "assistant", "content": "The next round is ready for /cook. Run /cook to confirm this fresh implementation mission.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1545
- ]
1546
- print(json.dumps(messages, ensure_ascii=False))
1547
- PY
1548
- )"
1549
- write_session_messages "$HANDOFF_SESSION_DONE" "$HANDOFF_ROOT_DONE" "$HANDOFF_MESSAGES_DONE"
1550
-
1551
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
1552
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_DONE" \
1553
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1554
- pi --session "$HANDOFF_SESSION_DONE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-done.out" 2>"$TMPDIR/pi-completion-handoff-done.err"
1555
-
1556
- python3 - "$HANDOFF_SNAPSHOT_DONE" <<'PY'
1557
- import json
1558
- import sys
1559
- from pathlib import Path
1560
-
1561
- snapshot = json.loads(Path(sys.argv[1]).read_text())
1562
551
  state = json.loads(Path('.agent/state.json').read_text())
552
+ startup_plan = json.loads(Path('.agent/startup-plan.json').read_text())
553
+ plan = json.loads(Path('.agent/plan.json').read_text())
554
+ active = json.loads(Path('.agent/active-slice.json').read_text())
555
+ mission = 'Start the next workflow round for widget follow-up docs.'
1563
556
 
1564
- assert snapshot['source'] == 'handoff_capsule', 'done-workflow handoff should still use the explicit handoff capsule'
1565
- assert snapshot['mission'] == 'Reopen the login redirect work for the callback edge case.', 'done-workflow handoff should preserve the fresh mission'
1566
- assert state['mission_anchor'] == 'Reopen the login redirect work for the callback edge case.', 'done-workflow handoff should override done-state suppression with the fresh mission'
1567
- assert state['continuation_policy'] == 'continue', 'done-workflow handoff should reopen canonical workflow state for the new round'
1568
- assert state['advisory_startup_brief']['source'] == 'primary_agent_handoff', 'done-workflow handoff should preserve the handoff advisory source'
1569
- assert 'First slice goal: Patch the callback edge case and cover it with a focused regression test.' in state['advisory_startup_brief']['notes'], 'done-workflow handoff should preserve first_slice_goal in advisory notes'
1570
- assert 'Verification commands: npm test -- redirect-edge.spec.ts' in state['advisory_startup_brief']['notes'], 'done-workflow handoff should preserve verification_commands in advisory notes'
1571
- PY
1572
-
1573
- # Stale handoff: later discussion should invalidate the older handoff capsule and fail closed instead of falling back to newer discussion.
1574
- HANDOFF_ROOT_STALE="$TMPDIR/handoff-root-stale"
1575
- mkdir -p "$HANDOFF_ROOT_STALE"
1576
- cd "$HANDOFF_ROOT_STALE"
1577
- git init -q
1578
-
1579
- HANDOFF_SESSION_STALE="$TMPDIR/handoff-session-stale.jsonl"
1580
- HANDOFF_SNAPSHOT_STALE="$TMPDIR/handoff-proposal-stale.json"
1581
- HANDOFF_MESSAGES_STALE="$(python3 - <<'PY'
1582
- import json
1583
- capsule = {
1584
- "kind": "cook_handoff",
1585
- "source": "primary_agent",
1586
- "captured_at": "2026-01-01T00:00:02.000Z",
1587
- "source_turn_id": "m0002",
1588
- "mission": "Fix the original login redirect callback behavior.",
1589
- "scope": ["Update the original callback redirect logic."],
1590
- "constraints": ["Do not refactor the auth stack."],
1591
- "acceptance": ["Add the original callback regression test."],
1592
- "risks": [],
1593
- "notes": [],
1594
- "handoff_kind": "implementation_workflow_handoff",
1595
- "first_slice_goal": "Ship the original login callback follow-up.",
1596
- "first_slice_non_goals": ["Do not refactor the auth stack."],
1597
- "implementation_surfaces": ["src/auth/login-redirect.ts"],
1598
- "verification_commands": ["npm test -- login-redirect.spec.ts"],
1599
- "why_this_slice_first": "The original callback follow-up was the first bounded implementation slice before later discussion replaced it."
1600
- }
1601
- newer_discussion = "Mission: Ship logout redirect consistency instead.\nScope:\n- Update the logout redirect path.\nConstraints:\n- Leave the login callback flow unchanged.\nAcceptance:\n- Add a logout redirect regression test."
1602
- messages = [
1603
- {"role": "user", "content": "Please plan the login redirect follow-up."},
1604
- {"role": "assistant", "content": "Run /cook if you want to start the original follow-up.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1605
- {"role": "user", "content": newer_discussion},
1606
- ]
1607
- print(json.dumps(messages, ensure_ascii=False))
1608
- PY
1609
- )"
1610
- write_session_messages "$HANDOFF_SESSION_STALE" "$HANDOFF_ROOT_STALE" "$HANDOFF_MESSAGES_STALE"
1611
-
1612
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
1613
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
1614
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_STALE" \
1615
- PI_COMPLETION_DISABLE_PRIMARY_HANDOFF_SYNTHESIS=1 \
1616
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1617
- pi --session "$HANDOFF_SESSION_STALE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-stale.out" 2>"$TMPDIR/pi-completion-handoff-stale.err"
1618
-
1619
- python3 - "$HANDOFF_SNAPSHOT_STALE" "$TMPDIR/pi-completion-handoff-stale.out" "$TMPDIR/pi-completion-handoff-stale.err" <<'PY'
1620
- import sys
1621
- from pathlib import Path
1622
-
1623
- snapshot = Path(sys.argv[1])
1624
- output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
1625
-
1626
- assert not snapshot.exists(), 'stale handoff should not emit a startup proposal snapshot'
1627
- assert not Path('.agent').exists(), 'stale handoff should fail closed without writing canonical state'
1628
- assert 'startup-plan step could not prepare a concrete workflow startup plan' in output, 'stale handoff should fail closed when the synthesized startup-plan step produces nothing'
1629
- PY
1630
-
1631
- # Negative handoff rationale: a non-startable capsule must not become the startup mission.
1632
- HANDOFF_ROOT_NEGATIVE="$TMPDIR/handoff-root-negative"
1633
- mkdir -p "$HANDOFF_ROOT_NEGATIVE"
1634
- cd "$HANDOFF_ROOT_NEGATIVE"
1635
- git init -q
1636
-
1637
- HANDOFF_SESSION_NEGATIVE="$TMPDIR/handoff-session-negative.jsonl"
1638
- HANDOFF_SNAPSHOT_NEGATIVE="$TMPDIR/handoff-proposal-negative.json"
1639
- HANDOFF_MESSAGES_NEGATIVE="$(python3 - <<'PY'
1640
- import json
1641
- capsule = {
1642
- "kind": "cook_handoff",
1643
- "source": "primary_agent",
1644
- "captured_at": "2026-01-01T00:00:02.000Z",
1645
- "source_turn_id": "m0002",
1646
- "mission": "Do not reopen implementation for the finished workflow.",
1647
- "scope": ["Keep the old workflow closed."],
1648
- "constraints": ["Do not start repo changes."],
1649
- "acceptance": ["Explain that the finished workflow should stay closed."],
1650
- "risks": [],
1651
- "notes": [],
1652
- "handoff_kind": "implementation_workflow_handoff",
1653
- "first_slice_goal": "Keep the finished workflow closed.",
1654
- "first_slice_non_goals": ["Do not start repo changes."],
1655
- "implementation_surfaces": ["docs/workflow-status.md"],
1656
- "verification_commands": ["npm test -- workflow-status"],
1657
- "why_this_slice_first": "This is the only bounded next step being proposed, even though the mission itself is invalid."
1658
- }
1659
- messages = [
1660
- {"role": "user", "content": "Should we reopen the finished workflow?"},
1661
- {"role": "assistant", "content": "Do not reopen it directly.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
1662
- ]
1663
- print(json.dumps(messages, ensure_ascii=False))
1664
- PY
1665
- )"
1666
- write_session_messages "$HANDOFF_SESSION_NEGATIVE" "$HANDOFF_ROOT_NEGATIVE" "$HANDOFF_MESSAGES_NEGATIVE"
1667
-
1668
- PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
1669
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_NEGATIVE" \
1670
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1671
- pi --session "$HANDOFF_SESSION_NEGATIVE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-negative.out" 2>"$TMPDIR/pi-completion-handoff-negative.err"
1672
-
1673
- python3 - "$HANDOFF_SNAPSHOT_NEGATIVE" "$TMPDIR/pi-completion-handoff-negative.out" "$TMPDIR/pi-completion-handoff-negative.err" <<'PY'
1674
- import sys
1675
- from pathlib import Path
1676
-
1677
- snapshot = Path(sys.argv[1])
1678
- output = Path(sys.argv[2]).read_text() + Path(sys.argv[3]).read_text()
1679
-
1680
- assert not snapshot.exists(), 'negative handoff rationale should not emit a startup proposal snapshot'
1681
- assert not Path('.agent').exists(), 'negative handoff rationale should fail closed without writing canonical state'
1682
- assert '/cook failed closed' in output, 'negative handoff rationale should fail closed instead of becoming the startup mission'
557
+ assert proposal['source'] == 'deferred_primary_agent_handoff', proposal
558
+ assert state['mission_anchor'] == mission, state
559
+ assert state['current_phase'] == 'reground', state
560
+ assert state['next_mandatory_role'] == 'completion-regrounder', state
561
+ assert startup_plan['mission_anchor'] == mission, startup_plan
562
+ assert startup_plan['source'] == 'deferred_primary_agent_handoff', startup_plan
563
+ assert plan['mission_anchor'] == mission, plan
564
+ assert active['mission_anchor'] == mission, active
565
+ assert 'Started a new completion workflow round and recorded the approved startup plan' in output, output
1683
566
  PY
1684
567
 
568
+ grep -q 'export function assessCookHandoffText' "$PKG_ROOT/extensions/completion/proposal.ts"
1685
569
  grep -q 'export async function deriveCookContextProposalFromRecentDiscussion' "$PKG_ROOT/extensions/completion/proposal.ts"
1686
- grep -q 'export function extractLatestCookHandoffProposal' "$PKG_ROOT/extensions/completion/proposal.ts"
1687
- grep -q 'export function parseContextProposalAnalystOutput' "$PKG_ROOT/extensions/completion/proposal.ts"
1688
- grep -q 'export function buildContextProposalConfirmationLayout' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
1689
- grep -q 'export function buildEvaluationRoleContextLines' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
570
+ grep -q 'export async function analyzeContextProposalWithAgent' "$PKG_ROOT/extensions/completion/role-runner.ts"
1690
571
 
1691
- echo "context proposal test passed: $ROOT"
572
+ echo "context proposal test passed: $PKG_ROOT"