@linimin/pi-letscook 0.1.66 → 0.1.68
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 +4 -0
- package/.agent/verify_completion_control_plane.sh +34 -0
- package/CHANGELOG.md +17 -0
- package/README.md +26 -21
- package/agents/completion-bootstrapper.md +2 -1
- package/agents/completion-regrounder.md +16 -10
- package/extensions/completion/driver.ts +85 -37
- package/extensions/completion/index.ts +54 -41
- package/extensions/completion/prompt-surfaces.ts +158 -17
- package/extensions/completion/proposal.ts +61 -26
- package/extensions/completion/role-runner.ts +15 -10
- package/extensions/completion/state-store.ts +87 -2
- package/extensions/completion/types.ts +3 -0
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +7 -12
- package/scripts/canonical-evidence-artifact-test.sh +55 -15
- package/scripts/context-proposal-test.sh +307 -1427
- package/scripts/refocus-test.sh +185 -459
- package/scripts/release-check.sh +13 -11
- package/scripts/role-runner-contract-test.sh +2 -2
- package/scripts/smoke-test.sh +76 -21
- package/skills/completion-protocol/SKILL.md +9 -3
- package/skills/completion-protocol/references/completion.md +37 -2
- package/skills/cook-handoff-boundary/SKILL.md +18 -17
|
@@ -60,11 +60,11 @@ export type CookHandoffCapsule = {
|
|
|
60
60
|
risks: string[];
|
|
61
61
|
notes: string[];
|
|
62
62
|
handoff_kind: "implementation_workflow_handoff";
|
|
63
|
-
first_slice_goal
|
|
63
|
+
first_slice_goal?: string;
|
|
64
64
|
first_slice_non_goals: string[];
|
|
65
65
|
implementation_surfaces: string[];
|
|
66
66
|
verification_commands: string[];
|
|
67
|
-
why_this_slice_first
|
|
67
|
+
why_this_slice_first?: string;
|
|
68
68
|
task_type?: string;
|
|
69
69
|
evaluation_profile?: string;
|
|
70
70
|
why_cook_now?: string;
|
|
@@ -1279,7 +1279,7 @@ function parseCookHandoffCapsulesFromText(
|
|
|
1279
1279
|
const mission = deps.asString(parsed.mission);
|
|
1280
1280
|
const firstSliceGoal = deps.asString(parsed.first_slice_goal ?? parsed.firstSliceGoal);
|
|
1281
1281
|
const whyThisSliceFirst = deps.asString(parsed.why_this_slice_first ?? parsed.whyThisSliceFirst);
|
|
1282
|
-
if (!mission
|
|
1282
|
+
if (!mission) continue;
|
|
1283
1283
|
const scope = deps.asStringArray(parsed.scope);
|
|
1284
1284
|
const constraints = deps.asStringArray(parsed.constraints);
|
|
1285
1285
|
const nonGoals = deps.asStringArray(parsed.non_goals ?? parsed.nonGoals);
|
|
@@ -1325,12 +1325,12 @@ function buildCookHandoffBasisPreview(capsule: CookHandoffCapsule): string {
|
|
|
1325
1325
|
...capsule.constraints,
|
|
1326
1326
|
...capsule.non_goals,
|
|
1327
1327
|
...capsule.acceptance,
|
|
1328
|
-
`first_slice_goal: ${capsule.first_slice_goal}`,
|
|
1329
|
-
...capsule.first_slice_non_goals.map((item) => `first_slice_non_goals: ${item}`),
|
|
1330
|
-
...capsule.implementation_surfaces.map((item) => `implementation_surfaces: ${item}`),
|
|
1331
|
-
...capsule.verification_commands.map((item) => `verification_commands: ${item}`),
|
|
1332
|
-
`why_this_slice_first: ${capsule.why_this_slice_first}`,
|
|
1333
1328
|
];
|
|
1329
|
+
if (capsule.first_slice_goal) parts.push(`first_slice_goal: ${capsule.first_slice_goal}`);
|
|
1330
|
+
parts.push(...capsule.first_slice_non_goals.map((item) => `first_slice_non_goals: ${item}`));
|
|
1331
|
+
parts.push(...capsule.implementation_surfaces.map((item) => `implementation_surfaces: ${item}`));
|
|
1332
|
+
parts.push(...capsule.verification_commands.map((item) => `verification_commands: ${item}`));
|
|
1333
|
+
if (capsule.why_this_slice_first) parts.push(`why_this_slice_first: ${capsule.why_this_slice_first}`);
|
|
1334
1334
|
if (capsule.why_cook_now) parts.push(`why_cook_now: ${capsule.why_cook_now}`);
|
|
1335
1335
|
return parts.join("\n").trim();
|
|
1336
1336
|
}
|
|
@@ -1363,23 +1363,32 @@ function cookHandoffStartabilityFailures(
|
|
|
1363
1363
|
else if (!cookHandoffAcceptanceIsRepoChangeOriented(capsule)) {
|
|
1364
1364
|
failures.push("acceptance is not anchored to concrete repo changes or verification");
|
|
1365
1365
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1366
|
+
if (capsule.first_slice_goal) {
|
|
1367
|
+
const firstSliceGoal = deps.normalizeMissionAnchorText(capsule.first_slice_goal);
|
|
1368
|
+
if (!firstSliceGoal || deps.isWeakMissionAnchor(firstSliceGoal) || COOK_HANDOFF_NEGATIVE_MISSION_REGEX.test(firstSliceGoal)) {
|
|
1369
|
+
failures.push("first_slice_goal is not a useful sequencing hint");
|
|
1370
|
+
} else if (hasExplicitPlanningOnlyDeliverable([capsule.first_slice_goal]) || hasClearNoImplementationSignal([capsule.first_slice_goal])) {
|
|
1371
|
+
failures.push("first_slice_goal is planning-only instead of a repo-change sequencing hint");
|
|
1372
|
+
}
|
|
1371
1373
|
}
|
|
1372
|
-
if (capsule.implementation_surfaces.length === 0) failures.push("implementation_surfaces is empty");
|
|
1373
|
-
if (capsule.verification_commands.length === 0) failures.push("verification_commands is empty");
|
|
1374
1374
|
return failures;
|
|
1375
1375
|
}
|
|
1376
1376
|
|
|
1377
|
-
function buildNonStartableCookHandoffMessage(
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1377
|
+
function buildNonStartableCookHandoffMessage(
|
|
1378
|
+
failures: string[],
|
|
1379
|
+
context: "explicit_preview" | "same_entry_synthesis" = "explicit_preview",
|
|
1380
|
+
): string {
|
|
1381
|
+
return context === "same_entry_synthesis"
|
|
1382
|
+
? [
|
|
1383
|
+
"/cook failed closed because the same-entry primary-agent startup-plan synthesis step returned a startup plan that is still not concrete enough to seed workflow planning yet.",
|
|
1384
|
+
"Clarify the mission, scope, acceptance, or verification intent in the main chat, then rerun /cook so the primary agent can synthesize a tighter startup plan.",
|
|
1385
|
+
`Blocking details: ${failures.join("; ")}.`,
|
|
1386
|
+
].join(" ")
|
|
1387
|
+
: [
|
|
1388
|
+
"/cook failed closed because a fresh explicit primary-agent startup plan exists, but it is not concrete enough to seed workflow planning yet.",
|
|
1389
|
+
"Tighten the startup plan in the main chat so it captures a concrete mission, repo-change-oriented acceptance, and truthful verification intent, then rerun /cook.",
|
|
1390
|
+
`Blocking details: ${failures.join("; ")}.`,
|
|
1391
|
+
].join(" ");
|
|
1383
1392
|
}
|
|
1384
1393
|
|
|
1385
1394
|
function isStartableCookHandoffCapsule(
|
|
@@ -1435,11 +1444,11 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1435
1444
|
evaluationProfile: capsule.evaluation_profile,
|
|
1436
1445
|
critique: [
|
|
1437
1446
|
...capsule.notes,
|
|
1438
|
-
`First slice goal: ${capsule.first_slice_goal}
|
|
1447
|
+
...(capsule.first_slice_goal ? [`First slice goal: ${capsule.first_slice_goal}`] : []),
|
|
1439
1448
|
...(capsule.first_slice_non_goals.length > 0 ? [`First slice non-goals: ${capsule.first_slice_non_goals.join(" | ")}`] : []),
|
|
1440
1449
|
...(capsule.implementation_surfaces.length > 0 ? [`Implementation surfaces: ${capsule.implementation_surfaces.join(" | ")}`] : []),
|
|
1441
1450
|
...(capsule.verification_commands.length > 0 ? [`Verification commands: ${capsule.verification_commands.join(" | ")}`] : []),
|
|
1442
|
-
`Why this slice first: ${capsule.why_this_slice_first}
|
|
1451
|
+
...(capsule.why_this_slice_first ? [`Why this slice first: ${capsule.why_this_slice_first}`] : []),
|
|
1443
1452
|
...(capsule.why_cook_now ? [`Primary-agent /cook handoff rationale: ${capsule.why_cook_now}`] : []),
|
|
1444
1453
|
],
|
|
1445
1454
|
risks: capsule.risks,
|
|
@@ -1455,11 +1464,11 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1455
1464
|
...capsule.scope,
|
|
1456
1465
|
...constraints,
|
|
1457
1466
|
...capsule.acceptance,
|
|
1458
|
-
capsule.first_slice_goal,
|
|
1467
|
+
...(capsule.first_slice_goal ? [capsule.first_slice_goal] : []),
|
|
1459
1468
|
...capsule.first_slice_non_goals,
|
|
1460
1469
|
...capsule.implementation_surfaces,
|
|
1461
1470
|
...capsule.verification_commands,
|
|
1462
|
-
capsule.why_this_slice_first,
|
|
1471
|
+
...(capsule.why_this_slice_first ? [capsule.why_this_slice_first] : []),
|
|
1463
1472
|
],
|
|
1464
1473
|
),
|
|
1465
1474
|
goalText,
|
|
@@ -1470,6 +1479,32 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1470
1479
|
return finalizeContextProposal(proposal, projectName, deps);
|
|
1471
1480
|
}
|
|
1472
1481
|
|
|
1482
|
+
export function assessCookHandoffText(
|
|
1483
|
+
text: string,
|
|
1484
|
+
projectName: string,
|
|
1485
|
+
deps: ProposalParseDeps,
|
|
1486
|
+
options?: {
|
|
1487
|
+
messageId?: string;
|
|
1488
|
+
timestampMs?: number;
|
|
1489
|
+
context?: "explicit_preview" | "same_entry_synthesis";
|
|
1490
|
+
},
|
|
1491
|
+
): CookHandoffProposalAssessment {
|
|
1492
|
+
const capsules = parseCookHandoffCapsulesFromText(text, options?.messageId, options?.timestampMs, deps);
|
|
1493
|
+
for (let capsuleIndex = capsules.length - 1; capsuleIndex >= 0; capsuleIndex -= 1) {
|
|
1494
|
+
const capsule = capsules[capsuleIndex];
|
|
1495
|
+
const failures = cookHandoffStartabilityFailures(capsule, deps);
|
|
1496
|
+
if (failures.length > 0) {
|
|
1497
|
+
return {
|
|
1498
|
+
status: "fresh_but_not_startable",
|
|
1499
|
+
message: buildNonStartableCookHandoffMessage(failures, options?.context ?? "explicit_preview"),
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
const proposal = buildContextProposalFromCookHandoffCapsule(capsule, projectName, deps);
|
|
1503
|
+
if (proposal) return { status: "startable", proposal };
|
|
1504
|
+
}
|
|
1505
|
+
return { status: "none" };
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1473
1508
|
export function assessLatestCookHandoffProposal(
|
|
1474
1509
|
recentMessages: RecentSessionMessage[],
|
|
1475
1510
|
projectName: string,
|
|
@@ -1489,7 +1524,7 @@ export function assessLatestCookHandoffProposal(
|
|
|
1489
1524
|
if (failures.length > 0) {
|
|
1490
1525
|
return {
|
|
1491
1526
|
status: "fresh_but_not_startable",
|
|
1492
|
-
message: buildNonStartableCookHandoffMessage(failures),
|
|
1527
|
+
message: buildNonStartableCookHandoffMessage(failures, "explicit_preview"),
|
|
1493
1528
|
};
|
|
1494
1529
|
}
|
|
1495
1530
|
const proposal = buildContextProposalFromCookHandoffCapsule(capsule, projectName, deps);
|
|
@@ -97,14 +97,18 @@ const STARTUP_ANALYST_ROLE = "cook-proposal-analyst";
|
|
|
97
97
|
const ANALYST_HEARTBEAT_MS = 5_000;
|
|
98
98
|
|
|
99
99
|
const PRIMARY_AGENT_HANDOFF_SYSTEM_PROMPT = [
|
|
100
|
-
"You are the primary agent preparing an explicit /cook
|
|
101
|
-
"Return either exactly one fenced ```cook_handoff JSON block or one brief plain sentence explaining why no concrete
|
|
102
|
-
"If you can prepare a
|
|
103
|
-
"
|
|
104
|
-
"
|
|
100
|
+
"You are the primary agent preparing an explicit /cook startup plan after the user already chose workflow mode.",
|
|
101
|
+
"Return either exactly one fenced ```cook_handoff JSON block or one brief plain sentence explaining why no concrete startup plan can be prepared.",
|
|
102
|
+
"If you can prepare a plan, the JSON must use kind cook_handoff, source primary_agent, and handoff_kind implementation_workflow_handoff.",
|
|
103
|
+
"Author the approved workflow startup plan now from the primary-agent view of the task so /cook can persist it under .agent before completion-regrounder derives canonical slices.",
|
|
104
|
+
"Capture the agreed mission, scope, constraints or non_goals, acceptance, risks, notes, and any concrete planning hints that will help completion-regrounder split slices later.",
|
|
105
|
+
"If a bounded first slice, likely implementation surfaces, or likely verification commands are already obvious, include first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, and why_this_slice_first as optional hints only. They are not required when the overall startup plan is already concrete enough to begin workflow planning.",
|
|
106
|
+
"Prefer the latest user-authored task context plus canonical workflow context over older assistant-authored previews or stale planning text.",
|
|
107
|
+
"Do not directly reuse an old preview capsule as-is; either synthesize a fresh startup plan from the current task context or return a brief plain sentence saying no concrete startup plan should replace canonical state yet.",
|
|
108
|
+
"If canonical workflow context already exists and the latest discussion does not clearly ask to replace the mission or start the next round, return a brief plain sentence instead of inventing a replacement startup plan.",
|
|
109
|
+
"Do not make /cook infer or rediscover the mission later; author the startup plan now from the primary-agent view of the task.",
|
|
105
110
|
"Do not emit markdown commentary before or after the capsule.",
|
|
106
|
-
"If the task is not concrete enough for
|
|
107
|
-
"A valid implementation-ready handoff must include mission, scope, constraints or non_goals, acceptance, risks, notes, first_slice_goal, first_slice_non_goals, implementation_surfaces, verification_commands, and why_this_slice_first.",
|
|
111
|
+
"If the task is not concrete enough for workflow startup, do not invent missing detail.",
|
|
108
112
|
].join(" ");
|
|
109
113
|
const PRIMARY_AGENT_HANDOFF_ROLE = "cook-primary-agent-handoff";
|
|
110
114
|
|
|
@@ -342,7 +346,8 @@ function buildPrimaryAgentHandoffPrompt(projectName: string, recentEntries: Rece
|
|
|
342
346
|
lines.push(
|
|
343
347
|
"",
|
|
344
348
|
"Task:",
|
|
345
|
-
"The user explicitly invoked /cook. Prepare the primary-agent
|
|
349
|
+
"The user explicitly invoked /cook. Prepare the primary-agent startup plan that /cook should synthesize immediately for Start/Cancel confirmation, persistence under .agent, and later slice derivation by completion-regrounder.",
|
|
350
|
+
"If the latest discussion does not justify a concrete new startup plan, return a brief plain sentence instead of speculative JSON.",
|
|
346
351
|
);
|
|
347
352
|
return lines.join("\n");
|
|
348
353
|
}
|
|
@@ -359,7 +364,7 @@ async function runPrimaryAgentHandoffSubprocess(params: GenerateCookHandoffWithA
|
|
|
359
364
|
const invocation = getPiInvocation(args);
|
|
360
365
|
const liveActivity = createLiveRoleActivity(PRIMARY_AGENT_HANDOFF_ROLE);
|
|
361
366
|
liveActivity.progress = "Preparing primary-agent /cook handoff";
|
|
362
|
-
liveActivity.currentAction = "Authoring explicit startup
|
|
367
|
+
liveActivity.currentAction = "Authoring explicit startup plan from current task context";
|
|
363
368
|
liveActivity.assistantSummary = liveActivity.progress;
|
|
364
369
|
try {
|
|
365
370
|
const output = await new Promise<string | undefined>((resolve) => {
|
|
@@ -409,7 +414,7 @@ export async function generateCookHandoffWithAgent(params: GenerateCookHandoffWi
|
|
|
409
414
|
try {
|
|
410
415
|
return await runPrimaryAgentHandoffSubprocess(params);
|
|
411
416
|
} catch (error) {
|
|
412
|
-
console.warn("[completion] primary-agent
|
|
417
|
+
console.warn("[completion] primary-agent startup-plan generation failed", error);
|
|
413
418
|
return undefined;
|
|
414
419
|
}
|
|
415
420
|
}
|
|
@@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
import { promises as fsp } from "node:fs";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
+
import { buildApprovedStartupPlanMarkdown } from "./prompt-surfaces";
|
|
6
7
|
import type { CompletionStateSnapshot, JsonRecord } from "./types";
|
|
7
8
|
|
|
8
9
|
const PROTOCOL_ID = "completion";
|
|
@@ -45,6 +46,8 @@ export function resolveFiles(root: string) {
|
|
|
45
46
|
statePath: path.join(agentDir, "state.json"),
|
|
46
47
|
planPath: path.join(agentDir, "plan.json"),
|
|
47
48
|
activePath: path.join(agentDir, "active-slice.json"),
|
|
49
|
+
startupPlanPath: path.join(agentDir, "startup-plan.json"),
|
|
50
|
+
startupPlanMarkdownPath: path.join(agentDir, "startup-plan.md"),
|
|
48
51
|
sliceHistoryPath: path.join(agentDir, "slice-history.jsonl"),
|
|
49
52
|
stopHistoryPath: path.join(agentDir, "stop-check-history.jsonl"),
|
|
50
53
|
verificationEvidencePath: path.join(agentDir, "verification-evidence.json"),
|
|
@@ -141,6 +144,7 @@ export async function loadCompletionSnapshot(startCwd: string): Promise<Completi
|
|
|
141
144
|
const state = await readJson(files.statePath);
|
|
142
145
|
const plan = await readJson(files.planPath);
|
|
143
146
|
const active = await readJson(files.activePath);
|
|
147
|
+
const startupPlan = await readJson(files.startupPlanPath);
|
|
144
148
|
const verificationEvidence = await readJson(files.verificationEvidencePath);
|
|
145
149
|
return {
|
|
146
150
|
files,
|
|
@@ -148,6 +152,7 @@ export async function loadCompletionSnapshot(startCwd: string): Promise<Completi
|
|
|
148
152
|
state,
|
|
149
153
|
plan,
|
|
150
154
|
active,
|
|
155
|
+
startupPlan,
|
|
151
156
|
verificationEvidence,
|
|
152
157
|
activeSlice: findActiveSlice(plan, active),
|
|
153
158
|
};
|
|
@@ -275,6 +280,32 @@ export function defaultPlan(
|
|
|
275
280
|
};
|
|
276
281
|
}
|
|
277
282
|
|
|
283
|
+
export function defaultStartupPlan(
|
|
284
|
+
missionAnchor: string,
|
|
285
|
+
routing?: { taskType?: string; evaluationProfile?: string },
|
|
286
|
+
approvedStartupPlan?: JsonRecord,
|
|
287
|
+
): JsonRecord {
|
|
288
|
+
return approvedStartupPlan ?? {
|
|
289
|
+
schema_version: 1,
|
|
290
|
+
artifact_type: "completion-startup-plan",
|
|
291
|
+
status: "approved",
|
|
292
|
+
source: "recent_discussion",
|
|
293
|
+
captured_at: null,
|
|
294
|
+
mission_anchor: missionAnchor,
|
|
295
|
+
goal_text: `Mission: ${missionAnchor}`,
|
|
296
|
+
task_type: routing?.taskType ?? DEFAULT_TASK_TYPE,
|
|
297
|
+
evaluation_profile: routing?.evaluationProfile ?? DEFAULT_EVALUATION_PROFILE,
|
|
298
|
+
scope: [],
|
|
299
|
+
constraints: [],
|
|
300
|
+
acceptance: [],
|
|
301
|
+
risks: [],
|
|
302
|
+
notes: ["No approved startup plan has been recorded yet."],
|
|
303
|
+
planned_surfaces: [],
|
|
304
|
+
verification_intent: [],
|
|
305
|
+
sequencing_hints: [],
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
278
309
|
export function defaultActiveSlice(
|
|
279
310
|
missionAnchor: string,
|
|
280
311
|
routing?: { taskType?: string; evaluationProfile?: string },
|
|
@@ -321,7 +352,7 @@ export function defaultVerificationEvidence(): JsonRecord {
|
|
|
321
352
|
}
|
|
322
353
|
|
|
323
354
|
export function buildAgentReadme(projectName: string): string {
|
|
324
|
-
return `# Completion Control Plane\n\nThis repository uses the \`completion\` workflow for long-running coding tasks.\n\n## Canonical tracked contract files\n\n- \`.agent/README.md\`\n- \`.agent/mission.md\`\n- \`.agent/profile.json\`\n- \`.agent/verify_completion_stop.sh\`\n- \`.agent/verify_completion_control_plane.sh\`\n\n## Ignored canonical execution state\n\n- \`.agent/state.json\`\n- \`.agent/plan.json\`\n- \`.agent/active-slice.json\`\n- \`.agent/slice-history.jsonl\`\n- \`.agent/stop-check-history.jsonl\`\n- \`.agent/verification-evidence.json\`\n- \`.agent/*.log\`\n- \`.agent/tmp/\`\n\n\`.agent/verification-evidence.json\` is the durable canonical record of deterministic verification for the selected slice or current HEAD. Recovery, review, audit, and stop-check reminder surfaces consume it instead of temp-only artifacts or conversational summaries when it is populated.\n\nThe source of truth for long-running completion work is canonical \`.agent/**\` state plus current repo truth.\n\nProject: ${projectName}\n`;
|
|
355
|
+
return `# Completion Control Plane\n\nThis repository uses the \`completion\` workflow for long-running coding tasks.\n\n## Canonical tracked contract files\n\n- \`.agent/README.md\`\n- \`.agent/mission.md\`\n- \`.agent/profile.json\`\n- \`.agent/verify_completion_stop.sh\`\n- \`.agent/verify_completion_control_plane.sh\`\n\n## Ignored canonical execution state\n\n- \`.agent/state.json\`\n- \`.agent/startup-plan.json\`\n- \`.agent/startup-plan.md\`\n- \`.agent/plan.json\`\n- \`.agent/active-slice.json\`\n- \`.agent/slice-history.jsonl\`\n- \`.agent/stop-check-history.jsonl\`\n- \`.agent/verification-evidence.json\`\n- \`.agent/*.log\`\n- \`.agent/tmp/\`\n\n\`.agent/startup-plan.json\` plus \`.agent/startup-plan.md\` preserve the approved workflow startup plan captured at \`/cook\`. \`completion-regrounder\` consumes that plan as planning input, then derives canonical slices in \`.agent/plan.json\` from current repo truth.\n\n\`.agent/verification-evidence.json\` is the durable canonical record of deterministic verification for the selected slice or current HEAD. Recovery, review, audit, and stop-check reminder surfaces consume it instead of temp-only artifacts or conversational summaries when it is populated.\n\nThe source of truth for long-running completion work is canonical \`.agent/**\` state plus current repo truth.\n\nProject: ${projectName}\n`;
|
|
325
356
|
}
|
|
326
357
|
|
|
327
358
|
export function buildMission(projectName: string, missionAnchor: string): string {
|
|
@@ -424,15 +455,23 @@ function trackedDiffFiles(fromCommit, toCommit) {
|
|
|
424
455
|
|
|
425
456
|
const profile = readJson('.agent/profile.json');
|
|
426
457
|
const state = readJson('.agent/state.json');
|
|
458
|
+
const startupPlan = readJson('.agent/startup-plan.json');
|
|
427
459
|
const plan = readJson('.agent/plan.json');
|
|
428
460
|
const active = readJson('.agent/active-slice.json');
|
|
429
461
|
const evidence = readJson('.agent/verification-evidence.json');
|
|
462
|
+
let startupPlanMarkdown = '';
|
|
463
|
+
try {
|
|
464
|
+
startupPlanMarkdown = fs.readFileSync('.agent/startup-plan.md', 'utf8');
|
|
465
|
+
} catch (error) {
|
|
466
|
+
fail('.agent/startup-plan.md must be present and readable: ' + error.message);
|
|
467
|
+
}
|
|
430
468
|
|
|
431
469
|
ensureTrackedContractFiles();
|
|
432
470
|
|
|
433
471
|
for (const [file, record] of [
|
|
434
472
|
['.agent/profile.json', profile],
|
|
435
473
|
['.agent/state.json', state],
|
|
474
|
+
['.agent/startup-plan.json', startupPlan],
|
|
436
475
|
['.agent/plan.json', plan],
|
|
437
476
|
['.agent/active-slice.json', active],
|
|
438
477
|
]) {
|
|
@@ -443,12 +482,38 @@ for (const [file, record] of [
|
|
|
443
482
|
const taskType = asString(profile.task_type);
|
|
444
483
|
const evaluationProfile = asString(profile.evaluation_profile);
|
|
445
484
|
if (asString(state.task_type) !== taskType) fail('.agent/state.json task_type must match .agent/profile.json task_type');
|
|
485
|
+
if (asString(startupPlan.task_type) !== taskType) fail('.agent/startup-plan.json task_type must match .agent/profile.json task_type');
|
|
446
486
|
if (asString(plan.task_type) !== taskType) fail('.agent/plan.json task_type must match .agent/profile.json task_type');
|
|
447
487
|
if (asString(active.task_type) !== taskType) fail('.agent/active-slice.json task_type must match .agent/profile.json task_type');
|
|
448
488
|
if (asString(state.evaluation_profile) !== evaluationProfile) fail('.agent/state.json evaluation_profile must match .agent/profile.json evaluation_profile');
|
|
489
|
+
if (asString(startupPlan.evaluation_profile) !== evaluationProfile) fail('.agent/startup-plan.json evaluation_profile must match .agent/profile.json evaluation_profile');
|
|
449
490
|
if (asString(plan.evaluation_profile) !== evaluationProfile) fail('.agent/plan.json evaluation_profile must match .agent/profile.json evaluation_profile');
|
|
450
491
|
if (asString(active.evaluation_profile) !== evaluationProfile) fail('.agent/active-slice.json evaluation_profile must match .agent/profile.json evaluation_profile');
|
|
451
492
|
|
|
493
|
+
if (asString(startupPlan.artifact_type) !== 'completion-startup-plan') {
|
|
494
|
+
fail('.agent/startup-plan.json artifact_type must be completion-startup-plan');
|
|
495
|
+
}
|
|
496
|
+
if (asString(startupPlan.status) !== 'approved') {
|
|
497
|
+
fail('.agent/startup-plan.json status must be approved');
|
|
498
|
+
}
|
|
499
|
+
const startupPlanMissionAnchor = asString(startupPlan.mission_anchor);
|
|
500
|
+
if (!startupPlanMissionAnchor) fail('.agent/startup-plan.json mission_anchor must be present');
|
|
501
|
+
if (startupPlanMissionAnchor !== asString(state.mission_anchor)) fail('.agent/startup-plan.json mission_anchor must match .agent/state.json mission_anchor');
|
|
502
|
+
if (startupPlanMissionAnchor !== asString(plan.mission_anchor)) fail('.agent/startup-plan.json mission_anchor must match .agent/plan.json mission_anchor');
|
|
503
|
+
if (startupPlanMissionAnchor !== asString(active.mission_anchor)) fail('.agent/startup-plan.json mission_anchor must match .agent/active-slice.json mission_anchor');
|
|
504
|
+
if (!asString(startupPlan.goal_text)) fail('.agent/startup-plan.json goal_text must be present');
|
|
505
|
+
for (const field of ['scope', 'constraints', 'acceptance', 'risks', 'notes', 'planned_surfaces', 'verification_intent', 'sequencing_hints']) {
|
|
506
|
+
if (!Array.isArray(startupPlan[field])) fail('.agent/startup-plan.json is missing ' + field);
|
|
507
|
+
}
|
|
508
|
+
if (startupPlanMarkdown.trim().length === 0) fail('.agent/startup-plan.md must not be empty');
|
|
509
|
+
if (startupPlanMissionAnchor && !startupPlanMarkdown.includes(startupPlanMissionAnchor)) {
|
|
510
|
+
fail('.agent/startup-plan.md must mention the startup-plan mission_anchor');
|
|
511
|
+
}
|
|
512
|
+
const startupPlanGoalText = asString(startupPlan.goal_text);
|
|
513
|
+
if (startupPlanGoalText && !startupPlanMarkdown.includes(startupPlanGoalText)) {
|
|
514
|
+
fail('.agent/startup-plan.md must render the startup-plan goal_text');
|
|
515
|
+
}
|
|
516
|
+
|
|
452
517
|
if (asString(evidence.artifact_type) !== 'completion-verification-evidence') {
|
|
453
518
|
fail('.agent/verification-evidence.json artifact_type must be completion-verification-evidence');
|
|
454
519
|
}
|
|
@@ -595,7 +660,12 @@ export type ScaffoldResult = {
|
|
|
595
660
|
export async function scaffoldCompletionFiles(
|
|
596
661
|
root: string,
|
|
597
662
|
missionAnchor: string,
|
|
598
|
-
options?: {
|
|
663
|
+
options?: {
|
|
664
|
+
analysis?: { taskType?: string; evaluationProfile?: string };
|
|
665
|
+
continuationReason?: string;
|
|
666
|
+
advisoryStartupBrief?: JsonRecord;
|
|
667
|
+
approvedStartupPlan?: JsonRecord;
|
|
668
|
+
},
|
|
599
669
|
): Promise<ScaffoldResult> {
|
|
600
670
|
const files = resolveFiles(root);
|
|
601
671
|
const created: string[] = [];
|
|
@@ -605,6 +675,10 @@ export async function scaffoldCompletionFiles(
|
|
|
605
675
|
const projectName = path.basename(root);
|
|
606
676
|
const docsSurfaces = await detectDocsSurfaces(root);
|
|
607
677
|
const verifierCommand = await detectVerifierCommand(root);
|
|
678
|
+
const startupPlanRecord =
|
|
679
|
+
options?.approvedStartupPlan ??
|
|
680
|
+
(await readJson(files.startupPlanPath)) ??
|
|
681
|
+
defaultStartupPlan(missionAnchor, { taskType: options?.analysis?.taskType, evaluationProfile: options?.analysis?.evaluationProfile });
|
|
608
682
|
const trackedFiles: Array<{ path: string; content: string; executable?: boolean }> = [
|
|
609
683
|
{ path: path.join(files.agentDir, "README.md"), content: buildAgentReadme(projectName) },
|
|
610
684
|
{ path: path.join(files.agentDir, "mission.md"), content: buildMission(projectName, missionAnchor) },
|
|
@@ -618,6 +692,14 @@ export async function scaffoldCompletionFiles(
|
|
|
618
692
|
path: files.statePath,
|
|
619
693
|
content: `${JSON.stringify(defaultState(missionAnchor, { taskType: options?.analysis?.taskType, evaluationProfile: options?.analysis?.evaluationProfile, continuationReason: options?.continuationReason }, options?.advisoryStartupBrief), null, 2)}\n`,
|
|
620
694
|
},
|
|
695
|
+
{
|
|
696
|
+
path: files.startupPlanPath,
|
|
697
|
+
content: `${JSON.stringify(startupPlanRecord, null, 2)}\n`,
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
path: files.startupPlanMarkdownPath,
|
|
701
|
+
content: buildApprovedStartupPlanMarkdown(startupPlanRecord as any),
|
|
702
|
+
},
|
|
621
703
|
{ path: files.planPath, content: `${JSON.stringify(defaultPlan(missionAnchor, { taskType: options?.analysis?.taskType, evaluationProfile: options?.analysis?.evaluationProfile }), null, 2)}\n` },
|
|
622
704
|
{ path: files.activePath, content: `${JSON.stringify(defaultActiveSlice(missionAnchor, { taskType: options?.analysis?.taskType, evaluationProfile: options?.analysis?.evaluationProfile }), null, 2)}\n` },
|
|
623
705
|
{ path: files.verificationEvidencePath, content: `${JSON.stringify(defaultVerificationEvidence(), null, 2)}\n` },
|
|
@@ -639,6 +721,7 @@ export function currentTaskType(snapshot: CompletionStateSnapshot): string | und
|
|
|
639
721
|
return (
|
|
640
722
|
asString(snapshot.active?.task_type) ??
|
|
641
723
|
asString(snapshot.state?.task_type) ??
|
|
724
|
+
asString(snapshot.startupPlan?.task_type) ??
|
|
642
725
|
asString(snapshot.plan?.task_type) ??
|
|
643
726
|
asString(snapshot.profile?.task_type)
|
|
644
727
|
);
|
|
@@ -648,6 +731,7 @@ export function currentEvaluationProfile(snapshot: CompletionStateSnapshot): str
|
|
|
648
731
|
return (
|
|
649
732
|
asString(snapshot.active?.evaluation_profile) ??
|
|
650
733
|
asString(snapshot.state?.evaluation_profile) ??
|
|
734
|
+
asString(snapshot.startupPlan?.evaluation_profile) ??
|
|
651
735
|
asString(snapshot.plan?.evaluation_profile) ??
|
|
652
736
|
asString(snapshot.profile?.evaluation_profile)
|
|
653
737
|
);
|
|
@@ -656,6 +740,7 @@ export function currentEvaluationProfile(snapshot: CompletionStateSnapshot): str
|
|
|
656
740
|
export function currentMissionAnchor(snapshot: CompletionStateSnapshot): string {
|
|
657
741
|
return (
|
|
658
742
|
asString(snapshot.state?.mission_anchor) ??
|
|
743
|
+
asString(snapshot.startupPlan?.mission_anchor) ??
|
|
659
744
|
asString(snapshot.plan?.mission_anchor) ??
|
|
660
745
|
asString(snapshot.active?.mission_anchor) ??
|
|
661
746
|
path.basename(snapshot.files.root)
|
|
@@ -18,6 +18,8 @@ export type CompletionFiles = {
|
|
|
18
18
|
statePath: string;
|
|
19
19
|
planPath: string;
|
|
20
20
|
activePath: string;
|
|
21
|
+
startupPlanPath: string;
|
|
22
|
+
startupPlanMarkdownPath: string;
|
|
21
23
|
sliceHistoryPath: string;
|
|
22
24
|
stopHistoryPath: string;
|
|
23
25
|
verificationEvidencePath: string;
|
|
@@ -30,6 +32,7 @@ export type CompletionStateSnapshot = {
|
|
|
30
32
|
state?: JsonRecord;
|
|
31
33
|
plan?: JsonRecord;
|
|
32
34
|
active?: JsonRecord;
|
|
35
|
+
startupPlan?: JsonRecord;
|
|
33
36
|
verificationEvidence?: JsonRecord;
|
|
34
37
|
activeSlice?: JsonRecord;
|
|
35
38
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linimin/pi-letscook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.68",
|
|
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,
|
|
@@ -137,17 +137,16 @@ NODE
|
|
|
137
137
|
ROOT="$TMPDIR/repo"
|
|
138
138
|
PROMPT="$TMPDIR/resume-prompt.txt"
|
|
139
139
|
BOOTSTRAP_SESSION="$TMPDIR/session-active-slice-bootstrap.jsonl"
|
|
140
|
-
|
|
140
|
+
BOOTSTRAP_DISCUSSION=$'Prepare the active-slice contract bootstrap fixture and tell me when it is ready for /cook.'
|
|
141
|
+
GENERATED_HANDOFF="$(python3 - <<'PY'
|
|
141
142
|
import json
|
|
142
143
|
capsule = {
|
|
143
144
|
"kind": "cook_handoff",
|
|
144
145
|
"source": "primary_agent",
|
|
145
|
-
"captured_at": "2026-01-01T00:00:02.000Z",
|
|
146
|
-
"source_turn_id": "m0002",
|
|
147
146
|
"mission": "Exercise active-slice contract parity.",
|
|
148
147
|
"scope": [
|
|
149
148
|
"Bootstrap canonical completion files for the active-slice contract fixture.",
|
|
150
|
-
"Keep the fixture on the shipped
|
|
149
|
+
"Keep the fixture on the shipped same-entry synthesis startup path."
|
|
151
150
|
],
|
|
152
151
|
"constraints": [
|
|
153
152
|
"Use supported bare /cook startup only."
|
|
@@ -157,7 +156,7 @@ capsule = {
|
|
|
157
156
|
"Keep scripts/active-slice-contract-test.sh aligned with the packaged startup contract."
|
|
158
157
|
],
|
|
159
158
|
"risks": [
|
|
160
|
-
"Active-slice fixture bootstrap must stay anchored to
|
|
159
|
+
"Active-slice fixture bootstrap must stay anchored to same-entry primary-agent startup-plan synthesis."
|
|
161
160
|
],
|
|
162
161
|
"notes": [
|
|
163
162
|
"This handoff exists only to scaffold canonical files before the fixture rewrites them for contract parity coverage."
|
|
@@ -179,20 +178,16 @@ capsule = {
|
|
|
179
178
|
"evaluation_profile": "completion-rubric-v1",
|
|
180
179
|
"why_cook_now": "The fixture bootstrap is concrete enough to scaffold canonical control-plane files."
|
|
181
180
|
}
|
|
182
|
-
|
|
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))
|
|
181
|
+
print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
|
|
187
182
|
PY
|
|
188
183
|
)"
|
|
189
184
|
mkdir -p "$ROOT"
|
|
190
185
|
cd "$ROOT"
|
|
191
186
|
git init -q
|
|
192
|
-
|
|
187
|
+
write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
|
|
193
188
|
|
|
194
189
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
195
|
-
|
|
190
|
+
PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$GENERATED_HANDOFF" \
|
|
196
191
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
197
192
|
pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" \
|
|
198
193
|
>"$TMPDIR/pi-active-slice-bootstrap.out" 2>"$TMPDIR/pi-active-slice-bootstrap.err"
|
|
@@ -134,11 +134,11 @@ assertIncludes('.agent/README.md', 'durable canonical record of deterministic ve
|
|
|
134
134
|
assertSectionIncludes('skills/completion-protocol/SKILL.md', '## Canonical Files', '- `.agent/verification-evidence.json`');
|
|
135
135
|
assertSectionIncludes('skills/completion-protocol/SKILL.md', '## Canonical Inputs', '- `.agent/verification-evidence.json`');
|
|
136
136
|
assertSectionIncludes('skills/completion-protocol/SKILL.md', '## Compaction And Recovery', '- `.agent/verification-evidence.json`');
|
|
137
|
-
assertSectionIncludes('skills/completion-protocol/SKILL.md', '## Compaction And Recovery', '`completion-implementer` must also re-read canonical `.agent/state.json`, `.agent/plan.json`, `.agent/active-slice.json`, and `.agent/verification-evidence.json` before resuming work.');
|
|
137
|
+
assertSectionIncludes('skills/completion-protocol/SKILL.md', '## Compaction And Recovery', '`completion-implementer` must also re-read canonical `.agent/state.json`, `.agent/startup-plan.json`, `.agent/plan.json`, `.agent/active-slice.json`, and `.agent/verification-evidence.json` before resuming work.');
|
|
138
138
|
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Ignored Canonical Execution State', '- `.agent/verification-evidence.json`');
|
|
139
139
|
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Canonical Inputs', '- `.agent/verification-evidence.json`');
|
|
140
140
|
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '- `.agent/verification-evidence.json`');
|
|
141
|
-
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '`completion-implementer` must also re-read canonical `.agent/state.json`, `.agent/plan.json`, `.agent/active-slice.json`, and `.agent/verification-evidence.json` before resuming work.');
|
|
141
|
+
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '`completion-implementer` must also re-read canonical `.agent/state.json`, `.agent/startup-plan.json`, `.agent/plan.json`, `.agent/active-slice.json`, and `.agent/verification-evidence.json` before resuming work.');
|
|
142
142
|
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})');
|
|
143
143
|
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Verification evidence summary: ${args.evidence.summary}');
|
|
144
144
|
assertIncludes('extensions/completion/index.ts', 'Canonical verification evidence artifact is currently: ${evidence.path} (${evidence.status})');
|
|
@@ -182,17 +182,16 @@ bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
|
182
182
|
ROOT="$TMPDIR/repo"
|
|
183
183
|
SYSTEM_REMINDER="$TMPDIR/system-reminder.txt"
|
|
184
184
|
BOOTSTRAP_SESSION="$TMPDIR/session-canonical-evidence-bootstrap.jsonl"
|
|
185
|
-
|
|
185
|
+
BOOTSTRAP_DISCUSSION=$'Prepare the canonical evidence bootstrap fixture and tell me when it is ready for /cook.'
|
|
186
|
+
GENERATED_HANDOFF="$(python3 - <<'PY'
|
|
186
187
|
import json
|
|
187
188
|
capsule = {
|
|
188
189
|
"kind": "cook_handoff",
|
|
189
190
|
"source": "primary_agent",
|
|
190
|
-
"captured_at": "2026-01-01T00:00:02.000Z",
|
|
191
|
-
"source_turn_id": "m0002",
|
|
192
191
|
"mission": "Exercise canonical evidence fixture bootstrap.",
|
|
193
192
|
"scope": [
|
|
194
193
|
"Materialize canonical completion files for the evidence artifact fixture.",
|
|
195
|
-
"Keep the verification-evidence bootstrap on the supported
|
|
194
|
+
"Keep the verification-evidence bootstrap on the supported same-entry synthesis startup path."
|
|
196
195
|
],
|
|
197
196
|
"constraints": [
|
|
198
197
|
"Use supported bare /cook startup only."
|
|
@@ -202,7 +201,7 @@ capsule = {
|
|
|
202
201
|
"Keep scripts/canonical-evidence-artifact-test.sh aligned with packaged bootstrap behavior."
|
|
203
202
|
],
|
|
204
203
|
"risks": [
|
|
205
|
-
"Evidence-artifact bootstrap must stay anchored to
|
|
204
|
+
"Evidence-artifact bootstrap must stay anchored to same-entry primary-agent startup-plan synthesis."
|
|
206
205
|
],
|
|
207
206
|
"notes": [
|
|
208
207
|
"This fixture exists only to scaffold canonical files before rewriting them for evidence parity coverage."
|
|
@@ -224,25 +223,21 @@ capsule = {
|
|
|
224
223
|
"evaluation_profile": "completion-rubric-v1",
|
|
225
224
|
"why_cook_now": "The fixture bootstrap is concrete enough to create canonical control-plane files."
|
|
226
225
|
}
|
|
227
|
-
|
|
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))
|
|
226
|
+
print("```cook_handoff\n" + json.dumps(capsule, ensure_ascii=False, indent=2) + "\n```")
|
|
232
227
|
PY
|
|
233
228
|
)"
|
|
234
229
|
mkdir -p "$ROOT"
|
|
235
230
|
cd "$ROOT"
|
|
236
231
|
git init -q
|
|
237
|
-
|
|
232
|
+
write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
|
|
238
233
|
|
|
239
234
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
240
|
-
|
|
235
|
+
PI_COMPLETION_PRIMARY_HANDOFF_OUTPUT="$GENERATED_HANDOFF" \
|
|
241
236
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
242
237
|
pi --session "$BOOTSTRAP_SESSION" -e "$PKG_ROOT" -p "/cook" \
|
|
243
238
|
>"$TMPDIR/pi-canonical-evidence-bootstrap.out" 2>"$TMPDIR/pi-canonical-evidence-bootstrap.err"
|
|
244
239
|
|
|
245
|
-
for file in .agent/profile.json .agent/state.json .agent/plan.json .agent/active-slice.json .agent/verification-evidence.json; do
|
|
240
|
+
for file in .agent/profile.json .agent/state.json .agent/startup-plan.json .agent/startup-plan.md .agent/plan.json .agent/active-slice.json .agent/verification-evidence.json; do
|
|
246
241
|
[[ -f "$file" ]] || { echo "missing canonical bootstrap file: $file" >&2; exit 1; }
|
|
247
242
|
done
|
|
248
243
|
|
|
@@ -288,6 +283,36 @@ acceptance = [
|
|
|
288
283
|
'Canonical verification evidence is recorded for the selected slice.',
|
|
289
284
|
'Fail-closed verification rejects missing or stale evidence.',
|
|
290
285
|
]
|
|
286
|
+
startup_plan = {
|
|
287
|
+
'schema_version': 1,
|
|
288
|
+
'artifact_type': 'completion-startup-plan',
|
|
289
|
+
'status': 'approved',
|
|
290
|
+
'source': 'deferred_primary_agent_handoff',
|
|
291
|
+
'captured_at': '2026-05-03T00:00:00Z',
|
|
292
|
+
'mission_anchor': mission,
|
|
293
|
+
'goal_text': 'Mission: Exercise canonical verification evidence parity.\n\nScope:\n- Persist canonical verification evidence for the selected slice.\n- Keep the verifier fail-closed on stale or missing evidence.\n\nAcceptance:\n- Canonical verification evidence is recorded for the selected slice.\n- Fail-closed verification rejects missing or stale evidence.',
|
|
294
|
+
'task_type': task_type,
|
|
295
|
+
'evaluation_profile': evaluation_profile,
|
|
296
|
+
'scope': [
|
|
297
|
+
'Persist canonical verification evidence for the selected slice.',
|
|
298
|
+
'Keep the verifier fail-closed on stale or missing evidence.',
|
|
299
|
+
],
|
|
300
|
+
'constraints': [
|
|
301
|
+
'Keep the fixture scoped to canonical verification evidence parity.',
|
|
302
|
+
],
|
|
303
|
+
'acceptance': acceptance,
|
|
304
|
+
'risks': [
|
|
305
|
+
'Stale startup-plan parity could mask canonical evidence regressions.',
|
|
306
|
+
],
|
|
307
|
+
'notes': [
|
|
308
|
+
'Use startup-plan parity to prove the verifier reads the approved startup plan alongside other canonical state.',
|
|
309
|
+
],
|
|
310
|
+
'planned_surfaces': implementation_surfaces,
|
|
311
|
+
'verification_intent': verification_commands,
|
|
312
|
+
'sequencing_hints': [
|
|
313
|
+
'First slice goal: Persist canonical verification evidence for the selected slice.',
|
|
314
|
+
],
|
|
315
|
+
}
|
|
291
316
|
state = {
|
|
292
317
|
'schema_version': 1,
|
|
293
318
|
'mission_anchor': mission,
|
|
@@ -365,6 +390,21 @@ active = {
|
|
|
365
390
|
}
|
|
366
391
|
|
|
367
392
|
Path('.agent/state.json').write_text(json.dumps(state, indent=2) + '\n')
|
|
393
|
+
Path('.agent/startup-plan.json').write_text(json.dumps(startup_plan, indent=2) + '\n')
|
|
394
|
+
Path('.agent/startup-plan.md').write_text(
|
|
395
|
+
'# Approved Startup Plan\n\n'
|
|
396
|
+
f'Mission anchor: {mission}\n'
|
|
397
|
+
'Source: deferred_primary_agent_handoff\n'
|
|
398
|
+
'Captured at: 2026-05-03T00:00:00Z\n'
|
|
399
|
+
f'Task type: {task_type}\n'
|
|
400
|
+
f'Evaluation profile: {evaluation_profile}\n\n'
|
|
401
|
+
'## Goal\n\n'
|
|
402
|
+
f"{startup_plan['goal_text']}\n\n"
|
|
403
|
+
'## Planned surfaces\n\n'
|
|
404
|
+
+ ''.join(f'- {item}\n' for item in startup_plan['planned_surfaces'])
|
|
405
|
+
+ '\n## Verification intent\n\n'
|
|
406
|
+
+ ''.join(f'- {item}\n' for item in startup_plan['verification_intent'])
|
|
407
|
+
)
|
|
368
408
|
Path('.agent/plan.json').write_text(json.dumps(plan, indent=2) + '\n')
|
|
369
409
|
Path('.agent/active-slice.json').write_text(json.dumps(active, indent=2) + '\n')
|
|
370
410
|
PY
|