@linimin/pi-letscook 0.1.58 → 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/README.md +28 -0
- package/.agent/mission.md +8 -0
- package/.agent/profile.json +13 -0
- package/.agent/verify_completion_control_plane.sh +203 -0
- package/.agent/verify_completion_stop.sh +20 -0
- package/CHANGELOG.md +12 -0
- package/README.md +28 -22
- package/extensions/completion/driver.ts +51 -39
- package/extensions/completion/index.ts +29 -21
- package/extensions/completion/prompt-surfaces.ts +12 -10
- package/extensions/completion/proposal.ts +0 -15
- package/package.json +6 -1
- package/scripts/active-slice-contract-test.sh +93 -2
- package/scripts/canonical-evidence-artifact-test.sh +93 -2
- package/scripts/context-proposal-test.sh +577 -727
- package/scripts/refocus-test.sh +196 -28
- package/scripts/release-check.sh +57 -34
- package/scripts/smoke-test.sh +73 -14
- package/skills/cook-handoff-boundary/SKILL.md +16 -10
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
missionAnchorsStrictlyEquivalent,
|
|
26
26
|
normalizeMissionAnchorText,
|
|
27
27
|
resolveContextProposalConfirmationAction,
|
|
28
|
-
shouldTreatBareActiveWorkflowProposalAsClearRefocus,
|
|
29
28
|
stripCodeBlocks,
|
|
30
29
|
} from "./proposal";
|
|
31
30
|
import type {
|
|
@@ -128,15 +127,14 @@ type CookContextProposalResult = {
|
|
|
128
127
|
};
|
|
129
128
|
|
|
130
129
|
type ActiveWorkflowProposalAssessment = {
|
|
131
|
-
action: "continue" | "refocus" | "
|
|
130
|
+
action: "continue" | "refocus" | "blocked";
|
|
132
131
|
currentMissionAnchor: string;
|
|
133
132
|
proposal?: ContextProposal;
|
|
134
133
|
blockedFailureMessage?: string;
|
|
135
134
|
reason:
|
|
136
135
|
| "matching_mission"
|
|
137
|
-
| "
|
|
138
|
-
| "
|
|
139
|
-
| "ambiguous_discussion"
|
|
136
|
+
| "missing_explicit_handoff"
|
|
137
|
+
| "fresh_explicit_handoff"
|
|
140
138
|
| "fresh_explicit_handoff_not_startable";
|
|
141
139
|
};
|
|
142
140
|
|
|
@@ -211,7 +209,7 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
|
|
|
211
209
|
|
|
212
210
|
const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
|
|
213
211
|
const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
|
|
214
|
-
"/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.";
|
|
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.";
|
|
215
213
|
|
|
216
214
|
function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
217
215
|
return asString(snapshot?.state?.continuation_policy) === "done";
|
|
@@ -374,6 +372,27 @@ async function promptContextProposalConfirmationAction(
|
|
|
374
372
|
});
|
|
375
373
|
}
|
|
376
374
|
|
|
375
|
+
async function deriveCookStartupProposal(
|
|
376
|
+
ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
|
|
377
|
+
projectName: string,
|
|
378
|
+
): Promise<CookContextProposalResult> {
|
|
379
|
+
const recentMessages = collectRecentSessionMessages(ctx, { isRecord, asString, asNumber, isStaleContextError });
|
|
380
|
+
const explicitHandoff = assessLatestCookHandoffProposal(recentMessages, projectName, {
|
|
381
|
+
asString,
|
|
382
|
+
asStringArray,
|
|
383
|
+
assessMissionAnchor,
|
|
384
|
+
normalizeMissionAnchorText,
|
|
385
|
+
isWeakMissionAnchor,
|
|
386
|
+
missionAnchorsStrictlyEquivalent,
|
|
387
|
+
stripCodeBlocks,
|
|
388
|
+
});
|
|
389
|
+
if (explicitHandoff.status === "startable") return { proposal: explicitHandoff.proposal };
|
|
390
|
+
if (explicitHandoff.status === "fresh_but_not_startable") {
|
|
391
|
+
return { blockedFailureMessage: explicitHandoff.message };
|
|
392
|
+
}
|
|
393
|
+
return {};
|
|
394
|
+
}
|
|
395
|
+
|
|
377
396
|
async function deriveCookContextProposal(
|
|
378
397
|
ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
|
|
379
398
|
projectName: string,
|
|
@@ -396,19 +415,8 @@ async function deriveCookContextProposal(
|
|
|
396
415
|
`verification summary: ${asString(snapshot.verificationEvidence?.summary) ?? "(none)"}`,
|
|
397
416
|
]
|
|
398
417
|
: [];
|
|
399
|
-
const explicitHandoff =
|
|
400
|
-
|
|
401
|
-
asStringArray,
|
|
402
|
-
assessMissionAnchor,
|
|
403
|
-
normalizeMissionAnchorText,
|
|
404
|
-
isWeakMissionAnchor,
|
|
405
|
-
missionAnchorsStrictlyEquivalent,
|
|
406
|
-
stripCodeBlocks,
|
|
407
|
-
});
|
|
408
|
-
if (explicitHandoff.status === "startable") return { proposal: explicitHandoff.proposal };
|
|
409
|
-
if (explicitHandoff.status === "fresh_but_not_startable") {
|
|
410
|
-
return { blockedFailureMessage: explicitHandoff.message };
|
|
411
|
-
}
|
|
418
|
+
const explicitHandoff = await deriveCookStartupProposal(ctx, projectName);
|
|
419
|
+
if (explicitHandoff.proposal || explicitHandoff.blockedFailureMessage) return explicitHandoff;
|
|
412
420
|
return {
|
|
413
421
|
proposal: await deriveCookContextProposalFromRecentDiscussion(projectName, recentEntries, {
|
|
414
422
|
asString,
|
|
@@ -921,7 +929,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
921
929
|
structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
|
|
922
930
|
mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
|
|
923
931
|
cookCommandSpec: {
|
|
924
|
-
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",
|
|
925
933
|
},
|
|
926
934
|
buildContextProposalContinuationReason,
|
|
927
935
|
completionKickoff,
|
|
@@ -934,6 +942,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
934
942
|
completionTestWorkflowMissionOverride,
|
|
935
943
|
confirmContextProposal,
|
|
936
944
|
deriveCookContextProposal,
|
|
945
|
+
deriveCookStartupProposal,
|
|
937
946
|
emitCommandText,
|
|
938
947
|
finalizeContextProposalAnalysis,
|
|
939
948
|
getCtxCwd,
|
|
@@ -948,7 +957,6 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
948
957
|
scaffoldCompletionFiles,
|
|
949
958
|
shouldSkipDriverKickoffForTests,
|
|
950
959
|
shouldTestAutoContinueOnSessionStart,
|
|
951
|
-
shouldTreatBareActiveWorkflowProposalAsClearRefocus,
|
|
952
960
|
};
|
|
953
961
|
|
|
954
962
|
|
|
@@ -27,17 +27,19 @@ export type AdvisoryStartupBrief = {
|
|
|
27
27
|
export function buildCookHandoffBoundaryReminder(): string {
|
|
28
28
|
return [
|
|
29
29
|
"You are still in ordinary main chat before any explicit /cook workflow entry.",
|
|
30
|
-
"Use ordinary chat to clarify requirements, discuss tradeoffs,
|
|
30
|
+
"Use ordinary chat to clarify requirements, discuss tradeoffs, propose implementation approaches, and refine scope with the user.",
|
|
31
31
|
"/cook is the only explicit entrypoint into long-running completion workflow.",
|
|
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 —
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"If the task is workflow-worthy but
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"If the
|
|
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
|
+
"Instead, recommend /cook as the workflow boundary while keeping the conversation in ordinary chat until the user explicitly runs /cook.",
|
|
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 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.",
|
|
41
43
|
].join(" ");
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -1246,7 +1246,6 @@ export function extractContextProposalFromStructuredSession(
|
|
|
1246
1246
|
|
|
1247
1247
|
const COOK_HANDOFF_BLOCK_REGEX = /```cook_handoff\s*([\s\S]*?)```/giu;
|
|
1248
1248
|
const COOK_HANDOFF_MAX_AGE_MS = 45 * 60 * 1000;
|
|
1249
|
-
const COOK_HANDOFF_MAX_LATER_NON_COMMAND_MESSAGES = 2;
|
|
1250
1249
|
const COOK_HANDOFF_NEGATIVE_MISSION_REGEX =
|
|
1251
1250
|
/(?:\b(?:do not|don't|dont|not|never|avoid|skip|refuse|recognize that|suppress|ignore|block|prevent)\b|(?:不要|別|别|勿|禁止|避免|忽略|阻止))/iu;
|
|
1252
1251
|
const COOK_HANDOFF_WORKFLOW_ONLY_ACCEPTANCE_REGEX =
|
|
@@ -1389,19 +1388,6 @@ function isStartableCookHandoffCapsule(
|
|
|
1389
1388
|
return cookHandoffStartabilityFailures(capsule, deps).length === 0;
|
|
1390
1389
|
}
|
|
1391
1390
|
|
|
1392
|
-
function laterMessagesInvalidateCookHandoff(
|
|
1393
|
-
laterMessages: RecentSessionMessage[],
|
|
1394
|
-
deps: Pick<ProposalParseDeps, "stripCodeBlocks">,
|
|
1395
|
-
): boolean {
|
|
1396
|
-
const laterNonCommandMessages = laterMessages.filter((entry) => !entry.isCommand);
|
|
1397
|
-
if (laterNonCommandMessages.length > COOK_HANDOFF_MAX_LATER_NON_COMMAND_MESSAGES) return true;
|
|
1398
|
-
return laterNonCommandMessages.some((entry) => {
|
|
1399
|
-
if (entry.role === "summary") return false;
|
|
1400
|
-
if (!hasRecentDiscussionImplementationIntent(entry.text, deps.stripCodeBlocks)) return false;
|
|
1401
|
-
return true;
|
|
1402
|
-
});
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
1391
|
function cookHandoffIsFreshEnough(capsule: CookHandoffCapsule, laterMessages: RecentSessionMessage[]): boolean {
|
|
1406
1392
|
const capturedAtMs = Date.parse(capsule.captured_at);
|
|
1407
1393
|
if (!Number.isFinite(capturedAtMs)) return false;
|
|
@@ -1484,7 +1470,6 @@ export function assessLatestCookHandoffProposal(
|
|
|
1484
1470
|
const capsule = capsules[capsuleIndex];
|
|
1485
1471
|
const laterMessages = recentMessages.slice(0, index);
|
|
1486
1472
|
if (!cookHandoffIsFreshEnough(capsule, laterMessages)) continue;
|
|
1487
|
-
if (laterMessagesInvalidateCookHandoff(laterMessages, deps)) continue;
|
|
1488
1473
|
const failures = cookHandoffStartabilityFailures(capsule, deps);
|
|
1489
1474
|
if (failures.length > 0) {
|
|
1490
1475
|
return {
|
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,
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"subagent"
|
|
23
23
|
],
|
|
24
24
|
"files": [
|
|
25
|
+
".agent/README.md",
|
|
26
|
+
".agent/mission.md",
|
|
27
|
+
".agent/profile.json",
|
|
28
|
+
".agent/verify_completion_stop.sh",
|
|
29
|
+
".agent/verify_completion_control_plane.sh",
|
|
25
30
|
"extensions",
|
|
26
31
|
"skills",
|
|
27
32
|
"agents",
|
|
@@ -47,6 +47,49 @@ with session_path.open('w', encoding='utf-8') as fh:
|
|
|
47
47
|
PY
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
write_session_messages() {
|
|
51
|
+
local session_path="$1"
|
|
52
|
+
local cwd="$2"
|
|
53
|
+
local messages_json="$3"
|
|
54
|
+
python3 - "$session_path" "$cwd" "$messages_json" <<'PY'
|
|
55
|
+
import json
|
|
56
|
+
import sys
|
|
57
|
+
from pathlib import Path
|
|
58
|
+
|
|
59
|
+
session_path = Path(sys.argv[1])
|
|
60
|
+
cwd = sys.argv[2]
|
|
61
|
+
messages = json.loads(sys.argv[3])
|
|
62
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
entries = [
|
|
64
|
+
{
|
|
65
|
+
"type": "session",
|
|
66
|
+
"version": 3,
|
|
67
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
68
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
69
|
+
"cwd": cwd,
|
|
70
|
+
},
|
|
71
|
+
]
|
|
72
|
+
parent_id = None
|
|
73
|
+
for index, message in enumerate(messages, start=1):
|
|
74
|
+
entry_id = f"m{index:04d}"
|
|
75
|
+
entries.append({
|
|
76
|
+
"type": "message",
|
|
77
|
+
"id": entry_id,
|
|
78
|
+
"parentId": parent_id,
|
|
79
|
+
"timestamp": f"2026-01-01T00:00:{index:02d}.000Z",
|
|
80
|
+
"message": {
|
|
81
|
+
"role": message["role"],
|
|
82
|
+
"content": message["content"],
|
|
83
|
+
"timestamp": 1767225600000 + index * 1000,
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
parent_id = entry_id
|
|
87
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
88
|
+
for entry in entries:
|
|
89
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
90
|
+
PY
|
|
91
|
+
}
|
|
92
|
+
|
|
50
93
|
cd "$PKG_ROOT"
|
|
51
94
|
|
|
52
95
|
node <<'NODE'
|
|
@@ -94,11 +137,59 @@ NODE
|
|
|
94
137
|
ROOT="$TMPDIR/repo"
|
|
95
138
|
PROMPT="$TMPDIR/resume-prompt.txt"
|
|
96
139
|
BOOTSTRAP_SESSION="$TMPDIR/session-active-slice-bootstrap.jsonl"
|
|
97
|
-
|
|
140
|
+
BOOTSTRAP_MESSAGES="$(python3 - <<'PY'
|
|
141
|
+
import json
|
|
142
|
+
capsule = {
|
|
143
|
+
"kind": "cook_handoff",
|
|
144
|
+
"source": "primary_agent",
|
|
145
|
+
"captured_at": "2026-01-01T00:00:02.000Z",
|
|
146
|
+
"source_turn_id": "m0002",
|
|
147
|
+
"mission": "Exercise active-slice contract parity.",
|
|
148
|
+
"scope": [
|
|
149
|
+
"Bootstrap canonical completion files for the active-slice contract fixture.",
|
|
150
|
+
"Keep the fixture on the shipped explicit-handoff startup path."
|
|
151
|
+
],
|
|
152
|
+
"constraints": [
|
|
153
|
+
"Use supported bare /cook startup only."
|
|
154
|
+
],
|
|
155
|
+
"acceptance": [
|
|
156
|
+
"Materialize .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before the fixture rewrites them.",
|
|
157
|
+
"Keep scripts/active-slice-contract-test.sh aligned with the packaged startup contract."
|
|
158
|
+
],
|
|
159
|
+
"risks": [
|
|
160
|
+
"Active-slice fixture bootstrap must stay anchored to the fresh explicit handoff."
|
|
161
|
+
],
|
|
162
|
+
"notes": [
|
|
163
|
+
"This handoff exists only to scaffold canonical files before the fixture rewrites them for contract parity coverage."
|
|
164
|
+
],
|
|
165
|
+
"handoff_kind": "implementation_workflow_handoff",
|
|
166
|
+
"first_slice_goal": "Scaffold active-slice contract fixture files before rewriting them for parity verification.",
|
|
167
|
+
"first_slice_non_goals": [
|
|
168
|
+
"Do not broaden the fixture beyond active-slice contract surfaces."
|
|
169
|
+
],
|
|
170
|
+
"implementation_surfaces": [
|
|
171
|
+
".agent/active-slice.json",
|
|
172
|
+
"scripts/active-slice-contract-test.sh"
|
|
173
|
+
],
|
|
174
|
+
"verification_commands": [
|
|
175
|
+
"bash scripts/active-slice-contract-test.sh"
|
|
176
|
+
],
|
|
177
|
+
"why_this_slice_first": "The active-slice fixture cannot validate parity until canonical files exist.",
|
|
178
|
+
"task_type": "completion-workflow",
|
|
179
|
+
"evaluation_profile": "completion-rubric-v1",
|
|
180
|
+
"why_cook_now": "The fixture bootstrap is concrete enough to scaffold canonical control-plane files."
|
|
181
|
+
}
|
|
182
|
+
messages = [
|
|
183
|
+
{"role": "user", "content": "Prepare the active-slice contract bootstrap fixture and tell me when it is ready for /cook."},
|
|
184
|
+
{"role": "assistant", "content": "The active-slice contract bootstrap fixture is ready for /cook. Run /cook to confirm it.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
|
|
185
|
+
]
|
|
186
|
+
print(json.dumps(messages, ensure_ascii=False))
|
|
187
|
+
PY
|
|
188
|
+
)"
|
|
98
189
|
mkdir -p "$ROOT"
|
|
99
190
|
cd "$ROOT"
|
|
100
191
|
git init -q
|
|
101
|
-
|
|
192
|
+
write_session_messages "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_MESSAGES"
|
|
102
193
|
|
|
103
194
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
104
195
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
@@ -47,6 +47,49 @@ with session_path.open('w', encoding='utf-8') as fh:
|
|
|
47
47
|
PY
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
write_session_messages() {
|
|
51
|
+
local session_path="$1"
|
|
52
|
+
local cwd="$2"
|
|
53
|
+
local messages_json="$3"
|
|
54
|
+
python3 - "$session_path" "$cwd" "$messages_json" <<'PY'
|
|
55
|
+
import json
|
|
56
|
+
import sys
|
|
57
|
+
from pathlib import Path
|
|
58
|
+
|
|
59
|
+
session_path = Path(sys.argv[1])
|
|
60
|
+
cwd = sys.argv[2]
|
|
61
|
+
messages = json.loads(sys.argv[3])
|
|
62
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
entries = [
|
|
64
|
+
{
|
|
65
|
+
"type": "session",
|
|
66
|
+
"version": 3,
|
|
67
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
68
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
69
|
+
"cwd": cwd,
|
|
70
|
+
},
|
|
71
|
+
]
|
|
72
|
+
parent_id = None
|
|
73
|
+
for index, message in enumerate(messages, start=1):
|
|
74
|
+
entry_id = f"m{index:04d}"
|
|
75
|
+
entries.append({
|
|
76
|
+
"type": "message",
|
|
77
|
+
"id": entry_id,
|
|
78
|
+
"parentId": parent_id,
|
|
79
|
+
"timestamp": f"2026-01-01T00:00:{index:02d}.000Z",
|
|
80
|
+
"message": {
|
|
81
|
+
"role": message["role"],
|
|
82
|
+
"content": message["content"],
|
|
83
|
+
"timestamp": 1767225600000 + index * 1000,
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
parent_id = entry_id
|
|
87
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
88
|
+
for entry in entries:
|
|
89
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
90
|
+
PY
|
|
91
|
+
}
|
|
92
|
+
|
|
50
93
|
cleanup() {
|
|
51
94
|
if [[ -n "$CURRENT_EVIDENCE_BACKUP" && -f "$CURRENT_EVIDENCE_BACKUP" ]]; then
|
|
52
95
|
cp "$CURRENT_EVIDENCE_BACKUP" "$PKG_ROOT/.agent/verification-evidence.json"
|
|
@@ -139,11 +182,59 @@ bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
|
139
182
|
ROOT="$TMPDIR/repo"
|
|
140
183
|
SYSTEM_REMINDER="$TMPDIR/system-reminder.txt"
|
|
141
184
|
BOOTSTRAP_SESSION="$TMPDIR/session-canonical-evidence-bootstrap.jsonl"
|
|
142
|
-
|
|
185
|
+
BOOTSTRAP_MESSAGES="$(python3 - <<'PY'
|
|
186
|
+
import json
|
|
187
|
+
capsule = {
|
|
188
|
+
"kind": "cook_handoff",
|
|
189
|
+
"source": "primary_agent",
|
|
190
|
+
"captured_at": "2026-01-01T00:00:02.000Z",
|
|
191
|
+
"source_turn_id": "m0002",
|
|
192
|
+
"mission": "Exercise canonical evidence fixture bootstrap.",
|
|
193
|
+
"scope": [
|
|
194
|
+
"Materialize canonical completion files for the evidence artifact fixture.",
|
|
195
|
+
"Keep the verification-evidence bootstrap on the supported explicit-handoff startup path."
|
|
196
|
+
],
|
|
197
|
+
"constraints": [
|
|
198
|
+
"Use supported bare /cook startup only."
|
|
199
|
+
],
|
|
200
|
+
"acceptance": [
|
|
201
|
+
"Scaffold .agent/profile.json, .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before the fixture rewrites them.",
|
|
202
|
+
"Keep scripts/canonical-evidence-artifact-test.sh aligned with packaged bootstrap behavior."
|
|
203
|
+
],
|
|
204
|
+
"risks": [
|
|
205
|
+
"Evidence-artifact bootstrap must stay anchored to the fresh explicit handoff."
|
|
206
|
+
],
|
|
207
|
+
"notes": [
|
|
208
|
+
"This fixture exists only to scaffold canonical files before rewriting them for evidence parity coverage."
|
|
209
|
+
],
|
|
210
|
+
"handoff_kind": "implementation_workflow_handoff",
|
|
211
|
+
"first_slice_goal": "Scaffold canonical evidence-artifact fixture files before rewriting them for parity checks.",
|
|
212
|
+
"first_slice_non_goals": [
|
|
213
|
+
"Do not broaden the bootstrap fixture beyond the evidence-artifact surfaces."
|
|
214
|
+
],
|
|
215
|
+
"implementation_surfaces": [
|
|
216
|
+
".agent/verification-evidence.json",
|
|
217
|
+
"scripts/canonical-evidence-artifact-test.sh"
|
|
218
|
+
],
|
|
219
|
+
"verification_commands": [
|
|
220
|
+
"bash ./scripts/canonical-evidence-artifact-test.sh"
|
|
221
|
+
],
|
|
222
|
+
"why_this_slice_first": "The evidence-artifact fixture cannot validate fail-closed parity until canonical files exist.",
|
|
223
|
+
"task_type": "completion-workflow",
|
|
224
|
+
"evaluation_profile": "completion-rubric-v1",
|
|
225
|
+
"why_cook_now": "The fixture bootstrap is concrete enough to create canonical control-plane files."
|
|
226
|
+
}
|
|
227
|
+
messages = [
|
|
228
|
+
{"role": "user", "content": "Prepare the canonical evidence bootstrap fixture and tell me when it is ready for /cook."},
|
|
229
|
+
{"role": "assistant", "content": "The canonical evidence bootstrap fixture is ready for /cook. Run /cook to confirm it.\n\n```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```"},
|
|
230
|
+
]
|
|
231
|
+
print(json.dumps(messages, ensure_ascii=False))
|
|
232
|
+
PY
|
|
233
|
+
)"
|
|
143
234
|
mkdir -p "$ROOT"
|
|
144
235
|
cd "$ROOT"
|
|
145
236
|
git init -q
|
|
146
|
-
|
|
237
|
+
write_session_messages "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_MESSAGES"
|
|
147
238
|
|
|
148
239
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
149
240
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|