@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 +17 -0
- package/PUBLISHING.md +2 -0
- package/README.md +9 -5
- package/extensions/completion/index.ts +38 -35
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +3 -0
- package/scripts/canonical-evidence-artifact-test.sh +3 -0
- package/scripts/context-proposal-test.sh +272 -20
- package/scripts/observability-status-test.sh +3 -0
- package/scripts/refocus-test.sh +3 -0
- package/scripts/release-check.sh +7 -1
- package/scripts/smoke-test.sh +3 -0
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
|
-
|
|
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
|
|
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**.
|
|
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
|
|
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
|
|
763
|
-
|
|
764
|
-
|
|
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
|
|
784
|
-
|
|
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
|
|
788
|
-
return
|
|
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
|
|
792
|
-
const normalized =
|
|
793
|
-
if (!normalized) return
|
|
794
|
-
if (hasExplicitPlanningOnlyDeliverable([normalized])) return
|
|
795
|
-
if (
|
|
796
|
-
if (
|
|
797
|
-
return
|
|
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
|
-
|
|
807
|
+
const candidate = implementationMissionSourceCandidateText(item);
|
|
808
|
+
if (candidate) return candidate;
|
|
809
809
|
}
|
|
810
810
|
for (const item of proposal.acceptance) {
|
|
811
|
-
|
|
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 === "
|
|
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.
|
|
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
|
|
|
@@ -265,8 +268,8 @@ PY
|
|
|
265
268
|
|
|
266
269
|
rm -rf .agent
|
|
267
270
|
|
|
268
|
-
# No workflow yet: planning-only
|
|
269
|
-
#
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
assert
|
|
291
|
-
assert
|
|
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
|
-
|
|
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, '
|
|
319
|
-
assert state['mission_anchor'] == mission, '
|
|
320
|
-
assert proposal['scope'] ==
|
|
321
|
-
assert proposal['acceptance'] == ['
|
|
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"
|
package/scripts/refocus-test.sh
CHANGED
package/scripts/release-check.sh
CHANGED
|
@@ -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
|
|
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
|
|