@linimin/pi-letscook 0.1.59 → 0.1.60
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/.agent/mission.md +1 -1
- package/CHANGELOG.md +6 -4
- package/README.md +20 -18
- package/extensions/completion/driver.ts +13 -7
- package/extensions/completion/index.ts +2 -2
- package/extensions/completion/prompt-surfaces.ts +8 -8
- package/package.json +1 -1
- package/scripts/context-proposal-test.sh +67 -34
- package/scripts/release-check.sh +29 -28
- package/scripts/smoke-test.sh +18 -62
- package/skills/cook-handoff-boundary/SKILL.md +10 -9
package/.agent/mission.md
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
Project: pi-letscook
|
|
4
4
|
|
|
5
5
|
Mission anchor:
|
|
6
|
-
|
|
6
|
+
Refactor the /cook startup boundary into the agreed mixed model: ordinary chat stays advisory-first by default with no default pre-/cook handoff capsule formation, while explicit /cook performs structured startup synthesis from recent discussion and preserves the approval-only Start/Cancel gate.
|
|
7
7
|
|
|
8
8
|
This file is a tracked human-readable statement of the repo's completion mission. Re-grounders may refine this file when repo truth becomes clearer, but it must stay truthful to shipped behavior and the active completion objective.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.60
|
|
6
|
+
|
|
5
7
|
## 0.1.59
|
|
6
8
|
|
|
7
9
|
### Changed
|
|
8
10
|
|
|
9
11
|
- relaxed the pre-`/cook` ordinary-chat boundary so the primary agent can keep discussing and refining requirements before explicit `/cook` instead of switching into a hard handoff-only refusal mode as soon as workflow-worthiness is detected
|
|
10
|
-
- kept `/cook` as the only explicit workflow boundary, while
|
|
11
|
-
-
|
|
12
|
-
- kept
|
|
13
|
-
- updated public parity and
|
|
12
|
+
- kept `/cook` as the only explicit workflow boundary, while moving default startup and done-workflow next-round synthesis to bare `/cook` from recent ordinary-chat discussion behind the existing **Start** / **Cancel** approval gate
|
|
13
|
+
- kept pre-`/cook` previews or `cook_handoff` capsules opt-in only, non-canonical, and advisory until the user explicitly runs `/cook`; bare `/cook` no longer depends on a default prebuilt capsule for new-workflow startup
|
|
14
|
+
- kept active workflows resuming from canonical `.agent/**` state unless a fresh explicit handoff proposes a replacement, so discussion-only context does not silently rewrite an in-progress workflow
|
|
15
|
+
- updated public parity and packaged release verification so README/help/changelog/release-check all describe and gate the shipped mixed model truthfully, while still packaging the tracked `.agent` contract files
|
|
14
16
|
|
|
15
17
|
## 0.1.58
|
|
16
18
|
|
package/README.md
CHANGED
|
@@ -43,22 +43,22 @@ Then run `/reload` in Pi.
|
|
|
43
43
|
|
|
44
44
|
| If you want to... | Do this |
|
|
45
45
|
|---|---|
|
|
46
|
-
| Start a long-running task | Discuss the concrete repo change in the main chat,
|
|
46
|
+
| Start a long-running task | Discuss the concrete repo change in the main chat, then run `/cook` once the recent discussion is specific enough for a startup brief. If you explicitly want a pre-`/cook` preview or capsule first, ask for one. |
|
|
47
47
|
| Continue the current workflow | Run `/cook` |
|
|
48
|
-
| Refocus or start the next round | Discuss the new concrete repo change in the main chat,
|
|
48
|
+
| Refocus or start the next round | Discuss the new concrete repo change in the main chat, then run `/cook` to synthesize the next startup brief. Active-workflow replacement still stays explicit and confirm-first. |
|
|
49
49
|
|
|
50
50
|
## What `/cook` expects
|
|
51
51
|
|
|
52
|
-
-
|
|
53
|
-
- for that
|
|
54
|
-
- enough detail in the main chat for the primary agent to form that bounded handoff capsule before you run `/cook`
|
|
52
|
+
- recent ordinary-chat discussion concrete enough for bare `/cook` to synthesize a startup brief for a new workflow or the next round after a completed workflow
|
|
53
|
+
- enough repo-change detail for that startup brief to stay implementation-oriented once you review it behind **Start** or **Cancel**
|
|
55
54
|
- README/CHANGELOG updates still count as concrete repo changes
|
|
56
|
-
- assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts still do not
|
|
57
|
-
-
|
|
55
|
+
- assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts still do not become canonical workflow state by themselves
|
|
56
|
+
- any pre-`/cook` preview or `cook_handoff` capsule only when you explicitly ask for it; that preview stays advisory startup intake, not canonical `.agent/**` state
|
|
57
|
+
- active-workflow replacement still stays conservative: `/cook` resumes from canonical state unless a fresh explicit handoff proposes a different concrete repo change and you confirm that replacement
|
|
58
58
|
|
|
59
|
-
If
|
|
59
|
+
If recent discussion is too weak, ambiguous, stale, or planning-only for new-workflow or next-round entry, `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to clarify the concrete repo change in the main chat before rerunning `/cook`.
|
|
60
60
|
|
|
61
|
-
If a
|
|
61
|
+
If you explicitly asked for a preview capsule and it is still workflow-worthy rather than implementation-startable, `/cook` also fails closed instead of silently treating that preview as planning support or canonical workflow state.
|
|
62
62
|
|
|
63
63
|
If you pass inline arguments to `/cook`, it also fails closed and tells you to move that intent into the main chat before rerunning bare `/cook`.
|
|
64
64
|
|
|
@@ -70,36 +70,37 @@ If a task has clearly matured into completion-workflow scope, the primary agent
|
|
|
70
70
|
|
|
71
71
|
Before you explicitly run `/cook`, the conversation can still stay in ordinary chat: the primary agent may keep answering follow-up questions and refining requirements rather than switching into a hard handoff-only refusal mode.
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
If you explicitly ask for a pre-`/cook` preview or capsule, the primary agent may provide one, but that preview is opt-in only and stays non-canonical until you later run `/cook` and choose **Start**.
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Bare `/cook` is still the canonical workflow boundary: it synthesizes the startup brief from recent ordinary-chat discussion at `/cook` time, then waits for **Start** or **Cancel** before any canonical `.agent/**` write.
|
|
76
76
|
|
|
77
77
|
Important behavior:
|
|
78
78
|
- `/cook` is the canonical workflow boundary and manual entry point
|
|
79
|
-
- startup and next-round entry stay confirm-first
|
|
79
|
+
- startup and next-round entry stay confirm-first: bare `/cook` synthesizes the startup brief from recent discussion, then waits for **Start** or **Cancel**
|
|
80
80
|
- active workflows resume from canonical `.agent/**` state unless a fresh valid explicit handoff proposes a replacement
|
|
81
|
+
- any pre-`/cook` preview or capsule is explicit-request-only and non-canonical
|
|
81
82
|
- explicit slash commands other than `/cook` continue normally in the main chat
|
|
82
83
|
- ordinary main-chat discussion may clarify or propose, but mature long-running implementation should be handed off to `/cook`
|
|
83
84
|
|
|
84
85
|
## Typical examples
|
|
85
86
|
|
|
86
|
-
Start a new workflow
|
|
87
|
+
Start a new workflow from recent discussion:
|
|
87
88
|
|
|
88
89
|
```text
|
|
89
90
|
I want to add login redirect handling and tests.
|
|
90
|
-
#
|
|
91
|
+
# discuss scope until the startup brief is clear enough
|
|
91
92
|
/cook
|
|
92
93
|
```
|
|
93
94
|
|
|
94
95
|
## What happens when you run `/cook`
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
When no workflow is active, bare `/cook` synthesizes a startup brief from recent ordinary-chat discussion and then waits for **Start** or **Cancel**. If recent discussion is too weak, ambiguous, stale, or planning-only, `/cook` fails closed instead of guessing. If you explicitly asked for a preview capsule first and that preview is fresh but still non-startable, `/cook` also fails closed instead of silently treating it as canonical state. When a workflow is already active and no fresh valid explicit replacement handoff is present, `/cook` resumes from canonical `.agent/**` state instead of deriving replacement startup from recent discussion.
|
|
97
98
|
|
|
98
99
|
| Repo state | What you'll see |
|
|
99
100
|
|---|---|
|
|
100
|
-
| No workflow yet |
|
|
101
|
+
| No workflow yet | `/cook` synthesizes a startup brief from recent discussion and shows **Start** / **Cancel**. If recent discussion is too weak, ambiguous, stale, or planning-only, `/cook` fails closed and leaves canonical state unchanged. An explicit-request preview capsule can inform that startup brief, but it is still non-canonical until you choose **Start**. |
|
|
101
102
|
| Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If a fresh explicit handoff capsule points to a different concrete repo change, `/cook` shows a chooser first and only rewrites canonical state after you confirm the replacement. Ambiguous intake stays conservative. |
|
|
102
|
-
| Previous workflow is `done` |
|
|
103
|
+
| Previous workflow is `done` | `/cook` synthesizes the next implementation round from recent discussion behind **Start** / **Cancel**. If that recent discussion is too weak or ambiguous, `/cook` fails closed and leaves the finished workflow state unchanged. |
|
|
103
104
|
|
|
104
105
|
## Confirmation and fail-closed behavior
|
|
105
106
|
|
|
@@ -109,6 +110,7 @@ I want to add login redirect handling and tests.
|
|
|
109
110
|
- actions are **Start** and **Cancel**
|
|
110
111
|
- **Cancel** is side-effect free: canonical workflow state stays unchanged, so you can discuss changes in the main chat and rerun `/cook`
|
|
111
112
|
- weak, ambiguous, stale, invalid, assistant-produced, or planning-only intake does not start a workflow
|
|
113
|
+
- any pre-`/cook` preview or capsule is advisory only and never writes canonical workflow state by itself
|
|
112
114
|
- when a fresh explicit handoff suggests replacing an active workflow, `/cook` shows a chooser before any canonical state rewrite
|
|
113
115
|
|
|
114
116
|
When you accept startup or refocus, `/cook` persists the chosen workflow state in canonical `.agent/**` files before the re-ground round begins.
|
|
@@ -262,7 +264,7 @@ npm run rubric-contract-test
|
|
|
262
264
|
npm run release-check
|
|
263
265
|
```
|
|
264
266
|
|
|
265
|
-
`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 `/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`.
|
|
267
|
+
`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 mixed-model `/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`.
|
|
266
268
|
|
|
267
269
|
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.
|
|
268
270
|
|
|
@@ -60,9 +60,9 @@ type CookContextProposalResult = {
|
|
|
60
60
|
blockedFailureMessage?: string;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
function
|
|
63
|
+
function buildCookStartupBriefRequiredMessage(deps: CompletionDriverDeps, prefix?: string): string {
|
|
64
64
|
const requirement =
|
|
65
|
-
"/cook failed closed because
|
|
65
|
+
"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook.";
|
|
66
66
|
return prefix ? `${prefix} ${requirement}` : requirement;
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -536,14 +536,14 @@ export async function runCookEntry(
|
|
|
536
536
|
if (!snapshot) {
|
|
537
537
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
538
538
|
const projectName = path.basename(root);
|
|
539
|
-
const derived = await deps.
|
|
539
|
+
const derived = await deps.deriveCookContextProposal(ctx, projectName);
|
|
540
540
|
if (derived.blockedFailureMessage) {
|
|
541
541
|
deps.emitCommandText(ctx, derived.blockedFailureMessage, "info");
|
|
542
542
|
return;
|
|
543
543
|
}
|
|
544
544
|
const proposal = derived.proposal;
|
|
545
545
|
if (!proposal) {
|
|
546
|
-
deps.emitCommandText(ctx,
|
|
546
|
+
deps.emitCommandText(ctx, buildCookStartupBriefRequiredMessage(deps), "info");
|
|
547
547
|
return;
|
|
548
548
|
}
|
|
549
549
|
const decision = await deps.confirmContextProposal(ctx, proposal, {
|
|
@@ -581,14 +581,14 @@ export async function runCookEntry(
|
|
|
581
581
|
if (!goal) {
|
|
582
582
|
if (workflowDone) {
|
|
583
583
|
const projectName = path.basename(snapshot.files.root);
|
|
584
|
-
const derived = await deps.
|
|
584
|
+
const derived = await deps.deriveCookContextProposal(ctx, projectName);
|
|
585
585
|
if (derived.blockedFailureMessage) {
|
|
586
586
|
deps.emitCommandText(ctx, derived.blockedFailureMessage, "info");
|
|
587
587
|
return;
|
|
588
588
|
}
|
|
589
589
|
const proposal = derived.proposal;
|
|
590
590
|
if (!proposal) {
|
|
591
|
-
deps.emitCommandText(ctx,
|
|
591
|
+
deps.emitCommandText(ctx, buildCookStartupBriefRequiredMessage(deps, "The previous completion workflow is already done."), "info");
|
|
592
592
|
return;
|
|
593
593
|
}
|
|
594
594
|
const decision = await deps.confirmContextProposal(ctx, proposal, {
|
|
@@ -610,7 +610,13 @@ export async function runCookEntry(
|
|
|
610
610
|
buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis }),
|
|
611
611
|
);
|
|
612
612
|
snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
|
|
613
|
-
deps.emitCommandText(
|
|
613
|
+
deps.emitCommandText(
|
|
614
|
+
ctx,
|
|
615
|
+
proposal.source === "handoff_capsule"
|
|
616
|
+
? `Started a new completion workflow round from explicit primary-agent handoff: ${decision.missionAnchor}`
|
|
617
|
+
: `Started a new completion workflow round from recent discussion: ${decision.missionAnchor}`,
|
|
618
|
+
"info",
|
|
619
|
+
);
|
|
614
620
|
} else {
|
|
615
621
|
const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, deps);
|
|
616
622
|
if (assessment.action === "blocked") {
|
|
@@ -209,7 +209,7 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
|
|
|
209
209
|
|
|
210
210
|
const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
|
|
211
211
|
const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
|
|
212
|
-
"/cook failed closed because
|
|
212
|
+
"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief for bare /cook with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook; canonical workflow state is still only written after Start.";
|
|
213
213
|
|
|
214
214
|
function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
215
215
|
return asString(snapshot?.state?.continuation_policy) === "done";
|
|
@@ -929,7 +929,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
929
929
|
structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
|
|
930
930
|
mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
|
|
931
931
|
cookCommandSpec: {
|
|
932
|
-
description: "/cook workflow:
|
|
932
|
+
description: "/cook workflow: synthesize an approval-gated startup brief from recent discussion for new-workflow or next-round entry, resume the current workflow from canonical state, or confirm an explicit active-workflow replacement",
|
|
933
933
|
},
|
|
934
934
|
buildContextProposalContinuationReason,
|
|
935
935
|
completionKickoff,
|
|
@@ -32,14 +32,14 @@ export function buildCookHandoffBoundaryReminder(): string {
|
|
|
32
32
|
"When you judge that the task has matured into completion-workflow scope — for example the user has clearly shifted from exploration into implementation intent, you have just produced a concrete plan or proposal whose next step would naturally be implementation, or the task spans multiple files, steps, or verification surfaces — do not begin long-running product implementation in ordinary chat and do not edit tracked product files for that workflow-level task.",
|
|
33
33
|
"Instead, recommend /cook as the workflow boundary while keeping the conversation in ordinary chat until the user explicitly runs /cook.",
|
|
34
34
|
"If the user keeps asking follow-up questions or refining requirements before /cook, continue that ordinary-chat discussion normally instead of switching into a handoff-only refusal mode, but do not act as though /cook had already been invoked.",
|
|
35
|
-
"Distinguish a workflow-worthy handoff from an
|
|
36
|
-
"If the task is workflow-worthy but
|
|
37
|
-
"When handing off, explain that /cook
|
|
38
|
-
"
|
|
39
|
-
"Use handoff_kind implementation_workflow_handoff for that
|
|
40
|
-
"If later ordinary-chat discussion materially changes the startup brief before /cook runs, update or replace the capsule in a later assistant reply instead of pretending the workflow already started.",
|
|
41
|
-
"
|
|
42
|
-
"If the task is still ordinary Q&A, lightweight brainstorming, or a tiny one-off fix, continue normally without forcing /cook.",
|
|
35
|
+
"Distinguish a workflow-worthy handoff from an opt-in preview request: by default, do not emit a structured preview or cook_handoff capsule in ordinary chat once the task is concrete enough; recommend bare /cook instead.",
|
|
36
|
+
"If the task is workflow-worthy but the user still wants to refine scope before /cook, keep refining in ordinary chat without acting as though workflow already started.",
|
|
37
|
+
"When handing off, explain that bare /cook will synthesize a startup brief from recent ordinary-chat discussion for a new workflow or next round, while already-active workflows resume from canonical .agent state unless the user explicitly chooses a replacement path backed by a fresh valid explicit handoff.",
|
|
38
|
+
"If the user explicitly asks for a /cook preview or capsule before running /cook, you may append one exact fenced block in the same assistant reply using ```cook_handoff ... ``` JSON with kind/source/handoff_kind plus mission, scope, constraints or non_goals, acceptance, risks, notes, captured_at, source_turn_id, first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, why_this_slice_first, and optional task_type/evaluation_profile/why_cook_now.",
|
|
39
|
+
"Use handoff_kind implementation_workflow_handoff for that opt-in preview capsule.",
|
|
40
|
+
"If later ordinary-chat discussion materially changes the startup brief before /cook runs, update or replace the preview capsule in a later assistant reply instead of pretending the workflow already started.",
|
|
41
|
+
"Any preview capsule is startup intake for /cook only: do not present it as canonical .agent state, an active slice, or a persistent repo contract.",
|
|
42
|
+
"If the task is still ordinary Q&A, lightweight brainstorming, or a tiny one-off fix, continue normally without forcing /cook or emitting an unsolicited preview capsule.",
|
|
43
43
|
].join(" ");
|
|
44
44
|
}
|
|
45
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linimin/pi-letscook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.60",
|
|
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,
|
|
@@ -147,28 +147,32 @@ mkdir -p "$ROOT"
|
|
|
147
147
|
cd "$ROOT"
|
|
148
148
|
git init -q
|
|
149
149
|
|
|
150
|
-
# No workflow yet: bare /cook should
|
|
151
|
-
#
|
|
150
|
+
# No workflow yet: bare /cook should synthesize a startup brief from recent discussion,
|
|
151
|
+
# and Cancel should leave canonical workflow state untouched.
|
|
152
152
|
SESSION_ZERO="$TMPDIR/session-zero.jsonl"
|
|
153
153
|
DISCUSSION_ZERO=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\n- Suppress the widget while a completion role is active.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Update README to match the shipped behavior.\n- Keep observability regression coverage truthful.'
|
|
154
154
|
DISCUSSION_SNAPSHOT_ZERO="$TMPDIR/context-proposal-structured-fallback.json"
|
|
155
155
|
write_session "$SESSION_ZERO" "$ROOT" "$DISCUSSION_ZERO"
|
|
156
156
|
|
|
157
|
-
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=
|
|
157
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=cancel \
|
|
158
158
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
159
159
|
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO" \
|
|
160
160
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
161
161
|
pi --session "$SESSION_ZERO" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-structured-fallback.out" 2>"$TMPDIR/pi-completion-context-proposal-structured-fallback.err"
|
|
162
162
|
|
|
163
163
|
python3 - "$TMPDIR/pi-completion-context-proposal-structured-fallback.out" "$TMPDIR/pi-completion-context-proposal-structured-fallback.err" "$DISCUSSION_SNAPSHOT_ZERO" <<'PY'
|
|
164
|
+
import json
|
|
164
165
|
import sys
|
|
165
166
|
from pathlib import Path
|
|
166
167
|
|
|
167
168
|
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
168
169
|
snapshot = Path(sys.argv[3])
|
|
169
|
-
|
|
170
|
-
assert not
|
|
171
|
-
assert '
|
|
170
|
+
proposal = json.loads(snapshot.read_text())
|
|
171
|
+
assert not Path('.agent').exists(), 'recent-discussion Cancel should leave canonical state untouched before workflow bootstrap'
|
|
172
|
+
assert snapshot.exists(), 'recent-discussion startup synthesis should emit a proposal snapshot before the Start/Cancel gate'
|
|
173
|
+
assert proposal['mission'] == 'Remove the completion status line while keeping the completion widget.', 'recent-discussion startup synthesis should preserve the structured mission anchor'
|
|
174
|
+
assert proposal['source'] == 'session', 'recent-discussion startup synthesis should snapshot the structured-session proposal source'
|
|
175
|
+
assert 'Cancelled recent-discussion workflow proposal' in output, 'recent-discussion Cancel should report that canonical state was left unchanged'
|
|
172
176
|
PY
|
|
173
177
|
|
|
174
178
|
# No workflow yet: user-authored faux handoffs must not bootstrap canonical workflow state.
|
|
@@ -202,6 +206,7 @@ PY
|
|
|
202
206
|
)"
|
|
203
207
|
write_session_messages "$SESSION_ZERO_USER_AUTHORED" "$ROOT" "$USER_AUTHORED_MESSAGES_ZERO"
|
|
204
208
|
|
|
209
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
205
210
|
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$USER_AUTHORED_SNAPSHOT_ZERO" \
|
|
206
211
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
207
212
|
pi --session "$SESSION_ZERO_USER_AUTHORED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-user-authored.out" 2>"$TMPDIR/pi-completion-context-proposal-user-authored.err"
|
|
@@ -214,7 +219,7 @@ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
|
214
219
|
snapshot = Path(sys.argv[3])
|
|
215
220
|
assert not Path('.agent').exists(), 'user-authored faux handoff should fail closed without writing canonical state'
|
|
216
221
|
assert not snapshot.exists(), 'user-authored faux handoff should not emit a startup proposal snapshot'
|
|
217
|
-
assert '
|
|
222
|
+
assert 'recent discussion did not produce a clear execution-ready startup brief' in output, 'user-authored faux handoff should still fail closed unless ordinary discussion is concretely startable'
|
|
218
223
|
PY
|
|
219
224
|
|
|
220
225
|
# No workflow yet: malformed or invalid assistant handoff capsules must also fail closed.
|
|
@@ -223,6 +228,7 @@ INVALID_SNAPSHOT_ZERO="$TMPDIR/context-proposal-invalid-handoff.json"
|
|
|
223
228
|
INVALID_MESSAGES_ZERO='[{"role":"assistant","content":"This is not a valid startup capsule.\n\n```cook_handoff\n{\"kind\":\"cook_handoff\",\"source\":\"primary_agent\",\"mission\":\"Broken JSON handoff\"\n```"}]'
|
|
224
229
|
write_session_messages "$SESSION_ZERO_INVALID" "$ROOT" "$INVALID_MESSAGES_ZERO"
|
|
225
230
|
|
|
231
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
226
232
|
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$INVALID_SNAPSHOT_ZERO" \
|
|
227
233
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
228
234
|
pi --session "$SESSION_ZERO_INVALID" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-invalid-handoff.out" 2>"$TMPDIR/pi-completion-context-proposal-invalid-handoff.err"
|
|
@@ -235,7 +241,7 @@ output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
|
235
241
|
snapshot = Path(sys.argv[3])
|
|
236
242
|
assert not Path('.agent').exists(), 'invalid assistant handoff should fail closed without writing canonical state'
|
|
237
243
|
assert not snapshot.exists(), 'invalid assistant handoff should not emit a startup proposal snapshot'
|
|
238
|
-
assert '
|
|
244
|
+
assert 'recent discussion did not produce a clear execution-ready startup brief' in output, 'invalid assistant handoff should still fail closed when no clear recent-discussion startup brief exists'
|
|
239
245
|
PY
|
|
240
246
|
|
|
241
247
|
# No workflow yet: a fresh explicit primary-agent handoff should still bootstrap canonical startup state.
|
|
@@ -702,10 +708,9 @@ assert not snapshot.exists(), 'verification-evidence overlap suppression should
|
|
|
702
708
|
assert '/cook failed closed' in output, 'verification-evidence overlap suppression should fail closed when the latest discussion only repeats verified work'
|
|
703
709
|
PY
|
|
704
710
|
|
|
705
|
-
# Completed workflow: bare /cook should
|
|
706
|
-
# even when the discussion is well structured.
|
|
711
|
+
# Completed workflow: bare /cook should synthesize the next-round startup brief from recent discussion.
|
|
707
712
|
SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
|
|
708
|
-
DISCUSSION_TWO_NORMALIZED=$'Mission:
|
|
713
|
+
DISCUSSION_TWO_NORMALIZED=$'Mission: Ship the next workflow round from recent discussion.\nScope:\n- Reset canonical state back to reground for the new mission.\n- Preserve the tracked completion control-plane files.\nConstraints:\n- Do not resume the completed workflow when the new round is clearly different.\nAcceptance:\n- Reset canonical state back to reground for the new mission.\n- Preserve the tracked completion control-plane files.'
|
|
709
714
|
DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
|
|
710
715
|
write_session "$SESSION_TWO_NORMALIZED" "$ROOT" "$DISCUSSION_TWO_NORMALIZED"
|
|
711
716
|
|
|
@@ -715,20 +720,45 @@ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" \
|
|
|
715
720
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
716
721
|
pi --session "$SESSION_TWO_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-normalized.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-normalized.err"
|
|
717
722
|
|
|
718
|
-
python3 - "$
|
|
723
|
+
python3 - "$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" <<'PY'
|
|
719
724
|
import json
|
|
720
725
|
import sys
|
|
721
726
|
from pathlib import Path
|
|
722
727
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
728
|
+
mission = 'Ship the next workflow round from recent discussion.'
|
|
729
|
+
expected_task_type = 'completion-workflow'
|
|
730
|
+
expected_eval_profile = 'completion-rubric-v1'
|
|
731
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
732
|
+
profile = json.loads(Path('.agent/profile.json').read_text())
|
|
726
733
|
state = json.loads(Path('.agent/state.json').read_text())
|
|
734
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
735
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
736
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
727
737
|
|
|
728
|
-
assert
|
|
729
|
-
assert
|
|
730
|
-
assert
|
|
731
|
-
assert '
|
|
738
|
+
assert mission in mission_text, '.agent/mission.md did not update to the recent-discussion next-round mission anchor'
|
|
739
|
+
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after recent-discussion next-round startup'
|
|
740
|
+
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after recent-discussion next-round startup'
|
|
741
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after starting the next workflow round from recent discussion'
|
|
742
|
+
assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after starting the next workflow round from recent discussion'
|
|
743
|
+
assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after starting the next workflow round from recent discussion'
|
|
744
|
+
assert state['advisory_startup_brief']['mission'] == mission, 'recent-discussion next round should preserve the confirmed startup brief as advisory intake'
|
|
745
|
+
assert state['advisory_startup_brief']['source'] == 'recent_discussion', 'recent-discussion next round should preserve the advisory source'
|
|
746
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after starting the next workflow round from recent discussion'
|
|
747
|
+
assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after starting the next workflow round from recent discussion'
|
|
748
|
+
assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after starting the next workflow round from recent discussion'
|
|
749
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after starting the next workflow round from recent discussion'
|
|
750
|
+
assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after starting the next workflow round from recent discussion'
|
|
751
|
+
assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after starting the next workflow round from recent discussion'
|
|
752
|
+
assert proposal['mission'] == mission, 'recent-discussion next-round proposal snapshot should preserve the synthesized mission anchor'
|
|
753
|
+
assert proposal['source'] == 'session', 'recent-discussion next-round proposal snapshot should record the structured-session source'
|
|
754
|
+
assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground for the recent-discussion next workflow round'
|
|
755
|
+
assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue for the recent-discussion next workflow round'
|
|
756
|
+
assert state['requires_reground'] is True, 'requires_reground should reset to true for the recent-discussion next workflow round'
|
|
757
|
+
assert state['project_done'] is False, 'project_done should reset to false for the recent-discussion next workflow round'
|
|
758
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should reset to completion-regrounder for the recent-discussion next workflow round'
|
|
759
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the recent-discussion next-round refocus'
|
|
760
|
+
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_refocus for the recent-discussion next workflow round'
|
|
761
|
+
assert active['status'] == 'idle', 'active-slice should reset to idle for the recent-discussion next workflow round'
|
|
732
762
|
PY
|
|
733
763
|
|
|
734
764
|
# Completed workflow: a fresh explicit primary-agent handoff should still start the next round.
|
|
@@ -947,8 +977,7 @@ after = {path.name: path.read_text() for path in tracked}
|
|
|
947
977
|
assert before == after, 'done /cook inline-args rejection should leave canonical files unchanged'
|
|
948
978
|
PY
|
|
949
979
|
|
|
950
|
-
# Completed workflow again: model-assisted discussion analysis
|
|
951
|
-
# without a fresh valid explicit primary-agent handoff.
|
|
980
|
+
# Completed workflow again: model-assisted discussion analysis should also synthesize the next-round startup brief.
|
|
952
981
|
mark_done
|
|
953
982
|
|
|
954
983
|
SESSION_FIVE="$TMPDIR/session-five.jsonl"
|
|
@@ -963,18 +992,20 @@ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_FIVE" \
|
|
|
963
992
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
964
993
|
pi --session "$SESSION_FIVE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-analyst.out" 2>"$TMPDIR/pi-completion-context-proposal-analyst.err"
|
|
965
994
|
|
|
966
|
-
python3 - "$
|
|
995
|
+
python3 - "$DISCUSSION_SNAPSHOT_FIVE" <<'PY'
|
|
967
996
|
import json
|
|
968
997
|
import sys
|
|
969
998
|
from pathlib import Path
|
|
970
999
|
|
|
971
|
-
|
|
972
|
-
snapshot = Path(sys.argv[3])
|
|
1000
|
+
mission = 'Use a proposal analyst to summarize natural discussion before /cook writes canonical state.'
|
|
973
1001
|
state = json.loads(Path('.agent/state.json').read_text())
|
|
1002
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
974
1003
|
|
|
975
|
-
assert
|
|
976
|
-
assert
|
|
977
|
-
assert '
|
|
1004
|
+
assert proposal['mission'] == mission, 'analyst-driven restart should emit the synthesized mission in the proposal snapshot'
|
|
1005
|
+
assert proposal['source'] == 'analyst', 'analyst-driven restart should preserve the analyst proposal source'
|
|
1006
|
+
assert state['mission_anchor'] == mission, 'analyst-driven restart should promote the synthesized mission into canonical state after Start'
|
|
1007
|
+
assert state['continuation_policy'] == 'continue', 'analyst-driven restart should reopen the workflow after Start'
|
|
1008
|
+
assert state['advisory_startup_brief']['source'] == 'recent_discussion', 'analyst-driven restart should still record recent-discussion advisory intake'
|
|
978
1009
|
PY
|
|
979
1010
|
|
|
980
1011
|
# Custom confirmation UI: start should render proposal content separately from approval-only Start/Cancel actions.
|
|
@@ -1545,7 +1576,7 @@ assert 'First slice goal: Patch the callback edge case and cover it with a focus
|
|
|
1545
1576
|
assert 'Verification commands: npm test -- redirect-edge.spec.ts' in state['advisory_startup_brief']['notes'], 'done-workflow handoff should preserve verification_commands in advisory notes'
|
|
1546
1577
|
PY
|
|
1547
1578
|
|
|
1548
|
-
# Stale handoff: an aged-out capsule should
|
|
1579
|
+
# Stale handoff: an aged-out capsule should no longer block /cook from synthesizing a fresh startup brief from later discussion.
|
|
1549
1580
|
HANDOFF_ROOT_STALE="$TMPDIR/handoff-root-stale"
|
|
1550
1581
|
mkdir -p "$HANDOFF_ROOT_STALE"
|
|
1551
1582
|
cd "$HANDOFF_ROOT_STALE"
|
|
@@ -1590,16 +1621,18 @@ PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$HANDOFF_SNAPSHOT_STALE" \
|
|
|
1590
1621
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
1591
1622
|
pi --session "$HANDOFF_SESSION_STALE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-handoff-stale.out" 2>"$TMPDIR/pi-completion-handoff-stale.err"
|
|
1592
1623
|
|
|
1593
|
-
python3 - "$HANDOFF_SNAPSHOT_STALE"
|
|
1624
|
+
python3 - "$HANDOFF_SNAPSHOT_STALE" <<'PY'
|
|
1625
|
+
import json
|
|
1594
1626
|
import sys
|
|
1595
1627
|
from pathlib import Path
|
|
1596
1628
|
|
|
1597
|
-
snapshot = Path(sys.argv[1])
|
|
1598
|
-
|
|
1629
|
+
snapshot = json.loads(Path(sys.argv[1]).read_text())
|
|
1630
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
1599
1631
|
|
|
1600
|
-
assert
|
|
1601
|
-
assert
|
|
1602
|
-
assert '
|
|
1632
|
+
assert snapshot['mission'] == 'Ship logout redirect consistency instead.', 'aged-out handoff should fall back to the newer recent-discussion mission'
|
|
1633
|
+
assert snapshot['source'] == 'session', 'aged-out handoff fallback should preserve the structured-session proposal source'
|
|
1634
|
+
assert state['mission_anchor'] == 'Ship logout redirect consistency instead.', 'aged-out handoff fallback should promote the newer recent-discussion mission after Start'
|
|
1635
|
+
assert state['advisory_startup_brief']['source'] == 'recent_discussion', 'aged-out handoff fallback should keep the advisory startup source non-canonical'
|
|
1603
1636
|
PY
|
|
1604
1637
|
|
|
1605
1638
|
# Negative handoff rationale: a non-startable capsule must not become the startup mission.
|
package/scripts/release-check.sh
CHANGED
|
@@ -5,56 +5,57 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
5
5
|
cd "$ROOT"
|
|
6
6
|
export PI_COMPLETION_RUNNING_RELEASE_CHECK=1
|
|
7
7
|
|
|
8
|
-
echo "[release-check] running control-plane validation, tracked .agent contract coverage, slice-surface parity,
|
|
8
|
+
echo "[release-check] running control-plane validation, tracked .agent contract coverage, slice-surface parity, mixed-model /cook parity, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, legacy cleanup, evaluator calibration, and rubric contract coverage"
|
|
9
9
|
bash .agent/verify_completion_control_plane.sh
|
|
10
10
|
git ls-files --error-unmatch .agent/README.md .agent/mission.md .agent/profile.json .agent/verify_completion_stop.sh .agent/verify_completion_control_plane.sh >/dev/null
|
|
11
11
|
|
|
12
|
-
echo "[release-check] verifying public /cook parity
|
|
12
|
+
echo "[release-check] verifying public /cook mixed-model parity across docs/help"
|
|
13
13
|
python3 - <<'PY'
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
checks = {
|
|
17
17
|
"README.md": [
|
|
18
18
|
"`/cook` is the explicit workflow boundary for starting, continuing, refocusing, or beginning the next round of long-running repo work.",
|
|
19
|
-
"Only explicit `/cook` enters the workflow. Ordinary prompts stay in the main chat and go straight to the primary agent.",
|
|
20
19
|
"Before you explicitly run `/cook`, the conversation can still stay in ordinary chat: the primary agent may keep answering follow-up questions and refining requirements rather than switching into a hard handoff-only refusal mode.",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"- startup and next-round entry stay confirm-first
|
|
24
|
-
"-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
20
|
+
"If you explicitly ask for a pre-`/cook` preview or capsule, the primary agent may provide one, but that preview is opt-in only and stays non-canonical until you later run `/cook` and choose **Start**.",
|
|
21
|
+
"Bare `/cook` is still the canonical workflow boundary: it synthesizes the startup brief from recent ordinary-chat discussion at `/cook` time, then waits for **Start** or **Cancel** before any canonical `.agent/**` write.",
|
|
22
|
+
"- startup and next-round entry stay confirm-first: bare `/cook` synthesizes the startup brief from recent discussion, then waits for **Start** or **Cancel**",
|
|
23
|
+
"- any pre-`/cook` preview or capsule is explicit-request-only and non-canonical",
|
|
24
|
+
"When no workflow is active, bare `/cook` synthesizes a startup brief from recent ordinary-chat discussion and then waits for **Start** or **Cancel**.",
|
|
25
|
+
"| No workflow yet | `/cook` synthesizes a startup brief from recent discussion and shows **Start** / **Cancel**.",
|
|
26
|
+
"| Previous workflow is `done` | `/cook` synthesizes the next implementation round from recent discussion behind **Start** / **Cancel**.",
|
|
27
|
+
"any pre-`/cook` preview or capsule is advisory only and never writes canonical workflow state by itself",
|
|
29
28
|
],
|
|
30
29
|
"CHANGELOG.md": [
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
30
|
+
"moving default startup and done-workflow next-round synthesis to bare `/cook` from recent ordinary-chat discussion behind the existing **Start** / **Cancel** approval gate",
|
|
31
|
+
"kept pre-`/cook` previews or `cook_handoff` capsules opt-in only, non-canonical, and advisory until the user explicitly runs `/cook`; bare `/cook` no longer depends on a default prebuilt capsule for new-workflow startup",
|
|
32
|
+
"updated public parity and packaged release verification so README/help/changelog/release-check all describe and gate the shipped mixed model truthfully",
|
|
34
33
|
],
|
|
35
34
|
"extensions/completion/prompt-surfaces.ts": [
|
|
36
|
-
'"
|
|
37
|
-
'"
|
|
38
|
-
'"
|
|
39
|
-
'"
|
|
35
|
+
'"Distinguish a workflow-worthy handoff from an opt-in preview request: by default, do not emit a structured preview or cook_handoff capsule in ordinary chat once the task is concrete enough; recommend bare /cook instead."',
|
|
36
|
+
'"When handing off, explain that bare /cook will synthesize a startup brief from recent ordinary-chat discussion for a new workflow or next round, while already-active workflows resume from canonical .agent state unless the user explicitly chooses a replacement path backed by a fresh valid explicit handoff."',
|
|
37
|
+
'"If the user explicitly asks for a /cook preview or capsule before running /cook, you may append one exact fenced block in the same assistant reply using ```cook_handoff ... ``` JSON',
|
|
38
|
+
'"Any preview capsule is startup intake for /cook only: do not present it as canonical .agent state, an active slice, or a persistent repo contract."',
|
|
40
39
|
],
|
|
41
40
|
"extensions/completion/index.ts": [
|
|
42
|
-
'"/cook failed closed because
|
|
43
|
-
'description: "/cook workflow:
|
|
41
|
+
'"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief for bare /cook with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook; canonical workflow state is still only written after Start."',
|
|
42
|
+
'description: "/cook workflow: synthesize an approval-gated startup brief from recent discussion for new-workflow or next-round entry, resume the current workflow from canonical state, or confirm an explicit active-workflow replacement"',
|
|
44
43
|
],
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
forbidden = {
|
|
48
47
|
"README.md": [
|
|
49
|
-
"
|
|
50
|
-
"`/cook`
|
|
51
|
-
"
|
|
48
|
+
"wait for a fresh primary-agent handoff, then run `/cook`",
|
|
49
|
+
"That handoff should include an explicit structured `/cook` capsule in the assistant reply once the first slice is implementation-ready, so `/cook` can confirm the already-formed mission instead of re-deriving it from broad ambient context.",
|
|
50
|
+
"- startup and next-round entry stay confirm-first and require a fresh valid explicit primary-agent handoff",
|
|
51
|
+
"`/cook` first looks for a fresh explicit primary-agent handoff capsule from recent ordinary-chat discussion.",
|
|
52
|
+
"Without one, `/cook` fails closed instead of deriving the next round from recent discussion.",
|
|
52
53
|
],
|
|
53
|
-
"
|
|
54
|
-
|
|
54
|
+
"CHANGELOG.md": [
|
|
55
|
+
"kept bare `/cook` startup and done-workflow next-round entry fail-closed on missing or non-startable explicit handoffs, while active workflows still resume from canonical `.agent/**` state unless a fresh explicit handoff proposes replacement",
|
|
55
56
|
],
|
|
56
57
|
"extensions/completion/index.ts": [
|
|
57
|
-
'description: "/cook workflow: derive a startup brief from recent discussion
|
|
58
|
+
'description: "/cook workflow: derive a startup brief from recent discussion for new-workflow or next-round entry, resume the current workflow from canonical state, or confirm an explicit active-workflow replacement"',
|
|
58
59
|
'"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook."',
|
|
59
60
|
],
|
|
60
61
|
}
|
|
@@ -63,13 +64,13 @@ for path, needles in checks.items():
|
|
|
63
64
|
text = Path(path).read_text()
|
|
64
65
|
for needle in needles:
|
|
65
66
|
if needle not in text:
|
|
66
|
-
raise SystemExit(f"[release-check] missing expected /cook parity text in {path}: {needle}")
|
|
67
|
+
raise SystemExit(f"[release-check] missing expected /cook mixed-model parity text in {path}: {needle}")
|
|
67
68
|
|
|
68
69
|
for path, needles in forbidden.items():
|
|
69
70
|
text = Path(path).read_text()
|
|
70
71
|
for needle in needles:
|
|
71
72
|
if needle in text:
|
|
72
|
-
raise SystemExit(f"[release-check] found stale /cook
|
|
73
|
+
raise SystemExit(f"[release-check] found stale /cook explicit-handoff-only text in {path}: {needle}")
|
|
73
74
|
PY
|
|
74
75
|
|
|
75
76
|
npm run smoke-test
|
package/scripts/smoke-test.sh
CHANGED
|
@@ -103,55 +103,8 @@ INLINE_REJECTION_ROUTING_SNAPSHOT="$TMPDIR/inline-arg-routing.json"
|
|
|
103
103
|
INLINE_REJECTION_PROPOSAL_SNAPSHOT="$TMPDIR/inline-arg-proposal.json"
|
|
104
104
|
INLINE_REJECTION_CHOOSER_SNAPSHOT="$TMPDIR/inline-arg-chooser.json"
|
|
105
105
|
BOOTSTRAP_SESSION="$TMPDIR/session-smoke-bootstrap.jsonl"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
capsule = {
|
|
109
|
-
"kind": "cook_handoff",
|
|
110
|
-
"source": "primary_agent",
|
|
111
|
-
"captured_at": "2026-01-01T00:00:02.000Z",
|
|
112
|
-
"source_turn_id": "m0002",
|
|
113
|
-
"mission": "Exercise smoke-test bootstrap.",
|
|
114
|
-
"scope": [
|
|
115
|
-
"Materialize the canonical completion control-plane files.",
|
|
116
|
-
"Keep the smoke test on supported /cook startup behavior."
|
|
117
|
-
],
|
|
118
|
-
"constraints": [
|
|
119
|
-
"Keep startup proposal confirmation approval-only."
|
|
120
|
-
],
|
|
121
|
-
"acceptance": [
|
|
122
|
-
"Scaffold .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json for the smoke fixture.",
|
|
123
|
-
"Keep scripts/smoke-test.sh and kickoff-prompt coverage truthful for packaged bootstrap."
|
|
124
|
-
],
|
|
125
|
-
"risks": [
|
|
126
|
-
"Smoke-test bootstrap should stay anchored to the fresh explicit handoff."
|
|
127
|
-
],
|
|
128
|
-
"notes": [
|
|
129
|
-
"Keep the smoke fixture aligned with the shipped explicit-handoff-only startup contract."
|
|
130
|
-
],
|
|
131
|
-
"handoff_kind": "implementation_workflow_handoff",
|
|
132
|
-
"first_slice_goal": "Scaffold canonical completion files and verify the packaged startup contract.",
|
|
133
|
-
"first_slice_non_goals": [
|
|
134
|
-
"Do not broaden the smoke fixture beyond the packaged startup surfaces."
|
|
135
|
-
],
|
|
136
|
-
"implementation_surfaces": [
|
|
137
|
-
".agent/README.md",
|
|
138
|
-
"scripts/smoke-test.sh"
|
|
139
|
-
],
|
|
140
|
-
"verification_commands": [
|
|
141
|
-
"npm run smoke-test"
|
|
142
|
-
],
|
|
143
|
-
"why_this_slice_first": "The packaged explicit-handoff startup path must work before later workflow verification can run.",
|
|
144
|
-
"task_type": "completion-workflow",
|
|
145
|
-
"evaluation_profile": "completion-rubric-v1",
|
|
146
|
-
"why_cook_now": "The startup handoff is concrete enough to bootstrap canonical workflow files."
|
|
147
|
-
}
|
|
148
|
-
messages = [
|
|
149
|
-
{"role": "user", "content": "Please prepare the packaged smoke-test bootstrap path and tell me when it is ready for /cook."},
|
|
150
|
-
{"role": "assistant", "content": "This bootstrap path is ready for /cook. Run /cook to confirm the startup brief.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
|
|
151
|
-
]
|
|
152
|
-
print(json.dumps(messages, ensure_ascii=False))
|
|
153
|
-
PY
|
|
154
|
-
)"
|
|
106
|
+
BOOTSTRAP_DISCUSSION=$'Mission: Exercise smoke-test bootstrap.\nScope:\n- Materialize the canonical completion control-plane files.\n- Keep the smoke test on supported /cook startup behavior.\nConstraints:\n- Keep startup proposal confirmation approval-only.\nAcceptance:\n- Scaffold .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json for the smoke fixture.\n- Keep scripts/smoke-test.sh and kickoff-prompt coverage truthful for packaged bootstrap.'
|
|
107
|
+
BOOTSTRAP_PROPOSAL="$TMPDIR/bootstrap-proposal.json"
|
|
155
108
|
|
|
156
109
|
mkdir -p "$ROOT"
|
|
157
110
|
cd "$ROOT"
|
|
@@ -180,11 +133,12 @@ assert not chooser.exists(), 'startup /cook inline-args rejection should not ope
|
|
|
180
133
|
assert '/cook no longer accepts inline arguments.' in output, 'startup /cook inline-args rejection should explain the bare-only entry contract'
|
|
181
134
|
PY
|
|
182
135
|
|
|
183
|
-
|
|
136
|
+
write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
|
|
184
137
|
|
|
185
138
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
186
139
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
187
140
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
141
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$BOOTSTRAP_PROPOSAL" \
|
|
188
142
|
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$KICKOFF_PROMPT" \
|
|
189
143
|
pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" \
|
|
190
144
|
>"$TMPDIR/pi-completion-smoke-bootstrap.out" 2>"$TMPDIR/pi-completion-smoke-bootstrap.err"
|
|
@@ -197,7 +151,7 @@ git ls-files --error-unmatch .agent/README.md .agent/mission.md .agent/profile.j
|
|
|
197
151
|
bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
198
152
|
bash .agent/verify_completion_stop.sh >/dev/null
|
|
199
153
|
|
|
200
|
-
python3 - "$KICKOFF_PROMPT" <<'PY'
|
|
154
|
+
python3 - "$KICKOFF_PROMPT" "$BOOTSTRAP_PROPOSAL" <<'PY'
|
|
201
155
|
import json
|
|
202
156
|
import sys
|
|
203
157
|
from pathlib import Path
|
|
@@ -211,6 +165,7 @@ plan = json.loads(Path('.agent/plan.json').read_text())
|
|
|
211
165
|
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
212
166
|
evidence = json.loads(Path('.agent/verification-evidence.json').read_text())
|
|
213
167
|
kickoff = Path(sys.argv[1]).read_text()
|
|
168
|
+
proposal = json.loads(Path(sys.argv[2]).read_text())
|
|
214
169
|
|
|
215
170
|
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after bootstrap'
|
|
216
171
|
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after bootstrap'
|
|
@@ -224,7 +179,7 @@ assert active['implementation_surfaces'] == [], 'active-slice.json should scaffo
|
|
|
224
179
|
assert active['verification_commands'] == [], 'active-slice.json should scaffold empty verification_commands'
|
|
225
180
|
brief = state['advisory_startup_brief']
|
|
226
181
|
assert brief['kind'] == 'startup_brief', 'state.json should preserve the confirmed startup brief as advisory intake'
|
|
227
|
-
assert brief['source'] == '
|
|
182
|
+
assert brief['source'] == 'recent_discussion', 'smoke bootstrap should record recent-discussion synthesis in advisory intake'
|
|
228
183
|
assert brief['mission'] == state['mission_anchor'], 'advisory startup brief mission should match the canonical mission anchor after bootstrap'
|
|
229
184
|
assert brief['scope'] == ['Materialize the canonical completion control-plane files.', 'Keep the smoke test on supported /cook startup behavior.'], 'advisory startup brief should preserve scope items'
|
|
230
185
|
assert brief['constraints'] == ['Keep startup proposal confirmation approval-only.'], 'advisory startup brief should preserve constraints'
|
|
@@ -232,9 +187,10 @@ assert brief['acceptance'] == [
|
|
|
232
187
|
'Scaffold .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json for the smoke fixture.',
|
|
233
188
|
'Keep scripts/smoke-test.sh and kickoff-prompt coverage truthful for packaged bootstrap.'
|
|
234
189
|
], 'advisory startup brief should preserve acceptance'
|
|
235
|
-
assert brief['risks'] == [
|
|
236
|
-
assert '
|
|
237
|
-
assert '
|
|
190
|
+
assert brief['risks'] == [], 'recent-discussion smoke bootstrap should not invent handoff-only risks'
|
|
191
|
+
assert brief['notes'] == ['No additional operator notes were derived from recent discussion.'], 'recent-discussion smoke bootstrap should keep advisory notes non-canonical'
|
|
192
|
+
assert proposal['mission'] == state['mission_anchor'], 'recent-discussion smoke bootstrap should persist the synthesized mission in the proposal snapshot'
|
|
193
|
+
assert proposal['source'] == 'session', 'recent-discussion smoke bootstrap should snapshot the structured-session proposal source'
|
|
238
194
|
assert evidence['artifact_type'] == 'completion-verification-evidence', 'verification-evidence.json artifact_type mismatch after bootstrap'
|
|
239
195
|
assert evidence['subject_type'] == 'none', 'verification-evidence.json should scaffold idle subject_type'
|
|
240
196
|
assert evidence['verification_commands'] == [], 'verification-evidence.json should scaffold empty verification_commands'
|
|
@@ -269,14 +225,14 @@ assert '/cook is the only explicit entrypoint into long-running completion workf
|
|
|
269
225
|
assert 'do not begin long-running product implementation in ordinary chat' in handoff_text, 'ordinary handoff reminder should block workflow-level implementation before /cook'
|
|
270
226
|
assert 'recommend /cook as the workflow boundary while keeping the conversation in ordinary chat until the user explicitly runs /cook.' in handoff_text, 'ordinary handoff reminder should keep pre-/cook discussion advisory-first'
|
|
271
227
|
assert 'continue that ordinary-chat discussion normally instead of switching into a handoff-only refusal mode' in handoff_text, 'ordinary handoff reminder should avoid hard refusal mode before explicit /cook'
|
|
272
|
-
assert '
|
|
273
|
-
assert '
|
|
274
|
-
assert '
|
|
275
|
-
assert '
|
|
276
|
-
assert '
|
|
277
|
-
assert '
|
|
228
|
+
assert 'do not emit a structured preview or cook_handoff capsule in ordinary chat once the task is concrete enough' in handoff_text, 'ordinary handoff reminder should keep pre-/cook capsules opt-in instead of default'
|
|
229
|
+
assert 'bare /cook will synthesize a startup brief from recent ordinary-chat discussion' in handoff_text, 'ordinary handoff reminder should describe /cook-time startup synthesis truthfully'
|
|
230
|
+
assert 'If the user explicitly asks for a /cook preview or capsule before running /cook' in handoff_text, 'ordinary handoff reminder should keep previews opt-in'
|
|
231
|
+
assert 'implementation_workflow_handoff' in handoff_text, 'ordinary handoff reminder should preserve the explicit preview handoff kind'
|
|
232
|
+
assert 'first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, why_this_slice_first' in handoff_text, 'ordinary handoff reminder should preserve first-slice preview fields when explicitly requested'
|
|
233
|
+
assert 'Any preview capsule is startup intake for /cook only' in handoff_text, 'ordinary handoff reminder should keep any preview non-canonical'
|
|
278
234
|
assert 'resume from canonical .agent state' in handoff_text, 'ordinary handoff reminder should preserve active-workflow canonical resume wording'
|
|
279
|
-
assert '
|
|
235
|
+
assert 'fresh valid explicit primary-agent handoff capsule from recent ordinary-chat discussion' not in handoff_text, 'ordinary handoff reminder should no longer describe explicit capsules as the default startup path'
|
|
280
236
|
assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
|
|
281
237
|
assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
|
|
282
238
|
PY
|
|
@@ -36,12 +36,13 @@ When the task is judged ready for completion workflow, the primary agent must:
|
|
|
36
36
|
- stop before long-running implementation
|
|
37
37
|
- not edit tracked product files in ordinary chat for that workflow-level task
|
|
38
38
|
- recommend bare `/cook` as the explicit workflow boundary once the task is implementation-ready
|
|
39
|
-
- explain that `/cook`
|
|
40
|
-
- distinguish a workflow-worthy handoff from an
|
|
41
|
-
-
|
|
39
|
+
- explain that bare `/cook` synthesizes a startup brief from recent ordinary-chat discussion for a new workflow or next round, while active workflows resume from canonical state unless the user explicitly chooses a replacement path backed by a fresh valid explicit handoff
|
|
40
|
+
- distinguish a workflow-worthy handoff from an opt-in preview request
|
|
41
|
+
- not append an implementation-ready `/cook` handoff capsule by default once the task becomes concrete enough; ordinary chat stays advisory-first until explicit `/cook`
|
|
42
|
+
- only provide a `/cook` startup preview or `cook_handoff` capsule when the user explicitly asks for that preview behavior in ordinary chat
|
|
42
43
|
- if the user asks follow-up questions or refines requirements before running `/cook`, continue ordinary-chat discussion normally without acting as though workflow already started
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
Optional preview capsule format when the user explicitly asks for it:
|
|
45
46
|
|
|
46
47
|
````text
|
|
47
48
|
```cook_handoff
|
|
@@ -73,15 +74,15 @@ Required capsule format:
|
|
|
73
74
|
Notes:
|
|
74
75
|
|
|
75
76
|
- `constraints` may be replaced or supplemented by `non_goals` when clearer.
|
|
76
|
-
- `first_slice_goal`, `first_slice_non_goals`, `implementation_surfaces`, `verification_commands`, and `why_this_slice_first` are required only for implementation-ready
|
|
77
|
-
- If the work is workflow-worthy but
|
|
78
|
-
- If later ordinary-chat discussion materially changes the startup brief before `/cook` runs, update or replace the capsule in a later assistant reply.
|
|
77
|
+
- `first_slice_goal`, `first_slice_non_goals`, `implementation_surfaces`, `verification_commands`, and `why_this_slice_first` are required only for an implementation-ready preview capsule.
|
|
78
|
+
- If the work is workflow-worthy but the first slice still needs refinement, say that `/cook` will be the right next step once the slice is concrete enough, then keep refining in ordinary chat without emitting a preview capsule unless the user explicitly asks for one.
|
|
79
|
+
- If later ordinary-chat discussion materially changes the startup brief before `/cook` runs, update or replace the preview capsule in a later assistant reply.
|
|
79
80
|
- The mission must be positively startable implementation work; do not use rejection or suppression text as the mission.
|
|
80
|
-
-
|
|
81
|
+
- Any preview capsule is startup intake for `/cook` only. It is not canonical `.agent/**` state, not active-slice state, and not a second repo contract source.
|
|
81
82
|
|
|
82
83
|
Suggested wording:
|
|
83
84
|
|
|
84
|
-
> This task now looks like `/cook` workflow work, but we are still in ordinary chat until you explicitly run `/cook`. If you want to keep refining the first slice first, we can do that here. Once you want to start implementation workflow, run bare `/cook
|
|
85
|
+
> This task now looks like `/cook` workflow work, but we are still in ordinary chat until you explicitly run `/cook`. If you want to keep refining the first slice first, we can do that here. Once you want to start implementation workflow, run bare `/cook` and it will synthesize the startup brief from our recent discussion before the Start/Cancel gate. If you explicitly want a preview capsule first, ask and I can sketch one here.
|
|
85
86
|
|
|
86
87
|
A short recap may include mission, scope, or acceptance, but that recap must not be presented as canonical plan state.
|
|
87
88
|
|