@linimin/pi-letscook 0.1.37 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -2
- package/README.md +9 -15
- package/extensions/completion/index.ts +166 -229
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +45 -1
- package/scripts/canonical-evidence-artifact-test.sh +45 -1
- package/scripts/context-proposal-test.sh +324 -133
- package/scripts/refocus-test.sh +76 -31
- package/scripts/release-check.sh +39 -11
- package/scripts/smoke-test.sh +73 -1
|
@@ -5,6 +5,45 @@ PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
5
5
|
TMPDIR="$(mktemp -d)"
|
|
6
6
|
CURRENT_EVIDENCE_BACKUP=""
|
|
7
7
|
|
|
8
|
+
write_session() {
|
|
9
|
+
local session_path="$1"
|
|
10
|
+
local cwd="$2"
|
|
11
|
+
local text="$3"
|
|
12
|
+
python3 - "$session_path" "$cwd" "$text" <<'PY'
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
session_path = Path(sys.argv[1])
|
|
18
|
+
cwd = sys.argv[2]
|
|
19
|
+
text = sys.argv[3]
|
|
20
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
entries = [
|
|
22
|
+
{
|
|
23
|
+
"type": "session",
|
|
24
|
+
"version": 3,
|
|
25
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
26
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
27
|
+
"cwd": cwd,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"type": "message",
|
|
31
|
+
"id": "a1b2c3d4",
|
|
32
|
+
"parentId": None,
|
|
33
|
+
"timestamp": "2026-01-01T00:00:01.000Z",
|
|
34
|
+
"message": {
|
|
35
|
+
"role": "user",
|
|
36
|
+
"content": text,
|
|
37
|
+
"timestamp": 1767225601000,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
]
|
|
41
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
42
|
+
for entry in entries:
|
|
43
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
44
|
+
PY
|
|
45
|
+
}
|
|
46
|
+
|
|
8
47
|
cleanup() {
|
|
9
48
|
if [[ -n "$CURRENT_EVIDENCE_BACKUP" && -f "$CURRENT_EVIDENCE_BACKUP" ]]; then
|
|
10
49
|
cp "$CURRENT_EVIDENCE_BACKUP" "$PKG_ROOT/.agent/verification-evidence.json"
|
|
@@ -96,12 +135,17 @@ bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
|
96
135
|
|
|
97
136
|
ROOT="$TMPDIR/repo"
|
|
98
137
|
SYSTEM_REMINDER="$TMPDIR/system-reminder.txt"
|
|
138
|
+
BOOTSTRAP_SESSION="$TMPDIR/session-canonical-evidence-bootstrap.jsonl"
|
|
139
|
+
BOOTSTRAP_DISCUSSION=$'Mission: Exercise canonical evidence fixture bootstrap.\nScope:\n- Materialize canonical completion files for the evidence artifact fixture.\nConstraints:\n- Use supported bare /cook startup only.\nAcceptance:\n- Scaffold canonical files before the fixture rewrites them.'
|
|
99
140
|
mkdir -p "$ROOT"
|
|
100
141
|
cd "$ROOT"
|
|
101
142
|
git init -q
|
|
143
|
+
write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
|
|
102
144
|
|
|
145
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
146
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
103
147
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
104
|
-
pi -e "$PKG_ROOT" -p "/cook
|
|
148
|
+
pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" \
|
|
105
149
|
>"$TMPDIR/pi-canonical-evidence-bootstrap.out" 2>"$TMPDIR/pi-canonical-evidence-bootstrap.err"
|
|
106
150
|
|
|
107
151
|
for file in .agent/profile.json .agent/state.json .agent/plan.json .agent/active-slice.json .agent/verification-evidence.json; do
|
|
@@ -199,6 +199,155 @@ assert 'Bare /cook failed closed' in output, 'ambiguous structured discussion sh
|
|
|
199
199
|
assert 'Mission/Scope/Constraints/Acceptance' in output, 'ambiguous structured discussion should explain the strict fallback requirement'
|
|
200
200
|
PY
|
|
201
201
|
|
|
202
|
+
# No workflow yet: bare /cook structured fallback should normalize placeholder planning phrasing
|
|
203
|
+
# into the concrete implementation mission when scope/acceptance clearly describe shipped work.
|
|
204
|
+
SESSION_ZERO_NORMALIZED="$TMPDIR/session-zero-normalized.jsonl"
|
|
205
|
+
DISCUSSION_ZERO_NORMALIZED=$'Mission: 開始實作這個方案\nScope:\n- Normalize bare /cook planning phrasing into shipped implementation missions.\n- Keep analyst-derived and structured-fallback proposals aligned.\nConstraints:\n- Do not rewrite the supported bare-discussion mission anchor once it is clear.\nAcceptance:\n- Add deterministic regression coverage for startup normalization and refocus gating.\n- Keep the approval-only Start/Cancel rewrite gate.'
|
|
206
|
+
DISCUSSION_SNAPSHOT_ZERO_NORMALIZED="$TMPDIR/context-proposal-normalized-fallback.json"
|
|
207
|
+
write_session "$SESSION_ZERO_NORMALIZED" "$ROOT" "$DISCUSSION_ZERO_NORMALIZED"
|
|
208
|
+
|
|
209
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
210
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
211
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_NORMALIZED" \
|
|
212
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
213
|
+
pi --session "$SESSION_ZERO_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-normalized-fallback.out" 2>"$TMPDIR/pi-completion-context-proposal-normalized-fallback.err"
|
|
214
|
+
|
|
215
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_NORMALIZED" <<'PY'
|
|
216
|
+
import json
|
|
217
|
+
import sys
|
|
218
|
+
from pathlib import Path
|
|
219
|
+
|
|
220
|
+
mission = 'Normalize bare /cook planning phrasing into shipped implementation missions.'
|
|
221
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
222
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
223
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
224
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
225
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
226
|
+
|
|
227
|
+
assert mission in mission_text, 'normalized structured-fallback startup should update .agent/mission.md to the implementation mission'
|
|
228
|
+
assert proposal['mission'] == mission, 'structured-fallback startup should normalize the placeholder planning mission'
|
|
229
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor should use the normalized implementation mission'
|
|
230
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor should use the normalized implementation mission'
|
|
231
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor should use the normalized implementation mission'
|
|
232
|
+
assert proposal['source'] == 'session', 'normalized structured-fallback startup should still record session fallback as the proposal source'
|
|
233
|
+
assert proposal['scope'][0] == mission, 'normalized structured-fallback startup should derive the mission from shipped-work scope'
|
|
234
|
+
PY
|
|
235
|
+
|
|
236
|
+
rm -rf .agent
|
|
237
|
+
|
|
238
|
+
# No workflow yet: analyst-derived and strict structured fallback proposals should converge on the same
|
|
239
|
+
# normalized implementation mission for the same planning-phrased discussion.
|
|
240
|
+
SESSION_ZERO_ANALYST_NORMALIZED="$TMPDIR/session-zero-analyst-normalized.jsonl"
|
|
241
|
+
ANALYST_OUTPUT_ZERO_NORMALIZED='{"mission":"開始實作這個方案","scope":["Normalize bare /cook planning phrasing into shipped implementation missions.","Keep analyst-derived and structured-fallback proposals aligned."],"constraints":["Do not rewrite the supported bare-discussion mission anchor once it is clear."],"acceptance":["Add deterministic regression coverage for startup normalization and refocus gating.","Keep the approval-only Start/Cancel rewrite gate."],"task_type":"completion-workflow","evaluation_profile":"completion-rubric-v1","confidence":0.93}'
|
|
242
|
+
DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED="$TMPDIR/context-proposal-normalized-analyst.json"
|
|
243
|
+
write_session "$SESSION_ZERO_ANALYST_NORMALIZED" "$ROOT" "$DISCUSSION_ZERO_NORMALIZED"
|
|
244
|
+
|
|
245
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
246
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_ZERO_NORMALIZED" \
|
|
247
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED" \
|
|
248
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
249
|
+
pi --session "$SESSION_ZERO_ANALYST_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-normalized-analyst.out" 2>"$TMPDIR/pi-completion-context-proposal-normalized-analyst.err"
|
|
250
|
+
|
|
251
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED" <<'PY'
|
|
252
|
+
import json
|
|
253
|
+
import sys
|
|
254
|
+
from pathlib import Path
|
|
255
|
+
|
|
256
|
+
mission = 'Normalize bare /cook planning phrasing into shipped implementation missions.'
|
|
257
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
258
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
259
|
+
|
|
260
|
+
assert proposal['mission'] == mission, 'analyst-derived startup should normalize the same placeholder planning mission to the same implementation mission'
|
|
261
|
+
assert state['mission_anchor'] == mission, 'analyst-derived startup should converge on the same canonical mission anchor as structured fallback'
|
|
262
|
+
assert proposal['analysis']['taskType'] == 'completion-workflow', 'analyst-derived normalization should preserve task_type hints'
|
|
263
|
+
assert proposal['analysis']['evaluationProfile'] == 'completion-rubric-v1', 'analyst-derived normalization should preserve evaluation_profile hints'
|
|
264
|
+
PY
|
|
265
|
+
|
|
266
|
+
rm -rf .agent
|
|
267
|
+
|
|
268
|
+
# No workflow yet: planning-only deliverables should preserve planning missions when discussion clearly says
|
|
269
|
+
# docs-only / no-code plan output instead of shipped code, test, or runtime work.
|
|
270
|
+
SESSION_ZERO_PLANNING_ONLY="$TMPDIR/session-zero-planning-only.jsonl"
|
|
271
|
+
DISCUSSION_ZERO_PLANNING_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Draft the migration plan for the /cook mission-normalization rollout.\nConstraints:\n- Docs only; do not implement runtime changes.\nAcceptance:\n- Produce the proposal text for review.'
|
|
272
|
+
DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY="$TMPDIR/context-proposal-planning-only.json"
|
|
273
|
+
write_session "$SESSION_ZERO_PLANNING_ONLY" "$ROOT" "$DISCUSSION_ZERO_PLANNING_ONLY"
|
|
274
|
+
|
|
275
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
276
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
277
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" \
|
|
278
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
279
|
+
pi --session "$SESSION_ZERO_PLANNING_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-planning-only.out" 2>"$TMPDIR/pi-completion-context-proposal-planning-only.err"
|
|
280
|
+
|
|
281
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" <<'PY'
|
|
282
|
+
import json
|
|
283
|
+
import sys
|
|
284
|
+
from pathlib import Path
|
|
285
|
+
|
|
286
|
+
mission = '開始實作這個方案.'
|
|
287
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
288
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
289
|
+
|
|
290
|
+
assert proposal['mission'] == mission, 'planning-only startup should preserve the planning mission when the deliverable is explicitly a plan'
|
|
291
|
+
assert state['mission_anchor'] == mission, 'planning-only startup should keep the planning mission anchor in canonical state'
|
|
292
|
+
PY
|
|
293
|
+
|
|
294
|
+
rm -rf .agent
|
|
295
|
+
|
|
296
|
+
# No workflow yet: support-docs-only deliverables should also preserve planning missions even without
|
|
297
|
+
# an explicit docs-only/no-code phrase, as long as the deliverable stays limited to support documentation.
|
|
298
|
+
SESSION_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/session-zero-support-docs-only.jsonl"
|
|
299
|
+
DISCUSSION_ZERO_SUPPORT_DOCS_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Update README for the /cook mission-normalization behavior.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Add documentation for the operator-facing refocus flow.'
|
|
300
|
+
DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/context-proposal-support-docs-only.json"
|
|
301
|
+
write_session "$SESSION_ZERO_SUPPORT_DOCS_ONLY" "$ROOT" "$DISCUSSION_ZERO_SUPPORT_DOCS_ONLY"
|
|
302
|
+
|
|
303
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
304
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
305
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY" \
|
|
306
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
307
|
+
pi --session "$SESSION_ZERO_SUPPORT_DOCS_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-support-docs-only.out" 2>"$TMPDIR/pi-completion-context-proposal-support-docs-only.err"
|
|
308
|
+
|
|
309
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY" <<'PY'
|
|
310
|
+
import json
|
|
311
|
+
import sys
|
|
312
|
+
from pathlib import Path
|
|
313
|
+
|
|
314
|
+
mission = '開始實作這個方案.'
|
|
315
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
316
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
317
|
+
|
|
318
|
+
assert proposal['mission'] == mission, 'support-docs-only startup should preserve the planning mission even without an explicit docs-only phrase'
|
|
319
|
+
assert state['mission_anchor'] == mission, 'support-docs-only startup should keep the planning mission anchor in canonical state'
|
|
320
|
+
assert proposal['scope'] == ['Update README for the /cook mission-normalization behavior.'], 'support-docs-only startup should keep the documentation scope item'
|
|
321
|
+
assert proposal['acceptance'] == ['Add documentation for the operator-facing refocus flow.'], 'support-docs-only startup should keep the documentation acceptance item'
|
|
322
|
+
PY
|
|
323
|
+
|
|
324
|
+
rm -rf .agent
|
|
325
|
+
|
|
326
|
+
# No workflow yet: analyst-derived generic planning missions should still fail closed when discussion
|
|
327
|
+
# never provides a clear implementation anchor, instead of promoting vague non-doc scope.
|
|
328
|
+
SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC="$TMPDIR/session-zero-analyst-ambiguous-generic.jsonl"
|
|
329
|
+
DISCUSSION_ZERO_ANALYST_AMBIGUOUS_GENERIC=$'We should revisit the completion widget while roles are active and make the outcome easier to follow without deciding the exact implementation yet.'
|
|
330
|
+
ANALYST_OUTPUT_ZERO_AMBIGUOUS_GENERIC='{"mission":"開始實作這個方案","scope":["The completion widget during active roles."],"constraints":["Keep the approval-only Start/Cancel gate unchanged."],"acceptance":["Current behavior stays understandable."],"task_type":"completion-workflow","evaluation_profile":"completion-rubric-v1","confidence":0.74}'
|
|
331
|
+
DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC="$TMPDIR/context-proposal-analyst-ambiguous-generic.json"
|
|
332
|
+
write_session "$SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC" "$ROOT" "$DISCUSSION_ZERO_ANALYST_AMBIGUOUS_GENERIC"
|
|
333
|
+
|
|
334
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
335
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_ZERO_AMBIGUOUS_GENERIC" \
|
|
336
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC" \
|
|
337
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
338
|
+
pi --session "$SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.out" 2>"$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.err"
|
|
339
|
+
|
|
340
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.out" "$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.err" "$DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC" <<'PY'
|
|
341
|
+
import sys
|
|
342
|
+
from pathlib import Path
|
|
343
|
+
|
|
344
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
345
|
+
snapshot = Path(sys.argv[3])
|
|
346
|
+
assert not Path('.agent').exists(), 'analyst-derived ambiguous generic discussion should fail closed without writing canonical state'
|
|
347
|
+
assert not snapshot.exists(), 'analyst-derived ambiguous generic discussion should not emit a proposal snapshot when bare /cook fails closed'
|
|
348
|
+
assert 'Bare /cook failed closed' in output, 'analyst-derived ambiguous generic discussion should explain the fail-closed startup outcome'
|
|
349
|
+
PY
|
|
350
|
+
|
|
202
351
|
# No workflow yet: /cook with no goal should infer from recent discussion through analyst output.
|
|
203
352
|
SESSION_ONE="$TMPDIR/session-one.jsonl"
|
|
204
353
|
DISCUSSION_ONE="$DISCUSSION_ZERO"
|
|
@@ -285,6 +434,8 @@ plan = json.loads(Path('.agent/plan.json').read_text())
|
|
|
285
434
|
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
286
435
|
|
|
287
436
|
assert routing['mode'] == 'bare', 'active bare /cook continue regression should snapshot bare routing mode'
|
|
437
|
+
assert 'explicitGoal' not in routing, 'active bare /cook continue routing should not expose removed explicit-goal shim fields'
|
|
438
|
+
assert 'explicitGoalProvided' not in routing, 'active bare /cook continue routing should not expose removed explicit-goal shim fields'
|
|
288
439
|
assert routing['action'] == 'continue', 'matching structured discussion should classify active bare /cook as continue'
|
|
289
440
|
assert routing['reason'] == 'matching_mission', 'matching structured discussion should keep the current mission rather than refocus'
|
|
290
441
|
assert routing['currentMissionAnchor'] == mission, 'continue routing should preserve the current mission anchor'
|
|
@@ -296,6 +447,87 @@ assert plan['mission_anchor'] == mission, 'active bare /cook continue should kee
|
|
|
296
447
|
assert active['mission_anchor'] == mission, 'active bare /cook continue should keep active-slice.json unchanged'
|
|
297
448
|
PY
|
|
298
449
|
|
|
450
|
+
# Active workflow: bare /cook with a placeholder planning mission should still route through the existing
|
|
451
|
+
# refocus chooser and final Start/Cancel gate before canonical state is rewritten.
|
|
452
|
+
SESSION_ONE_REFOCUS_NORMALIZED="$TMPDIR/session-one-refocus-normalized.jsonl"
|
|
453
|
+
DISCUSSION_ONE_REFOCUS_NORMALIZED=$'Mission: 開始實作這個方案\nScope:\n- Normalize bare /cook planning phrasing into implementation-result missions.\n- Keep the approval-only Start/Cancel gate before rewriting canonical state.\nConstraints:\n- Do not resume the current widget mission.\nAcceptance:\n- Route through chooser-driven refocus before rewriting canonical state.'
|
|
454
|
+
REFOCUS_ROUTING_ONE="$TMPDIR/active-refocus-routing.json"
|
|
455
|
+
REFOCUS_CHOOSER_ONE="$TMPDIR/active-refocus-chooser.json"
|
|
456
|
+
REFOCUS_PROPOSAL_ONE="$TMPDIR/active-refocus-proposal.json"
|
|
457
|
+
REFOCUS_UI_ONE="$TMPDIR/active-refocus-ui.json"
|
|
458
|
+
write_session "$SESSION_ONE_REFOCUS_NORMALIZED" "$ROOT" "$DISCUSSION_ONE_REFOCUS_NORMALIZED"
|
|
459
|
+
|
|
460
|
+
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
461
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_ACTION=start \
|
|
462
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
463
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$REFOCUS_ROUTING_ONE" \
|
|
464
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$REFOCUS_CHOOSER_ONE" \
|
|
465
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$REFOCUS_PROPOSAL_ONE" \
|
|
466
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_PATH="$REFOCUS_UI_ONE" \
|
|
467
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
468
|
+
pi --session "$SESSION_ONE_REFOCUS_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-refocus-normalized.out" 2>"$TMPDIR/pi-completion-context-proposal-active-refocus-normalized.err"
|
|
469
|
+
|
|
470
|
+
python3 - "$REFOCUS_ROUTING_ONE" "$REFOCUS_CHOOSER_ONE" "$REFOCUS_PROPOSAL_ONE" "$REFOCUS_UI_ONE" <<'PY'
|
|
471
|
+
import json
|
|
472
|
+
import sys
|
|
473
|
+
from pathlib import Path
|
|
474
|
+
|
|
475
|
+
mission = 'Normalize bare /cook planning phrasing into implementation-result missions.'
|
|
476
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
477
|
+
chooser = json.loads(Path(sys.argv[2]).read_text())
|
|
478
|
+
proposal = json.loads(Path(sys.argv[3]).read_text())
|
|
479
|
+
ui = json.loads(Path(sys.argv[4]).read_text())
|
|
480
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
481
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
482
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
483
|
+
|
|
484
|
+
assert routing['mode'] == 'bare', 'active bare /cook refocus normalization should snapshot bare routing mode'
|
|
485
|
+
assert 'explicitGoal' not in routing, 'active bare /cook refocus routing should not expose removed explicit-goal shim fields'
|
|
486
|
+
assert 'explicitGoalProvided' not in routing, 'active bare /cook refocus routing should not expose removed explicit-goal shim fields'
|
|
487
|
+
assert routing['action'] == 'refocus', 'placeholder planning mission should still classify active bare /cook as refocus when the normalized mission changes'
|
|
488
|
+
assert routing['reason'] == 'clear_refocus', 'active bare /cook refocus normalization should keep the clear_refocus routing reason'
|
|
489
|
+
assert routing['proposedMissionAnchor'] == mission, 'active bare /cook refocus should normalize the proposed mission before canonical rewrite'
|
|
490
|
+
assert chooser['choices'][1].startswith('Start new workflow from recent discussion'), 'active bare /cook refocus should still route through the existing chooser copy before rewrite'
|
|
491
|
+
assert [action['id'] for action in ui['actions']] == ['start', 'cancel'], 'active bare /cook refocus should still end at the approval-only Start/Cancel gate'
|
|
492
|
+
assert proposal['mission'] == mission, 'active bare /cook refocus proposal snapshot should expose the normalized implementation mission'
|
|
493
|
+
assert state['mission_anchor'] == mission, 'active bare /cook refocus should rewrite canonical state to the normalized mission only after approval'
|
|
494
|
+
assert plan['mission_anchor'] == mission, 'active bare /cook refocus should rewrite plan.json only after approval'
|
|
495
|
+
assert active['mission_anchor'] == mission, 'active bare /cook refocus should rewrite active-slice.json only after approval'
|
|
496
|
+
PY
|
|
497
|
+
|
|
498
|
+
# Completed workflow: bare /cook should normalize placeholder planning phrasing for the next workflow
|
|
499
|
+
# round too, not only for fresh startup.
|
|
500
|
+
mark_done
|
|
501
|
+
|
|
502
|
+
SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
|
|
503
|
+
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.'
|
|
504
|
+
DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
|
|
505
|
+
write_session "$SESSION_TWO_NORMALIZED" "$ROOT" "$DISCUSSION_TWO_NORMALIZED"
|
|
506
|
+
|
|
507
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
508
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
509
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" \
|
|
510
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
511
|
+
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"
|
|
512
|
+
|
|
513
|
+
python3 - "$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" <<'PY'
|
|
514
|
+
import json
|
|
515
|
+
import sys
|
|
516
|
+
from pathlib import Path
|
|
517
|
+
|
|
518
|
+
mission = 'Normalize bare /cook planning phrasing for the next workflow round.'
|
|
519
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
520
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
521
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
522
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
523
|
+
|
|
524
|
+
assert proposal['mission'] == mission, 'done-workflow structured fallback should normalize the placeholder planning mission for the next round'
|
|
525
|
+
assert state['mission_anchor'] == mission, 'done-workflow startup should rewrite canonical state to the normalized next-round mission'
|
|
526
|
+
assert plan['mission_anchor'] == mission, 'done-workflow startup should rewrite plan.json to the normalized next-round mission'
|
|
527
|
+
assert active['mission_anchor'] == mission, 'done-workflow startup should rewrite active-slice.json to the normalized next-round mission'
|
|
528
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'done-workflow normalization should still route through refocus semantics for the next round'
|
|
529
|
+
PY
|
|
530
|
+
|
|
299
531
|
# Completed workflow: bare /cook should use the same strict structured fallback for the next workflow round when analyst output is unavailable.
|
|
300
532
|
mark_done
|
|
301
533
|
|
|
@@ -352,165 +584,124 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
|
|
|
352
584
|
assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
|
|
353
585
|
PY
|
|
354
586
|
|
|
355
|
-
# Active workflow: /cook
|
|
356
|
-
#
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
364
|
-
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
365
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
366
|
-
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_THREE" \
|
|
367
|
-
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ROUTING_SNAPSHOT_THREE" \
|
|
368
|
-
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
369
|
-
pi --session "$SESSION_THREE" -e "$PKG_ROOT" -p "/cook Explicit replacement mission for the active workflow" >"$TMPDIR/pi-completion-context-proposal-active-goal.out" 2>"$TMPDIR/pi-completion-context-proposal-active-goal.err"
|
|
370
|
-
|
|
371
|
-
python3 - "$DISCUSSION_SNAPSHOT_THREE" "$ROUTING_SNAPSHOT_THREE" <<'PY'
|
|
587
|
+
# Active workflow: inline /cook args should be rejected before proposal/routing helpers run
|
|
588
|
+
# and should leave canonical state unchanged.
|
|
589
|
+
ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
|
|
590
|
+
ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
|
|
591
|
+
ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
|
|
592
|
+
ACTIVE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-active-inline-before.json"
|
|
593
|
+
python3 - "$ACTIVE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
372
594
|
import json
|
|
373
595
|
import sys
|
|
374
596
|
from pathlib import Path
|
|
375
597
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
386
|
-
|
|
387
|
-
assert mission in mission_text, '.agent/mission.md did not update to the explicit replacement mission anchor'
|
|
388
|
-
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after explicit-goal replacement'
|
|
389
|
-
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after explicit-goal replacement'
|
|
390
|
-
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after explicit-goal replacement'
|
|
391
|
-
assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after explicit-goal replacement'
|
|
392
|
-
assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after explicit-goal replacement'
|
|
393
|
-
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after explicit-goal replacement'
|
|
394
|
-
assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after explicit-goal replacement'
|
|
395
|
-
assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after explicit-goal replacement'
|
|
396
|
-
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after explicit-goal replacement'
|
|
397
|
-
assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after explicit-goal replacement'
|
|
398
|
-
assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after explicit-goal replacement'
|
|
399
|
-
assert proposal['mission'] == mission, 'explicit-goal replacement should route through the shared proposal confirmation pipeline'
|
|
400
|
-
assert routing['mode'] == 'explicit', 'explicit-goal replacement should snapshot explicit active-workflow routing mode'
|
|
401
|
-
assert routing['action'] == 'refocus', 'explicit-goal replacement should classify as refocus when the mission changes'
|
|
402
|
-
assert routing['reason'] == 'explicit_goal', 'explicit-goal replacement should record the explicit-goal routing reason'
|
|
403
|
-
assert routing['proposedMissionAnchor'] == mission, 'explicit-goal routing should expose the replacement mission anchor'
|
|
404
|
-
assert state['current_phase'] == 'reground', 'current_phase should reset to reground after explicit-goal replacement'
|
|
405
|
-
assert state['continuation_policy'] == 'continue', 'continuation_policy should stay continue after explicit-goal replacement'
|
|
406
|
-
assert state['next_mandatory_role'] == 'completion-regrounder', 'next role should reset to completion-regrounder after explicit-goal replacement'
|
|
407
|
-
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the explicit-goal replacement'
|
|
408
|
-
assert 'task_type=completion-workflow' in state['continuation_reason'], 'explicit-goal replacement should persist the selected task_type'
|
|
409
|
-
assert 'evaluation_profile=completion-rubric-v1' in state['continuation_reason'], 'explicit-goal replacement should persist the selected evaluation_profile'
|
|
410
|
-
assert 'critique outcome=accepted critique=none' in state['continuation_reason'], 'explicit-goal replacement should persist the accepted critique outcome even when no critique was derived'
|
|
411
|
-
assert 'Preserve the richer proposal structure from discussion.' not in state['continuation_reason'], 'session scope should not be merged when analyst output is unavailable'
|
|
412
|
-
assert 'Keep explicit goals as the mission anchor when they conflict with earlier text.' not in state['continuation_reason'], 'session constraints should not be merged when analyst output is unavailable'
|
|
413
|
-
assert 'Refresh canonical state from the replacement mission.' not in state['continuation_reason'], 'session acceptance should not be merged when analyst output is unavailable'
|
|
414
|
-
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should be user_refocus after explicit-goal replacement'
|
|
415
|
-
assert active['status'] == 'idle', 'active slice should reset to idle after explicit-goal replacement'
|
|
598
|
+
tracked = [
|
|
599
|
+
Path('.agent/mission.md'),
|
|
600
|
+
Path('.agent/profile.json'),
|
|
601
|
+
Path('.agent/state.json'),
|
|
602
|
+
Path('.agent/plan.json'),
|
|
603
|
+
Path('.agent/active-slice.json'),
|
|
604
|
+
Path('.agent/verification-evidence.json'),
|
|
605
|
+
]
|
|
606
|
+
Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
|
|
416
607
|
PY
|
|
417
608
|
|
|
418
|
-
# Active workflow: cancelling the replacement proposal should keep the current workflow and redirect
|
|
419
|
-
# the user back to the main chat before rerunning /cook.
|
|
420
|
-
SESSION_THREE_CANCEL="$TMPDIR/session-three-cancel.jsonl"
|
|
421
|
-
DISCUSSION_THREE_CANCEL=$'Scope:\n- Keep the current workflow unchanged when replacement confirmation is cancelled.\nConstraints:\n- Do not rewrite canonical state after cancel.\nAcceptance:\n- Print rerun guidance.'
|
|
422
|
-
write_session "$SESSION_THREE_CANCEL" "$ROOT" "$DISCUSSION_THREE_CANCEL"
|
|
423
|
-
|
|
424
609
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
425
|
-
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=
|
|
610
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
426
611
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
612
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$ACTIVE_INLINE_REJECTION_PROPOSAL" \
|
|
613
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ACTIVE_INLINE_REJECTION_ROUTING" \
|
|
614
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$ACTIVE_INLINE_REJECTION_CHOOSER" \
|
|
427
615
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
428
|
-
pi
|
|
616
|
+
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"
|
|
429
617
|
|
|
430
|
-
python3 - "$TMPDIR/pi-completion-context-proposal-
|
|
618
|
+
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'
|
|
431
619
|
import json
|
|
432
620
|
import sys
|
|
433
621
|
from pathlib import Path
|
|
434
622
|
|
|
435
|
-
mission = 'Explicit replacement mission for the active workflow.'
|
|
436
|
-
state = json.loads(Path('.agent/state.json').read_text())
|
|
437
|
-
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
438
|
-
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
439
623
|
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
624
|
+
routing = Path(sys.argv[3])
|
|
625
|
+
proposal = Path(sys.argv[4])
|
|
626
|
+
chooser = Path(sys.argv[5])
|
|
627
|
+
before = json.loads(Path(sys.argv[6]).read_text())
|
|
628
|
+
tracked = [
|
|
629
|
+
Path('.agent/mission.md'),
|
|
630
|
+
Path('.agent/profile.json'),
|
|
631
|
+
Path('.agent/state.json'),
|
|
632
|
+
Path('.agent/plan.json'),
|
|
633
|
+
Path('.agent/active-slice.json'),
|
|
634
|
+
Path('.agent/verification-evidence.json'),
|
|
635
|
+
]
|
|
440
636
|
|
|
441
|
-
assert
|
|
442
|
-
assert
|
|
443
|
-
assert
|
|
444
|
-
assert
|
|
637
|
+
assert 'Inline /cook arguments are no longer supported.' in output, 'active inline /cook args should explain the hard rejection'
|
|
638
|
+
assert 'Clarify the mission in the main chat and rerun bare /cook.' in output, 'active inline /cook args should redirect users back to main chat plus bare /cook'
|
|
639
|
+
assert not routing.exists(), 'active inline /cook args should not run active-workflow routing'
|
|
640
|
+
assert not proposal.exists(), 'active inline /cook args should not open proposal confirmation'
|
|
641
|
+
assert not chooser.exists(), 'active inline /cook args should not open the existing-workflow chooser'
|
|
642
|
+
after = {path.name: path.read_text() for path in tracked}
|
|
643
|
+
assert before == after, 'active inline /cook args should leave canonical files unchanged'
|
|
445
644
|
PY
|
|
446
645
|
|
|
447
|
-
# Completed workflow
|
|
448
|
-
# even when analyst output is unavailable, without merging session-derived scope, constraints, or acceptance.
|
|
646
|
+
# Completed workflow: inline /cook args should also fail closed without starting the next round.
|
|
449
647
|
mark_done
|
|
450
648
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
649
|
+
DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
|
|
650
|
+
DONE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-done-inline-arg-proposal.json"
|
|
651
|
+
DONE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-done-inline-arg-chooser.json"
|
|
652
|
+
DONE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-done-inline-before.json"
|
|
653
|
+
python3 - "$DONE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
654
|
+
import json
|
|
655
|
+
import sys
|
|
656
|
+
from pathlib import Path
|
|
657
|
+
|
|
658
|
+
tracked = [
|
|
659
|
+
Path('.agent/mission.md'),
|
|
660
|
+
Path('.agent/profile.json'),
|
|
661
|
+
Path('.agent/state.json'),
|
|
662
|
+
Path('.agent/plan.json'),
|
|
663
|
+
Path('.agent/active-slice.json'),
|
|
664
|
+
Path('.agent/verification-evidence.json'),
|
|
665
|
+
]
|
|
666
|
+
Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
|
|
667
|
+
PY
|
|
456
668
|
|
|
457
669
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
458
670
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
459
|
-
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$
|
|
671
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DONE_INLINE_REJECTION_PROPOSAL" \
|
|
672
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$DONE_INLINE_REJECTION_ROUTING" \
|
|
673
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$DONE_INLINE_REJECTION_CHOOSER" \
|
|
460
674
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
461
|
-
pi
|
|
675
|
+
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"
|
|
462
676
|
|
|
463
|
-
python3 - "$
|
|
677
|
+
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'
|
|
464
678
|
import json
|
|
465
679
|
import sys
|
|
466
680
|
from pathlib import Path
|
|
467
681
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
assert
|
|
483
|
-
assert
|
|
484
|
-
assert
|
|
485
|
-
assert
|
|
486
|
-
assert
|
|
487
|
-
assert
|
|
488
|
-
assert
|
|
489
|
-
|
|
490
|
-
assert
|
|
491
|
-
assert proposal['mission'] == mission, 'explicit-goal proposal snapshot should preserve the explicit mission anchor'
|
|
492
|
-
assert proposal['analysis']['taskType'] == expected_task_type, 'explicit-goal proposal snapshot should preserve task_type hints from the goal text'
|
|
493
|
-
assert proposal['analysis']['evaluationProfile'] == expected_eval_profile, 'explicit-goal proposal snapshot should preserve evaluation_profile hints from the goal text'
|
|
494
|
-
assert proposal['analysis']['critique'] == ['Keep critique notes separate from the mission anchor.'], 'explicit-goal proposal snapshot should preserve critique hints from the goal text'
|
|
495
|
-
assert proposal['analysis']['risks'] == ['Session-only scope could leak into the next workflow round.'], 'explicit-goal proposal snapshot should preserve risk hints from the goal text'
|
|
496
|
-
assert 'Critique:' not in proposal['goalText'], 'goalText should keep critique notes separate from mission/scope/constraints/acceptance'
|
|
497
|
-
assert 'Task type:' not in proposal['goalText'], 'goalText should keep task_type hints separate from the mission body'
|
|
498
|
-
assert state['current_phase'] == 'reground', 'current_phase should reset to reground after explicit-goal next-round start'
|
|
499
|
-
assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue after explicit-goal next-round start'
|
|
500
|
-
assert state['project_done'] is False, 'project_done should reset to false after explicit-goal next-round start'
|
|
501
|
-
assert state['requires_reground'] is True, 'requires_reground should reset to true after explicit-goal next-round start'
|
|
502
|
-
assert state['next_mandatory_role'] == 'completion-regrounder', 'next role should reset to completion-regrounder after explicit-goal next-round start'
|
|
503
|
-
assert continuation_reason.startswith('User refocused workflow via /cook:'), 'continuation_reason should record the explicit-goal next-round start'
|
|
504
|
-
assert 'task_type=completion-workflow' in continuation_reason, 'explicit-goal next-round start should persist the selected task_type'
|
|
505
|
-
assert 'evaluation_profile=completion-rubric-v1' in continuation_reason, 'explicit-goal next-round start should persist the selected evaluation_profile'
|
|
506
|
-
assert 'Keep critique notes separate from the mission anchor.' in continuation_reason, 'explicit-goal next-round start should persist the accepted critique outcome'
|
|
507
|
-
assert 'Keep explicit scope.' in continuation_reason, 'explicit scope should remain in the explicit-goal proposal'
|
|
508
|
-
assert 'Add session-only scope.' not in continuation_reason, 'session-derived scope should not be merged when analyst output is unavailable'
|
|
509
|
-
assert 'Restyle widget.' not in continuation_reason, 'unrelated session-derived scope should not be merged when analyst output is unavailable'
|
|
510
|
-
assert 'Keep rules.' not in continuation_reason, 'session-derived constraints should not merge when analyst output is unavailable'
|
|
511
|
-
assert 'Add test.' not in continuation_reason, 'session-derived acceptance should not merge when analyst output is unavailable'
|
|
512
|
-
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should be user_refocus after explicit-goal next-round start'
|
|
513
|
-
assert active['status'] == 'idle', 'active slice should reset to idle after explicit-goal next-round start'
|
|
682
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
683
|
+
routing = Path(sys.argv[3])
|
|
684
|
+
proposal = Path(sys.argv[4])
|
|
685
|
+
chooser = Path(sys.argv[5])
|
|
686
|
+
before = json.loads(Path(sys.argv[6]).read_text())
|
|
687
|
+
tracked = [
|
|
688
|
+
Path('.agent/mission.md'),
|
|
689
|
+
Path('.agent/profile.json'),
|
|
690
|
+
Path('.agent/state.json'),
|
|
691
|
+
Path('.agent/plan.json'),
|
|
692
|
+
Path('.agent/active-slice.json'),
|
|
693
|
+
Path('.agent/verification-evidence.json'),
|
|
694
|
+
]
|
|
695
|
+
state_before = json.loads(before['state.json'])
|
|
696
|
+
assert state_before['current_phase'] == 'done', 'done inline /cook rejection should start from a completed workflow'
|
|
697
|
+
assert state_before['project_done'] is True, 'done inline /cook rejection should start from project_done=true'
|
|
698
|
+
assert 'Inline /cook arguments are no longer supported.' in output, 'done inline /cook args should explain the hard rejection'
|
|
699
|
+
assert 'Clarify the mission in the main chat and rerun bare /cook.' in output, 'done inline /cook args should redirect users back to main chat plus bare /cook'
|
|
700
|
+
assert not routing.exists(), 'done inline /cook args should not run active/done workflow routing'
|
|
701
|
+
assert not proposal.exists(), 'done inline /cook args should not open next-round proposal confirmation'
|
|
702
|
+
assert not chooser.exists(), 'done inline /cook args should not open the chooser flow'
|
|
703
|
+
after = {path.name: path.read_text() for path in tracked}
|
|
704
|
+
assert before == after, 'done inline /cook args should leave canonical files unchanged'
|
|
514
705
|
PY
|
|
515
706
|
|
|
516
707
|
# Completed workflow again: /cook with no goal should be able to use model-assisted
|
|
@@ -518,8 +709,8 @@ PY
|
|
|
518
709
|
mark_done
|
|
519
710
|
|
|
520
711
|
SESSION_FIVE="$TMPDIR/session-five.jsonl"
|
|
521
|
-
DISCUSSION_FIVE=$'I do not want to rewrite the parser. The safer path is to let /cook analyze the discussion first, keep the
|
|
522
|
-
ANALYST_OUTPUT_FIVE='{"mission":"Use a proposal analyst to summarize natural discussion before /cook writes canonical state.","scope":["Keep
|
|
712
|
+
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.'
|
|
713
|
+
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"]}'
|
|
523
714
|
write_session "$SESSION_FIVE" "$ROOT" "$DISCUSSION_FIVE"
|
|
524
715
|
|
|
525
716
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
@@ -559,7 +750,7 @@ assert continuation_reason.startswith('User refocused workflow via /cook:'), 'co
|
|
|
559
750
|
assert 'task_type=completion-workflow' in continuation_reason, 'analyst-derived restart should persist the selected task_type'
|
|
560
751
|
assert 'evaluation_profile=completion-rubric-v1' in continuation_reason, 'analyst-derived restart should persist the selected evaluation_profile'
|
|
561
752
|
assert 'critique outcome=accepted critique=none' in continuation_reason, 'analyst-derived restart should persist that no critique notes were accepted'
|
|
562
|
-
assert 'Keep
|
|
753
|
+
assert 'Keep the discussion-derived mission anchored once it is clear.' in continuation_reason, 'analyst-derived scope should be preserved'
|
|
563
754
|
PY
|
|
564
755
|
|
|
565
756
|
# Custom confirmation UI: start should render proposal content separately from approval-only Start/Cancel actions.
|