@linimin/pi-letscook 0.1.44 → 0.1.46

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');
@@ -3,7 +3,7 @@ set -euo pipefail
3
3
 
4
4
  PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
  pi() {
6
- command pi --no-extensions "$@"
6
+ env -u PI_COMPLETION_ROLE command pi --no-extensions "$@"
7
7
  }
8
8
  TMPDIR="$(mktemp -d)"
9
9
  CURRENT_EVIDENCE_BACKUP=""
@@ -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');
@@ -377,6 +377,7 @@ PY
377
377
  bash .agent/verify_completion_control_plane.sh >/dev/null
378
378
  bash .agent/verify_completion_stop.sh >/dev/null
379
379
 
380
+ rm -f "$SYSTEM_REMINDER"
380
381
  PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH="$SYSTEM_REMINDER" \
381
382
  pi -e "$PKG_ROOT" -p "Summarize the completion reminder briefly." \
382
383
  >"$TMPDIR/pi-canonical-evidence-reminder.out" 2>"$TMPDIR/pi-canonical-evidence-reminder.err"
@@ -385,11 +386,8 @@ python3 - "$SYSTEM_REMINDER" <<'PY'
385
386
  import sys
386
387
  from pathlib import Path
387
388
 
388
- text = Path(sys.argv[1]).read_text()
389
- assert 'Canonical truth lives in .agent/state.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json.' in text, text
390
- assert 'Verification evidence artifact: .agent/verification-evidence.json (present)' in text, text
391
- assert 'Verification evidence summary:' in text, text
392
- assert 'selected_slice' in text, text
389
+ reminder = Path(sys.argv[1])
390
+ assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from selected-slice canonical state'
393
391
  PY
394
392
 
395
393
  python3 - <<'PY'
@@ -435,13 +433,8 @@ python3 - "$SYSTEM_REMINDER" <<'PY'
435
433
  import sys
436
434
  from pathlib import Path
437
435
 
438
- text = Path(sys.argv[1]).read_text()
439
- assert 'A previous completion workflow exists for this repo, but it is closed.' in text, text
440
- assert 'Treat the previous completion workflow as historical context only.' in text, text
441
- assert 'Do not resume, reground, refocus, reopen, or otherwise restart completion workflow from this context unless the user explicitly runs /cook.' in text, text
442
- assert 'For ordinary user requests, respond normally and ignore prior completion-protocol instructions that were only relevant to the finished workflow.' in text, text
443
- assert 'Only /cook may reactivate workflow routing for the next round.' in text, text
444
- assert 'Completion workflow detected.' not in text, text
436
+ reminder = Path(sys.argv[1])
437
+ assert not reminder.exists(), 'ordinary non-/cook turn should not inject closed-workflow boundary routing before /cook reactivation'
445
438
  PY
446
439
 
447
440
  echo "canonical evidence artifact test passed: $TMPDIR"
@@ -836,8 +836,8 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
836
836
  assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
837
837
  PY
838
838
 
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.
839
+ # Active workflow: /cook <text> should be rejected before routing or proposal confirmation
840
+ # and still leave canonical state unchanged.
841
841
  ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
842
842
  ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
843
843
  ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
@@ -886,15 +886,16 @@ tracked = [
886
886
  Path('.agent/verification-evidence.json'),
887
887
  ]
888
888
 
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'
889
+ assert '/cook only supports the bare /cook entrypoint.' in output, 'active /cook <text> rejection should explain the bare-only contract'
890
+ assert not routing.exists(), 'active /cook <text> rejection should not run active-workflow routing'
891
+ assert not proposal.exists(), 'active /cook <text> rejection should not open proposal confirmation'
892
+ assert not chooser.exists(), 'active /cook <text> rejection should not open the existing-workflow chooser'
892
893
  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'
894
+ assert before == after, 'active /cook <text> rejection should leave canonical files unchanged'
894
895
  PY
895
896
 
896
- # Completed workflow: inline /cook hint should also drive the next-round proposal and leave
897
- # canonical state unchanged when the proposal is cancelled.
897
+ # Completed workflow: /cook <text> should be rejected before any next-round proposal is derived
898
+ # and still leave canonical state unchanged.
898
899
  mark_done
899
900
 
900
901
  DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
@@ -944,13 +945,14 @@ tracked = [
944
945
  Path('.agent/verification-evidence.json'),
945
946
  ]
946
947
  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'
948
+ assert state_before['current_phase'] == 'done', 'done /cook <text> rejection should start from a completed workflow'
949
+ assert state_before['project_done'] is True, 'done /cook <text> rejection should start from project_done=true'
950
+ assert '/cook only supports the bare /cook entrypoint.' in output, 'done /cook <text> rejection should explain the bare-only contract'
951
+ assert not routing.exists(), 'done /cook <text> rejection should not run active/done workflow routing'
952
+ assert not proposal.exists(), 'done /cook <text> rejection should not open next-round proposal confirmation'
953
+ assert not chooser.exists(), 'done /cook <text> rejection should not open the chooser flow'
952
954
  after = {path.name: path.read_text() for path in tracked}
953
- assert before == after, 'done inline /cook hint cancel should leave canonical files unchanged'
955
+ assert before == after, 'done /cook <text> rejection should leave canonical files unchanged'
954
956
  PY
955
957
 
956
958
  # Completed workflow again: /cook with no goal should be able to use model-assisted
@@ -1086,4 +1088,9 @@ assert 'Discuss changes in the main chat and rerun /cook.' in output, 'cancel co
1086
1088
  assert not Path('.agent').exists(), 'cancel action should not write canonical workflow state'
1087
1089
  PY
1088
1090
 
1091
+ grep -q 'export async function deriveCookContextProposalFromRecentDiscussion' "$PKG_ROOT/extensions/completion/proposal.ts"
1092
+ grep -q 'export function parseContextProposalAnalystOutput' "$PKG_ROOT/extensions/completion/proposal.ts"
1093
+ grep -q 'export function buildContextProposalConfirmationLayout' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
1094
+ grep -q 'export function buildEvaluationRoleContextLines' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
1095
+
1089
1096
  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"
@@ -4,7 +4,7 @@ set -euo pipefail
4
4
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
  cd "$ROOT"
6
6
 
7
- echo "[release-check] running control-plane validation, bare /cook parity, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, evaluator calibration, and rubric contract coverage"
7
+ echo "[release-check] running control-plane validation, bare /cook parity, role-runner extraction, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, legacy cleanup, evaluator calibration, and rubric contract coverage"
8
8
  bash .agent/verify_completion_control_plane.sh
9
9
 
10
10
  echo "[release-check] verifying public /cook parity"
@@ -14,9 +14,9 @@ from pathlib import Path
14
14
 
15
15
  checks = {
16
16
  "README.md": [
17
- "Bare `/cook` and `/cook <hint>` are the supported workflow entrypoints.",
18
- "If the recent context is fuzzy, `/cook <hint>` can provide a short high-priority user hint for the proposal analyst",
19
- "clarify the mission in the main chat before rerunning `/cook`",
17
+ "Bare `/cook` is the only supported workflow entrypoint.",
18
+ "`/cook <text>` is no longer supported; put mission text in the main chat, then rerun bare `/cook`.",
19
+ "clarify the mission in the main chat before rerunning bare `/cook`",
20
20
  "Matching or unclear discussion resumes from canonical `.agent/**` state.",
21
21
  "approval-only Start/Cancel gate",
22
22
  "Start new workflow from recent discussion",
@@ -24,27 +24,26 @@ checks = {
24
24
  "README/CHANGELOG updates still count as concrete repo changes",
25
25
  "assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts do not",
26
26
  "Assistant/summary artifacts or plan/spec/design-doc/proposal-only context do not refocus the workflow.",
27
+ "`/cook <text>` is rejected without running proposal routing or rewriting workflow state.",
27
28
  ],
28
29
  "CHANGELOG.md": [
29
- "bare `/cook` as the only supported workflow entrypoint",
30
- "clarify the mission before rerunning bare `/cook`",
31
- "packaged parity now fails closed on the bare-only contract",
32
- "that old inline-argument path is no longer supported now that bare `/cook` is the only public entrypoint",
33
- "README/CHANGELOG/docs-only deliverables as concrete repo-change missions",
34
- "ignore assistant/branch/compaction summary artifacts for startup/refocus readiness",
35
- "plan/spec/design-doc/proposal-only context without rewriting canonical state",
30
+ "removed inline `/cook <text>` argument support so bare `/cook` is now the only supported workflow entrypoint",
31
+ "packaged release parity fail closed when command arguments are passed instead of discussion driving proposal derivation",
32
+ "historically allowed `/cook <hint>` as an analyst-only high-priority prompt",
33
+ "that inline-argument path has since been removed so bare `/cook` is now the only supported entrypoint",
36
34
  ],
37
35
  "extensions/completion/index.ts": [
38
- 'description: "Discussion-driven /cook workflow: start, continue, refocus, or start the next round"',
39
- "If an inline /cook hint is present, treat it as a high-priority user hint that may focus the mission, but do not ignore conflicting discussion or skip missing details.",
40
- "/cook failed closed because recent discussion and any optional inline /cook hint did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook.",
36
+ 'description: "Bare /cook workflow: start, continue, refocus, or start the next round"',
37
+ 'const COOK_BARE_ONLY_GUIDANCE =',
38
+ '"/cook only supports the bare /cook entrypoint. Move mission text into the main chat, then rerun /cook."',
39
+ '"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook."',
41
40
  ],
42
41
  }
43
42
 
44
43
  forbidden = {
45
- "README.md": ["compatibility" + " shim"],
44
+ "README.md": ["compatibility" + " shim", "/cook <hint>", "optional inline /cook hint"],
46
45
  "CHANGELOG.md": ["compatibility" + " shim"],
47
- "extensions/completion/index.ts": ["temporary" + " compatibility" + " shim, pass /cook"],
46
+ "extensions/completion/index.ts": ["temporary" + " compatibility" + " shim, pass /cook", "inline /cook hint", "optional inline /cook hint"],
48
47
  }
49
48
 
50
49
  for path, needles in checks.items():
@@ -63,9 +62,11 @@ PY
63
62
  npm run smoke-test
64
63
  npm run refocus-test
65
64
  npm run context-proposal-test
65
+ bash ./scripts/role-runner-contract-test.sh
66
66
  bash ./scripts/canonical-evidence-artifact-test.sh
67
67
  bash ./scripts/active-slice-contract-test.sh
68
68
  npm run observability-status-test
69
+ bash ./scripts/legacy-cleanup-test.sh
69
70
  npm run evaluator-calibration-test
70
71
  npm run rubric-contract-test
71
72
  npm pack --dry-run >/dev/null
@@ -0,0 +1,44 @@
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 required role-runner extraction 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 inline role-runner text: ${snippet}`);
21
+ }
22
+ };
23
+
24
+ assertIncludes('extensions/completion/role-runner.ts', 'import { completionRootKey, findCompletionRoot, findRepoRoot, loadCompletionDataForReminder } from "./state-store";');
25
+ assertIncludes('extensions/completion/role-runner.ts', 'import { parseReportFields, transcribeRoleOutput, type TranscriptionResult } from "./transcription";');
26
+ assertIncludes('extensions/completion/role-runner.ts', 'const agent = await loadAgentDefinition(params.root, params.role);');
27
+ assertIncludes('extensions/completion/role-runner.ts', 'await loadCompletionDataForReminder(params.root);');
28
+ assertIncludes('extensions/completion/role-runner.ts', 'const systemPromptTemp = await writeTempFile(params.root, "pi-completion-role-", agent.systemPrompt);');
29
+ assertIncludes('extensions/completion/role-runner.ts', 'const reportFields = parseReportFields(output);');
30
+ assertIncludes('extensions/completion/role-runner.ts', 'const transcription = exitCode === 0 ? await transcribeRoleOutput(params.role, params.root, output, reportFields) : undefined;');
31
+ assertIncludes('extensions/completion/role-runner.ts', 'env: { ...process.env, PI_COMPLETION_ROLE: params.role },');
32
+ assertIncludes('extensions/completion/role-runner.ts', 'async function runContextProposalAnalystSubprocess(');
33
+ assertIncludes('extensions/completion/role-runner.ts', 'export async function analyzeContextProposalWithAgent(');
34
+ assertIncludes('extensions/completion/index.ts', 'import { analyzeContextProposalWithAgent, runCompletionRole } from "./role-runner";');
35
+ assertIncludes('extensions/completion/index.ts', 'const result = await runCompletionRole({');
36
+ assertIncludes('extensions/completion/index.ts', 'await analyzeContextProposalWithAgent({');
37
+ assertNotIncludes('extensions/completion/index.ts', 'const systemPromptTemp = await writeTempFile(runCwd, "pi-cook-proposal-analyst-", CONTEXT_PROPOSAL_ANALYST_SYSTEM_PROMPT);');
38
+ assertNotIncludes('extensions/completion/index.ts', 'const invocation = getPiInvocation(args);');
39
+ assertNotIncludes('extensions/completion/index.ts', 'async function loadAgentDefinition(');
40
+ assertNotIncludes('extensions/completion/index.ts', 'async function writeTempFile(');
41
+ assertNotIncludes('extensions/completion/index.ts', 'function getPiInvocation(');
42
+ NODE
43
+
44
+ echo "role-runner contract test passed"
@@ -77,13 +77,15 @@ assertIncludes('CHANGELOG.md', 'added canonical `task_type: completion-workflow`
77
77
  assertIncludes('CHANGELOG.md', 'threaded canonical `evaluation_profile` plus the active-slice implementation contract into reviewer/auditor/stop-judge reminder and dispatch surfaces');
78
78
  assertIncludes('CHANGELOG.md', 'made reviewer/auditor/stop-judge transcription fail closed on malformed rubric-bearing outputs while still accepting valid reports');
79
79
  assertIncludes('extensions/completion/index.ts', 'Canonical routing profile:\\n- task_type: ${taskType}\\n- evaluation_profile: ${evaluationProfile}');
80
- assertIncludes('extensions/completion/index.ts', '`Task type: ${currentTaskType(snapshot) ?? "(missing)"}`');
81
- assertIncludes('extensions/completion/index.ts', '`Evaluation profile: ${currentEvaluationProfile(snapshot) ?? "(missing)"}`');
82
- assertIncludes('extensions/completion/index.ts', '`task_type: ${currentTaskType(snapshot) ?? "(missing)"}`');
83
- assertIncludes('extensions/completion/index.ts', '`evaluation_profile: ${currentEvaluationProfile(snapshot) ?? "(missing)"}`');
84
- assertIncludes('extensions/completion/index.ts', 'Canonical evaluation handoff for ${role}:');
80
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`Task type: ${args.taskType ?? "(missing)"}`');
81
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`Evaluation profile: ${args.evaluationProfile ?? "(missing)"}`');
82
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`- task_type: ${deps.currentTaskType(snapshot) ?? "(missing)"}`');
83
+ assertIncludes('extensions/completion/prompt-surfaces.ts', '`- evaluation_profile: ${deps.currentEvaluationProfile(snapshot) ?? "(missing)"}`');
84
+ assertIncludes('extensions/completion/prompt-surfaces.ts', 'Canonical evaluation handoff for ${role}:');
85
85
  assertIncludes('extensions/completion/index.ts', 'buildEvaluationRoleReminderText(snapshot, nextRole)');
86
- assertIncludes('extensions/completion/index.ts', 'roleReporting.transcribeCanonicalRoleReport');
86
+ assertIncludes('extensions/completion/index.ts', 'import { parseFirstNumber, parseYesNo } from "./transcription";');
87
+ assertIncludes('extensions/completion/role-runner.ts', 'import { parseReportFields, transcribeRoleOutput, type TranscriptionResult } from "./transcription";');
88
+ assertIncludes('extensions/completion/transcription.ts', 'roleReporting.transcribeCanonicalRoleReport');
87
89
  assertIncludes('extensions/completion/role-reporting.js', 'Missing Rubric heading for ${role}.');
88
90
  assertIncludes('extensions/completion/role-reporting.js', 'Reviewer output cannot mark \'Acceptable as-is: yes\' when any rubric line is fail.');
89
91
  assertIncludes('extensions/completion/role-reporting.js', 'Auditor output must answer \'Tracked and unignored worktree is clean\' with yes or no.');
@@ -3,7 +3,7 @@ set -euo pipefail
3
3
 
4
4
  PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
  pi() {
6
- command pi --no-extensions "$@"
6
+ env -u PI_COMPLETION_ROLE command pi --no-extensions "$@"
7
7
  }
8
8
  TMPDIR="$(mktemp -d)"
9
9
  trap 'rm -rf "$TMPDIR"' EXIT
@@ -50,8 +50,10 @@ PY
50
50
  ROOT="$TMPDIR/repo"
51
51
  KICKOFF_PROMPT="$TMPDIR/kickoff-prompt.txt"
52
52
  RESUME_PROMPT="$TMPDIR/resume-prompt.txt"
53
+ ORDINARY_SYSTEM_REMINDER="$TMPDIR/ordinary-system-reminder.txt"
53
54
  UNCLEAR_ROUTING_SNAPSHOT="$TMPDIR/active-unclear-routing.json"
54
55
  UNCLEAR_CHOOSER_SNAPSHOT="$TMPDIR/unexpected-existing-workflow-chooser.json"
56
+ ORDINARY_AUTO_RESUME_PROMPT="$TMPDIR/ordinary-auto-resume-prompt.txt"
55
57
  AUTO_RESUME_PROMPT="$TMPDIR/auto-resume-prompt.txt"
56
58
  INLINE_REJECTION_ROUTING_SNAPSHOT="$TMPDIR/inline-arg-routing.json"
57
59
  INLINE_REJECTION_PROPOSAL_SNAPSHOT="$TMPDIR/inline-arg-proposal.json"
@@ -82,11 +84,11 @@ routing = Path(sys.argv[3])
82
84
  proposal = Path(sys.argv[4])
83
85
  chooser = Path(sys.argv[5])
84
86
 
85
- assert not Path('.agent').exists(), 'startup inline /cook hint cancel should leave canonical state untouched'
86
- assert not routing.exists(), 'startup inline /cook hint should not open active-workflow routing'
87
- assert proposal.exists(), 'startup inline /cook hint should still prepare a proposal for confirmation'
88
- assert not chooser.exists(), 'startup inline /cook hint should not open the chooser flow during startup'
89
- assert 'Cancelled recent-discussion workflow proposal.' in output, 'startup inline /cook hint cancel should surface the proposal cancellation message'
87
+ assert not Path('.agent').exists(), 'startup /cook <text> rejection should leave canonical state untouched'
88
+ assert not routing.exists(), 'startup /cook <text> rejection should not open active-workflow routing'
89
+ assert not proposal.exists(), 'startup /cook <text> rejection should not prepare a proposal snapshot'
90
+ assert not chooser.exists(), 'startup /cook <text> rejection should not open the chooser flow'
91
+ assert '/cook only supports the bare /cook entrypoint.' in output, 'startup /cook <text> rejection should explain the bare-only contract'
90
92
  PY
91
93
 
92
94
  write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
@@ -139,6 +141,27 @@ assert f'- task_type: {expected_task_type}' in kickoff, 'kickoff prompt missing
139
141
  assert f'- evaluation_profile: {expected_eval_profile}' in kickoff, 'kickoff prompt missing canonical evaluation_profile'
140
142
  PY
141
143
 
144
+ rm -f "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT"
145
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
146
+ PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH="$ORDINARY_SYSTEM_REMINDER" \
147
+ PI_COMPLETION_TEST_AUTO_CONTINUE_ON_SESSION_START=1 \
148
+ PI_COMPLETION_TEST_AUTO_CONTINUE_PROMPT_PATH="$ORDINARY_AUTO_RESUME_PROMPT" \
149
+ pi -e "$PKG_ROOT" -p "Summarize the repo briefly." \
150
+ >"$TMPDIR/pi-completion-smoke-ordinary.out" 2>"$TMPDIR/pi-completion-smoke-ordinary.err"
151
+
152
+ python3 - "$TMPDIR/pi-completion-smoke-ordinary.out" "$TMPDIR/pi-completion-smoke-ordinary.err" "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT" <<'PY'
153
+ import sys
154
+ from pathlib import Path
155
+
156
+ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
157
+ reminder = Path(sys.argv[3])
158
+ auto_resume = Path(sys.argv[4])
159
+
160
+ assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from canonical state'
161
+ assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
162
+ assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
163
+ PY
164
+
142
165
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
143
166
  PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$RESUME_PROMPT" \
144
167
  PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$UNCLEAR_ROUTING_SNAPSHOT" \