@linimin/pi-letscook 0.1.66 → 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.
@@ -47,130 +47,31 @@ with session_path.open('w', encoding='utf-8') as fh:
47
47
  PY
48
48
  }
49
49
 
50
- write_session_messages() {
51
- local session_path="$1"
52
- local cwd="$2"
53
- local messages_json="$3"
54
- python3 - "$session_path" "$cwd" "$messages_json" <<'PY'
55
- import json
56
- import sys
57
- from pathlib import Path
58
-
59
- session_path = Path(sys.argv[1])
60
- cwd = sys.argv[2]
61
- messages = json.loads(sys.argv[3])
62
- session_path.parent.mkdir(parents=True, exist_ok=True)
63
- entries = [
64
- {
65
- "type": "session",
66
- "version": 3,
67
- "id": "11111111-1111-4111-8111-111111111111",
68
- "timestamp": "2026-01-01T00:00:00.000Z",
69
- "cwd": cwd,
70
- },
71
- ]
72
- parent_id = None
73
- for index, message in enumerate(messages, start=1):
74
- entry_id = f"m{index:04d}"
75
- entries.append({
76
- "type": "message",
77
- "id": entry_id,
78
- "parentId": parent_id,
79
- "timestamp": f"2026-01-01T00:00:{index:02d}.000Z",
80
- "message": {
81
- "role": message["role"],
82
- "content": message["content"],
83
- "timestamp": 1767225600000 + index * 1000,
84
- },
85
- })
86
- parent_id = entry_id
87
- with session_path.open('w', encoding='utf-8') as fh:
88
- for entry in entries:
89
- fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
90
- PY
91
- }
92
-
93
- cd "$TMPDIR"
94
- git init -q
95
-
96
- BOOTSTRAP_SESSION="$TMPDIR/session-bootstrap.jsonl"
97
- BOOTSTRAP_MESSAGES="$(python3 - <<'PY'
98
- import json
99
- capsule = {
100
- "kind": "cook_handoff",
101
- "source": "primary_agent",
102
- "captured_at": "2026-01-01T00:00:02.000Z",
103
- "source_turn_id": "m0002",
104
- "mission": "Smoke-test mission.",
105
- "scope": [
106
- "Bootstrap a completion workflow for the refocus regression fixture."
107
- ],
108
- "constraints": [
109
- "Keep active-workflow refocus behavior under the explicit-handoff startup contract."
110
- ],
111
- "acceptance": [
112
- "Bootstrap canonical refocus-fixture state for the active-workflow regression.",
113
- "Verify the refocus regression with npm run refocus-test."
114
- ],
115
- "risks": [],
116
- "notes": [
117
- "Use explicit primary-agent handoff startup for the refocus regression fixture."
118
- ],
119
- "handoff_kind": "implementation_workflow_handoff",
120
- "first_slice_goal": "Bootstrap the refocus regression fixture from a fresh explicit handoff.",
121
- "first_slice_non_goals": [],
122
- "implementation_surfaces": [
123
- "scripts/refocus-test.sh"
124
- ],
125
- "verification_commands": [
126
- "npm run refocus-test"
127
- ],
128
- "why_this_slice_first": "The refocus regression fixture needs canonical state before active-workflow routing can be exercised.",
129
- "task_type": "completion-workflow",
130
- "evaluation_profile": "completion-rubric-v1",
131
- "why_cook_now": "The active-workflow refocus regression needs a fresh explicit startup boundary."
50
+ bootstrap_active_workflow() {
51
+ local repo_root="$1"
52
+ local session_path="$2"
53
+ local bootstrap_discussion="$3"
54
+ local generated_handoff="$4"
55
+
56
+ mkdir -p "$repo_root"
57
+ cd "$repo_root"
58
+ git init -q
59
+ write_session "$session_path" "$repo_root" "$bootstrap_discussion"
60
+
61
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
62
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$generated_handoff" \
63
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
64
+ pi --session "$session_path" -e "$PKG_ROOT" -p "/cook" \
65
+ >"$TMPDIR/bootstrap.out" 2>"$TMPDIR/bootstrap.err"
66
+
67
+ for file in .agent/profile.json .agent/state.json .agent/startup-plan.json .agent/startup-plan.md .agent/plan.json .agent/active-slice.json .agent/verification-evidence.json; do
68
+ [[ -f "$file" ]] || { echo "missing canonical bootstrap file: $file" >&2; exit 1; }
69
+ done
132
70
  }
133
- messages = [
134
- {"role": "user", "content": "Prepare the refocus regression fixture and tell me when it is ready for /cook."},
135
- {"role": "assistant", "content": "The refocus regression fixture is ready for /cook.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
136
- ]
137
- print(json.dumps(messages, ensure_ascii=False))
138
- PY
139
- )"
140
- write_session_messages "$BOOTSTRAP_SESSION" "$TMPDIR" "$BOOTSTRAP_MESSAGES"
141
-
142
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
143
- pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-refocus-bootstrap.out" 2>"$TMPDIR/pi-completion-refocus-bootstrap.err" &
144
- PI_PID=$!
145
- for _ in $(seq 1 60); do
146
- if [[ -f .agent/profile.json && -f .agent/state.json && -f .agent/plan.json && -f .agent/active-slice.json ]]; then
147
- break
148
- fi
149
- sleep 1
150
- done
151
- if [[ ! -f .agent/profile.json || ! -f .agent/state.json || ! -f .agent/plan.json || ! -f .agent/active-slice.json ]]; then
152
- echo "completion bootstrap did not materialize canonical files in time" >&2
153
- cat "$TMPDIR/pi-completion-refocus-bootstrap.err" >&2 || true
154
- kill "$PI_PID" >/dev/null 2>&1 || true
155
- wait "$PI_PID" >/dev/null 2>&1 || true
156
- exit 1
157
- fi
158
- kill "$PI_PID" >/dev/null 2>&1 || true
159
- wait "$PI_PID" >/dev/null 2>&1 || true
160
71
 
161
- INITIAL_MISSION="$(python3 - <<'PY'
162
- import json
163
- from pathlib import Path
164
- state = json.loads(Path('.agent/state.json').read_text())
165
- print(state['mission_anchor'])
166
- PY
167
- )"
168
-
169
- INLINE_REJECTION_ROUTING="$TMPDIR/inline-arg-routing.json"
170
- INLINE_REJECTION_PROPOSAL="$TMPDIR/inline-arg-proposal.json"
171
- INLINE_REJECTION_CHOOSER="$TMPDIR/inline-arg-chooser.json"
172
- INLINE_REJECTION_BASELINE="$TMPDIR/inline-arg-before.json"
173
- python3 - "$INLINE_REJECTION_BASELINE" <<'PY'
72
+ snapshot_tracked() {
73
+ local baseline_path="$1"
74
+ python3 - "$baseline_path" <<'PY'
174
75
  import json
175
76
  import sys
176
77
  from pathlib import Path
@@ -179,421 +80,246 @@ tracked = [
179
80
  Path('.agent/mission.md'),
180
81
  Path('.agent/profile.json'),
181
82
  Path('.agent/state.json'),
83
+ Path('.agent/startup-plan.json'),
84
+ Path('.agent/startup-plan.md'),
182
85
  Path('.agent/plan.json'),
183
86
  Path('.agent/active-slice.json'),
184
87
  Path('.agent/verification-evidence.json'),
185
88
  ]
186
89
  Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
187
90
  PY
91
+ }
188
92
 
189
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$INLINE_REJECTION_ROUTING" \
190
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$INLINE_REJECTION_PROPOSAL" \
191
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$INLINE_REJECTION_CHOOSER" \
192
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
193
- pi -e "$PKG_ROOT" -p "/cook replacement mission that should stay in the main chat" \
194
- >"$TMPDIR/pi-completion-refocus-inline-arg.out" 2>"$TMPDIR/pi-completion-refocus-inline-arg.err"
195
-
196
- python3 - "$TMPDIR/pi-completion-refocus-inline-arg.out" "$TMPDIR/pi-completion-refocus-inline-arg.err" "$INLINE_REJECTION_ROUTING" "$INLINE_REJECTION_PROPOSAL" "$INLINE_REJECTION_CHOOSER" "$INITIAL_MISSION" "$INLINE_REJECTION_BASELINE" <<'PY'
197
- import json
198
- import sys
199
- from pathlib import Path
200
-
201
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
202
- routing = Path(sys.argv[3])
203
- proposal = Path(sys.argv[4])
204
- chooser = Path(sys.argv[5])
205
- initial_mission = sys.argv[6]
206
- before = json.loads(Path(sys.argv[7]).read_text())
207
- tracked = [
208
- Path('.agent/mission.md'),
209
- Path('.agent/profile.json'),
210
- Path('.agent/state.json'),
211
- Path('.agent/plan.json'),
212
- Path('.agent/active-slice.json'),
213
- Path('.agent/verification-evidence.json'),
214
- ]
215
- current_state = json.loads(before['state.json'])
216
- assert current_state['mission_anchor'] == initial_mission, 'active /cook inline-args rejection should start from the current mission anchor'
217
- assert not routing.exists(), 'active /cook inline-args rejection should not run active-workflow routing'
218
- assert not proposal.exists(), 'active /cook inline-args rejection should not open final startup-brief confirmation'
219
- assert not chooser.exists(), 'active /cook inline-args rejection should not open the existing-workflow chooser'
220
- assert '/cook no longer accepts inline arguments.' in output, 'active /cook inline-args rejection should explain the bare-only entry contract'
221
- after = {path.name: path.read_text() for path in tracked}
222
- assert before == after, 'active /cook inline-args rejection should leave canonical files unchanged'
223
- PY
224
-
225
- SESSION_INITIAL_REFOCUS="$TMPDIR/session-initial-bare-refocus.jsonl"
226
- INITIAL_REFOCUS_ROUTING="$TMPDIR/initial-bare-refocus-routing.json"
227
- INITIAL_REFOCUS_MESSAGES="$(python3 - <<'PY'
93
+ BOOTSTRAP_DISCUSSION=$'Prepare the active workflow fixture and keep the initial mission small and truthful.'
94
+ BOOTSTRAP_HANDOFF="$(python3 - <<'PY'
228
95
  import json
229
96
  capsule = {
230
97
  "kind": "cook_handoff",
231
98
  "source": "primary_agent",
232
- "captured_at": "2026-01-01T00:00:02.000Z",
233
- "source_turn_id": "m0002",
234
- "mission": "Remove completion status line, keep widget.",
99
+ "mission": "Maintain the active workflow fixture baseline.",
235
100
  "scope": [
236
- "Replace the initial smoke-test workflow with the widget mission."
101
+ "Scaffold canonical completion files for the refocus regression fixture.",
102
+ "Keep the initial workflow ready for later replacement checks."
237
103
  ],
238
104
  "constraints": [
239
- "Keep the approval-only Start/Cancel refocus gate."
105
+ "Use supported bare /cook startup only."
240
106
  ],
241
107
  "acceptance": [
242
- "Rewrite canonical state only after the replacement mission is approved."
108
+ "Bootstrap canonical completion state for the refocus regression fixture.",
109
+ "Keep scripts/refocus-test.sh aligned with the shipped startup behavior."
243
110
  ],
244
111
  "risks": [],
245
112
  "notes": [
246
- "Use a fresh explicit primary-agent handoff for the active-workflow replacement."
113
+ "Bootstrap from same-entry primary-agent startup-plan synthesis."
247
114
  ],
248
115
  "handoff_kind": "implementation_workflow_handoff",
249
- "first_slice_goal": "Replace the initial smoke-test workflow with the widget mission.",
116
+ "first_slice_goal": "Scaffold canonical files for the refocus regression fixture.",
250
117
  "first_slice_non_goals": [],
251
- "implementation_surfaces": [
252
- "scripts/refocus-test.sh"
253
- ],
254
- "verification_commands": [
255
- "npm run refocus-test"
256
- ],
257
- "why_this_slice_first": "The fresh explicit handoff is the only supported replacement entry while a workflow is active.",
118
+ "implementation_surfaces": ["scripts/refocus-test.sh"],
119
+ "verification_commands": ["npm run refocus-test"],
120
+ "why_this_slice_first": "Canonical state must exist before refocus can be exercised.",
258
121
  "task_type": "completion-workflow",
259
122
  "evaluation_profile": "completion-rubric-v1",
260
- "why_cook_now": "A different active workflow is ready and explicitly handed off by the primary agent."
123
+ "why_cook_now": "The fixture startup is concrete enough to begin workflow planning."
261
124
  }
262
- messages = [
263
- {"role": "user", "content": "The smoke-test workflow is active, but a different replacement workflow may now be ready."},
264
- {"role": "assistant", "content": "Use this fresh explicit handoff if you want /cook to replace the active workflow.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
265
- ]
266
- print(json.dumps(messages, ensure_ascii=False))
267
- PY
268
- )"
269
- write_session_messages "$SESSION_INITIAL_REFOCUS" "$TMPDIR" "$INITIAL_REFOCUS_MESSAGES"
270
-
271
- PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
272
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
273
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$INITIAL_REFOCUS_ROUTING" \
274
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
275
- pi --session "$SESSION_INITIAL_REFOCUS" -e "$PKG_ROOT" -p "/cook" \
276
- >"$TMPDIR/pi-completion-refocus.out" 2>"$TMPDIR/pi-completion-refocus.err"
277
-
278
- python3 - "$INITIAL_REFOCUS_ROUTING" <<'PY'
279
- import json
280
- import sys
281
- from pathlib import Path
282
-
283
- new_anchor = 'Remove completion status line, keep widget.'
284
- expected_task_type = 'completion-workflow'
285
- expected_eval_profile = 'completion-rubric-v1'
286
- routing = json.loads(Path(sys.argv[1]).read_text())
287
- mission_text = Path('.agent/mission.md').read_text()
288
- profile = json.loads(Path('.agent/profile.json').read_text())
289
- state = json.loads(Path('.agent/state.json').read_text())
290
- plan = json.loads(Path('.agent/plan.json').read_text())
291
- active = json.loads(Path('.agent/active-slice.json').read_text())
292
-
293
- assert new_anchor in mission_text, '.agent/mission.md did not update to the refocused mission anchor'
294
- assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after refocus'
295
- assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after refocus'
296
- assert state['mission_anchor'] == new_anchor, 'state.json mission_anchor mismatch after refocus'
297
- assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after refocus'
298
- assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after refocus'
299
- assert state['advisory_startup_brief']['mission'] == new_anchor, 'refocus should preserve the confirmed startup brief as advisory intake'
300
- assert plan['mission_anchor'] == new_anchor, 'plan.json mission_anchor mismatch after refocus'
301
- assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after refocus'
302
- assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after refocus'
303
- assert active['mission_anchor'] == new_anchor, 'active-slice.json mission_anchor mismatch after refocus'
304
- assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after refocus'
305
- assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after refocus'
306
- assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground after refocus'
307
- assert state['requires_reground'] is True, 'state.json requires_reground should be true after refocus'
308
- assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should reset to completion-regrounder'
309
- assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the refocus'
310
- assert plan['plan_basis'] == 'user_refocus', 'plan.json plan_basis should be user_refocus after refocus'
311
- assert active['status'] == 'idle', 'active-slice.json status should reset to idle after refocus'
312
- assert routing['mode'] == 'bare', 'supported refocus should use bare active-workflow routing mode'
313
- assert 'explicitGoal' not in routing, 'supported bare refocus should not expose removed explicit-goal shim fields'
314
- assert 'explicitGoalProvided' not in routing, 'supported bare refocus should not expose removed explicit-goal shim fields'
315
- assert routing['action'] == 'refocus', 'supported bare /cook should classify as refocus when a fresh explicit handoff proposes a different mission'
316
- assert routing['reason'] == 'fresh_explicit_handoff', 'supported bare /cook should record the explicit-handoff replacement reason'
317
- assert routing['proposedMissionAnchor'] == new_anchor, 'explicit handoff routing snapshot should expose the replacement mission anchor'
318
- assert routing['proposalSource'] == 'handoff_capsule', 'explicit handoff routing snapshot should preserve the handoff source'
319
- PY
320
-
321
- UPDATED_MISSION="$(python3 - <<'PY'
322
- import json
323
- from pathlib import Path
324
- state = json.loads(Path('.agent/state.json').read_text())
325
- print(state['mission_anchor'])
125
+ print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
326
126
  PY
327
127
  )"
328
-
329
- if [[ "$INITIAL_MISSION" == "$UPDATED_MISSION" ]]; then
330
- echo "expected mission anchor to change during supported bare refocus" >&2
331
- exit 1
332
- fi
333
-
334
- # Fresh explicit handoff replacements must still reach the chooser and final Start/Cancel gate while the
335
- # workflow is active.
336
- BARE_REFOCUS_MISSION='Exercise explicit active-workflow replacement coverage.'
337
- BARE_REFOCUS_MESSAGES="$(python3 - <<'PY'
128
+ REPLACEMENT_DISCUSSION=$'Switch the active workflow to the widget mission and keep the change confirm-first.'
129
+ REPLACEMENT_HANDOFF="$(python3 - <<'PY'
338
130
  import json
339
131
  capsule = {
340
132
  "kind": "cook_handoff",
341
133
  "source": "primary_agent",
342
- "captured_at": "2026-01-01T00:00:02.000Z",
343
- "source_turn_id": "m0002",
344
- "mission": "Exercise explicit active-workflow replacement coverage.",
134
+ "mission": "Remove the completion status line while keeping the completion widget.",
345
135
  "scope": [
346
- "Treat the active bare /cook request as an explicit replacement workflow.",
347
- "Keep the replacement behind the existing approval-only Start/Cancel gate."
136
+ "Replace the active fixture mission with the widget mission.",
137
+ "Keep refocus approval-only before canonical rewrite."
348
138
  ],
349
139
  "constraints": [
350
- "Do not rewrite canonical state before the final Start confirmation."
140
+ "Do not bypass the Start/Cancel gate."
351
141
  ],
352
142
  "acceptance": [
353
- "Add deterministic coverage proving the chooser and final approval path for this explicit replacement mission."
143
+ "Rewrite canonical workflow state only after the replacement mission is approved.",
144
+ "Keep refocus regression coverage aligned with same-entry synthesis behavior."
145
+ ],
146
+ "risks": [
147
+ "Do not leave the old mission partially active after refocus."
354
148
  ],
355
- "risks": [],
356
149
  "notes": [
357
- "This replacement should come only from the fresh explicit handoff, not recent discussion inference."
150
+ "This replacement must come from same-entry primary-agent startup-plan synthesis."
358
151
  ],
359
152
  "handoff_kind": "implementation_workflow_handoff",
360
- "first_slice_goal": "Exercise the active-workflow explicit-handoff replacement path.",
361
- "first_slice_non_goals": [],
362
- "implementation_surfaces": [
363
- "scripts/refocus-test.sh"
364
- ],
365
- "verification_commands": [
366
- "npm run refocus-test"
367
- ],
368
- "why_this_slice_first": "The active workflow should only replace from a fresh explicit handoff.",
153
+ "first_slice_goal": "Rewrite canonical workflow state for the widget mission.",
154
+ "first_slice_non_goals": ["Do not start implementation before the refocus is approved."],
155
+ "implementation_surfaces": ["scripts/refocus-test.sh", ".agent/startup-plan.json"],
156
+ "verification_commands": ["npm run refocus-test"],
157
+ "why_this_slice_first": "The active workflow must be re-anchored before later role dispatch.",
369
158
  "task_type": "completion-workflow",
370
159
  "evaluation_profile": "completion-rubric-v1",
371
- "why_cook_now": "The primary agent explicitly handed off a replacement workflow while the current one is active."
160
+ "why_cook_now": "A different workflow mission is now concrete enough to replace the active one."
372
161
  }
373
- messages = [
374
- {"role": "user", "content": "The current workflow is active, but there is a fresh explicit replacement handoff ready."},
375
- {"role": "assistant", "content": "Use this fresh explicit handoff if you want /cook to replace the active workflow.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
376
- ]
377
- print(json.dumps(messages, ensure_ascii=False))
162
+ print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
378
163
  PY
379
164
  )"
380
165
 
381
- SESSION_BARE_CHOOSER_CANCEL="$TMPDIR/session-bare-chooser-cancel.jsonl"
382
- BARE_CHOOSER_SNAPSHOT="$TMPDIR/bare-existing-workflow-chooser.json"
383
- BARE_ROUTING_CHOOSER_CANCEL="$TMPDIR/bare-routing-chooser-cancel.json"
384
- write_session_messages "$SESSION_BARE_CHOOSER_CANCEL" "$TMPDIR" "$BARE_REFOCUS_MESSAGES"
385
-
166
+ # Case 1: chooser cancel leaves canonical state untouched while still surfacing synthesized replacement routing.
167
+ ROOT_ONE="$TMPDIR/repo-one"
168
+ SESSION_ONE_BOOTSTRAP="$TMPDIR/session-one-bootstrap.jsonl"
169
+ SESSION_ONE_REFOCUS="$TMPDIR/session-one-refocus.jsonl"
170
+ ROUTING_ONE="$TMPDIR/routing-one.json"
171
+ CHOOSER_ONE="$TMPDIR/chooser-one.json"
172
+ PROPOSAL_ONE="$TMPDIR/proposal-one.json"
173
+ BASELINE_ONE="$TMPDIR/baseline-one.json"
174
+ bootstrap_active_workflow "$ROOT_ONE" "$SESSION_ONE_BOOTSTRAP" "$BOOTSTRAP_DISCUSSION" "$BOOTSTRAP_HANDOFF"
175
+ cd "$ROOT_ONE"
176
+ snapshot_tracked "$BASELINE_ONE"
177
+ write_session "$SESSION_ONE_REFOCUS" "$ROOT_ONE" "$REPLACEMENT_DISCUSSION"
178
+
179
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$REPLACEMENT_HANDOFF" \
386
180
  PI_COMPLETION_EXISTING_WORKFLOW_ACTION=cancel \
387
- PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$BARE_CHOOSER_SNAPSHOT" \
388
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$BARE_ROUTING_CHOOSER_CANCEL" \
389
181
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
390
- pi --session "$SESSION_BARE_CHOOSER_CANCEL" -e "$PKG_ROOT" -p "/cook" \
391
- >"$TMPDIR/pi-completion-bare-chooser-cancel.out" 2>"$TMPDIR/pi-completion-bare-chooser-cancel.err"
182
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ROUTING_ONE" \
183
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CHOOSER_ONE" \
184
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_ONE" \
185
+ pi --session "$SESSION_ONE_REFOCUS" -e "$PKG_ROOT" -p "/cook" \
186
+ >"$TMPDIR/refocus-one.out" 2>"$TMPDIR/refocus-one.err"
392
187
 
393
- python3 - "$BARE_CHOOSER_SNAPSHOT" "$BARE_ROUTING_CHOOSER_CANCEL" "$TMPDIR/pi-completion-bare-chooser-cancel.out" "$TMPDIR/pi-completion-bare-chooser-cancel.err" "$UPDATED_MISSION" "$BARE_REFOCUS_MISSION" <<'PY'
188
+ python3 - "$ROUTING_ONE" "$CHOOSER_ONE" "$PROPOSAL_ONE" "$BASELINE_ONE" "$TMPDIR/refocus-one.out" "$TMPDIR/refocus-one.err" <<'PY'
394
189
  import json
395
190
  import sys
396
191
  from pathlib import Path
397
192
 
398
- chooser = json.loads(Path(sys.argv[1]).read_text())
399
- routing = json.loads(Path(sys.argv[2]).read_text())
400
- output = Path(sys.argv[3]).read_text() + Path(sys.argv[4]).read_text()
401
- updated_mission = sys.argv[5]
402
- replacement_mission = sys.argv[6]
403
- state = json.loads(Path('.agent/state.json').read_text())
404
- plan = json.loads(Path('.agent/plan.json').read_text())
405
- active = json.loads(Path('.agent/active-slice.json').read_text())
193
+ routing = json.loads(Path(sys.argv[1]).read_text())
194
+ chooser = json.loads(Path(sys.argv[2]).read_text())
195
+ proposal = Path(sys.argv[3])
196
+ before = json.loads(Path(sys.argv[4]).read_text())
197
+ output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
198
+ tracked = [
199
+ Path('.agent/mission.md'),
200
+ Path('.agent/profile.json'),
201
+ Path('.agent/state.json'),
202
+ Path('.agent/startup-plan.json'),
203
+ Path('.agent/startup-plan.md'),
204
+ Path('.agent/plan.json'),
205
+ Path('.agent/active-slice.json'),
206
+ Path('.agent/verification-evidence.json'),
207
+ ]
208
+ after = {path.name: path.read_text() for path in tracked}
406
209
 
407
- assert state['mission_anchor'] == updated_mission, 'chooser cancel should keep the current mission anchor'
408
- assert plan['mission_anchor'] == updated_mission, 'chooser cancel should keep plan.json unchanged'
409
- assert active['mission_anchor'] == updated_mission, 'chooser cancel should keep active-slice.json unchanged'
410
- assert routing['mode'] == 'bare', 'bare /cook should snapshot bare active-workflow routing mode'
411
- assert 'explicitGoal' not in routing, 'bare chooser routing should not expose removed explicit-goal shim fields'
412
- assert 'explicitGoalProvided' not in routing, 'bare chooser routing should not expose removed explicit-goal shim fields'
413
- assert routing['action'] == 'refocus', 'fresh explicit replacement handoff should classify active bare /cook as refocus'
414
- assert routing['reason'] == 'fresh_explicit_handoff', 'fresh explicit replacement handoff should record the explicit-handoff reason'
415
- assert routing['currentMissionAnchor'] == updated_mission, 'explicit-handoff routing should keep the current mission anchor until the user approves replacement'
416
- assert routing['proposedMissionAnchor'] == replacement_mission, 'explicit-handoff routing should expose the proposed replacement mission'
417
- assert routing['proposalSource'] == 'handoff_capsule', 'explicit-handoff routing should preserve the handoff source'
418
- assert chooser['title'].startswith('Existing completion workflow found'), 'bare chooser snapshot should describe the existing-workflow prompt'
419
- assert chooser['choices'][0].startswith('Continue current workflow'), 'bare chooser should keep the continue option'
420
- assert chooser['choices'][1].startswith('Start new workflow from explicit primary-agent handoff'), 'bare chooser should offer the explicit-handoff replacement option'
421
- assert 'Start/Cancel confirmation' in chooser['choices'][1], 'bare chooser should mention the approval-only replacement confirmation'
422
- assert chooser['choices'][2].startswith('Cancel'), 'bare chooser should keep the cancel option'
423
- assert 'Discuss changes in the main chat and rerun /cook.' in output, 'bare chooser cancel should redirect users back to the main chat and rerun /cook'
210
+ assert routing['action'] == 'refocus', routing
211
+ assert routing['reason'] == 'generated_replacement_startup_plan', routing
212
+ assert routing['proposedMissionAnchor'] == 'Remove the completion status line while keeping the completion widget.', routing
213
+ assert chooser['choices'][1].startswith('Start new workflow from same-entry primary-agent startup plan'), chooser
214
+ assert not proposal.exists(), 'chooser cancel should not open the final Start/Cancel proposal snapshot'
215
+ assert before == after, 'chooser cancel should leave canonical workflow files unchanged'
216
+ assert 'Cancelled existing workflow confirmation.' in output, output
424
217
  PY
425
218
 
426
- SESSION_BARE_FINAL_CANCEL="$TMPDIR/session-bare-final-cancel.jsonl"
427
- BARE_ROUTING_FINAL_CANCEL="$TMPDIR/bare-routing-final-cancel.json"
428
- BARE_PROPOSAL_CANCEL="$TMPDIR/bare-replacement-proposal-cancel.json"
429
- write_session_messages "$SESSION_BARE_FINAL_CANCEL" "$TMPDIR" "$BARE_REFOCUS_MESSAGES"
430
-
219
+ # Case 2: final Start/Cancel cancel still leaves canonical state untouched after chooser acceptance.
220
+ ROOT_TWO="$TMPDIR/repo-two"
221
+ SESSION_TWO_BOOTSTRAP="$TMPDIR/session-two-bootstrap.jsonl"
222
+ SESSION_TWO_REFOCUS="$TMPDIR/session-two-refocus.jsonl"
223
+ ROUTING_TWO="$TMPDIR/routing-two.json"
224
+ CHOOSER_TWO="$TMPDIR/chooser-two.json"
225
+ PROPOSAL_TWO="$TMPDIR/proposal-two.json"
226
+ BASELINE_TWO="$TMPDIR/baseline-two.json"
227
+ bootstrap_active_workflow "$ROOT_TWO" "$SESSION_TWO_BOOTSTRAP" "$BOOTSTRAP_DISCUSSION" "$BOOTSTRAP_HANDOFF"
228
+ cd "$ROOT_TWO"
229
+ snapshot_tracked "$BASELINE_TWO"
230
+ write_session "$SESSION_TWO_REFOCUS" "$ROOT_TWO" "$REPLACEMENT_DISCUSSION"
231
+
232
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$REPLACEMENT_HANDOFF" \
431
233
  PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
432
234
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=cancel \
433
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$BARE_PROPOSAL_CANCEL" \
434
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$BARE_ROUTING_FINAL_CANCEL" \
435
235
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
436
- pi --session "$SESSION_BARE_FINAL_CANCEL" -e "$PKG_ROOT" -p "/cook" \
437
- >"$TMPDIR/pi-completion-bare-final-cancel.out" 2>"$TMPDIR/pi-completion-bare-final-cancel.err"
236
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ROUTING_TWO" \
237
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CHOOSER_TWO" \
238
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_TWO" \
239
+ pi --session "$SESSION_TWO_REFOCUS" -e "$PKG_ROOT" -p "/cook" \
240
+ >"$TMPDIR/refocus-two.out" 2>"$TMPDIR/refocus-two.err"
438
241
 
439
- python3 - "$BARE_PROPOSAL_CANCEL" "$BARE_ROUTING_FINAL_CANCEL" "$TMPDIR/pi-completion-bare-final-cancel.out" "$TMPDIR/pi-completion-bare-final-cancel.err" "$UPDATED_MISSION" "$BARE_REFOCUS_MISSION" <<'PY'
242
+ python3 - "$ROUTING_TWO" "$CHOOSER_TWO" "$PROPOSAL_TWO" "$BASELINE_TWO" "$TMPDIR/refocus-two.out" "$TMPDIR/refocus-two.err" <<'PY'
440
243
  import json
441
244
  import sys
442
245
  from pathlib import Path
443
246
 
444
- proposal = json.loads(Path(sys.argv[1]).read_text())
445
- routing = json.loads(Path(sys.argv[2]).read_text())
446
- output = Path(sys.argv[3]).read_text() + Path(sys.argv[4]).read_text()
447
- updated_mission = sys.argv[5]
448
- replacement_mission = sys.argv[6]
449
- state = json.loads(Path('.agent/state.json').read_text())
450
- plan = json.loads(Path('.agent/plan.json').read_text())
451
- active = json.loads(Path('.agent/active-slice.json').read_text())
452
-
453
- assert state['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep the current mission anchor'
454
- assert plan['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep plan.json unchanged'
455
- assert active['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep active-slice.json unchanged'
456
- assert routing['action'] == 'refocus', 'final Start/Cancel cancel should still come from an explicit-handoff refocus classification'
457
- assert routing['reason'] == 'fresh_explicit_handoff', 'final Start/Cancel cancel should preserve the explicit-handoff reason'
458
- assert routing['currentMissionAnchor'] == updated_mission, 'final Start/Cancel cancel should keep the current mission anchor until the user approves replacement'
459
- assert proposal['mission'] == replacement_mission, 'final Start/Cancel cancel should still prepare the replacement proposal before rewriting state'
460
- assert proposal['source'] == 'handoff_capsule', 'final Start/Cancel cancel should preserve the explicit-handoff proposal source'
461
- assert 'Discuss changes in the main chat and rerun /cook.' in output, 'final Start/Cancel cancel should redirect users back to the main chat and rerun /cook'
462
- PY
463
-
464
- SESSION_BARE_ACCEPT="$TMPDIR/session-bare-accept.jsonl"
465
- BARE_ROUTING_ACCEPT="$TMPDIR/bare-routing-accept.json"
466
- BARE_PROPOSAL_ACCEPT="$TMPDIR/bare-replacement-proposal-accept.json"
467
- write_session_messages "$SESSION_BARE_ACCEPT" "$TMPDIR" "$BARE_REFOCUS_MESSAGES"
468
-
469
- PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
470
- PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
471
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$BARE_PROPOSAL_ACCEPT" \
472
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$BARE_ROUTING_ACCEPT" \
473
- PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
474
- pi --session "$SESSION_BARE_ACCEPT" -e "$PKG_ROOT" -p "/cook" \
475
- >"$TMPDIR/pi-completion-bare-accept.out" 2>"$TMPDIR/pi-completion-bare-accept.err"
476
-
477
- python3 - "$BARE_PROPOSAL_ACCEPT" "$BARE_ROUTING_ACCEPT" <<'PY'
478
- import json
479
- import sys
480
- from pathlib import Path
481
-
482
- new_anchor = 'Exercise explicit active-workflow replacement coverage.'
483
- expected_task_type = 'completion-workflow'
484
- expected_eval_profile = 'completion-rubric-v1'
485
- proposal = json.loads(Path(sys.argv[1]).read_text())
486
- routing = json.loads(Path(sys.argv[2]).read_text())
487
- mission_text = Path('.agent/mission.md').read_text()
488
- profile = json.loads(Path('.agent/profile.json').read_text())
489
- state = json.loads(Path('.agent/state.json').read_text())
490
- plan = json.loads(Path('.agent/plan.json').read_text())
491
- active = json.loads(Path('.agent/active-slice.json').read_text())
492
-
493
- assert proposal['mission'] == new_anchor, 'accepted bare refocus should preserve the replacement proposal mission'
494
- assert routing['mode'] == 'bare', 'accepted bare refocus should keep bare routing mode'
495
- assert 'explicitGoal' not in routing, 'accepted bare refocus should not expose removed explicit-goal shim fields'
496
- assert 'explicitGoalProvided' not in routing, 'accepted bare refocus should not expose removed explicit-goal shim fields'
497
- assert routing['action'] == 'refocus', 'accepted bare refocus should keep the explicit-handoff refocus classification'
498
- assert routing['reason'] == 'fresh_explicit_handoff', 'accepted bare refocus should keep the explicit-handoff reason'
499
- assert routing['currentMissionAnchor'] == 'Remove completion status line, keep widget.', 'accepted bare refocus should expose the original mission until Start is accepted'
500
- assert routing['proposalSource'] == 'handoff_capsule', 'accepted bare refocus should preserve the explicit-handoff source'
501
- assert new_anchor in mission_text, '.agent/mission.md did not update to the bare refocus mission anchor'
502
- assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after bare refocus'
503
- assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after bare refocus'
504
- assert state['mission_anchor'] == new_anchor, 'state.json mission_anchor mismatch after bare refocus'
505
- assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after bare refocus'
506
- assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after bare refocus'
507
- assert state['advisory_startup_brief']['mission'] == new_anchor, 'bare refocus should preserve the confirmed startup brief as advisory intake'
508
- assert plan['mission_anchor'] == new_anchor, 'plan.json mission_anchor mismatch after bare refocus'
509
- assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after bare refocus'
510
- assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after bare refocus'
511
- assert active['mission_anchor'] == new_anchor, 'active-slice.json mission_anchor mismatch after bare refocus'
512
- assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after bare refocus'
513
- assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after bare refocus'
514
- assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground after bare refocus'
515
- assert state['requires_reground'] is True, 'state.json requires_reground should be true after bare refocus'
516
- assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should reset to completion-regrounder after bare refocus'
517
- assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the bare refocus'
518
- assert plan['plan_basis'] == 'user_refocus', 'plan.json plan_basis should be user_refocus after bare refocus'
519
- assert active['status'] == 'idle', 'active-slice.json status should reset to idle after bare refocus'
520
- PY
521
-
522
- SYNTH_REPLACEMENT_SESSION="$TMPDIR/session-synthesized-active-replacement.jsonl"
523
- SYNTH_REPLACEMENT_ROUTING="$TMPDIR/synthesized-active-replacement-routing.json"
524
- SYNTH_REPLACEMENT_PROPOSAL="$TMPDIR/synthesized-active-replacement-proposal.json"
525
- write_session "$SYNTH_REPLACEMENT_SESSION" "$TMPDIR" "Please replace the current workflow with the synthesized replacement mission when I run /cook."
247
+ routing = json.loads(Path(sys.argv[1]).read_text())
248
+ chooser = json.loads(Path(sys.argv[2]).read_text())
249
+ proposal = json.loads(Path(sys.argv[3]).read_text())
250
+ before = json.loads(Path(sys.argv[4]).read_text())
251
+ output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
252
+ tracked = [
253
+ Path('.agent/mission.md'),
254
+ Path('.agent/profile.json'),
255
+ Path('.agent/state.json'),
256
+ Path('.agent/startup-plan.json'),
257
+ Path('.agent/startup-plan.md'),
258
+ Path('.agent/plan.json'),
259
+ Path('.agent/active-slice.json'),
260
+ Path('.agent/verification-evidence.json'),
261
+ ]
262
+ after = {path.name: path.read_text() for path in tracked}
526
263
 
527
- SYNTH_REPLACEMENT_HANDOFF="$(python3 - <<'PY'
528
- import json
529
- capsule = {
530
- "kind": "cook_handoff",
531
- "source": "primary_agent",
532
- "captured_at": "2026-01-01T00:00:02.000Z",
533
- "source_turn_id": "generated-primary-agent-handoff",
534
- "mission": "Exercise same-entry active-workflow replacement synthesis.",
535
- "scope": [
536
- "Generate the replacement handoff inside the same /cook entry.",
537
- "Keep the chooser and final Start/Cancel confirmation truthful."
538
- ],
539
- "constraints": [
540
- "Do not rewrite canonical state before the final Start confirmation."
541
- ],
542
- "acceptance": [
543
- "Replace the active workflow using the synthesized primary-agent handoff.",
544
- "Keep deterministic coverage for same-entry active replacement."
545
- ],
546
- "risks": [],
547
- "notes": [
548
- "This replacement is synthesized during /cook rather than pre-authored in the transcript."
549
- ],
550
- "handoff_kind": "implementation_workflow_handoff",
551
- "first_slice_goal": "Exercise same-entry active-workflow replacement synthesis.",
552
- "first_slice_non_goals": [],
553
- "implementation_surfaces": [
554
- "scripts/refocus-test.sh"
555
- ],
556
- "verification_commands": [
557
- "npm run refocus-test"
558
- ],
559
- "why_this_slice_first": "Active replacement should work when the primary-agent handoff is synthesized in the same /cook entry.",
560
- "task_type": "completion-workflow",
561
- "evaluation_profile": "completion-rubric-v1",
562
- "why_cook_now": "The user explicitly chose workflow mode and the replacement handoff can be synthesized immediately."
563
- }
564
- print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
264
+ assert routing['action'] == 'refocus', routing
265
+ assert routing['reason'] == 'generated_replacement_startup_plan', routing
266
+ assert chooser['choices'][1].startswith('Start new workflow from same-entry primary-agent startup plan'), chooser
267
+ assert proposal['source'] == 'deferred_primary_agent_handoff', proposal
268
+ assert proposal['mission'] == 'Remove the completion status line while keeping the completion widget.', proposal
269
+ assert before == after, 'final Start/Cancel cancel should leave canonical workflow files unchanged'
270
+ assert 'Cancelled replacement workflow proposal.' in output, output
565
271
  PY
566
- )"
567
272
 
568
- PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$SYNTH_REPLACEMENT_HANDOFF" \
273
+ # Case 3: accepting the synthesized replacement rewrites canonical mission and startup plan.
274
+ ROOT_THREE="$TMPDIR/repo-three"
275
+ SESSION_THREE_BOOTSTRAP="$TMPDIR/session-three-bootstrap.jsonl"
276
+ SESSION_THREE_REFOCUS="$TMPDIR/session-three-refocus.jsonl"
277
+ ROUTING_THREE="$TMPDIR/routing-three.json"
278
+ CHOOSER_THREE="$TMPDIR/chooser-three.json"
279
+ PROPOSAL_THREE="$TMPDIR/proposal-three.json"
280
+ bootstrap_active_workflow "$ROOT_THREE" "$SESSION_THREE_BOOTSTRAP" "$BOOTSTRAP_DISCUSSION" "$BOOTSTRAP_HANDOFF"
281
+ cd "$ROOT_THREE"
282
+ write_session "$SESSION_THREE_REFOCUS" "$ROOT_THREE" "$REPLACEMENT_DISCUSSION"
283
+
284
+ PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$REPLACEMENT_HANDOFF" \
569
285
  PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
570
286
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
571
- PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$SYNTH_REPLACEMENT_PROPOSAL" \
572
- PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$SYNTH_REPLACEMENT_ROUTING" \
573
287
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
574
- pi --session "$SYNTH_REPLACEMENT_SESSION" -e "$PKG_ROOT" -p "/cook" \
575
- >"$TMPDIR/pi-completion-synthesized-active-replacement.out" 2>"$TMPDIR/pi-completion-synthesized-active-replacement.err"
288
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ROUTING_THREE" \
289
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CHOOSER_THREE" \
290
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$PROPOSAL_THREE" \
291
+ pi --session "$SESSION_THREE_REFOCUS" -e "$PKG_ROOT" -p "/cook" \
292
+ >"$TMPDIR/refocus-three.out" 2>"$TMPDIR/refocus-three.err"
576
293
 
577
- python3 - "$SYNTH_REPLACEMENT_PROPOSAL" "$SYNTH_REPLACEMENT_ROUTING" <<'PY'
294
+ python3 - "$ROUTING_THREE" "$CHOOSER_THREE" "$PROPOSAL_THREE" "$TMPDIR/refocus-three.out" "$TMPDIR/refocus-three.err" <<'PY'
578
295
  import json
579
296
  import sys
580
297
  from pathlib import Path
581
298
 
582
- new_anchor = 'Exercise same-entry active-workflow replacement synthesis.'
583
- proposal = json.loads(Path(sys.argv[1]).read_text())
584
- routing = json.loads(Path(sys.argv[2]).read_text())
299
+ routing = json.loads(Path(sys.argv[1]).read_text())
300
+ chooser = json.loads(Path(sys.argv[2]).read_text())
301
+ proposal = json.loads(Path(sys.argv[3]).read_text())
302
+ output = Path(sys.argv[4]).read_text() + Path(sys.argv[5]).read_text()
585
303
  state = json.loads(Path('.agent/state.json').read_text())
304
+ startup_plan = json.loads(Path('.agent/startup-plan.json').read_text())
305
+ startup_plan_md = Path('.agent/startup-plan.md').read_text()
586
306
  plan = json.loads(Path('.agent/plan.json').read_text())
587
307
  active = json.loads(Path('.agent/active-slice.json').read_text())
588
308
 
589
- assert proposal['mission'] == new_anchor, 'same-entry synthesized replacement should preserve the replacement proposal mission'
590
- assert routing['action'] == 'refocus', 'same-entry synthesized replacement should classify active bare /cook as refocus'
591
- assert routing['reason'] == 'fresh_explicit_handoff', 'same-entry synthesized replacement should reuse the explicit-handoff routing reason because /cook synthesized an explicit handoff'
592
- assert routing['proposalSource'] == 'handoff_capsule', 'same-entry synthesized replacement should surface the synthesized handoff as a handoff capsule source'
593
- assert state['mission_anchor'] == new_anchor, 'state.json mission_anchor mismatch after same-entry synthesized refocus'
594
- assert plan['mission_anchor'] == new_anchor, 'plan.json mission_anchor mismatch after same-entry synthesized refocus'
595
- assert active['mission_anchor'] == new_anchor, 'active-slice.json mission_anchor mismatch after same-entry synthesized refocus'
596
- assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'same-entry synthesized replacement should record the /cook refocus continuation reason'
309
+ mission = 'Remove the completion status line while keeping the completion widget.'
310
+ assert routing['action'] == 'refocus', routing
311
+ assert routing['reason'] == 'generated_replacement_startup_plan', routing
312
+ assert chooser['choices'][1].startswith('Start new workflow from same-entry primary-agent startup plan'), chooser
313
+ assert proposal['source'] == 'deferred_primary_agent_handoff', proposal
314
+ assert state['mission_anchor'] == mission, state
315
+ assert state['current_phase'] == 'reground', state
316
+ assert state['next_mandatory_role'] == 'completion-regrounder', state
317
+ assert startup_plan['mission_anchor'] == mission, startup_plan
318
+ assert startup_plan['source'] == 'deferred_primary_agent_handoff', startup_plan
319
+ assert '## Goal' in startup_plan_md and mission in startup_plan_md, startup_plan_md
320
+ assert plan['mission_anchor'] == mission, plan
321
+ assert active['mission_anchor'] == mission, active
322
+ assert 'Refocused completion mission from same-entry primary-agent startup plan' in output, output
597
323
  PY
598
324
 
599
- echo "refocus test passed: $TMPDIR"
325
+ echo "refocus test passed: $PKG_ROOT"