@linimin/pi-letscook 0.1.58 → 0.1.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/README.md +28 -0
- package/.agent/mission.md +8 -0
- package/.agent/profile.json +13 -0
- package/.agent/verify_completion_control_plane.sh +203 -0
- package/.agent/verify_completion_stop.sh +20 -0
- package/CHANGELOG.md +12 -0
- package/README.md +28 -22
- package/extensions/completion/driver.ts +51 -39
- package/extensions/completion/index.ts +29 -21
- package/extensions/completion/prompt-surfaces.ts +12 -10
- package/extensions/completion/proposal.ts +0 -15
- package/package.json +6 -1
- package/scripts/active-slice-contract-test.sh +93 -2
- package/scripts/canonical-evidence-artifact-test.sh +93 -2
- package/scripts/context-proposal-test.sh +577 -727
- package/scripts/refocus-test.sh +196 -28
- package/scripts/release-check.sh +57 -34
- package/scripts/smoke-test.sh +73 -14
- package/skills/cook-handoff-boundary/SKILL.md +16 -10
package/scripts/refocus-test.sh
CHANGED
|
@@ -47,15 +47,99 @@ 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
|
+
|
|
50
93
|
cd "$TMPDIR"
|
|
51
94
|
git init -q
|
|
52
95
|
|
|
53
96
|
BOOTSTRAP_SESSION="$TMPDIR/session-bootstrap.jsonl"
|
|
54
|
-
|
|
55
|
-
|
|
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."
|
|
132
|
+
}
|
|
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"
|
|
56
141
|
|
|
57
142
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
58
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
59
143
|
pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-refocus-bootstrap.out" 2>"$TMPDIR/pi-completion-refocus-bootstrap.err" &
|
|
60
144
|
PI_PID=$!
|
|
61
145
|
for _ in $(seq 1 60); do
|
|
@@ -139,11 +223,51 @@ assert before == after, 'active /cook inline-args rejection should leave canonic
|
|
|
139
223
|
PY
|
|
140
224
|
|
|
141
225
|
SESSION_INITIAL_REFOCUS="$TMPDIR/session-initial-bare-refocus.jsonl"
|
|
142
|
-
INITIAL_REFOCUS_DISCUSSION=$'Mission: Remove completion status line, keep widget.\nScope:\n- Replace the initial smoke-test workflow with the widget mission.\nConstraints:\n- Keep the approval-only Start/Cancel refocus gate.\nAcceptance:\n- Rewrite canonical state only after the replacement mission is approved.'
|
|
143
226
|
INITIAL_REFOCUS_ROUTING="$TMPDIR/initial-bare-refocus-routing.json"
|
|
144
|
-
|
|
227
|
+
INITIAL_REFOCUS_MESSAGES="$(python3 - <<'PY'
|
|
228
|
+
import json
|
|
229
|
+
capsule = {
|
|
230
|
+
"kind": "cook_handoff",
|
|
231
|
+
"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.",
|
|
235
|
+
"scope": [
|
|
236
|
+
"Replace the initial smoke-test workflow with the widget mission."
|
|
237
|
+
],
|
|
238
|
+
"constraints": [
|
|
239
|
+
"Keep the approval-only Start/Cancel refocus gate."
|
|
240
|
+
],
|
|
241
|
+
"acceptance": [
|
|
242
|
+
"Rewrite canonical state only after the replacement mission is approved."
|
|
243
|
+
],
|
|
244
|
+
"risks": [],
|
|
245
|
+
"notes": [
|
|
246
|
+
"Use a fresh explicit primary-agent handoff for the active-workflow replacement."
|
|
247
|
+
],
|
|
248
|
+
"handoff_kind": "implementation_workflow_handoff",
|
|
249
|
+
"first_slice_goal": "Replace the initial smoke-test workflow with the widget mission.",
|
|
250
|
+
"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.",
|
|
258
|
+
"task_type": "completion-workflow",
|
|
259
|
+
"evaluation_profile": "completion-rubric-v1",
|
|
260
|
+
"why_cook_now": "A different active workflow is ready and explicitly handed off by the primary agent."
|
|
261
|
+
}
|
|
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"
|
|
145
270
|
|
|
146
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
147
271
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
148
272
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
149
273
|
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$INITIAL_REFOCUS_ROUTING" \
|
|
@@ -188,9 +312,10 @@ assert active['status'] == 'idle', 'active-slice.json status should reset to idl
|
|
|
188
312
|
assert routing['mode'] == 'bare', 'supported refocus should use bare active-workflow routing mode'
|
|
189
313
|
assert 'explicitGoal' not in routing, 'supported bare refocus should not expose removed explicit-goal shim fields'
|
|
190
314
|
assert 'explicitGoalProvided' not in routing, 'supported bare refocus should not expose removed explicit-goal shim fields'
|
|
191
|
-
assert routing['action'] == 'refocus', 'supported bare /cook should classify as refocus when
|
|
192
|
-
assert routing['reason'] == '
|
|
193
|
-
assert routing['proposedMissionAnchor'] == new_anchor, '
|
|
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'
|
|
194
319
|
PY
|
|
195
320
|
|
|
196
321
|
UPDATED_MISSION="$(python3 - <<'PY'
|
|
@@ -206,16 +331,58 @@ if [[ "$INITIAL_MISSION" == "$UPDATED_MISSION" ]]; then
|
|
|
206
331
|
exit 1
|
|
207
332
|
fi
|
|
208
333
|
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
|
|
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'
|
|
338
|
+
import json
|
|
339
|
+
capsule = {
|
|
340
|
+
"kind": "cook_handoff",
|
|
341
|
+
"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.",
|
|
345
|
+
"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."
|
|
348
|
+
],
|
|
349
|
+
"constraints": [
|
|
350
|
+
"Do not rewrite canonical state before the final Start confirmation."
|
|
351
|
+
],
|
|
352
|
+
"acceptance": [
|
|
353
|
+
"Add deterministic coverage proving the chooser and final approval path for this explicit replacement mission."
|
|
354
|
+
],
|
|
355
|
+
"risks": [],
|
|
356
|
+
"notes": [
|
|
357
|
+
"This replacement should come only from the fresh explicit handoff, not recent discussion inference."
|
|
358
|
+
],
|
|
359
|
+
"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.",
|
|
369
|
+
"task_type": "completion-workflow",
|
|
370
|
+
"evaluation_profile": "completion-rubric-v1",
|
|
371
|
+
"why_cook_now": "The primary agent explicitly handed off a replacement workflow while the current one is active."
|
|
372
|
+
}
|
|
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))
|
|
378
|
+
PY
|
|
379
|
+
)"
|
|
212
380
|
|
|
213
381
|
SESSION_BARE_CHOOSER_CANCEL="$TMPDIR/session-bare-chooser-cancel.jsonl"
|
|
214
382
|
BARE_CHOOSER_SNAPSHOT="$TMPDIR/bare-existing-workflow-chooser.json"
|
|
215
383
|
BARE_ROUTING_CHOOSER_CANCEL="$TMPDIR/bare-routing-chooser-cancel.json"
|
|
216
|
-
|
|
384
|
+
write_session_messages "$SESSION_BARE_CHOOSER_CANCEL" "$TMPDIR" "$BARE_REFOCUS_MESSAGES"
|
|
217
385
|
|
|
218
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
219
386
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=cancel \
|
|
220
387
|
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$BARE_CHOOSER_SNAPSHOT" \
|
|
221
388
|
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$BARE_ROUTING_CHOOSER_CANCEL" \
|
|
@@ -243,13 +410,14 @@ assert active['mission_anchor'] == updated_mission, 'chooser cancel should keep
|
|
|
243
410
|
assert routing['mode'] == 'bare', 'bare /cook should snapshot bare active-workflow routing mode'
|
|
244
411
|
assert 'explicitGoal' not in routing, 'bare chooser routing should not expose removed explicit-goal shim fields'
|
|
245
412
|
assert 'explicitGoalProvided' not in routing, 'bare chooser routing should not expose removed explicit-goal shim fields'
|
|
246
|
-
assert routing['action'] == 'refocus', '
|
|
247
|
-
assert routing['reason'] == '
|
|
248
|
-
assert routing['currentMissionAnchor'] == updated_mission, '
|
|
249
|
-
assert routing['proposedMissionAnchor'] == replacement_mission, '
|
|
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'
|
|
250
418
|
assert chooser['title'].startswith('Existing completion workflow found'), 'bare chooser snapshot should describe the existing-workflow prompt'
|
|
251
419
|
assert chooser['choices'][0].startswith('Continue current workflow'), 'bare chooser should keep the continue option'
|
|
252
|
-
assert chooser['choices'][1].startswith('Start new workflow from
|
|
420
|
+
assert chooser['choices'][1].startswith('Start new workflow from explicit primary-agent handoff'), 'bare chooser should offer the explicit-handoff replacement option'
|
|
253
421
|
assert 'Start/Cancel confirmation' in chooser['choices'][1], 'bare chooser should mention the approval-only replacement confirmation'
|
|
254
422
|
assert chooser['choices'][2].startswith('Cancel'), 'bare chooser should keep the cancel option'
|
|
255
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'
|
|
@@ -258,9 +426,8 @@ PY
|
|
|
258
426
|
SESSION_BARE_FINAL_CANCEL="$TMPDIR/session-bare-final-cancel.jsonl"
|
|
259
427
|
BARE_ROUTING_FINAL_CANCEL="$TMPDIR/bare-routing-final-cancel.json"
|
|
260
428
|
BARE_PROPOSAL_CANCEL="$TMPDIR/bare-replacement-proposal-cancel.json"
|
|
261
|
-
|
|
429
|
+
write_session_messages "$SESSION_BARE_FINAL_CANCEL" "$TMPDIR" "$BARE_REFOCUS_MESSAGES"
|
|
262
430
|
|
|
263
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
264
431
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
265
432
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=cancel \
|
|
266
433
|
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$BARE_PROPOSAL_CANCEL" \
|
|
@@ -286,19 +453,19 @@ active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
|
286
453
|
assert state['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep the current mission anchor'
|
|
287
454
|
assert plan['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep plan.json unchanged'
|
|
288
455
|
assert active['mission_anchor'] == updated_mission, 'final Start/Cancel cancel should keep active-slice.json unchanged'
|
|
289
|
-
assert routing['action'] == 'refocus', 'final Start/Cancel cancel should still come from
|
|
290
|
-
assert routing['reason'] == '
|
|
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'
|
|
291
458
|
assert routing['currentMissionAnchor'] == updated_mission, 'final Start/Cancel cancel should keep the current mission anchor until the user approves replacement'
|
|
292
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'
|
|
293
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'
|
|
294
462
|
PY
|
|
295
463
|
|
|
296
464
|
SESSION_BARE_ACCEPT="$TMPDIR/session-bare-accept.jsonl"
|
|
297
465
|
BARE_ROUTING_ACCEPT="$TMPDIR/bare-routing-accept.json"
|
|
298
466
|
BARE_PROPOSAL_ACCEPT="$TMPDIR/bare-replacement-proposal-accept.json"
|
|
299
|
-
|
|
467
|
+
write_session_messages "$SESSION_BARE_ACCEPT" "$TMPDIR" "$BARE_REFOCUS_MESSAGES"
|
|
300
468
|
|
|
301
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
302
469
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
303
470
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
304
471
|
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$BARE_PROPOSAL_ACCEPT" \
|
|
@@ -312,7 +479,7 @@ import json
|
|
|
312
479
|
import sys
|
|
313
480
|
from pathlib import Path
|
|
314
481
|
|
|
315
|
-
new_anchor = '
|
|
482
|
+
new_anchor = 'Exercise explicit active-workflow replacement coverage.'
|
|
316
483
|
expected_task_type = 'completion-workflow'
|
|
317
484
|
expected_eval_profile = 'completion-rubric-v1'
|
|
318
485
|
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
@@ -327,9 +494,10 @@ assert proposal['mission'] == new_anchor, 'accepted bare refocus should preserve
|
|
|
327
494
|
assert routing['mode'] == 'bare', 'accepted bare refocus should keep bare routing mode'
|
|
328
495
|
assert 'explicitGoal' not in routing, 'accepted bare refocus should not expose removed explicit-goal shim fields'
|
|
329
496
|
assert 'explicitGoalProvided' not in routing, 'accepted bare refocus should not expose removed explicit-goal shim fields'
|
|
330
|
-
assert routing['action'] == 'refocus', 'accepted bare refocus should keep the
|
|
331
|
-
assert routing['reason'] == '
|
|
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'
|
|
332
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'
|
|
333
501
|
assert new_anchor in mission_text, '.agent/mission.md did not update to the bare refocus mission anchor'
|
|
334
502
|
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after bare refocus'
|
|
335
503
|
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after bare refocus'
|
package/scripts/release-check.sh
CHANGED
|
@@ -5,57 +5,58 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
5
5
|
cd "$ROOT"
|
|
6
6
|
export PI_COMPLETION_RUNNING_RELEASE_CHECK=1
|
|
7
7
|
|
|
8
|
-
echo "[release-check] running control-plane validation, tracked .agent contract coverage, slice-surface parity,
|
|
8
|
+
echo "[release-check] running control-plane validation, tracked .agent contract coverage, slice-surface parity, mixed-model /cook parity, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, legacy cleanup, evaluator calibration, and rubric contract coverage"
|
|
9
9
|
bash .agent/verify_completion_control_plane.sh
|
|
10
10
|
git ls-files --error-unmatch .agent/README.md .agent/mission.md .agent/profile.json .agent/verify_completion_stop.sh .agent/verify_completion_control_plane.sh >/dev/null
|
|
11
11
|
|
|
12
|
-
echo "[release-check] verifying public /cook parity
|
|
12
|
+
echo "[release-check] verifying public /cook mixed-model parity across docs/help"
|
|
13
13
|
python3 - <<'PY'
|
|
14
|
-
import re
|
|
15
14
|
from pathlib import Path
|
|
16
15
|
|
|
17
16
|
checks = {
|
|
18
17
|
"README.md": [
|
|
19
18
|
"`/cook` is the explicit workflow boundary for starting, continuing, refocusing, or beginning the next round of long-running repo work.",
|
|
20
|
-
"
|
|
21
|
-
"If
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"`/cook`
|
|
27
|
-
"
|
|
19
|
+
"Before you explicitly run `/cook`, the conversation can still stay in ordinary chat: the primary agent may keep answering follow-up questions and refining requirements rather than switching into a hard handoff-only refusal mode.",
|
|
20
|
+
"If you explicitly ask for a pre-`/cook` preview or capsule, the primary agent may provide one, but that preview is opt-in only and stays non-canonical until you later run `/cook` and choose **Start**.",
|
|
21
|
+
"Bare `/cook` is still the canonical workflow boundary: it synthesizes the startup brief from recent ordinary-chat discussion at `/cook` time, then waits for **Start** or **Cancel** before any canonical `.agent/**` write.",
|
|
22
|
+
"- startup and next-round entry stay confirm-first: bare `/cook` synthesizes the startup brief from recent discussion, then waits for **Start** or **Cancel**",
|
|
23
|
+
"- any pre-`/cook` preview or capsule is explicit-request-only and non-canonical",
|
|
24
|
+
"When no workflow is active, bare `/cook` synthesizes a startup brief from recent ordinary-chat discussion and then waits for **Start** or **Cancel**.",
|
|
25
|
+
"| No workflow yet | `/cook` synthesizes a startup brief from recent discussion and shows **Start** / **Cancel**.",
|
|
26
|
+
"| Previous workflow is `done` | `/cook` synthesizes the next implementation round from recent discussion behind **Start** / **Cancel**.",
|
|
27
|
+
"any pre-`/cook` preview or capsule is advisory only and never writes canonical workflow state by itself",
|
|
28
28
|
],
|
|
29
29
|
"CHANGELOG.md": [
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"kept context-derived startup as a fallback only when there is no fresh explicit handoff blocking startup, so stale or invalidated capsules can still fall back to recent discussion while fresh non-startable handoffs fail closed instead of silently rewriting canonical state",
|
|
34
|
-
"made finished-workflow suppression stay a safety layer instead of a replacement mission when a fresh explicit `/cook` handoff exists, and blocked negative rejection/suppression text from becoming a Startable startup mission",
|
|
30
|
+
"moving default startup and done-workflow next-round synthesis to bare `/cook` from recent ordinary-chat discussion behind the existing **Start** / **Cancel** approval gate",
|
|
31
|
+
"kept pre-`/cook` previews or `cook_handoff` capsules opt-in only, non-canonical, and advisory until the user explicitly runs `/cook`; bare `/cook` no longer depends on a default prebuilt capsule for new-workflow startup",
|
|
32
|
+
"updated public parity and packaged release verification so README/help/changelog/release-check all describe and gate the shipped mixed model truthfully",
|
|
35
33
|
],
|
|
36
34
|
"extensions/completion/prompt-surfaces.ts": [
|
|
37
|
-
'"
|
|
38
|
-
'"When
|
|
39
|
-
'"
|
|
40
|
-
'"
|
|
41
|
-
|
|
35
|
+
'"Distinguish a workflow-worthy handoff from an opt-in preview request: by default, do not emit a structured preview or cook_handoff capsule in ordinary chat once the task is concrete enough; recommend bare /cook instead."',
|
|
36
|
+
'"When handing off, explain that bare /cook will synthesize a startup brief from recent ordinary-chat discussion for a new workflow or next round, while already-active workflows resume from canonical .agent state unless the user explicitly chooses a replacement path backed by a fresh valid explicit handoff."',
|
|
37
|
+
'"If the user explicitly asks for a /cook preview or capsule before running /cook, you may append one exact fenced block in the same assistant reply using ```cook_handoff ... ``` JSON',
|
|
38
|
+
'"Any preview capsule is startup intake for /cook only: do not present it as canonical .agent state, an active slice, or a persistent repo contract."',
|
|
39
|
+
],
|
|
40
|
+
"extensions/completion/index.ts": [
|
|
41
|
+
'"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief for bare /cook with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook; canonical workflow state is still only written after Start."',
|
|
42
|
+
'description: "/cook workflow: synthesize an approval-gated startup brief from recent discussion for new-workflow or next-round entry, resume the current workflow from canonical state, or confirm an explicit active-workflow replacement"',
|
|
42
43
|
],
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
forbidden = {
|
|
46
47
|
"README.md": [
|
|
47
|
-
"`/cook
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
48
|
+
"wait for a fresh primary-agent handoff, then run `/cook`",
|
|
49
|
+
"That handoff should include an explicit structured `/cook` capsule in the assistant reply once the first slice is implementation-ready, so `/cook` can confirm the already-formed mission instead of re-deriving it from broad ambient context.",
|
|
50
|
+
"- startup and next-round entry stay confirm-first and require a fresh valid explicit primary-agent handoff",
|
|
51
|
+
"`/cook` first looks for a fresh explicit primary-agent handoff capsule from recent ordinary-chat discussion.",
|
|
52
|
+
"Without one, `/cook` fails closed instead of deriving the next round from recent discussion.",
|
|
53
|
+
],
|
|
54
|
+
"CHANGELOG.md": [
|
|
55
|
+
"kept bare `/cook` startup and done-workflow next-round entry fail-closed on missing or non-startable explicit handoffs, while active workflows still resume from canonical `.agent/**` state unless a fresh explicit handoff proposes replacement",
|
|
53
56
|
],
|
|
54
|
-
"CHANGELOG.md": ["compatibility" + " shim"],
|
|
55
57
|
"extensions/completion/index.ts": [
|
|
56
|
-
'description: "/cook workflow:
|
|
57
|
-
'"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance
|
|
58
|
-
'handleCookNaturalLanguageTrigger',
|
|
58
|
+
'description: "/cook workflow: derive a startup brief from recent discussion for new-workflow or next-round entry, resume the current workflow from canonical state, or confirm an explicit active-workflow replacement"',
|
|
59
|
+
'"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook."',
|
|
59
60
|
],
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -63,13 +64,13 @@ for path, needles in checks.items():
|
|
|
63
64
|
text = Path(path).read_text()
|
|
64
65
|
for needle in needles:
|
|
65
66
|
if needle not in text:
|
|
66
|
-
raise SystemExit(f"[release-check] missing expected /cook parity text in {path}: {needle}")
|
|
67
|
+
raise SystemExit(f"[release-check] missing expected /cook mixed-model parity text in {path}: {needle}")
|
|
67
68
|
|
|
68
69
|
for path, needles in forbidden.items():
|
|
69
70
|
text = Path(path).read_text()
|
|
70
71
|
for needle in needles:
|
|
71
72
|
if needle in text:
|
|
72
|
-
raise SystemExit(f"[release-check] found stale
|
|
73
|
+
raise SystemExit(f"[release-check] found stale /cook explicit-handoff-only text in {path}: {needle}")
|
|
73
74
|
PY
|
|
74
75
|
|
|
75
76
|
npm run smoke-test
|
|
@@ -82,6 +83,28 @@ npm run observability-status-test
|
|
|
82
83
|
bash ./scripts/legacy-cleanup-test.sh
|
|
83
84
|
npm run evaluator-calibration-test
|
|
84
85
|
npm run rubric-contract-test
|
|
85
|
-
|
|
86
|
+
|
|
87
|
+
echo "[release-check] verifying packaged .agent contract files in npm pack output"
|
|
88
|
+
PACK_JSON="$(npm pack --dry-run --json)"
|
|
89
|
+
python3 - "$PACK_JSON" <<'PY'
|
|
90
|
+
import json
|
|
91
|
+
import sys
|
|
92
|
+
|
|
93
|
+
required = {
|
|
94
|
+
'.agent/README.md',
|
|
95
|
+
'.agent/mission.md',
|
|
96
|
+
'.agent/profile.json',
|
|
97
|
+
'.agent/verify_completion_stop.sh',
|
|
98
|
+
'.agent/verify_completion_control_plane.sh',
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
payload = json.loads(sys.argv[1])
|
|
102
|
+
if not isinstance(payload, list) or not payload:
|
|
103
|
+
raise SystemExit('[release-check] npm pack --dry-run --json returned no package payload')
|
|
104
|
+
files = {item.get('path') for item in payload[0].get('files', []) if isinstance(item, dict)}
|
|
105
|
+
missing = sorted(required - files)
|
|
106
|
+
if missing:
|
|
107
|
+
raise SystemExit(f"[release-check] npm pack --dry-run is missing tracked .agent contract files: {', '.join(missing)}")
|
|
108
|
+
PY
|
|
86
109
|
|
|
87
110
|
echo "release check passed"
|
package/scripts/smoke-test.sh
CHANGED
|
@@ -47,6 +47,49 @@ 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
|
+
|
|
50
93
|
ROOT="$TMPDIR/repo"
|
|
51
94
|
KICKOFF_PROMPT="$TMPDIR/kickoff-prompt.txt"
|
|
52
95
|
RESUME_PROMPT="$TMPDIR/resume-prompt.txt"
|
|
@@ -60,7 +103,8 @@ INLINE_REJECTION_ROUTING_SNAPSHOT="$TMPDIR/inline-arg-routing.json"
|
|
|
60
103
|
INLINE_REJECTION_PROPOSAL_SNAPSHOT="$TMPDIR/inline-arg-proposal.json"
|
|
61
104
|
INLINE_REJECTION_CHOOSER_SNAPSHOT="$TMPDIR/inline-arg-chooser.json"
|
|
62
105
|
BOOTSTRAP_SESSION="$TMPDIR/session-smoke-bootstrap.jsonl"
|
|
63
|
-
BOOTSTRAP_DISCUSSION=$'Mission: Exercise smoke-test bootstrap.\nScope:\n- Materialize the canonical completion control-plane files.\n- Keep the smoke test on supported /cook startup behavior.\nConstraints:\n- Keep startup proposal confirmation approval-only.\nAcceptance:\n- Scaffold
|
|
106
|
+
BOOTSTRAP_DISCUSSION=$'Mission: Exercise smoke-test bootstrap.\nScope:\n- Materialize the canonical completion control-plane files.\n- Keep the smoke test on supported /cook startup behavior.\nConstraints:\n- Keep startup proposal confirmation approval-only.\nAcceptance:\n- Scaffold .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json for the smoke fixture.\n- Keep scripts/smoke-test.sh and kickoff-prompt coverage truthful for packaged bootstrap.'
|
|
107
|
+
BOOTSTRAP_PROPOSAL="$TMPDIR/bootstrap-proposal.json"
|
|
64
108
|
|
|
65
109
|
mkdir -p "$ROOT"
|
|
66
110
|
cd "$ROOT"
|
|
@@ -94,6 +138,7 @@ write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
|
|
|
94
138
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
95
139
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
96
140
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
141
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$BOOTSTRAP_PROPOSAL" \
|
|
97
142
|
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$KICKOFF_PROMPT" \
|
|
98
143
|
pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" \
|
|
99
144
|
>"$TMPDIR/pi-completion-smoke-bootstrap.out" 2>"$TMPDIR/pi-completion-smoke-bootstrap.err"
|
|
@@ -106,7 +151,7 @@ git ls-files --error-unmatch .agent/README.md .agent/mission.md .agent/profile.j
|
|
|
106
151
|
bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
107
152
|
bash .agent/verify_completion_stop.sh >/dev/null
|
|
108
153
|
|
|
109
|
-
python3 - "$KICKOFF_PROMPT" <<'PY'
|
|
154
|
+
python3 - "$KICKOFF_PROMPT" "$BOOTSTRAP_PROPOSAL" <<'PY'
|
|
110
155
|
import json
|
|
111
156
|
import sys
|
|
112
157
|
from pathlib import Path
|
|
@@ -120,6 +165,7 @@ plan = json.loads(Path('.agent/plan.json').read_text())
|
|
|
120
165
|
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
121
166
|
evidence = json.loads(Path('.agent/verification-evidence.json').read_text())
|
|
122
167
|
kickoff = Path(sys.argv[1]).read_text()
|
|
168
|
+
proposal = json.loads(Path(sys.argv[2]).read_text())
|
|
123
169
|
|
|
124
170
|
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after bootstrap'
|
|
125
171
|
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after bootstrap'
|
|
@@ -133,11 +179,18 @@ assert active['implementation_surfaces'] == [], 'active-slice.json should scaffo
|
|
|
133
179
|
assert active['verification_commands'] == [], 'active-slice.json should scaffold empty verification_commands'
|
|
134
180
|
brief = state['advisory_startup_brief']
|
|
135
181
|
assert brief['kind'] == 'startup_brief', 'state.json should preserve the confirmed startup brief as advisory intake'
|
|
182
|
+
assert brief['source'] == 'recent_discussion', 'smoke bootstrap should record recent-discussion synthesis in advisory intake'
|
|
136
183
|
assert brief['mission'] == state['mission_anchor'], 'advisory startup brief mission should match the canonical mission anchor after bootstrap'
|
|
137
184
|
assert brief['scope'] == ['Materialize the canonical completion control-plane files.', 'Keep the smoke test on supported /cook startup behavior.'], 'advisory startup brief should preserve scope items'
|
|
138
185
|
assert brief['constraints'] == ['Keep startup proposal confirmation approval-only.'], 'advisory startup brief should preserve constraints'
|
|
139
|
-
assert brief['acceptance'] == [
|
|
140
|
-
|
|
186
|
+
assert brief['acceptance'] == [
|
|
187
|
+
'Scaffold .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json for the smoke fixture.',
|
|
188
|
+
'Keep scripts/smoke-test.sh and kickoff-prompt coverage truthful for packaged bootstrap.'
|
|
189
|
+
], 'advisory startup brief should preserve acceptance'
|
|
190
|
+
assert brief['risks'] == [], 'recent-discussion smoke bootstrap should not invent handoff-only risks'
|
|
191
|
+
assert brief['notes'] == ['No additional operator notes were derived from recent discussion.'], 'recent-discussion smoke bootstrap should keep advisory notes non-canonical'
|
|
192
|
+
assert proposal['mission'] == state['mission_anchor'], 'recent-discussion smoke bootstrap should persist the synthesized mission in the proposal snapshot'
|
|
193
|
+
assert proposal['source'] == 'session', 'recent-discussion smoke bootstrap should snapshot the structured-session proposal source'
|
|
141
194
|
assert evidence['artifact_type'] == 'completion-verification-evidence', 'verification-evidence.json artifact_type mismatch after bootstrap'
|
|
142
195
|
assert evidence['subject_type'] == 'none', 'verification-evidence.json should scaffold idle subject_type'
|
|
143
196
|
assert evidence['verification_commands'] == [], 'verification-evidence.json should scaffold empty verification_commands'
|
|
@@ -169,11 +222,17 @@ assert not reminder.exists(), 'ordinary non-/cook turn should not inject complet
|
|
|
169
222
|
assert handoff.exists(), 'ordinary non-/cook turn should inject the /cook handoff boundary reminder'
|
|
170
223
|
handoff_text = handoff.read_text()
|
|
171
224
|
assert '/cook is the only explicit entrypoint into long-running completion workflow.' in handoff_text, 'ordinary handoff reminder should preserve the explicit /cook workflow boundary'
|
|
172
|
-
assert '
|
|
173
|
-
assert '
|
|
174
|
-
assert '
|
|
175
|
-
assert '
|
|
176
|
-
assert '
|
|
225
|
+
assert 'do not begin long-running product implementation in ordinary chat' in handoff_text, 'ordinary handoff reminder should block workflow-level implementation before /cook'
|
|
226
|
+
assert 'recommend /cook as the workflow boundary while keeping the conversation in ordinary chat until the user explicitly runs /cook.' in handoff_text, 'ordinary handoff reminder should keep pre-/cook discussion advisory-first'
|
|
227
|
+
assert 'continue that ordinary-chat discussion normally instead of switching into a handoff-only refusal mode' in handoff_text, 'ordinary handoff reminder should avoid hard refusal mode before explicit /cook'
|
|
228
|
+
assert 'do not emit a structured preview or cook_handoff capsule in ordinary chat once the task is concrete enough' in handoff_text, 'ordinary handoff reminder should keep pre-/cook capsules opt-in instead of default'
|
|
229
|
+
assert 'bare /cook will synthesize a startup brief from recent ordinary-chat discussion' in handoff_text, 'ordinary handoff reminder should describe /cook-time startup synthesis truthfully'
|
|
230
|
+
assert 'If the user explicitly asks for a /cook preview or capsule before running /cook' in handoff_text, 'ordinary handoff reminder should keep previews opt-in'
|
|
231
|
+
assert 'implementation_workflow_handoff' in handoff_text, 'ordinary handoff reminder should preserve the explicit preview handoff kind'
|
|
232
|
+
assert 'first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, why_this_slice_first' in handoff_text, 'ordinary handoff reminder should preserve first-slice preview fields when explicitly requested'
|
|
233
|
+
assert 'Any preview capsule is startup intake for /cook only' in handoff_text, 'ordinary handoff reminder should keep any preview non-canonical'
|
|
234
|
+
assert 'resume from canonical .agent state' in handoff_text, 'ordinary handoff reminder should preserve active-workflow canonical resume wording'
|
|
235
|
+
assert 'fresh valid explicit primary-agent handoff capsule from recent ordinary-chat discussion' not in handoff_text, 'ordinary handoff reminder should no longer describe explicit capsules as the default startup path'
|
|
177
236
|
assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
|
|
178
237
|
assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
|
|
179
238
|
PY
|
|
@@ -201,11 +260,11 @@ assert 'Canonical routing profile:' in resume, 'resume prompt should expose cano
|
|
|
201
260
|
assert f'- task_type: {expected_task_type}' in resume, 'resume prompt missing canonical task_type'
|
|
202
261
|
assert f'- evaluation_profile: {expected_eval_profile}' in resume, 'resume prompt missing canonical evaluation_profile'
|
|
203
262
|
assert routing['mode'] == 'bare', 'active bare /cook should snapshot bare routing mode'
|
|
204
|
-
assert routing['action'] == '
|
|
205
|
-
assert routing['reason'] == '
|
|
206
|
-
assert routing['currentMissionAnchor'] == state['mission_anchor'], '
|
|
207
|
-
assert routing['proposedMissionAnchor'] is None, '
|
|
208
|
-
assert not chooser_path.exists(), '
|
|
263
|
+
assert routing['action'] == 'continue', 'no-discussion active bare /cook should resume from canonical state without a fresh explicit handoff'
|
|
264
|
+
assert routing['reason'] == 'missing_explicit_handoff', 'no-discussion active bare /cook should explain that resume happened because no fresh explicit handoff existed'
|
|
265
|
+
assert routing['currentMissionAnchor'] == state['mission_anchor'], 'resume routing snapshot should keep the current mission anchor'
|
|
266
|
+
assert routing['proposedMissionAnchor'] is None, 'no-discussion active bare /cook should not propose a replacement mission'
|
|
267
|
+
assert not chooser_path.exists(), 'active bare /cook resume should not open the chooser without a fresh explicit handoff'
|
|
209
268
|
PY
|
|
210
269
|
|
|
211
270
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|