@linimin/pi-letscook 0.1.39 → 0.1.41

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.1.41
6
+
7
+ ### Changed
8
+
9
+ - fixed the `/cook proposal analyst` startup overlay so `Ctrl+C` cancels the in-flight analysis the same way as `Esc`, and updated the helper text to advertise both cancellation paths
10
+
11
+ ## 0.1.40
12
+
13
+ ### Changed
14
+
15
+ - treated bare `/cook` README/CHANGELOG/docs-only deliverables as concrete repo-change missions instead of preserving generic planning phrasing
16
+ - made bare `/cook` ignore assistant/branch/compaction summary artifacts for startup/refocus readiness and fail closed on plan/spec/design-doc/proposal-only context without rewriting canonical state
17
+ - refreshed the context-proposal regressions, README guidance, and packaged release parity so the new execution-ready bare `/cook` behavior stays truthful
18
+ - internalized the repo-local `pi --no-extensions` isolation inside the packaged verifier scripts so direct `npm run smoke-test`, direct `npm run release-check`, and direct `bash .agent/verify_completion_stop.sh` stay truthful even when `@linimin/pi-letscook` is also installed globally on the same machine
19
+
3
20
  ## 0.1.39
4
21
 
5
22
  ### Changed
package/PUBLISHING.md CHANGED
@@ -9,6 +9,8 @@ npm run smoke-test
9
9
  npm run release-check
10
10
  ```
11
11
 
12
+ Those direct verifier entrypoints self-isolate the repo-local extension when they shell back into `pi`, so no extra `pi --no-extensions` wrapper is required even if this package is also installed globally on the publishing machine.
13
+
12
14
  ## GitHub release flow
13
15
 
14
16
  ```bash
package/README.md CHANGED
@@ -45,7 +45,9 @@ Use bare `/cook` after you discuss the mission in the main chat. The same comman
45
45
  - surface a conservative refocus chooser when recent discussion clearly points to a different workflow
46
46
  - start the next workflow round after the previous one is `done`
47
47
 
48
- On startup and next-round flows, if recent discussion is missing, weak, or ambiguous, bare `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to clarify the mission in the main chat before rerunning bare `/cook`.
48
+ Bare `/cook` expects recent main-chat discussion to describe concrete repo changes. README/CHANGELOG updates still count as concrete repo changes, but assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts do not.
49
+
50
+ On startup and next-round flows, if recent discussion is missing, weak, ambiguous, assistant-produced, or only describes planning artifacts instead of concrete repo changes, bare `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to clarify the mission in the main chat before rerunning bare `/cook`.
49
51
 
50
52
  ## How `/cook` works
51
53
 
@@ -53,9 +55,9 @@ Bare `/cook` is the only supported workflow entrypoint.
53
55
 
54
56
  | Repo state | Bare `/cook` |
55
57
  |---|---|
56
- | No workflow yet | Summarizes recent discussion into a startup proposal, then asks for approval with **Start** or **Cancel**. If the discussion is weak or ambiguous, `/cook` fails closed without writing `.agent/**` state and tells you to clarify the mission in the main chat before rerunning bare `/cook`. |
57
- | Active workflow exists | Reads the current mission plus recent non-command discussion. Matching or unclear discussion resumes from canonical `.agent/**` state. Clear replacement discussion opens a chooser first, then only rewrites canonical state after the follow-on **Start** confirmation. |
58
- | Previous workflow is `done` | Starts the next round from recent discussion, then asks for approval with **Start** or **Cancel**. Ambiguous discussion fails closed without rewriting canonical state and tells you to clarify the mission in the main chat before rerunning bare `/cook`. |
58
+ | No workflow yet | Summarizes recent main-chat discussion into a startup proposal, then asks for approval with **Start** or **Cancel**. If the discussion is weak, ambiguous, assistant-produced, or only a plan/spec/design-doc/proposal artifact instead of concrete repo changes, `/cook` fails closed without writing `.agent/**` state and tells you to clarify the mission in the main chat before rerunning bare `/cook`. |
59
+ | Active workflow exists | Reads the current mission plus recent non-command main-chat discussion. Matching or unclear discussion resumes from canonical `.agent/**` state. Clear replacement discussion about different concrete repo changes opens a chooser first, then only rewrites canonical state after the follow-on **Start** confirmation. Assistant/summary artifacts or plan/spec/design-doc/proposal-only context do not refocus the workflow. |
60
+ | Previous workflow is `done` | Starts the next round from recent main-chat discussion, then asks for approval with **Start** or **Cancel**. Weak, ambiguous, assistant-produced, or planning-artifact-only discussion fails closed without rewriting canonical state and tells you to clarify the mission in the main chat before rerunning bare `/cook`. |
59
61
 
60
62
  ## Approval-only confirmation and fail-closed behavior
61
63
 
@@ -65,7 +67,7 @@ All startup, next-round, and replacement proposals are **approval-only**:
65
67
  - actions are only **Start** and **Cancel**
66
68
  - **Cancel** is side-effect free: discuss changes in the main chat and rerun `/cook`
67
69
 
68
- When bare `/cook` cannot derive a clear startup, next-round, or replacement proposal from recent discussion, it fails closed instead of guessing. That means no canonical `.agent/**` state is created or rewritten until the discussion is clarified in the main chat and you rerun bare `/cook`.
70
+ When bare `/cook` cannot derive a clear startup, next-round, or replacement proposal for concrete repo changes from recent main-chat discussion, it fails closed instead of guessing. That means no canonical `.agent/**` state is created or rewritten until the discussion is clarified in the main chat and you rerun bare `/cook`. Tracked docs-only work such as README/CHANGELOG updates is still execution-ready, but assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts are not enough to start or refocus a workflow on their own.
69
71
 
70
72
  When an active workflow already exists and recent discussion clearly suggests a different workflow, `/cook` shows a separate chooser first:
71
73
 
@@ -220,6 +222,8 @@ npm run release-check
220
222
 
221
223
  `npm run release-check` is the broad packaged-release verifier. It begins with `bash .agent/verify_completion_control_plane.sh`, so missing or stale `.agent/verification-evidence.json` parity fails closed before the broader suite runs, then asserts the shipped single-command `/cook` public parity surfaces in `README.md`, `CHANGELOG.md`, and the `/cook` help/fail-closed copy in `extensions/completion/index.ts`, reruns the startup/refocus/context checks — including the critique-aware `/cook` confirmation regression and the smoke auto-resume prompt path — includes deterministic canonical evidence artifact coverage and includes deterministic active-slice contract coverage plus observability coverage, evaluator calibration, and the rubric-contract regression, and finishes with `npm pack --dry-run`.
222
224
 
225
+ The direct package-root verifier commands above intentionally self-isolate the repo-local extension when they shell back into `pi`, so you should not need to wrap them with `pi --no-extensions` even if `@linimin/pi-letscook` is also installed globally on the same machine.
226
+
223
227
  ## Release
224
228
 
225
229
  See [PUBLISHING.md](https://github.com/linimin/pi-letscook/blob/main/PUBLISHING.md) for GitHub and npm release steps.
@@ -205,11 +205,11 @@ class StartupAnalystOverlay extends Container {
205
205
  private updateDisplay(): void {
206
206
  this.title.setText(this.theme.fg("accent", this.theme.bold("/cook proposal analyst")));
207
207
  this.body.setText(formatInlineRunningText(this.theme, this.lines, { primaryAssistant: true }));
208
- this.footer.setText(this.theme.fg("muted", "Esc cancel • This analysis runs before /cook writes canonical workflow state"));
208
+ this.footer.setText(this.theme.fg("muted", "Esc/Ctrl+C cancel • This analysis runs before /cook writes canonical workflow state"));
209
209
  }
210
210
 
211
211
  override handleInput(data: string): void {
212
- if (data === "\u001b") {
212
+ if (data === "\u001b" || data === "\u0003") {
213
213
  this.onAbort?.();
214
214
  return;
215
215
  }
@@ -542,7 +542,7 @@ const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and reru
542
542
  const COOK_INLINE_ARG_REJECTION_MESSAGE =
543
543
  "Inline /cook arguments are no longer supported. Clarify the mission in the main chat and rerun bare /cook.";
544
544
  const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
545
- "Bare /cook failed closed because recent discussion did not contain a clear structured Mission/Scope/Constraints/Acceptance proposal. Add that structure in the main chat and rerun bare /cook.";
545
+ "Bare /cook failed closed because recent discussion did not contain a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun bare /cook.";
546
546
 
547
547
  function buildCookCancellationMessage(prefix: string): string {
548
548
  return `${prefix}. ${COOK_MAIN_CHAT_RERUN_GUIDANCE}`;
@@ -759,12 +759,11 @@ const CONTEXT_PROPOSAL_GENERIC_PLANNING_MISSION_REGEX =
759
759
  /(?:\b(?:start(?:ing)?|begin|continue|continu(?:e|ing)|resume|implement(?:ing)?|execute|execut(?:e|ing)|carry out|work on|ship|build(?:ing)?)\b.*\b(?:this|that|the|current|latest)\s+(?:plan|proposal|spec(?:ification)?|design(?: doc(?:ument)?)?|migration plan)\b|(?:開始|著手|繼續|继续|恢復|恢复)?(?:實作|实现|執行|执行|落地|完成)(?:這個|这个|此|該|该)?(?:方案|計畫|计划|提案|規劃|规划|設計|设计))/iu;
760
760
  const CONTEXT_PROPOSAL_PLANNING_ONLY_DELIVERABLE_REGEX =
761
761
  /(?:\b(?:write|draft|prepare|create|produce|share|deliver|document|review)\b.*\b(?:plan|spec(?:ification)?|design(?: doc(?:ument)?)?|migration plan|proposal)\b|(?:撰寫|撰写|編寫|编写|起草|準備|准备|產出|产出|整理|分享|交付|審查|审查).*(?:計畫|计划|規格|规格|設計文件|设计文档|提案|方案))/iu;
762
- const CONTEXT_PROPOSAL_NO_CODE_DOCS_ONLY_REGEX =
763
- /(?:\b(?:docs? only|documentation only|no code(?: changes?)?|without code(?: changes?)?|do not implement|don't implement|planning only|proposal only|spec only|design[- ]doc only|no runtime changes?)\b|(?:只改文件|僅文件|仅文件|不改(?:動)?代碼|不改代码|不要實作|不要实现|只規劃|只规划|僅規劃|仅规划|不改(?:動)?執行|不改运行))/iu;
764
- const CONTEXT_PROPOSAL_SUPPORT_ONLY_DOCS_REGEX =
765
- /(?:^(?:update|edit|document|refresh|write|add)\s+(?:the\s+)?(?:readme|docs?|documentation)\b|^(?:更新|補充|补充|撰寫|撰写|新增)\s*(?:README|文件|文檔|文档))/iu;
762
+ const CONTEXT_PROPOSAL_DOCS_ONLY_SIGNAL_REGEX = /(?:\b(?:docs? only|documentation only)\b|(?:只改文件|僅文件|仅文件))/iu;
763
+ const CONTEXT_PROPOSAL_NO_IMPLEMENTATION_SIGNAL_REGEX =
764
+ /(?:\b(?:no code(?: changes?)?|without code(?: changes?)?|do not implement|don't implement|planning only|proposal only|spec only|design[- ]doc only|no runtime changes?)\b|(?:不改(?:動)?代碼|不改代码|不要實作|不要实现|只規劃|只规划|僅規劃|仅规划|不改(?:動)?執行|不改运行))/iu;
766
765
  const CONTEXT_PROPOSAL_IMPLEMENTATION_SOURCE_REGEX =
767
- /(?:\b(?:normalize|fix|update|add|remove|restore|refactor|ship|support|wire|route|rewrite|replace|preserve|filter|separate|refresh|reroute|suppress|align|convert|reconcile|repair|correct|implement|build|land|block|allow|keep)\b|(?:修正|修復|修复|更新|新增|移除|恢復|恢复|重構|重构|調整|调整|正規化|规范化|規範化|过滤|過濾|分離|分离|刷新|替換|替换|抑制|對齊|对齐|實作|实现|落地|修補|修补|阻止|允許|允许|轉換|转换|保留|保持))/iu;
766
+ /(?:\b(?:normalize|fix|update|add|remove|restore|refactor|ship|support|wire|route|rewrite|replace|preserve|filter|separate|refresh|reroute|suppress|align|convert|reconcile|repair|correct|implement|build|land|block|allow|keep|edit(?:ing)?|document(?:ing)?|writ(?:e|ing))\b|(?:修正|修復|修复|更新|新增|移除|恢復|恢复|重構|重构|調整|调整|正規化|规范化|規範化|过滤|過濾|分離|分离|刷新|替換|替换|抑制|對齊|对齐|實作|实现|落地|修補|修补|阻止|允許|允许|轉換|转换|保留|保持))/iu;
768
767
 
769
768
  function contextProposalBodyTexts(proposal: Pick<ContextProposal, "scope" | "constraints" | "acceptance">): string[] {
770
769
  return [...proposal.scope, ...proposal.constraints, ...proposal.acceptance];
@@ -780,45 +779,52 @@ function hasExplicitPlanningOnlyDeliverable(texts: string[]): boolean {
780
779
  return texts.some((text) => CONTEXT_PROPOSAL_PLANNING_ONLY_DELIVERABLE_REGEX.test(normalizeProposalLine(text)));
781
780
  }
782
781
 
783
- function hasClearNoCodeOrDocsOnlySignal(texts: string[]): boolean {
784
- return texts.some((text) => CONTEXT_PROPOSAL_NO_CODE_DOCS_ONLY_REGEX.test(normalizeProposalLine(text)));
782
+ function normalizeImplementationMissionSourceText(text: string): string {
783
+ const normalized = normalizeProposalLine(text);
784
+ if (!normalized) return "";
785
+ return normalized
786
+ .replace(new RegExp(`${CONTEXT_PROPOSAL_DOCS_ONLY_SIGNAL_REGEX.source}[\\s::;,/\\-]*`, "giu"), " ")
787
+ .replace(/\s+([,.;:!?])/g, "$1")
788
+ .replace(/\s+/g, " ")
789
+ .trim();
785
790
  }
786
791
 
787
- function isSupportOnlyDocumentationItem(text: string): boolean {
788
- return CONTEXT_PROPOSAL_SUPPORT_ONLY_DOCS_REGEX.test(normalizeProposalLine(text));
792
+ function hasClearNoImplementationSignal(texts: string[]): boolean {
793
+ return texts.some((text) => CONTEXT_PROPOSAL_NO_IMPLEMENTATION_SIGNAL_REGEX.test(normalizeProposalLine(text)));
789
794
  }
790
795
 
791
- function isImplementationMissionSourceCandidate(text: string): boolean {
792
- const normalized = normalizeProposalLine(text);
793
- if (!normalized) return false;
794
- if (hasExplicitPlanningOnlyDeliverable([normalized])) return false;
795
- if (hasClearNoCodeOrDocsOnlySignal([normalized])) return false;
796
- if (isSupportOnlyDocumentationItem(normalized)) return false;
797
- return CONTEXT_PROPOSAL_IMPLEMENTATION_SOURCE_REGEX.test(normalized);
798
- }
799
-
800
- function hasSupportOnlyDocumentationDeliverable(proposal: Pick<ContextProposal, "scope" | "acceptance">): boolean {
801
- const deliverableTexts = [...proposal.scope, ...proposal.acceptance].map((item) => normalizeProposalLine(item)).filter(Boolean);
802
- if (deliverableTexts.length === 0) return false;
803
- return deliverableTexts.every((item) => isSupportOnlyDocumentationItem(item));
796
+ function implementationMissionSourceCandidateText(text: string): string | undefined {
797
+ const normalized = normalizeImplementationMissionSourceText(text);
798
+ if (!normalized) return undefined;
799
+ if (hasExplicitPlanningOnlyDeliverable([normalized])) return undefined;
800
+ if (hasClearNoImplementationSignal([normalized])) return undefined;
801
+ if (!CONTEXT_PROPOSAL_IMPLEMENTATION_SOURCE_REGEX.test(normalized)) return undefined;
802
+ return normalized;
804
803
  }
805
804
 
806
805
  function pickImplementationMissionSource(proposal: Pick<ContextProposal, "scope" | "constraints" | "acceptance">): string | undefined {
807
806
  for (const item of proposal.scope) {
808
- if (isImplementationMissionSourceCandidate(item)) return item;
807
+ const candidate = implementationMissionSourceCandidateText(item);
808
+ if (candidate) return candidate;
809
809
  }
810
810
  for (const item of proposal.acceptance) {
811
- if (isImplementationMissionSourceCandidate(item)) return item;
811
+ const candidate = implementationMissionSourceCandidateText(item);
812
+ if (candidate) return candidate;
812
813
  }
813
814
  return undefined;
814
815
  }
815
816
 
817
+ function hasPlanningArtifactOnlyContext(
818
+ proposal: Pick<ContextProposal, "mission" | "scope" | "constraints" | "acceptance">,
819
+ ): boolean {
820
+ const texts = [proposal.mission, ...contextProposalBodyTexts(proposal)];
821
+ if (!hasExplicitPlanningOnlyDeliverable(texts)) return false;
822
+ return !pickImplementationMissionSource(proposal);
823
+ }
824
+
816
825
  function finalizeContextProposal(proposal: ContextProposal, projectName: string): ContextProposal | undefined {
826
+ if (hasPlanningArtifactOnlyContext(proposal)) return undefined;
817
827
  if (!isGenericPlanningMissionAnchor(proposal.mission)) return proposal;
818
- const bodyTexts = contextProposalBodyTexts(proposal);
819
- if (hasExplicitPlanningOnlyDeliverable(bodyTexts) || hasClearNoCodeOrDocsOnlySignal(bodyTexts) || hasSupportOnlyDocumentationDeliverable(proposal)) {
820
- return proposal;
821
- }
822
828
  const missionSource = pickImplementationMissionSource(proposal);
823
829
  if (!missionSource) return undefined;
824
830
  const nextMission = assessMissionAnchor(missionSource, projectName).derived;
@@ -1026,12 +1032,9 @@ function collectRecentDiscussionEntries(ctx: { sessionManager: any }, limit = 8)
1026
1032
  let text = "";
1027
1033
  let role: RecentDiscussionEntry["role"] | undefined;
1028
1034
  const messageRole = asString(message.role);
1029
- if (messageRole === "user" || messageRole === "assistant" || messageRole === "custom") {
1035
+ if (messageRole === "user" || messageRole === "custom") {
1030
1036
  text = extractTextFromMessageContent(message.content);
1031
1037
  role = messageRole;
1032
- } else if (messageRole === "branchSummary" || messageRole === "compactionSummary") {
1033
- text = asString(message.summary) ?? "";
1034
- role = "summary";
1035
1038
  }
1036
1039
  if (!text || !role) continue;
1037
1040
  const trimmed = text.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linimin/pi-letscook",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -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
 
@@ -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
  CURRENT_EVIDENCE_BACKUP=""
7
10
 
@@ -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
 
@@ -265,8 +268,8 @@ PY
265
268
 
266
269
  rm -rf .agent
267
270
 
268
- # No workflow yet: planning-only deliverables should preserve planning missions when discussion clearly says
269
- # docs-only / no-code plan output instead of shipped code, test, or runtime work.
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.
270
273
  SESSION_ZERO_PLANNING_ONLY="$TMPDIR/session-zero-planning-only.jsonl"
271
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.'
272
275
  DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY="$TMPDIR/context-proposal-planning-only.json"
@@ -278,25 +281,23 @@ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONL
278
281
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
279
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"
280
283
 
281
- python3 - "$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" <<'PY'
282
- import json
284
+ python3 - "$TMPDIR/pi-completion-context-proposal-planning-only.out" "$TMPDIR/pi-completion-context-proposal-planning-only.err" "$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" <<'PY'
283
285
  import sys
284
286
  from pathlib import Path
285
287
 
286
- mission = '開始實作這個方案.'
287
- proposal = json.loads(Path(sys.argv[1]).read_text())
288
- state = json.loads(Path('.agent/state.json').read_text())
289
-
290
- assert proposal['mission'] == mission, 'planning-only startup should preserve the planning mission when the deliverable is explicitly a plan'
291
- assert state['mission_anchor'] == mission, 'planning-only startup should keep the planning mission anchor in canonical state'
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'
292
295
  PY
293
296
 
294
- rm -rf .agent
295
-
296
- # No workflow yet: support-docs-only deliverables should also preserve planning missions even without
297
- # an explicit docs-only/no-code phrase, as long as the deliverable stays limited to support documentation.
297
+ # No workflow yet: docs-only tracked deliverables such as README/CHANGELOG updates should
298
+ # normalize placeholder planning missions into concrete repo-change missions.
298
299
  SESSION_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/session-zero-support-docs-only.jsonl"
299
- DISCUSSION_ZERO_SUPPORT_DOCS_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Update README for the /cook mission-normalization behavior.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Add documentation for the operator-facing refocus flow.'
300
+ DISCUSSION_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.'
300
301
  DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/context-proposal-support-docs-only.json"
301
302
  write_session "$SESSION_ZERO_SUPPORT_DOCS_ONLY" "$ROOT" "$DISCUSSION_ZERO_SUPPORT_DOCS_ONLY"
302
303
 
@@ -311,14 +312,191 @@ import json
311
312
  import sys
312
313
  from pathlib import Path
313
314
 
314
- mission = '開始實作這個方案.'
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.'
315
346
  proposal = json.loads(Path(sys.argv[1]).read_text())
316
347
  state = json.loads(Path('.agent/state.json').read_text())
317
348
 
318
- assert proposal['mission'] == mission, 'support-docs-only startup should preserve the planning mission even without an explicit docs-only phrase'
319
- assert state['mission_anchor'] == mission, 'support-docs-only startup should keep the planning mission anchor in canonical state'
320
- assert proposal['scope'] == ['Update README for the /cook mission-normalization behavior.'], 'support-docs-only startup should keep the documentation scope item'
321
- assert proposal['acceptance'] == ['Add documentation for the operator-facing refocus flow.'], 'support-docs-only startup should keep the documentation acceptance item'
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'
322
500
  PY
323
501
 
324
502
  rm -rf .agent
@@ -447,6 +625,80 @@ assert plan['mission_anchor'] == mission, 'active bare /cook continue should kee
447
625
  assert active['mission_anchor'] == mission, 'active bare /cook continue should keep active-slice.json unchanged'
448
626
  PY
449
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
+
450
702
  # Active workflow: bare /cook with a placeholder planning mission should still route through the existing
451
703
  # refocus chooser and final Start/Cancel gate before canonical state is rewritten.
452
704
  SESSION_ONE_REFOCUS_NORMALIZED="$TMPDIR/session-one-refocus-normalized.jsonl"
@@ -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
 
@@ -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
 
@@ -20,17 +20,23 @@ checks = {
20
20
  "approval-only Start/Cancel gate",
21
21
  "Start new workflow from recent discussion",
22
22
  "fails closed instead of guessing",
23
+ "README/CHANGELOG updates still count as concrete repo changes",
24
+ "assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts do not",
25
+ "Assistant/summary artifacts or plan/spec/design-doc/proposal-only context do not refocus the workflow.",
23
26
  ],
24
27
  "CHANGELOG.md": [
25
28
  "bare `/cook` as the only supported workflow entrypoint",
26
29
  "clarify the mission before rerunning bare `/cook`",
27
30
  "packaged parity now fails closed on the bare-only contract",
28
31
  "that old inline-argument path is no longer supported now that bare `/cook` is the only public entrypoint",
32
+ "README/CHANGELOG/docs-only deliverables as concrete repo-change missions",
33
+ "ignore assistant/branch/compaction summary artifacts for startup/refocus readiness",
34
+ "plan/spec/design-doc/proposal-only context without rewriting canonical state",
29
35
  ],
30
36
  "extensions/completion/index.ts": [
31
37
  'description: "Discussion-driven /cook workflow: start, continue, refocus, or start the next round"',
32
38
  "Inline /cook arguments are no longer supported. Clarify the mission in the main chat and rerun bare /cook.",
33
- "Bare /cook failed closed because recent discussion did not contain a clear structured Mission/Scope/Constraints/Acceptance proposal. Add that structure in the main chat and rerun bare /cook.",
39
+ "Bare /cook failed closed because recent discussion did not contain a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun bare /cook.",
34
40
  ],
35
41
  }
36
42
 
@@ -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