@linimin/pi-letscook 0.1.37 → 0.1.40
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 +29 -2
- package/PUBLISHING.md +2 -0
- package/README.md +12 -14
- package/extensions/completion/index.ts +173 -233
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +48 -1
- package/scripts/canonical-evidence-artifact-test.sh +48 -1
- package/scripts/context-proposal-test.sh +576 -133
- package/scripts/observability-status-test.sh +3 -0
- package/scripts/refocus-test.sh +79 -31
- package/scripts/release-check.sh +45 -11
- package/scripts/smoke-test.sh +76 -1
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
pi() {
|
|
6
|
+
command pi --no-extensions "$@"
|
|
7
|
+
}
|
|
5
8
|
TMPDIR="$(mktemp -d)"
|
|
6
9
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
7
10
|
|
|
@@ -199,6 +202,330 @@ assert 'Bare /cook failed closed' in output, 'ambiguous structured discussion sh
|
|
|
199
202
|
assert 'Mission/Scope/Constraints/Acceptance' in output, 'ambiguous structured discussion should explain the strict fallback requirement'
|
|
200
203
|
PY
|
|
201
204
|
|
|
205
|
+
# No workflow yet: bare /cook structured fallback should normalize placeholder planning phrasing
|
|
206
|
+
# into the concrete implementation mission when scope/acceptance clearly describe shipped work.
|
|
207
|
+
SESSION_ZERO_NORMALIZED="$TMPDIR/session-zero-normalized.jsonl"
|
|
208
|
+
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.'
|
|
209
|
+
DISCUSSION_SNAPSHOT_ZERO_NORMALIZED="$TMPDIR/context-proposal-normalized-fallback.json"
|
|
210
|
+
write_session "$SESSION_ZERO_NORMALIZED" "$ROOT" "$DISCUSSION_ZERO_NORMALIZED"
|
|
211
|
+
|
|
212
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
213
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
214
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_NORMALIZED" \
|
|
215
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
216
|
+
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"
|
|
217
|
+
|
|
218
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_NORMALIZED" <<'PY'
|
|
219
|
+
import json
|
|
220
|
+
import sys
|
|
221
|
+
from pathlib import Path
|
|
222
|
+
|
|
223
|
+
mission = 'Normalize bare /cook planning phrasing into shipped implementation missions.'
|
|
224
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
225
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
226
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
227
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
228
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
229
|
+
|
|
230
|
+
assert mission in mission_text, 'normalized structured-fallback startup should update .agent/mission.md to the implementation mission'
|
|
231
|
+
assert proposal['mission'] == mission, 'structured-fallback startup should normalize the placeholder planning mission'
|
|
232
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor should use the normalized implementation mission'
|
|
233
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor should use the normalized implementation mission'
|
|
234
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor should use the normalized implementation mission'
|
|
235
|
+
assert proposal['source'] == 'session', 'normalized structured-fallback startup should still record session fallback as the proposal source'
|
|
236
|
+
assert proposal['scope'][0] == mission, 'normalized structured-fallback startup should derive the mission from shipped-work scope'
|
|
237
|
+
PY
|
|
238
|
+
|
|
239
|
+
rm -rf .agent
|
|
240
|
+
|
|
241
|
+
# No workflow yet: analyst-derived and strict structured fallback proposals should converge on the same
|
|
242
|
+
# normalized implementation mission for the same planning-phrased discussion.
|
|
243
|
+
SESSION_ZERO_ANALYST_NORMALIZED="$TMPDIR/session-zero-analyst-normalized.jsonl"
|
|
244
|
+
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}'
|
|
245
|
+
DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED="$TMPDIR/context-proposal-normalized-analyst.json"
|
|
246
|
+
write_session "$SESSION_ZERO_ANALYST_NORMALIZED" "$ROOT" "$DISCUSSION_ZERO_NORMALIZED"
|
|
247
|
+
|
|
248
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
249
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_ZERO_NORMALIZED" \
|
|
250
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED" \
|
|
251
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
252
|
+
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"
|
|
253
|
+
|
|
254
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED" <<'PY'
|
|
255
|
+
import json
|
|
256
|
+
import sys
|
|
257
|
+
from pathlib import Path
|
|
258
|
+
|
|
259
|
+
mission = 'Normalize bare /cook planning phrasing into shipped implementation missions.'
|
|
260
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
261
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
262
|
+
|
|
263
|
+
assert proposal['mission'] == mission, 'analyst-derived startup should normalize the same placeholder planning mission to the same implementation mission'
|
|
264
|
+
assert state['mission_anchor'] == mission, 'analyst-derived startup should converge on the same canonical mission anchor as structured fallback'
|
|
265
|
+
assert proposal['analysis']['taskType'] == 'completion-workflow', 'analyst-derived normalization should preserve task_type hints'
|
|
266
|
+
assert proposal['analysis']['evaluationProfile'] == 'completion-rubric-v1', 'analyst-derived normalization should preserve evaluation_profile hints'
|
|
267
|
+
PY
|
|
268
|
+
|
|
269
|
+
rm -rf .agent
|
|
270
|
+
|
|
271
|
+
# No workflow yet: planning-artifact-only context should fail closed even when the discussion is
|
|
272
|
+
# clearly structured, because bare /cook now expects execution-ready repo changes.
|
|
273
|
+
SESSION_ZERO_PLANNING_ONLY="$TMPDIR/session-zero-planning-only.jsonl"
|
|
274
|
+
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.'
|
|
275
|
+
DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY="$TMPDIR/context-proposal-planning-only.json"
|
|
276
|
+
write_session "$SESSION_ZERO_PLANNING_ONLY" "$ROOT" "$DISCUSSION_ZERO_PLANNING_ONLY"
|
|
277
|
+
|
|
278
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
279
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
280
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" \
|
|
281
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
282
|
+
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"
|
|
283
|
+
|
|
284
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-planning-only.out" "$TMPDIR/pi-completion-context-proposal-planning-only.err" "$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" <<'PY'
|
|
285
|
+
import sys
|
|
286
|
+
from pathlib import Path
|
|
287
|
+
|
|
288
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
289
|
+
snapshot = Path(sys.argv[3])
|
|
290
|
+
assert not Path('.agent').exists(), 'planning-only startup should fail closed without writing canonical state'
|
|
291
|
+
assert not snapshot.exists(), 'planning-only startup should not emit a proposal snapshot when bare /cook fails closed'
|
|
292
|
+
assert 'Bare /cook failed closed' in output, 'planning-only startup should explain the fail-closed startup outcome'
|
|
293
|
+
assert 'Mission/Scope/Constraints/Acceptance' in output, 'planning-only startup should still explain the structured discussion requirement'
|
|
294
|
+
assert 'concrete repo changes' in output, 'planning-only startup should explain that bare /cook now expects execution-ready repo changes'
|
|
295
|
+
PY
|
|
296
|
+
|
|
297
|
+
# No workflow yet: docs-only tracked deliverables such as README/CHANGELOG updates should
|
|
298
|
+
# normalize placeholder planning missions into concrete repo-change missions.
|
|
299
|
+
SESSION_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/session-zero-support-docs-only.jsonl"
|
|
300
|
+
DISCUSSION_ZERO_SUPPORT_DOCS_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Update README and CHANGELOG 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.'
|
|
301
|
+
DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/context-proposal-support-docs-only.json"
|
|
302
|
+
write_session "$SESSION_ZERO_SUPPORT_DOCS_ONLY" "$ROOT" "$DISCUSSION_ZERO_SUPPORT_DOCS_ONLY"
|
|
303
|
+
|
|
304
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
305
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
306
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY" \
|
|
307
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
308
|
+
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"
|
|
309
|
+
|
|
310
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY" <<'PY'
|
|
311
|
+
import json
|
|
312
|
+
import sys
|
|
313
|
+
from pathlib import Path
|
|
314
|
+
|
|
315
|
+
mission = 'Update README and CHANGELOG for the /cook mission-normalization behavior.'
|
|
316
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
317
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
318
|
+
|
|
319
|
+
assert proposal['mission'] == mission, 'docs-only startup should normalize the placeholder planning mission to the tracked-doc repo change'
|
|
320
|
+
assert state['mission_anchor'] == mission, 'docs-only startup should write the normalized tracked-doc mission into canonical state'
|
|
321
|
+
assert proposal['scope'][0] == mission, 'docs-only startup should derive the mission from the tracked-doc scope item'
|
|
322
|
+
assert proposal['acceptance'] == ['Add documentation for the operator-facing refocus flow.'], 'docs-only startup should keep the documentation acceptance item'
|
|
323
|
+
PY
|
|
324
|
+
|
|
325
|
+
rm -rf .agent
|
|
326
|
+
|
|
327
|
+
# No workflow yet: reviewer-reproduced docs-only phrasing with edit/document wording should
|
|
328
|
+
# also normalize placeholder planning missions into concrete repo-change missions.
|
|
329
|
+
SESSION_ZERO_EDIT_DOCUMENT_DOCS_ONLY="$TMPDIR/session-zero-edit-document-docs-only.jsonl"
|
|
330
|
+
DISCUSSION_ZERO_EDIT_DOCUMENT_DOCS_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Edit README to explain the /cook mission-normalization behavior.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Document the operator-facing refocus flow.'
|
|
331
|
+
DISCUSSION_SNAPSHOT_ZERO_EDIT_DOCUMENT_DOCS_ONLY="$TMPDIR/context-proposal-edit-document-docs-only.json"
|
|
332
|
+
write_session "$SESSION_ZERO_EDIT_DOCUMENT_DOCS_ONLY" "$ROOT" "$DISCUSSION_ZERO_EDIT_DOCUMENT_DOCS_ONLY"
|
|
333
|
+
|
|
334
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
335
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
336
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_EDIT_DOCUMENT_DOCS_ONLY" \
|
|
337
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
338
|
+
pi --session "$SESSION_ZERO_EDIT_DOCUMENT_DOCS_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-edit-document-docs-only.out" 2>"$TMPDIR/pi-completion-context-proposal-edit-document-docs-only.err"
|
|
339
|
+
|
|
340
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_EDIT_DOCUMENT_DOCS_ONLY" <<'PY'
|
|
341
|
+
import json
|
|
342
|
+
import sys
|
|
343
|
+
from pathlib import Path
|
|
344
|
+
|
|
345
|
+
mission = 'Edit README to explain the /cook mission-normalization behavior.'
|
|
346
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
347
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
348
|
+
|
|
349
|
+
assert proposal['mission'] == mission, 'edit/document docs-only startup should normalize the placeholder planning mission to the tracked-doc repo change'
|
|
350
|
+
assert state['mission_anchor'] == mission, 'edit/document docs-only startup should write the normalized tracked-doc mission into canonical state'
|
|
351
|
+
assert proposal['scope'][0] == mission, 'edit/document docs-only startup should derive the mission from the tracked-doc scope item'
|
|
352
|
+
assert proposal['acceptance'] == ['Document the operator-facing refocus flow.'], 'edit/document docs-only startup should keep the documentation acceptance item'
|
|
353
|
+
PY
|
|
354
|
+
|
|
355
|
+
rm -rf .agent
|
|
356
|
+
|
|
357
|
+
# No workflow yet: acceptance-only docs deliverables using write phrasing should also
|
|
358
|
+
# normalize placeholder planning missions into concrete repo-change missions.
|
|
359
|
+
SESSION_ZERO_WRITE_DOCS_ONLY="$TMPDIR/session-zero-write-docs-only.jsonl"
|
|
360
|
+
DISCUSSION_ZERO_WRITE_DOCS_ONLY=$'Mission: 開始實作這個方案\nScope:\n- README and CHANGELOG guidance for bare /cook.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Write README and CHANGELOG notes for the bare /cook fail-closed clarification path.'
|
|
361
|
+
DISCUSSION_SNAPSHOT_ZERO_WRITE_DOCS_ONLY="$TMPDIR/context-proposal-write-docs-only.json"
|
|
362
|
+
write_session "$SESSION_ZERO_WRITE_DOCS_ONLY" "$ROOT" "$DISCUSSION_ZERO_WRITE_DOCS_ONLY"
|
|
363
|
+
|
|
364
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
365
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
366
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_WRITE_DOCS_ONLY" \
|
|
367
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
368
|
+
pi --session "$SESSION_ZERO_WRITE_DOCS_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-write-docs-only.out" 2>"$TMPDIR/pi-completion-context-proposal-write-docs-only.err"
|
|
369
|
+
|
|
370
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_WRITE_DOCS_ONLY" <<'PY'
|
|
371
|
+
import json
|
|
372
|
+
import sys
|
|
373
|
+
from pathlib import Path
|
|
374
|
+
|
|
375
|
+
mission = 'Write README and CHANGELOG notes for the bare /cook fail-closed clarification path.'
|
|
376
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
377
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
378
|
+
|
|
379
|
+
assert proposal['mission'] == mission, 'write docs-only startup should normalize the placeholder planning mission from acceptance-only tracked-doc work'
|
|
380
|
+
assert state['mission_anchor'] == mission, 'write docs-only startup should write the acceptance-derived tracked-doc mission into canonical state'
|
|
381
|
+
assert proposal['scope'] == ['README and CHANGELOG guidance for bare /cook.'], 'write docs-only startup should preserve the noun-only scope item while deriving the mission from acceptance'
|
|
382
|
+
assert proposal['acceptance'] == [mission], 'write docs-only startup should keep the acceptance-derived docs deliverable intact'
|
|
383
|
+
PY
|
|
384
|
+
|
|
385
|
+
rm -rf .agent
|
|
386
|
+
|
|
387
|
+
# No workflow yet: explicit `Docs only:` tracked-doc scope wording should still
|
|
388
|
+
# normalize placeholder planning missions into concrete repo-change missions.
|
|
389
|
+
SESSION_ZERO_EXPLICIT_DOCS_ONLY_SCOPE="$TMPDIR/session-zero-explicit-docs-only-scope.jsonl"
|
|
390
|
+
DISCUSSION_ZERO_EXPLICIT_DOCS_ONLY_SCOPE=$'Mission: 開始實作這個方案\nScope:\n- Docs only: Update README and CHANGELOG for the /cook mission-normalization behavior.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Keep the operator-facing refocus flow guidance truthful.'
|
|
391
|
+
DISCUSSION_SNAPSHOT_ZERO_EXPLICIT_DOCS_ONLY_SCOPE="$TMPDIR/context-proposal-explicit-docs-only-scope.json"
|
|
392
|
+
write_session "$SESSION_ZERO_EXPLICIT_DOCS_ONLY_SCOPE" "$ROOT" "$DISCUSSION_ZERO_EXPLICIT_DOCS_ONLY_SCOPE"
|
|
393
|
+
|
|
394
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
395
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
396
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_EXPLICIT_DOCS_ONLY_SCOPE" \
|
|
397
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
398
|
+
pi --session "$SESSION_ZERO_EXPLICIT_DOCS_ONLY_SCOPE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-explicit-docs-only-scope.out" 2>"$TMPDIR/pi-completion-context-proposal-explicit-docs-only-scope.err"
|
|
399
|
+
|
|
400
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_EXPLICIT_DOCS_ONLY_SCOPE" <<'PY'
|
|
401
|
+
import json
|
|
402
|
+
import sys
|
|
403
|
+
from pathlib import Path
|
|
404
|
+
|
|
405
|
+
mission = 'Update README and CHANGELOG for the /cook mission-normalization behavior.'
|
|
406
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
407
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
408
|
+
|
|
409
|
+
assert proposal['mission'] == mission, 'explicit docs only scope should strip the docs-only qualifier while normalizing to the tracked-doc repo change'
|
|
410
|
+
assert state['mission_anchor'] == mission, 'explicit docs only scope should write the stripped tracked-doc mission into canonical state'
|
|
411
|
+
assert proposal['scope'] == ['Docs only: Update README and CHANGELOG for the /cook mission-normalization behavior.'], 'explicit docs only scope should preserve the original scope wording in the proposal body'
|
|
412
|
+
assert proposal['acceptance'] == ['Keep the operator-facing refocus flow guidance truthful.'], 'explicit docs only scope should preserve the non-mission acceptance item'
|
|
413
|
+
PY
|
|
414
|
+
|
|
415
|
+
rm -rf .agent
|
|
416
|
+
|
|
417
|
+
# No workflow yet: explicit `Documentation only:` tracked-doc acceptance wording should also
|
|
418
|
+
# normalize placeholder planning missions into concrete repo-change missions.
|
|
419
|
+
SESSION_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE="$TMPDIR/session-zero-explicit-documentation-only-acceptance.jsonl"
|
|
420
|
+
DISCUSSION_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE=$'Mission: 開始實作這個方案\nScope:\n- README and CHANGELOG guidance for bare /cook.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Documentation only: Write README and CHANGELOG notes for the bare /cook fail-closed clarification path.'
|
|
421
|
+
DISCUSSION_SNAPSHOT_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE="$TMPDIR/context-proposal-explicit-documentation-only-acceptance.json"
|
|
422
|
+
write_session "$SESSION_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE" "$ROOT" "$DISCUSSION_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE"
|
|
423
|
+
|
|
424
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
425
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
426
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE" \
|
|
427
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
428
|
+
pi --session "$SESSION_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-explicit-documentation-only-acceptance.out" 2>"$TMPDIR/pi-completion-context-proposal-explicit-documentation-only-acceptance.err"
|
|
429
|
+
|
|
430
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_EXPLICIT_DOCUMENTATION_ONLY_ACCEPTANCE" <<'PY'
|
|
431
|
+
import json
|
|
432
|
+
import sys
|
|
433
|
+
from pathlib import Path
|
|
434
|
+
|
|
435
|
+
mission = 'Write README and CHANGELOG notes for the bare /cook fail-closed clarification path.'
|
|
436
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
437
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
438
|
+
|
|
439
|
+
assert proposal['mission'] == mission, 'explicit documentation only acceptance should strip the docs-only qualifier while normalizing to the tracked-doc repo change'
|
|
440
|
+
assert state['mission_anchor'] == mission, 'explicit documentation only acceptance should write the stripped tracked-doc mission into canonical state'
|
|
441
|
+
assert proposal['scope'] == ['README and CHANGELOG guidance for bare /cook.'], 'explicit documentation only acceptance should preserve the noun-only scope item while deriving the mission from acceptance'
|
|
442
|
+
assert proposal['acceptance'] == ['Documentation only: Write README and CHANGELOG notes for the bare /cook fail-closed clarification path.'], 'explicit documentation only acceptance should preserve the original acceptance wording in the proposal body'
|
|
443
|
+
PY
|
|
444
|
+
|
|
445
|
+
rm -rf .agent
|
|
446
|
+
|
|
447
|
+
# No workflow yet: assistant-authored completed-plan summaries should fail closed instead of
|
|
448
|
+
# seeding startup proposals when the user has not restated an execution-ready mission.
|
|
449
|
+
SESSION_ZERO_ASSISTANT_SUMMARY="$TMPDIR/session-zero-assistant-summary.jsonl"
|
|
450
|
+
DISCUSSION_SNAPSHOT_ZERO_ASSISTANT_SUMMARY="$TMPDIR/context-proposal-assistant-summary.json"
|
|
451
|
+
python3 - "$SESSION_ZERO_ASSISTANT_SUMMARY" "$ROOT" <<'PY'
|
|
452
|
+
import json
|
|
453
|
+
import sys
|
|
454
|
+
from pathlib import Path
|
|
455
|
+
|
|
456
|
+
session_path = Path(sys.argv[1])
|
|
457
|
+
cwd = sys.argv[2]
|
|
458
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
459
|
+
entries = [
|
|
460
|
+
{
|
|
461
|
+
"type": "session",
|
|
462
|
+
"version": 3,
|
|
463
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
464
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
465
|
+
"cwd": cwd,
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"type": "message",
|
|
469
|
+
"id": "b2c3d4e5",
|
|
470
|
+
"parentId": None,
|
|
471
|
+
"timestamp": "2026-01-01T00:00:02.000Z",
|
|
472
|
+
"message": {
|
|
473
|
+
"role": "assistant",
|
|
474
|
+
"content": "Mission: Ship the replacement workflow from the completed plan.\nScope:\n- Rewrite bare /cook around the finished plan summary.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Start immediately from this summary without more user clarification.",
|
|
475
|
+
"timestamp": 1767225602000,
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
]
|
|
479
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
480
|
+
for entry in entries:
|
|
481
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
482
|
+
PY
|
|
483
|
+
|
|
484
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
485
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
486
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ASSISTANT_SUMMARY" \
|
|
487
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
488
|
+
pi --session "$SESSION_ZERO_ASSISTANT_SUMMARY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-assistant-summary.out" 2>"$TMPDIR/pi-completion-context-proposal-assistant-summary.err"
|
|
489
|
+
|
|
490
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-assistant-summary.out" "$TMPDIR/pi-completion-context-proposal-assistant-summary.err" "$DISCUSSION_SNAPSHOT_ZERO_ASSISTANT_SUMMARY" <<'PY'
|
|
491
|
+
import sys
|
|
492
|
+
from pathlib import Path
|
|
493
|
+
|
|
494
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
495
|
+
snapshot = Path(sys.argv[3])
|
|
496
|
+
assert not Path('.agent').exists(), 'assistant-only startup summary should fail closed without writing canonical state'
|
|
497
|
+
assert not snapshot.exists(), 'assistant-only startup summary should not emit a proposal snapshot when bare /cook fails closed'
|
|
498
|
+
assert 'Bare /cook failed closed' in output, 'assistant-only startup summary should explain the fail-closed startup outcome'
|
|
499
|
+
assert 'concrete repo changes' in output, 'assistant-only startup summary should explain that bare /cook expects execution-ready repo changes from main-chat discussion'
|
|
500
|
+
PY
|
|
501
|
+
|
|
502
|
+
rm -rf .agent
|
|
503
|
+
|
|
504
|
+
# No workflow yet: analyst-derived generic planning missions should still fail closed when discussion
|
|
505
|
+
# never provides a clear implementation anchor, instead of promoting vague non-doc scope.
|
|
506
|
+
SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC="$TMPDIR/session-zero-analyst-ambiguous-generic.jsonl"
|
|
507
|
+
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.'
|
|
508
|
+
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}'
|
|
509
|
+
DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC="$TMPDIR/context-proposal-analyst-ambiguous-generic.json"
|
|
510
|
+
write_session "$SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC" "$ROOT" "$DISCUSSION_ZERO_ANALYST_AMBIGUOUS_GENERIC"
|
|
511
|
+
|
|
512
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
513
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_ZERO_AMBIGUOUS_GENERIC" \
|
|
514
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC" \
|
|
515
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
516
|
+
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"
|
|
517
|
+
|
|
518
|
+
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'
|
|
519
|
+
import sys
|
|
520
|
+
from pathlib import Path
|
|
521
|
+
|
|
522
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
523
|
+
snapshot = Path(sys.argv[3])
|
|
524
|
+
assert not Path('.agent').exists(), 'analyst-derived ambiguous generic discussion should fail closed without writing canonical state'
|
|
525
|
+
assert not snapshot.exists(), 'analyst-derived ambiguous generic discussion should not emit a proposal snapshot when bare /cook fails closed'
|
|
526
|
+
assert 'Bare /cook failed closed' in output, 'analyst-derived ambiguous generic discussion should explain the fail-closed startup outcome'
|
|
527
|
+
PY
|
|
528
|
+
|
|
202
529
|
# No workflow yet: /cook with no goal should infer from recent discussion through analyst output.
|
|
203
530
|
SESSION_ONE="$TMPDIR/session-one.jsonl"
|
|
204
531
|
DISCUSSION_ONE="$DISCUSSION_ZERO"
|
|
@@ -285,6 +612,8 @@ plan = json.loads(Path('.agent/plan.json').read_text())
|
|
|
285
612
|
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
286
613
|
|
|
287
614
|
assert routing['mode'] == 'bare', 'active bare /cook continue regression should snapshot bare routing mode'
|
|
615
|
+
assert 'explicitGoal' not in routing, 'active bare /cook continue routing should not expose removed explicit-goal shim fields'
|
|
616
|
+
assert 'explicitGoalProvided' not in routing, 'active bare /cook continue routing should not expose removed explicit-goal shim fields'
|
|
288
617
|
assert routing['action'] == 'continue', 'matching structured discussion should classify active bare /cook as continue'
|
|
289
618
|
assert routing['reason'] == 'matching_mission', 'matching structured discussion should keep the current mission rather than refocus'
|
|
290
619
|
assert routing['currentMissionAnchor'] == mission, 'continue routing should preserve the current mission anchor'
|
|
@@ -296,6 +625,161 @@ assert plan['mission_anchor'] == mission, 'active bare /cook continue should kee
|
|
|
296
625
|
assert active['mission_anchor'] == mission, 'active bare /cook continue should keep active-slice.json unchanged'
|
|
297
626
|
PY
|
|
298
627
|
|
|
628
|
+
# Active workflow: summary-only replacement artifacts should fail closed and keep the current
|
|
629
|
+
# workflow instead of opening the refocus chooser.
|
|
630
|
+
SESSION_ONE_SUMMARY_ONLY="$TMPDIR/session-one-summary-only.jsonl"
|
|
631
|
+
SUMMARY_ROUTING_ONE="$TMPDIR/active-summary-only-routing.json"
|
|
632
|
+
SUMMARY_RESUME_PROMPT_ONE="$TMPDIR/active-summary-only-resume.txt"
|
|
633
|
+
SUMMARY_CHOOSER_ONE="$TMPDIR/unexpected-active-summary-only-chooser.json"
|
|
634
|
+
SUMMARY_PROPOSAL_ONE="$TMPDIR/unexpected-active-summary-only-proposal.json"
|
|
635
|
+
python3 - "$SESSION_ONE_SUMMARY_ONLY" "$ROOT" <<'PY'
|
|
636
|
+
import json
|
|
637
|
+
import sys
|
|
638
|
+
from pathlib import Path
|
|
639
|
+
|
|
640
|
+
session_path = Path(sys.argv[1])
|
|
641
|
+
cwd = sys.argv[2]
|
|
642
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
643
|
+
entries = [
|
|
644
|
+
{
|
|
645
|
+
"type": "session",
|
|
646
|
+
"version": 3,
|
|
647
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
648
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
649
|
+
"cwd": cwd,
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
"type": "message",
|
|
653
|
+
"id": "c3d4e5f6",
|
|
654
|
+
"parentId": None,
|
|
655
|
+
"timestamp": "2026-01-01T00:00:03.000Z",
|
|
656
|
+
"message": {
|
|
657
|
+
"role": "branchSummary",
|
|
658
|
+
"summary": "Mission: Replace the current workflow from the completed plan summary.\nScope:\n- Refocus to a different mission from this summary artifact alone.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Rewrite canonical state from the summary without new user discussion.",
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
]
|
|
662
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
663
|
+
for entry in entries:
|
|
664
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
665
|
+
PY
|
|
666
|
+
|
|
667
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
668
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$SUMMARY_ROUTING_ONE" \
|
|
669
|
+
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$SUMMARY_RESUME_PROMPT_ONE" \
|
|
670
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$SUMMARY_CHOOSER_ONE" \
|
|
671
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$SUMMARY_PROPOSAL_ONE" \
|
|
672
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
673
|
+
pi --session "$SESSION_ONE_SUMMARY_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-summary-only.out" 2>"$TMPDIR/pi-completion-context-proposal-active-summary-only.err"
|
|
674
|
+
|
|
675
|
+
python3 - "$SUMMARY_ROUTING_ONE" "$SUMMARY_RESUME_PROMPT_ONE" "$SUMMARY_CHOOSER_ONE" "$SUMMARY_PROPOSAL_ONE" <<'PY'
|
|
676
|
+
import json
|
|
677
|
+
import sys
|
|
678
|
+
from pathlib import Path
|
|
679
|
+
|
|
680
|
+
mission = 'Remove the completion status line while keeping the completion widget.'
|
|
681
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
682
|
+
resume = Path(sys.argv[2]).read_text()
|
|
683
|
+
chooser_path = Path(sys.argv[3])
|
|
684
|
+
proposal_path = Path(sys.argv[4])
|
|
685
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
686
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
687
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
688
|
+
|
|
689
|
+
assert routing['mode'] == 'bare', 'summary-only active bare /cook regression should snapshot bare routing mode'
|
|
690
|
+
assert routing['action'] == 'unclear', 'summary-only active bare /cook should fail closed instead of refocusing'
|
|
691
|
+
assert routing['reason'] == 'missing_proposal', 'summary-only active bare /cook should treat the summary artifact as unreadiness, not a new proposal'
|
|
692
|
+
assert routing['currentMissionAnchor'] == mission, 'summary-only active bare /cook should preserve the current mission anchor'
|
|
693
|
+
assert routing['proposedMissionAnchor'] is None, 'summary-only active bare /cook should not derive a replacement mission from summary artifacts alone'
|
|
694
|
+
assert 'Resume the completion workflow from canonical state.' in resume, 'summary-only active bare /cook should still resume the canonical workflow'
|
|
695
|
+
assert not chooser_path.exists(), 'summary-only active bare /cook should not open the refocus chooser'
|
|
696
|
+
assert not proposal_path.exists(), 'summary-only active bare /cook should not open replacement proposal confirmation'
|
|
697
|
+
assert state['mission_anchor'] == mission, 'summary-only active bare /cook should keep state.json unchanged'
|
|
698
|
+
assert plan['mission_anchor'] == mission, 'summary-only active bare /cook should keep plan.json unchanged'
|
|
699
|
+
assert active['mission_anchor'] == mission, 'summary-only active bare /cook should keep active-slice.json unchanged'
|
|
700
|
+
PY
|
|
701
|
+
|
|
702
|
+
# Active workflow: bare /cook with a placeholder planning mission should still route through the existing
|
|
703
|
+
# refocus chooser and final Start/Cancel gate before canonical state is rewritten.
|
|
704
|
+
SESSION_ONE_REFOCUS_NORMALIZED="$TMPDIR/session-one-refocus-normalized.jsonl"
|
|
705
|
+
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.'
|
|
706
|
+
REFOCUS_ROUTING_ONE="$TMPDIR/active-refocus-routing.json"
|
|
707
|
+
REFOCUS_CHOOSER_ONE="$TMPDIR/active-refocus-chooser.json"
|
|
708
|
+
REFOCUS_PROPOSAL_ONE="$TMPDIR/active-refocus-proposal.json"
|
|
709
|
+
REFOCUS_UI_ONE="$TMPDIR/active-refocus-ui.json"
|
|
710
|
+
write_session "$SESSION_ONE_REFOCUS_NORMALIZED" "$ROOT" "$DISCUSSION_ONE_REFOCUS_NORMALIZED"
|
|
711
|
+
|
|
712
|
+
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
713
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_ACTION=start \
|
|
714
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
715
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$REFOCUS_ROUTING_ONE" \
|
|
716
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$REFOCUS_CHOOSER_ONE" \
|
|
717
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$REFOCUS_PROPOSAL_ONE" \
|
|
718
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_PATH="$REFOCUS_UI_ONE" \
|
|
719
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
720
|
+
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"
|
|
721
|
+
|
|
722
|
+
python3 - "$REFOCUS_ROUTING_ONE" "$REFOCUS_CHOOSER_ONE" "$REFOCUS_PROPOSAL_ONE" "$REFOCUS_UI_ONE" <<'PY'
|
|
723
|
+
import json
|
|
724
|
+
import sys
|
|
725
|
+
from pathlib import Path
|
|
726
|
+
|
|
727
|
+
mission = 'Normalize bare /cook planning phrasing into implementation-result missions.'
|
|
728
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
729
|
+
chooser = json.loads(Path(sys.argv[2]).read_text())
|
|
730
|
+
proposal = json.loads(Path(sys.argv[3]).read_text())
|
|
731
|
+
ui = json.loads(Path(sys.argv[4]).read_text())
|
|
732
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
733
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
734
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
735
|
+
|
|
736
|
+
assert routing['mode'] == 'bare', 'active bare /cook refocus normalization should snapshot bare routing mode'
|
|
737
|
+
assert 'explicitGoal' not in routing, 'active bare /cook refocus routing should not expose removed explicit-goal shim fields'
|
|
738
|
+
assert 'explicitGoalProvided' not in routing, 'active bare /cook refocus routing should not expose removed explicit-goal shim fields'
|
|
739
|
+
assert routing['action'] == 'refocus', 'placeholder planning mission should still classify active bare /cook as refocus when the normalized mission changes'
|
|
740
|
+
assert routing['reason'] == 'clear_refocus', 'active bare /cook refocus normalization should keep the clear_refocus routing reason'
|
|
741
|
+
assert routing['proposedMissionAnchor'] == mission, 'active bare /cook refocus should normalize the proposed mission before canonical rewrite'
|
|
742
|
+
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'
|
|
743
|
+
assert [action['id'] for action in ui['actions']] == ['start', 'cancel'], 'active bare /cook refocus should still end at the approval-only Start/Cancel gate'
|
|
744
|
+
assert proposal['mission'] == mission, 'active bare /cook refocus proposal snapshot should expose the normalized implementation mission'
|
|
745
|
+
assert state['mission_anchor'] == mission, 'active bare /cook refocus should rewrite canonical state to the normalized mission only after approval'
|
|
746
|
+
assert plan['mission_anchor'] == mission, 'active bare /cook refocus should rewrite plan.json only after approval'
|
|
747
|
+
assert active['mission_anchor'] == mission, 'active bare /cook refocus should rewrite active-slice.json only after approval'
|
|
748
|
+
PY
|
|
749
|
+
|
|
750
|
+
# Completed workflow: bare /cook should normalize placeholder planning phrasing for the next workflow
|
|
751
|
+
# round too, not only for fresh startup.
|
|
752
|
+
mark_done
|
|
753
|
+
|
|
754
|
+
SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
|
|
755
|
+
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.'
|
|
756
|
+
DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
|
|
757
|
+
write_session "$SESSION_TWO_NORMALIZED" "$ROOT" "$DISCUSSION_TWO_NORMALIZED"
|
|
758
|
+
|
|
759
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
760
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
761
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" \
|
|
762
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
763
|
+
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"
|
|
764
|
+
|
|
765
|
+
python3 - "$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" <<'PY'
|
|
766
|
+
import json
|
|
767
|
+
import sys
|
|
768
|
+
from pathlib import Path
|
|
769
|
+
|
|
770
|
+
mission = 'Normalize bare /cook planning phrasing for the next workflow round.'
|
|
771
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
772
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
773
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
774
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
775
|
+
|
|
776
|
+
assert proposal['mission'] == mission, 'done-workflow structured fallback should normalize the placeholder planning mission for the next round'
|
|
777
|
+
assert state['mission_anchor'] == mission, 'done-workflow startup should rewrite canonical state to the normalized next-round mission'
|
|
778
|
+
assert plan['mission_anchor'] == mission, 'done-workflow startup should rewrite plan.json to the normalized next-round mission'
|
|
779
|
+
assert active['mission_anchor'] == mission, 'done-workflow startup should rewrite active-slice.json to the normalized next-round mission'
|
|
780
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'done-workflow normalization should still route through refocus semantics for the next round'
|
|
781
|
+
PY
|
|
782
|
+
|
|
299
783
|
# Completed workflow: bare /cook should use the same strict structured fallback for the next workflow round when analyst output is unavailable.
|
|
300
784
|
mark_done
|
|
301
785
|
|
|
@@ -352,165 +836,124 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
|
|
|
352
836
|
assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
|
|
353
837
|
PY
|
|
354
838
|
|
|
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'
|
|
839
|
+
# Active workflow: inline /cook args should be rejected before proposal/routing helpers run
|
|
840
|
+
# and should leave canonical state unchanged.
|
|
841
|
+
ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
|
|
842
|
+
ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
|
|
843
|
+
ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
|
|
844
|
+
ACTIVE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-active-inline-before.json"
|
|
845
|
+
python3 - "$ACTIVE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
372
846
|
import json
|
|
373
847
|
import sys
|
|
374
848
|
from pathlib import Path
|
|
375
849
|
|
|
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'
|
|
850
|
+
tracked = [
|
|
851
|
+
Path('.agent/mission.md'),
|
|
852
|
+
Path('.agent/profile.json'),
|
|
853
|
+
Path('.agent/state.json'),
|
|
854
|
+
Path('.agent/plan.json'),
|
|
855
|
+
Path('.agent/active-slice.json'),
|
|
856
|
+
Path('.agent/verification-evidence.json'),
|
|
857
|
+
]
|
|
858
|
+
Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
|
|
416
859
|
PY
|
|
417
860
|
|
|
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
861
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
425
|
-
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=
|
|
862
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
426
863
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
864
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$ACTIVE_INLINE_REJECTION_PROPOSAL" \
|
|
865
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ACTIVE_INLINE_REJECTION_ROUTING" \
|
|
866
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$ACTIVE_INLINE_REJECTION_CHOOSER" \
|
|
427
867
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
428
|
-
pi
|
|
868
|
+
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
869
|
|
|
430
|
-
python3 - "$TMPDIR/pi-completion-context-proposal-
|
|
870
|
+
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
871
|
import json
|
|
432
872
|
import sys
|
|
433
873
|
from pathlib import Path
|
|
434
874
|
|
|
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
875
|
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
876
|
+
routing = Path(sys.argv[3])
|
|
877
|
+
proposal = Path(sys.argv[4])
|
|
878
|
+
chooser = Path(sys.argv[5])
|
|
879
|
+
before = json.loads(Path(sys.argv[6]).read_text())
|
|
880
|
+
tracked = [
|
|
881
|
+
Path('.agent/mission.md'),
|
|
882
|
+
Path('.agent/profile.json'),
|
|
883
|
+
Path('.agent/state.json'),
|
|
884
|
+
Path('.agent/plan.json'),
|
|
885
|
+
Path('.agent/active-slice.json'),
|
|
886
|
+
Path('.agent/verification-evidence.json'),
|
|
887
|
+
]
|
|
440
888
|
|
|
441
|
-
assert
|
|
442
|
-
assert
|
|
443
|
-
assert
|
|
444
|
-
assert
|
|
889
|
+
assert 'Inline /cook arguments are no longer supported.' in output, 'active inline /cook args should explain the hard rejection'
|
|
890
|
+
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'
|
|
891
|
+
assert not routing.exists(), 'active inline /cook args should not run active-workflow routing'
|
|
892
|
+
assert not proposal.exists(), 'active inline /cook args should not open proposal confirmation'
|
|
893
|
+
assert not chooser.exists(), 'active inline /cook args should not open the existing-workflow chooser'
|
|
894
|
+
after = {path.name: path.read_text() for path in tracked}
|
|
895
|
+
assert before == after, 'active inline /cook args should leave canonical files unchanged'
|
|
445
896
|
PY
|
|
446
897
|
|
|
447
|
-
# Completed workflow
|
|
448
|
-
# even when analyst output is unavailable, without merging session-derived scope, constraints, or acceptance.
|
|
898
|
+
# Completed workflow: inline /cook args should also fail closed without starting the next round.
|
|
449
899
|
mark_done
|
|
450
900
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
901
|
+
DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
|
|
902
|
+
DONE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-done-inline-arg-proposal.json"
|
|
903
|
+
DONE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-done-inline-arg-chooser.json"
|
|
904
|
+
DONE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-done-inline-before.json"
|
|
905
|
+
python3 - "$DONE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
906
|
+
import json
|
|
907
|
+
import sys
|
|
908
|
+
from pathlib import Path
|
|
909
|
+
|
|
910
|
+
tracked = [
|
|
911
|
+
Path('.agent/mission.md'),
|
|
912
|
+
Path('.agent/profile.json'),
|
|
913
|
+
Path('.agent/state.json'),
|
|
914
|
+
Path('.agent/plan.json'),
|
|
915
|
+
Path('.agent/active-slice.json'),
|
|
916
|
+
Path('.agent/verification-evidence.json'),
|
|
917
|
+
]
|
|
918
|
+
Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
|
|
919
|
+
PY
|
|
456
920
|
|
|
457
921
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
458
922
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
459
|
-
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$
|
|
923
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DONE_INLINE_REJECTION_PROPOSAL" \
|
|
924
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$DONE_INLINE_REJECTION_ROUTING" \
|
|
925
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$DONE_INLINE_REJECTION_CHOOSER" \
|
|
460
926
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
461
|
-
pi
|
|
927
|
+
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
928
|
|
|
463
|
-
python3 - "$
|
|
929
|
+
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
930
|
import json
|
|
465
931
|
import sys
|
|
466
932
|
from pathlib import Path
|
|
467
933
|
|
|
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'
|
|
934
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
935
|
+
routing = Path(sys.argv[3])
|
|
936
|
+
proposal = Path(sys.argv[4])
|
|
937
|
+
chooser = Path(sys.argv[5])
|
|
938
|
+
before = json.loads(Path(sys.argv[6]).read_text())
|
|
939
|
+
tracked = [
|
|
940
|
+
Path('.agent/mission.md'),
|
|
941
|
+
Path('.agent/profile.json'),
|
|
942
|
+
Path('.agent/state.json'),
|
|
943
|
+
Path('.agent/plan.json'),
|
|
944
|
+
Path('.agent/active-slice.json'),
|
|
945
|
+
Path('.agent/verification-evidence.json'),
|
|
946
|
+
]
|
|
947
|
+
state_before = json.loads(before['state.json'])
|
|
948
|
+
assert state_before['current_phase'] == 'done', 'done inline /cook rejection should start from a completed workflow'
|
|
949
|
+
assert state_before['project_done'] is True, 'done inline /cook rejection should start from project_done=true'
|
|
950
|
+
assert 'Inline /cook arguments are no longer supported.' in output, 'done inline /cook args should explain the hard rejection'
|
|
951
|
+
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'
|
|
952
|
+
assert not routing.exists(), 'done inline /cook args should not run active/done workflow routing'
|
|
953
|
+
assert not proposal.exists(), 'done inline /cook args should not open next-round proposal confirmation'
|
|
954
|
+
assert not chooser.exists(), 'done inline /cook args should not open the chooser flow'
|
|
955
|
+
after = {path.name: path.read_text() for path in tracked}
|
|
956
|
+
assert before == after, 'done inline /cook args should leave canonical files unchanged'
|
|
514
957
|
PY
|
|
515
958
|
|
|
516
959
|
# Completed workflow again: /cook with no goal should be able to use model-assisted
|
|
@@ -518,8 +961,8 @@ PY
|
|
|
518
961
|
mark_done
|
|
519
962
|
|
|
520
963
|
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
|
|
964
|
+
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.'
|
|
965
|
+
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
966
|
write_session "$SESSION_FIVE" "$ROOT" "$DISCUSSION_FIVE"
|
|
524
967
|
|
|
525
968
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
@@ -559,7 +1002,7 @@ assert continuation_reason.startswith('User refocused workflow via /cook:'), 'co
|
|
|
559
1002
|
assert 'task_type=completion-workflow' in continuation_reason, 'analyst-derived restart should persist the selected task_type'
|
|
560
1003
|
assert 'evaluation_profile=completion-rubric-v1' in continuation_reason, 'analyst-derived restart should persist the selected evaluation_profile'
|
|
561
1004
|
assert 'critique outcome=accepted critique=none' in continuation_reason, 'analyst-derived restart should persist that no critique notes were accepted'
|
|
562
|
-
assert 'Keep
|
|
1005
|
+
assert 'Keep the discussion-derived mission anchored once it is clear.' in continuation_reason, 'analyst-derived scope should be preserved'
|
|
563
1006
|
PY
|
|
564
1007
|
|
|
565
1008
|
# Custom confirmation UI: start should render proposal content separately from approval-only Start/Cancel actions.
|