@linimin/pi-letscook 0.1.45 → 0.1.47

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.
@@ -75,11 +75,12 @@ assertIncludes('README.md', 'Deterministic active-slice contract regression now
75
75
  assertIncludes('README.md', 'includes deterministic active-slice contract coverage plus observability coverage');
76
76
  assertIncludes('scripts/release-check.sh', 'bash ./scripts/active-slice-contract-test.sh');
77
77
  assertIncludes('.agent/verify_completion_stop.sh', 'npm run release-check >/dev/null');
78
- assertIncludes('extensions/completion/index.ts', "const planMirrorFields = ['locked_notes', 'must_fix_findings', 'implementation_surfaces', 'verification_commands', 'basis_commit', 'remaining_contract_ids_before', 'release_blocker_count_before', 'high_value_gap_count_before'];");
79
- assertIncludes('extensions/completion/index.ts', 'Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.');
80
- assertIncludes('extensions/completion/index.ts', 'Active slice contract drift: ${activeContractDrift}');
78
+ assertIncludes('extensions/completion/state-store.ts', 'export function buildVerifyControlPlaneScript(');
79
+ assertIncludes('extensions/completion/state-store.ts', 'return fs.readFileSync(trackedScriptPath, "utf8");');
80
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.');
81
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'Active slice contract drift: ${args.activeContractDrift}');
81
82
  assertIncludes('extensions/completion/index.ts', 'Canonical active-slice contract drift is currently: ${activeContractDrift}');
82
- assertIncludes('extensions/completion/index.ts', '`active_slice_contract_drift_fields: ${activeContractDrift}`');
83
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`active_slice_contract_drift_fields: ${args.activeSliceContractDrift}`');
83
84
  assertIncludes('extensions/completion/index.ts', 'treat .agent/active-slice.json as the canonical implementation contract');
84
85
  assertIncludes('.agent/verify_completion_control_plane.sh', "const planMirrorFields = ['locked_notes', 'must_fix_findings', 'implementation_surfaces', 'verification_commands', 'basis_commit', 'remaining_contract_ids_before', 'release_blocker_count_before', 'high_value_gap_count_before'];");
85
86
  assertIncludes('.agent/verify_completion_control_plane.sh', 'slice_id must match a slice in .agent/plan.json when status carries an exact handoff');
@@ -96,11 +96,11 @@ assertSectionIncludes('skills/completion-protocol/references/completion.md', '##
96
96
  assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Canonical Inputs', '- `.agent/verification-evidence.json`');
97
97
  assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '- `.agent/verification-evidence.json`');
98
98
  assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '`completion-implementer` must also re-read canonical `.agent/state.json`, `.agent/plan.json`, `.agent/active-slice.json`, and `.agent/verification-evidence.json` before resuming work.');
99
- assertIncludes('extensions/completion/index.ts', 'Verification evidence artifact: ${evidence.path}');
100
- assertIncludes('extensions/completion/index.ts', 'Verification evidence summary: ${evidence.summary}');
99
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})');
100
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'Verification evidence summary: ${args.evidence.summary}');
101
101
  assertIncludes('extensions/completion/index.ts', 'Canonical verification evidence artifact is currently: ${evidence.path} (${evidence.status})');
102
- assertIncludes('extensions/completion/index.ts', '`- verification_evidence_path: ${evidence.path}`');
103
- assertIncludes('extensions/completion/index.ts', '`- verification_evidence_summary: ${evidence.summary}`');
102
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`- verification_evidence_path: ${evidence.path}`');
103
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`- verification_evidence_summary: ${evidence.summary}`');
104
104
  assertIncludes('extensions/completion/index.ts', 'Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.');
105
105
  assertIncludes('scripts/release-check.sh', 'bash .agent/verify_completion_control_plane.sh');
106
106
  assertIncludes('scripts/release-check.sh', 'bash ./scripts/canonical-evidence-artifact-test.sh');
@@ -158,6 +158,85 @@ PY
158
158
 
159
159
  rm -rf .agent
160
160
 
161
+ # No workflow yet: when multiple structured discussions exist, bare /cook should prioritize the latest
162
+ # concrete implementation mission instead of failing closed on older structured context.
163
+ SESSION_ZERO_LATEST_WINDOW="$TMPDIR/session-zero-latest-window.jsonl"
164
+ DISCUSSION_ZERO_LATEST_OLDER=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Keep observability regression coverage truthful.'
165
+ DISCUSSION_ZERO_LATEST_NEWER=$'Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic.\nConstraints:\n- Do not refactor the broader auth flow.\nAcceptance:\n- Add a regression test for returning to the requested page.'
166
+ DISCUSSION_SNAPSHOT_ZERO_LATEST_WINDOW="$TMPDIR/context-proposal-latest-window.json"
167
+ python3 - "$SESSION_ZERO_LATEST_WINDOW" "$ROOT" "$DISCUSSION_ZERO_LATEST_OLDER" "$DISCUSSION_ZERO_LATEST_NEWER" <<'PY'
168
+ import json
169
+ import sys
170
+ from pathlib import Path
171
+
172
+ session_path = Path(sys.argv[1])
173
+ cwd = sys.argv[2]
174
+ older = sys.argv[3]
175
+ newer = sys.argv[4]
176
+ session_path.parent.mkdir(parents=True, exist_ok=True)
177
+ entries = [
178
+ {
179
+ "type": "session",
180
+ "version": 3,
181
+ "id": "11111111-1111-4111-8111-111111111111",
182
+ "timestamp": "2026-01-01T00:00:00.000Z",
183
+ "cwd": cwd,
184
+ },
185
+ {
186
+ "type": "message",
187
+ "id": "a1b2c3d4",
188
+ "parentId": None,
189
+ "timestamp": "2026-01-01T00:00:01.000Z",
190
+ "message": {
191
+ "role": "user",
192
+ "content": older,
193
+ "timestamp": 1767225601000,
194
+ },
195
+ },
196
+ {
197
+ "type": "message",
198
+ "id": "b2c3d4e5",
199
+ "parentId": "a1b2c3d4",
200
+ "timestamp": "2026-01-01T00:00:02.000Z",
201
+ "message": {
202
+ "role": "user",
203
+ "content": newer,
204
+ "timestamp": 1767225602000,
205
+ },
206
+ },
207
+ ]
208
+ with session_path.open('w', encoding='utf-8') as fh:
209
+ for entry in entries:
210
+ fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
211
+ PY
212
+
213
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
214
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
215
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_LATEST_WINDOW" \
216
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
217
+ pi --session "$SESSION_ZERO_LATEST_WINDOW" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-latest-window.out" 2>"$TMPDIR/pi-completion-context-proposal-latest-window.err"
218
+
219
+ python3 - "$DISCUSSION_SNAPSHOT_ZERO_LATEST_WINDOW" <<'PY'
220
+ import json
221
+ import sys
222
+ from pathlib import Path
223
+
224
+ mission = 'Fix login redirect callback behavior.'
225
+ proposal = json.loads(Path(sys.argv[1]).read_text())
226
+ state = json.loads(Path('.agent/state.json').read_text())
227
+ plan = json.loads(Path('.agent/plan.json').read_text())
228
+ active = json.loads(Path('.agent/active-slice.json').read_text())
229
+
230
+ assert proposal['mission'] == mission, 'latest structured discussion should win over older structured context'
231
+ assert proposal['scope'] == ['Update the callback redirect decision logic.'], 'latest structured discussion should preserve the latest scope only'
232
+ assert proposal['analysis']['suppressedNegatedTopics'] == ['Do not refactor the broader auth flow.'], 'latest structured discussion should preserve negated implementation topics separately from the mission'
233
+ assert state['mission_anchor'] == mission, 'latest structured discussion should initialize state.json with the latest mission'
234
+ assert plan['mission_anchor'] == mission, 'latest structured discussion should initialize plan.json with the latest mission'
235
+ assert active['mission_anchor'] == mission, 'latest structured discussion should initialize active-slice.json with the latest mission'
236
+ PY
237
+
238
+ rm -rf .agent
239
+
161
240
  # No workflow yet: bare /cook should fail closed when a required structured section is missing and analyst output is unavailable.
162
241
  SESSION_ZERO_MISSING="$TMPDIR/session-zero-missing-section.jsonl"
163
242
  DISCUSSION_ZERO_MISSING=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\n- Suppress the widget while a completion role is active.\nConstraints:\n- Do not reintroduce any other completion status surface.'
@@ -182,26 +261,35 @@ assert '/cook failed closed' in output, 'missing-section structured discussion s
182
261
  assert 'Mission/Scope/Constraints/Acceptance' in output, 'missing-section structured discussion should explain the strict fallback requirement'
183
262
  PY
184
263
 
185
- # No workflow yet: bare /cook should fail closed on ambiguous structured discussion when analyst output is unavailable.
264
+ # No workflow yet: when one structured discussion message contains multiple complete mission blocks,
265
+ # bare /cook should prioritize the latest block and preserve earlier blocks as alternate missions.
186
266
  SESSION_ZERO_AMBIG="$TMPDIR/session-zero-ambiguous.jsonl"
187
- DISCUSSION_ZERO_AMBIG=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Update README to match the shipped behavior.\nMission: Ship an unrelated widget overhaul.\nScope:\n- Replace the widget entirely.'
267
+ DISCUSSION_ZERO_AMBIG=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Update README to match the shipped behavior.\nMission: Ship an unrelated widget overhaul.\nScope:\n- Replace the widget entirely.\nConstraints:\n- Do not modify the completion widget.\nAcceptance:\n- Land the unrelated overhaul changes only.'
268
+ DISCUSSION_SNAPSHOT_ZERO_AMBIG="$TMPDIR/context-proposal-ambiguous-latest-block.json"
188
269
  write_session "$SESSION_ZERO_AMBIG" "$ROOT" "$DISCUSSION_ZERO_AMBIG"
189
270
 
190
271
  PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
191
272
  PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
273
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_AMBIG" \
192
274
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
193
275
  pi --session "$SESSION_ZERO_AMBIG" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-ambiguous.out" 2>"$TMPDIR/pi-completion-context-proposal-ambiguous.err"
194
276
 
195
- python3 - "$TMPDIR/pi-completion-context-proposal-ambiguous.out" "$TMPDIR/pi-completion-context-proposal-ambiguous.err" <<'PY'
277
+ python3 - "$DISCUSSION_SNAPSHOT_ZERO_AMBIG" <<'PY'
278
+ import json
196
279
  import sys
197
280
  from pathlib import Path
198
281
 
199
- output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
200
- assert not Path('.agent').exists(), 'ambiguous structured discussion should fail closed without writing canonical state'
201
- assert '/cook failed closed' in output, 'ambiguous structured discussion should explain the fail-closed startup outcome'
202
- assert 'Mission/Scope/Constraints/Acceptance' in output, 'ambiguous structured discussion should explain the strict fallback requirement'
282
+ mission = 'Ship an unrelated widget overhaul.'
283
+ proposal = json.loads(Path(sys.argv[1]).read_text())
284
+ state = json.loads(Path('.agent/state.json').read_text())
285
+
286
+ assert proposal['mission'] == mission, 'latest complete mission block should win inside a single structured discussion message'
287
+ assert proposal['analysis']['alternateMissions'] == ['Remove the completion status line while keeping the completion widget.'], 'earlier complete mission blocks should be preserved as alternate missions'
288
+ assert state['mission_anchor'] == mission, 'latest complete mission block should initialize canonical mission state'
203
289
  PY
204
290
 
291
+ rm -rf .agent
292
+
205
293
  # No workflow yet: bare /cook structured fallback should normalize placeholder planning phrasing
206
294
  # into the concrete implementation mission when scope/acceptance clearly describe shipped work.
207
295
  SESSION_ZERO_NORMALIZED="$TMPDIR/session-zero-normalized.jsonl"
@@ -699,6 +787,100 @@ assert plan['mission_anchor'] == mission, 'summary-only active bare /cook should
699
787
  assert active['mission_anchor'] == mission, 'summary-only active bare /cook should keep active-slice.json unchanged'
700
788
  PY
701
789
 
790
+ # Active workflow: when recent discussion suggests a different implementation goal but the proposal is
791
+ # still incomplete, bare /cook should surface the chooser instead of silently resuming the current workflow.
792
+ SESSION_ONE_AMBIGUOUS_CHOOSER="$TMPDIR/session-one-ambiguous-chooser.jsonl"
793
+ DISCUSSION_ONE_AMBIGUOUS_CHOOSER=$'Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic for the current auth flow.'
794
+ AMBIGUOUS_ROUTING_ONE="$TMPDIR/active-ambiguous-routing.json"
795
+ AMBIGUOUS_CHOOSER_ONE="$TMPDIR/active-ambiguous-chooser.json"
796
+ AMBIGUOUS_RESUME_PROMPT_ONE="$TMPDIR/unexpected-active-ambiguous-resume.txt"
797
+ AMBIGUOUS_PROPOSAL_ONE="$TMPDIR/unexpected-active-ambiguous-proposal.json"
798
+ write_session "$SESSION_ONE_AMBIGUOUS_CHOOSER" "$ROOT" "$DISCUSSION_ONE_AMBIGUOUS_CHOOSER"
799
+
800
+ PI_COMPLETION_EXISTING_WORKFLOW_ACTION=cancel \
801
+ PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT='{"mission":"Fix login redirect callback behavior.","scope":["Update the callback redirect decision logic for the current auth flow."],"constraints":[],"acceptance":[],"task_type":"completion-workflow","evaluation_profile":"completion-rubric-v1","confidence":0.72,"possible_noise":["older completion widget cleanup"]}' \
802
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$AMBIGUOUS_ROUTING_ONE" \
803
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$AMBIGUOUS_CHOOSER_ONE" \
804
+ PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$AMBIGUOUS_RESUME_PROMPT_ONE" \
805
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$AMBIGUOUS_PROPOSAL_ONE" \
806
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
807
+ pi --session "$SESSION_ONE_AMBIGUOUS_CHOOSER" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.out" 2>"$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.err"
808
+
809
+ python3 - "$AMBIGUOUS_ROUTING_ONE" "$AMBIGUOUS_CHOOSER_ONE" "$AMBIGUOUS_RESUME_PROMPT_ONE" "$AMBIGUOUS_PROPOSAL_ONE" "$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.out" "$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.err" <<'PY'
810
+ import json
811
+ import sys
812
+ from pathlib import Path
813
+
814
+ mission = 'Remove the completion status line while keeping the completion widget.'
815
+ routing = json.loads(Path(sys.argv[1]).read_text())
816
+ chooser = json.loads(Path(sys.argv[2]).read_text())
817
+ resume_path = Path(sys.argv[3])
818
+ proposal_path = Path(sys.argv[4])
819
+ output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
820
+ state = json.loads(Path('.agent/state.json').read_text())
821
+ plan = json.loads(Path('.agent/plan.json').read_text())
822
+ active = json.loads(Path('.agent/active-slice.json').read_text())
823
+
824
+ assert routing['mode'] == 'bare', 'ambiguous active bare /cook should snapshot bare routing mode'
825
+ assert routing['action'] == 'unclear', 'incomplete replacement discussion should stay ambiguous until the chooser resolves it'
826
+ assert routing['reason'] == 'ambiguous_discussion', 'incomplete replacement discussion should record the ambiguous-discussion reason'
827
+ assert routing['currentMissionAnchor'] == mission, 'ambiguous chooser routing should preserve the current mission anchor'
828
+ assert routing['proposedMissionAnchor'] == 'Fix login redirect callback behavior.', 'ambiguous chooser routing should expose the latest inferred mission'
829
+ assert chooser['title'].startswith('Existing completion workflow found'), 'ambiguous active bare /cook should still open the existing-workflow chooser'
830
+ assert chooser['choices'][0].startswith('Continue current workflow'), 'ambiguous chooser should keep the continue option'
831
+ assert chooser['choices'][1].startswith('Start new workflow from recent discussion'), 'ambiguous chooser should offer the recent-discussion replacement option'
832
+ assert not resume_path.exists(), 'ambiguous active bare /cook should not silently queue a resume prompt before the chooser resolves it'
833
+ assert not proposal_path.exists(), 'ambiguous chooser cancel should not open the final proposal confirmation'
834
+ assert 'Discuss changes in the main chat and rerun /cook.' in output, 'ambiguous chooser cancel should redirect users back to the main chat and rerun /cook'
835
+ assert state['mission_anchor'] == mission, 'ambiguous chooser cancel should keep state.json unchanged'
836
+ assert plan['mission_anchor'] == mission, 'ambiguous chooser cancel should keep plan.json unchanged'
837
+ assert active['mission_anchor'] == mission, 'ambiguous chooser cancel should keep active-slice.json unchanged'
838
+ PY
839
+
840
+ # Active workflow: when recent discussion contains multiple complete replacement missions, bare /cook should
841
+ # surface all candidates and allow the chooser to select a non-primary alternate mission.
842
+ SESSION_ONE_MULTI_CANDIDATE="$TMPDIR/session-one-multi-candidate.jsonl"
843
+ DISCUSSION_ONE_MULTI_CANDIDATE=$'Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic for the current auth flow.\nConstraints:\n- Do not refactor the broader auth flow.\nAcceptance:\n- Add a regression test for returning to the requested page.\nMission: Add logout redirect regression coverage.\nScope:\n- Add coverage for logout redirect behavior.\nConstraints:\n- Do not change login redirect behavior in this pass.\nAcceptance:\n- Land a dedicated logout redirect regression test.'
844
+ MULTI_ROUTING_ONE="$TMPDIR/active-multi-routing.json"
845
+ MULTI_CHOOSER_ONE="$TMPDIR/active-multi-chooser.json"
846
+ MULTI_PROPOSAL_ONE="$TMPDIR/active-multi-proposal.json"
847
+ write_session "$SESSION_ONE_MULTI_CANDIDATE" "$ROOT" "$DISCUSSION_ONE_MULTI_CANDIDATE"
848
+
849
+ PI_COMPLETION_EXISTING_WORKFLOW_MISSION='Fix login redirect callback behavior.' \
850
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
851
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
852
+ PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$MULTI_ROUTING_ONE" \
853
+ PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$MULTI_CHOOSER_ONE" \
854
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$MULTI_PROPOSAL_ONE" \
855
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
856
+ pi --session "$SESSION_ONE_MULTI_CANDIDATE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-multi.out" 2>"$TMPDIR/pi-completion-context-proposal-active-multi.err"
857
+
858
+ python3 - "$MULTI_ROUTING_ONE" "$MULTI_CHOOSER_ONE" "$MULTI_PROPOSAL_ONE" <<'PY'
859
+ import json
860
+ import sys
861
+ from pathlib import Path
862
+
863
+ selected = 'Fix login redirect callback behavior.'
864
+ routing = json.loads(Path(sys.argv[1]).read_text())
865
+ chooser = json.loads(Path(sys.argv[2]).read_text())
866
+ proposal = json.loads(Path(sys.argv[3]).read_text())
867
+ state = json.loads(Path('.agent/state.json').read_text())
868
+ plan = json.loads(Path('.agent/plan.json').read_text())
869
+ active = json.loads(Path('.agent/active-slice.json').read_text())
870
+
871
+ assert routing['action'] == 'unclear', 'multi-candidate replacement discussion should stay ambiguous until the chooser selects one mission'
872
+ assert routing['reason'] == 'ambiguous_discussion', 'multi-candidate replacement discussion should record ambiguous-discussion routing'
873
+ assert routing['alternateMissions'] == ['Fix login redirect callback behavior.'], 'routing snapshot should preserve alternate candidate missions'
874
+ assert chooser['candidateMissions'] == ['Add logout redirect regression coverage.', 'Fix login redirect callback behavior.'], 'chooser snapshot should list the primary and alternate candidate missions'
875
+ assert len(chooser['choices']) == 4, 'chooser should expose continue, primary, alternate, and cancel choices'
876
+ assert 'Scope\n- Add coverage for logout redirect behavior.' in chooser['choices'][1], 'primary chooser option should summarize the candidate scope'
877
+ assert 'Acceptance\n- Add a regression test for returning to the requested page.' in chooser['choices'][2], 'alternate chooser option should summarize the candidate acceptance'
878
+ assert proposal['mission'] == selected, 'selected alternate mission should flow into the final proposal confirmation'
879
+ assert state['mission_anchor'] == selected, 'selected alternate mission should rewrite state.json after approval'
880
+ assert plan['mission_anchor'] == selected, 'selected alternate mission should rewrite plan.json after approval'
881
+ assert active['mission_anchor'] == selected, 'selected alternate mission should rewrite active-slice.json after approval'
882
+ PY
883
+
702
884
  # Active workflow: bare /cook with a placeholder planning mission should still route through the existing
703
885
  # refocus chooser and final Start/Cancel gate before canonical state is rewritten.
704
886
  SESSION_ONE_REFOCUS_NORMALIZED="$TMPDIR/session-one-refocus-normalized.jsonl"
@@ -747,10 +929,93 @@ assert plan['mission_anchor'] == mission, 'active bare /cook refocus should rewr
747
929
  assert active['mission_anchor'] == mission, 'active bare /cook refocus should rewrite active-slice.json only after approval'
748
930
  PY
749
931
 
750
- # Completed workflow: bare /cook should normalize placeholder planning phrasing for the next workflow
751
- # round too, not only for fresh startup.
932
+ # Completed workflow: bare /cook should suppress proposals that simply restate the completed mission
933
+ # without a clear reopen or next-round signal.
752
934
  mark_done
753
935
 
936
+ SESSION_TWO_COMPLETED_SUPPRESS="$TMPDIR/session-two-completed-suppress.jsonl"
937
+ CURRENT_DONE_MISSION="$(python3 - <<'PY'
938
+ import json
939
+ from pathlib import Path
940
+ print(json.loads(Path('.agent/state.json').read_text())['mission_anchor'])
941
+ PY
942
+ )"
943
+ DISCUSSION_TWO_COMPLETED_SUPPRESS="Mission: ${CURRENT_DONE_MISSION}
944
+ Scope:
945
+ - Keep the current completed mission exactly as-is.
946
+ Constraints:
947
+ - Do not start a different workflow from this discussion.
948
+ Acceptance:
949
+ - Keep the finished mission closed and unchanged."
950
+ DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS="$TMPDIR/context-proposal-next-round-completed-suppress.json"
951
+ write_session "$SESSION_TWO_COMPLETED_SUPPRESS" "$ROOT" "$DISCUSSION_TWO_COMPLETED_SUPPRESS"
952
+
953
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
954
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
955
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS" \
956
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
957
+ pi --session "$SESSION_TWO_COMPLETED_SUPPRESS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.err"
958
+
959
+ python3 - "$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.out" "$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.err" "$DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS" "$CURRENT_DONE_MISSION" <<'PY'
960
+ import json
961
+ import sys
962
+ from pathlib import Path
963
+
964
+ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
965
+ snapshot = Path(sys.argv[3])
966
+ expected = sys.argv[4]
967
+ state = json.loads(Path('.agent/state.json').read_text())
968
+
969
+ assert state['mission_anchor'] == expected, 'completed-topic suppression should keep the done workflow mission anchor unchanged'
970
+ assert state['continuation_policy'] == 'done', 'completed-topic suppression should keep the workflow closed'
971
+ assert not snapshot.exists(), 'completed-topic suppression should not emit a proposal snapshot when the latest discussion only repeats finished work'
972
+ assert '/cook failed closed' in output, 'completed-topic suppression should fail closed instead of reopening the finished mission'
973
+ PY
974
+
975
+ # Completed workflow: bare /cook should also suppress proposals that merely restate canonical
976
+ # verification evidence for already verified work.
977
+ python3 - <<'PY'
978
+ import json
979
+ from pathlib import Path
980
+
981
+ state = json.loads(Path('.agent/state.json').read_text())
982
+ state['latest_verified_slice'] = 'verified-logout-redirect'
983
+ Path('.agent/state.json').write_text(json.dumps(state, indent=2) + '\n')
984
+
985
+ evidence = json.loads(Path('.agent/verification-evidence.json').read_text())
986
+ evidence.update({
987
+ 'subject_type': 'selected_slice',
988
+ 'slice_id': 'verified-logout-redirect',
989
+ 'goal': 'Add logout redirect regression coverage.',
990
+ 'summary': 'Verified logout redirect regression coverage already matches the selected slice and current HEAD.',
991
+ 'outcome': 'pass',
992
+ })
993
+ Path('.agent/verification-evidence.json').write_text(json.dumps(evidence, indent=2) + '\n')
994
+ PY
995
+
996
+ SESSION_TWO_VERIFIED_SUPPRESS="$TMPDIR/session-two-verified-suppress.jsonl"
997
+ DISCUSSION_TWO_VERIFIED_SUPPRESS=$'Mission: Add logout redirect regression coverage.\nScope:\n- Add coverage for logout redirect behavior.\nConstraints:\n- Do not change the verified logout redirect work.\nAcceptance:\n- Keep the verified logout redirect regression coverage unchanged.'
998
+ DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS="$TMPDIR/context-proposal-next-round-verified-suppress.json"
999
+ write_session "$SESSION_TWO_VERIFIED_SUPPRESS" "$ROOT" "$DISCUSSION_TWO_VERIFIED_SUPPRESS"
1000
+
1001
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
1002
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
1003
+ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS" \
1004
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
1005
+ pi --session "$SESSION_TWO_VERIFIED_SUPPRESS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.err"
1006
+
1007
+ python3 - "$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.out" "$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.err" "$DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS" <<'PY'
1008
+ import sys
1009
+ from pathlib import Path
1010
+
1011
+ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
1012
+ snapshot = Path(sys.argv[3])
1013
+ assert not snapshot.exists(), 'verification-evidence overlap suppression should not emit a proposal snapshot for already verified work'
1014
+ assert '/cook failed closed' in output, 'verification-evidence overlap suppression should fail closed when the latest discussion only repeats verified work'
1015
+ PY
1016
+
1017
+ # Completed workflow: bare /cook should normalize placeholder planning phrasing for the next workflow
1018
+ # round too, not only for fresh startup.
754
1019
  SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
755
1020
  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
1021
  DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
@@ -836,8 +1101,8 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
836
1101
  assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
837
1102
  PY
838
1103
 
839
- # Active workflow: inline /cook hint should flow through routing plus final proposal confirmation
840
- # and still leave canonical state unchanged when the final proposal is cancelled.
1104
+ # Active workflow: /cook <text> should be rejected before routing or proposal confirmation
1105
+ # and still leave canonical state unchanged.
841
1106
  ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
842
1107
  ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
843
1108
  ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
@@ -886,15 +1151,16 @@ tracked = [
886
1151
  Path('.agent/verification-evidence.json'),
887
1152
  ]
888
1153
 
889
- assert routing.exists(), 'active inline /cook hint should run active-workflow routing'
890
- assert proposal.exists(), 'active inline /cook hint should open final proposal confirmation after refocus is chosen'
891
- assert chooser.exists(), 'active inline /cook hint should open the existing-workflow chooser'
1154
+ assert '/cook only supports the bare /cook entrypoint.' in output, 'active /cook <text> rejection should explain the bare-only contract'
1155
+ assert not routing.exists(), 'active /cook <text> rejection should not run active-workflow routing'
1156
+ assert not proposal.exists(), 'active /cook <text> rejection should not open proposal confirmation'
1157
+ assert not chooser.exists(), 'active /cook <text> rejection should not open the existing-workflow chooser'
892
1158
  after = {path.name: path.read_text() for path in tracked}
893
- assert before == after, 'active inline /cook hint should leave canonical files unchanged when confirmation is cancelled'
1159
+ assert before == after, 'active /cook <text> rejection should leave canonical files unchanged'
894
1160
  PY
895
1161
 
896
- # Completed workflow: inline /cook hint should also drive the next-round proposal and leave
897
- # canonical state unchanged when the proposal is cancelled.
1162
+ # Completed workflow: /cook <text> should be rejected before any next-round proposal is derived
1163
+ # and still leave canonical state unchanged.
898
1164
  mark_done
899
1165
 
900
1166
  DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
@@ -944,13 +1210,14 @@ tracked = [
944
1210
  Path('.agent/verification-evidence.json'),
945
1211
  ]
946
1212
  state_before = json.loads(before['state.json'])
947
- assert state_before['current_phase'] == 'done', 'done inline /cook rejection should start from a completed workflow'
948
- assert state_before['project_done'] is True, 'done inline /cook rejection should start from project_done=true'
949
- assert not routing.exists(), 'done inline /cook args should not run active/done workflow routing'
950
- assert proposal.exists(), 'done inline /cook hint should open next-round proposal confirmation'
951
- assert not chooser.exists(), 'done inline /cook args should not open the chooser flow'
1213
+ assert state_before['current_phase'] == 'done', 'done /cook <text> rejection should start from a completed workflow'
1214
+ assert state_before['project_done'] is True, 'done /cook <text> rejection should start from project_done=true'
1215
+ assert '/cook only supports the bare /cook entrypoint.' in output, 'done /cook <text> rejection should explain the bare-only contract'
1216
+ assert not routing.exists(), 'done /cook <text> rejection should not run active/done workflow routing'
1217
+ assert not proposal.exists(), 'done /cook <text> rejection should not open next-round proposal confirmation'
1218
+ assert not chooser.exists(), 'done /cook <text> rejection should not open the chooser flow'
952
1219
  after = {path.name: path.read_text() for path in tracked}
953
- assert before == after, 'done inline /cook hint cancel should leave canonical files unchanged'
1220
+ assert before == after, 'done /cook <text> rejection should leave canonical files unchanged'
954
1221
  PY
955
1222
 
956
1223
  # Completed workflow again: /cook with no goal should be able to use model-assisted
@@ -1086,4 +1353,9 @@ assert 'Discuss changes in the main chat and rerun /cook.' in output, 'cancel co
1086
1353
  assert not Path('.agent').exists(), 'cancel action should not write canonical workflow state'
1087
1354
  PY
1088
1355
 
1356
+ grep -q 'export async function deriveCookContextProposalFromRecentDiscussion' "$PKG_ROOT/extensions/completion/proposal.ts"
1357
+ grep -q 'export function parseContextProposalAnalystOutput' "$PKG_ROOT/extensions/completion/proposal.ts"
1358
+ grep -q 'export function buildContextProposalConfirmationLayout' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
1359
+ grep -q 'export function buildEvaluationRoleContextLines' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
1360
+
1089
1361
  echo "context proposal test passed: $ROOT"
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "$ROOT"
6
+
7
+ node <<'NODE'
8
+ const fs = require('node:fs');
9
+
10
+ const read = (file) => fs.readFileSync(file, 'utf8');
11
+ const assertIncludes = (file, snippet) => {
12
+ const text = read(file);
13
+ if (!text.includes(snippet)) {
14
+ throw new Error(`${file} is missing expected legacy-cleanup ownership text: ${snippet}`);
15
+ }
16
+ };
17
+ const assertNotIncludes = (file, snippet) => {
18
+ const text = read(file);
19
+ if (text.includes(snippet)) {
20
+ throw new Error(`${file} still contains stale monolith ownership text: ${snippet}`);
21
+ }
22
+ };
23
+
24
+ assertIncludes('extensions/completion/state-store.ts', 'export async function scaffoldCompletionFiles(');
25
+ assertIncludes('extensions/completion/state-store.ts', 'export function buildAgentReadme(');
26
+ assertIncludes('extensions/completion/state-store.ts', 'export function buildVerifyStopScript(');
27
+ assertIncludes('extensions/completion/state-store.ts', 'export function buildVerifyControlPlaneScript(');
28
+ assertIncludes('extensions/completion/state-store.ts', 'export function currentTaskType(');
29
+ assertIncludes('extensions/completion/state-store.ts', 'export function currentEvaluationProfile(');
30
+
31
+ assertIncludes('extensions/completion/status-surface.ts', 'export function nowMs(');
32
+ assertIncludes('extensions/completion/status-surface.ts', 'export function formatElapsed(');
33
+ assertIncludes('extensions/completion/status-surface.ts', 'export function createLiveRoleActivity(');
34
+ assertIncludes('extensions/completion/status-surface.ts', 'export function cloneLiveRoleActivity(');
35
+ assertIncludes('extensions/completion/status-surface.ts', 'export function applyLiveRoleEvent(');
36
+ assertIncludes('extensions/completion/status-surface.ts', 'export function pushRecentActivity(');
37
+ assertIncludes('extensions/completion/status-surface.ts', 'export function truncateInline(');
38
+
39
+ assertIncludes('extensions/completion/proposal.ts', 'export function normalizeMissionAnchorText(');
40
+ assertIncludes('extensions/completion/proposal.ts', 'export function isWeakMissionAnchor(');
41
+ assertIncludes('extensions/completion/proposal.ts', 'export function deriveMissionAnchor(');
42
+ assertIncludes('extensions/completion/proposal.ts', 'export function assessMissionAnchor(');
43
+ assertIncludes('extensions/completion/proposal.ts', 'export function stripCodeBlocks(');
44
+ assertIncludes('extensions/completion/proposal.ts', 'export function missionAnchorsStrictlyEquivalent(');
45
+ assertIncludes('extensions/completion/proposal.ts', 'export function missionAnchorsLikelyEquivalent(');
46
+ assertIncludes('extensions/completion/proposal.ts', 'export function collectRecentDiscussionEntries(');
47
+ assertIncludes('extensions/completion/proposal.ts', 'export function serializeRecentDiscussionEntries(');
48
+ assertIncludes('extensions/completion/proposal.ts', 'export function extractJsonObjectFromText(');
49
+
50
+ assertIncludes('extensions/completion/role-runner.ts', 'export async function analyzeContextProposalWithAgent(');
51
+ assertIncludes('extensions/completion/role-runner.ts', 'class StartupAnalystOverlay extends Container');
52
+ assertIncludes('extensions/completion/role-runner.ts', 'async function runContextProposalAnalystSubprocess(');
53
+
54
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'export function buildSystemReminder(');
55
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'export function buildResumeCapsule(');
56
+
57
+ assertIncludes('extensions/completion/index.ts', 'scaffoldCompletionFiles as scaffoldCompletionFilesOnDisk');
58
+ assertIncludes('extensions/completion/index.ts', 'return await scaffoldCompletionFilesOnDisk(root, missionAnchor, {');
59
+ assertIncludes('extensions/completion/index.ts', 'applyLiveRoleEvent,');
60
+ assertIncludes('extensions/completion/index.ts', 'cloneLiveRoleActivity,');
61
+ assertIncludes('extensions/completion/index.ts', 'createLiveRoleActivity,');
62
+ assertIncludes('extensions/completion/index.ts', 'formatElapsed,');
63
+ assertIncludes('extensions/completion/index.ts', 'nowMs,');
64
+
65
+ assertNotIncludes('extensions/completion/index.ts', 'async function detectVerifierCommand(');
66
+ assertNotIncludes('extensions/completion/index.ts', 'function buildAgentReadme(');
67
+ assertNotIncludes('extensions/completion/index.ts', 'function buildMission(');
68
+ assertNotIncludes('extensions/completion/index.ts', 'function buildVerifyStopScript(');
69
+ assertNotIncludes('extensions/completion/index.ts', 'function buildVerifyControlPlaneScript(');
70
+ assertNotIncludes('extensions/completion/index.ts', 'async function ensureGitignore(');
71
+ assertNotIncludes('extensions/completion/index.ts', 'function currentTaskType(');
72
+ assertNotIncludes('extensions/completion/index.ts', 'function currentEvaluationProfile(');
73
+ assertNotIncludes('extensions/completion/index.ts', 'function formatCount(');
74
+ assertNotIncludes('extensions/completion/index.ts', 'function completionRemainingSummary(');
75
+ assertNotIncludes('extensions/completion/index.ts', 'function envNumber(');
76
+ assertNotIncludes('extensions/completion/index.ts', 'function nowMs(');
77
+ assertNotIncludes('extensions/completion/index.ts', 'type LiveActivitySignal = {');
78
+ assertNotIncludes('extensions/completion/index.ts', 'function cloneLiveRoleActivity(');
79
+ assertNotIncludes('extensions/completion/index.ts', 'function createLiveRoleActivity(');
80
+ assertNotIncludes('extensions/completion/index.ts', 'type RoleMessage = {');
81
+ assertNotIncludes('extensions/completion/index.ts', 'function applyLiveRoleEvent(');
82
+ assertNotIncludes('extensions/completion/index.ts', 'function maybeInjectTestLiveRoleActivity(');
83
+ assertNotIncludes('extensions/completion/index.ts', 'function maybeReplayTestLiveRoleEvents(');
84
+ assertNotIncludes('extensions/completion/index.ts', 'function formatElapsed(');
85
+ assertNotIncludes('extensions/completion/index.ts', 'function truncateInline(');
86
+ assertNotIncludes('extensions/completion/index.ts', 'function formatToolActivity(');
87
+ assertNotIncludes('extensions/completion/index.ts', 'function pushRecentActivity(');
88
+ assertNotIncludes('extensions/completion/index.ts', 'function parseStructuredProgress(');
89
+ assertNotIncludes('extensions/completion/index.ts', 'function lastAssistantText(');
90
+ assertNotIncludes('extensions/completion/index.ts', 'function normalizeMissionAnchorText(');
91
+ assertNotIncludes('extensions/completion/index.ts', 'function isWeakMissionAnchor(');
92
+ assertNotIncludes('extensions/completion/index.ts', 'function assessMissionAnchor(');
93
+ assertNotIncludes('extensions/completion/index.ts', 'function stripCodeBlocks(');
94
+ assertNotIncludes('extensions/completion/index.ts', 'function missionAnchorsStrictlyEquivalent(');
95
+ assertNotIncludes('extensions/completion/index.ts', 'function missionAnchorsLikelyEquivalent(');
96
+ assertNotIncludes('extensions/completion/index.ts', 'function collectRecentDiscussionEntries(');
97
+ assertNotIncludes('extensions/completion/index.ts', 'function serializeRecentDiscussionEntries(');
98
+ assertNotIncludes('extensions/completion/index.ts', 'function extractJsonObjectFromText(');
99
+ assertNotIncludes('extensions/completion/index.ts', 'function contextProposalAnalystModelArg(');
100
+ assertNotIncludes('extensions/completion/index.ts', 'async function runContextProposalAnalystSubprocess(');
101
+ assertNotIncludes('extensions/completion/index.ts', 'async function analyzeContextProposalWithAgent(');
102
+ assertNotIncludes('extensions/completion/index.ts', 'function deriveMissionAnchor(');
103
+ assertNotIncludes('extensions/completion/index.ts', 'function buildSystemReminder(');
104
+ assertNotIncludes('extensions/completion/index.ts', 'function buildResumeCapsule(');
105
+ NODE
106
+
107
+ echo "legacy cleanup test passed: $ROOT"
@@ -237,4 +237,43 @@ PI_COMPLETION_TEST_ROLE_EVENT_STREAM_JSON="$LIVE_ROLE_EVENT_STREAM_JSON" \
237
237
  pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-stalled.out" 2>"$TMPDIR/pi-completion-status-stalled.err"
238
238
  assert_status_json "$STALLED_JSON" stalled
239
239
 
240
+ cd "$PKG_ROOT"
241
+
242
+ node <<'NODE'
243
+ const fs = require('node:fs');
244
+
245
+ const read = (file) => fs.readFileSync(file, 'utf8');
246
+ const assertIncludes = (file, snippet) => {
247
+ const text = read(file);
248
+ if (!text.includes(snippet)) {
249
+ throw new Error(`${file} is missing required status/policy extraction text: ${snippet}`);
250
+ }
251
+ };
252
+ const assertNotIncludes = (file, snippet) => {
253
+ const text = read(file);
254
+ if (text.includes(snippet)) {
255
+ throw new Error(`${file} still contains stale inline status/policy text: ${snippet}`);
256
+ }
257
+ };
258
+
259
+ assertIncludes('extensions/completion/status-surface.ts', 'export async function refreshCompletionStatus(');
260
+ assertIncludes('extensions/completion/status-surface.ts', 'export function buildCompletionStatusSurface(');
261
+ assertIncludes('extensions/completion/status-surface.ts', 'export function buildInlineRunningLines(');
262
+ assertIncludes('extensions/completion/policy-guards.ts', 'export function toolCallBlockReason(');
263
+ assertIncludes('extensions/completion/state-store.ts', 'export function completionRootKey(');
264
+ assertIncludes('extensions/completion/index.ts', 'import { toolCallBlockReason } from "./policy-guards";');
265
+ assertIncludes('extensions/completion/index.ts', 'buildInlineRunningLines');
266
+ assertIncludes('extensions/completion/index.ts', 'formatInlineRunningText');
267
+ assertIncludes('extensions/completion/index.ts', 'refreshCompletionStatus');
268
+ assertIncludes('extensions/completion/index.ts', 'const reason = toolCallBlockReason({');
269
+ assertIncludes('extensions/completion/index.ts', 'await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });');
270
+ assertNotIncludes('extensions/completion/index.ts', 'function buildCompletionStatusSurface(');
271
+ assertNotIncludes('extensions/completion/index.ts', 'async function writeCompletionStatusProbe(');
272
+ assertNotIncludes('extensions/completion/index.ts', 'function isAllowedControlPlanePath(');
273
+ assertNotIncludes('extensions/completion/index.ts', 'function isMutatingBash(');
274
+ assertNotIncludes('extensions/completion/index.ts', 'async function refreshStatus(');
275
+ assertNotIncludes('extensions/completion/index.ts', 'function buildInlineRunningLines(');
276
+ assertNotIncludes('extensions/completion/index.ts', 'function formatInlineRunningText(');
277
+ NODE
278
+
240
279
  echo "observability status test passed: $TMPDIR"
@@ -131,13 +131,13 @@ tracked = [
131
131
  Path('.agent/verification-evidence.json'),
132
132
  ]
133
133
  current_state = json.loads(before['state.json'])
134
- assert current_state['mission_anchor'] == initial_mission, 'active inline /cook args should start from the current mission anchor'
135
- assert 'Cancelled existing workflow confirmation.' in output, 'active inline /cook hint cancel should surface chooser cancellation'
136
- assert routing.exists(), 'active inline /cook hint should run active-workflow routing'
137
- assert not proposal.exists(), 'active inline /cook hint cancel at chooser should not open final proposal confirmation'
138
- assert chooser.exists(), 'active inline /cook hint should open the existing-workflow chooser'
134
+ assert current_state['mission_anchor'] == initial_mission, 'active /cook <text> rejection should start from the current mission anchor'
135
+ assert '/cook only supports the bare /cook entrypoint.' in output, 'active /cook <text> rejection should explain the bare-only contract'
136
+ assert not routing.exists(), 'active /cook <text> rejection should not run active-workflow routing'
137
+ assert not proposal.exists(), 'active /cook <text> rejection should not open final proposal confirmation'
138
+ assert not chooser.exists(), 'active /cook <text> rejection should not open the existing-workflow chooser'
139
139
  after = {path.name: path.read_text() for path in tracked}
140
- assert before == after, 'active inline /cook hint cancel should leave canonical files unchanged'
140
+ assert before == after, 'active /cook <text> rejection should leave canonical files unchanged'
141
141
  PY
142
142
 
143
143
  SESSION_INITIAL_REFOCUS="$TMPDIR/session-initial-bare-refocus.jsonl"