@linimin/pi-letscook 0.1.67 → 0.1.69
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 +2 -3
- package/.agent/verify_completion_control_plane.sh +21 -34
- package/CHANGELOG.md +17 -0
- package/README.md +23 -26
- package/agents/completion-bootstrapper.md +1 -2
- package/agents/completion-regrounder.md +10 -16
- package/extensions/completion/driver.ts +49 -117
- package/extensions/completion/index.ts +74 -123
- package/extensions/completion/policy-guards.ts +1 -1
- package/extensions/completion/prompt-surfaces.ts +15 -156
- package/extensions/completion/proposal.ts +21 -21
- package/extensions/completion/role-runner.ts +10 -11
- package/extensions/completion/state-store.ts +43 -85
- package/extensions/completion/types.ts +2 -3
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +28 -12
- package/scripts/canonical-evidence-artifact-test.sh +46 -59
- package/scripts/context-proposal-test.sh +58 -57
- package/scripts/refocus-test.sh +17 -17
- package/scripts/release-check.sh +14 -13
- package/scripts/role-runner-contract-test.sh +2 -2
- package/scripts/smoke-test.sh +24 -71
- package/skills/completion-protocol/SKILL.md +8 -9
- package/skills/completion-protocol/references/completion.md +2 -37
- package/skills/cook-handoff-boundary/SKILL.md +18 -16
|
@@ -24,26 +24,6 @@ export type AdvisoryStartupBrief = {
|
|
|
24
24
|
evaluation_profile?: string;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
export type ApprovedStartupPlan = {
|
|
28
|
-
artifact_type: "completion-startup-plan";
|
|
29
|
-
schema_version: 1;
|
|
30
|
-
status: "approved";
|
|
31
|
-
source: AdvisoryStartupBrief["source"];
|
|
32
|
-
captured_at: string;
|
|
33
|
-
mission_anchor: string;
|
|
34
|
-
goal_text: string;
|
|
35
|
-
task_type?: string;
|
|
36
|
-
evaluation_profile?: string;
|
|
37
|
-
scope: string[];
|
|
38
|
-
constraints: string[];
|
|
39
|
-
acceptance: string[];
|
|
40
|
-
risks: string[];
|
|
41
|
-
notes: string[];
|
|
42
|
-
planned_surfaces: string[];
|
|
43
|
-
verification_intent: string[];
|
|
44
|
-
sequencing_hints: string[];
|
|
45
|
-
};
|
|
46
|
-
|
|
47
27
|
export function buildCookHandoffBoundaryReminder(): string {
|
|
48
28
|
return [
|
|
49
29
|
"You are in ordinary main chat unless the user explicitly runs /cook.",
|
|
@@ -53,11 +33,10 @@ export function buildCookHandoffBoundaryReminder(): string {
|
|
|
53
33
|
"In ordinary chat, do not load or follow completion-protocol, and do not call completion_role.",
|
|
54
34
|
"If the user wants direct implementation now, stay in ordinary chat and help directly instead of blocking on /cook.",
|
|
55
35
|
"If the user asks follow-up questions or wants to keep refining scope, continue helping naturally in ordinary chat.",
|
|
56
|
-
"If the user explicitly runs /cook, the extension should call a primary-agent
|
|
57
|
-
"Do not expect /cook to infer or guess startup intent from recent discussion alone; /cook should use primary-agent
|
|
58
|
-
"Only provide a preview startup
|
|
36
|
+
"If the user explicitly runs /cook, the extension should call a primary-agent handoff synthesis step from the current task context, show Start/Cancel confirmation, and persist the confirmed startup brief into .agent/** without making the user rerun /cook.",
|
|
37
|
+
"Do not expect /cook to infer or guess startup intent from recent discussion alone; /cook should use explicit primary-agent handoff data, whether it already exists or is synthesized in the same /cook entry.",
|
|
38
|
+
"Only provide a preview startup brief or ```cook_handoff``` capsule in ordinary chat when the user explicitly asks for that preview behavior.",
|
|
59
39
|
"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.",
|
|
60
|
-
"When /cook starts, the approved startup plan should be written into .agent and then handed to completion-regrounder so canonical slices can be derived from repo truth.",
|
|
61
40
|
"When you continue in ordinary chat, do not pretend /cook already started and do not silently rewrite discussion into canonical workflow state.",
|
|
62
41
|
].join(" ");
|
|
63
42
|
}
|
|
@@ -109,27 +88,6 @@ function buildAdvisoryStartupBriefNotes(analysis: ContextProposalAnalysis): stri
|
|
|
109
88
|
return notes.length > 0 ? notes : ["No additional operator notes were derived from recent discussion."];
|
|
110
89
|
}
|
|
111
90
|
|
|
112
|
-
function startupPlanSourceForProposal(source: ContextProposal["source"]): AdvisoryStartupBrief["source"] {
|
|
113
|
-
return source === "handoff_capsule"
|
|
114
|
-
? "primary_agent_handoff"
|
|
115
|
-
: source === "deferred_primary_agent_handoff"
|
|
116
|
-
? "deferred_primary_agent_handoff"
|
|
117
|
-
: "recent_discussion";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function extractDelimitedNoteValues(notes: string[], prefix: string): string[] {
|
|
121
|
-
return notes.flatMap((note) => {
|
|
122
|
-
if (!note.startsWith(prefix)) return [];
|
|
123
|
-
return note.slice(prefix.length).split("|").map((item) => item.trim()).filter(Boolean);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function extractSequencingHints(notes: string[]): string[] {
|
|
128
|
-
return notes.filter((note) =>
|
|
129
|
-
note.startsWith("First slice goal:") || note.startsWith("First slice non-goals:") || note.startsWith("Why this slice first:"),
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
91
|
export function buildAdvisoryStartupBrief(args: {
|
|
134
92
|
proposal: Pick<ContextProposal, "goalText" | "mission" | "scope" | "constraints" | "acceptance" | "source">;
|
|
135
93
|
analysis: ContextProposalAnalysis;
|
|
@@ -137,7 +95,12 @@ export function buildAdvisoryStartupBrief(args: {
|
|
|
137
95
|
}): AdvisoryStartupBrief {
|
|
138
96
|
return {
|
|
139
97
|
kind: "startup_brief",
|
|
140
|
-
source:
|
|
98
|
+
source:
|
|
99
|
+
args.proposal.source === "handoff_capsule"
|
|
100
|
+
? "primary_agent_handoff"
|
|
101
|
+
: args.proposal.source === "deferred_primary_agent_handoff"
|
|
102
|
+
? "deferred_primary_agent_handoff"
|
|
103
|
+
: "recent_discussion",
|
|
141
104
|
confirmed: true,
|
|
142
105
|
captured_at: args.capturedAt ?? new Date().toISOString(),
|
|
143
106
|
goal_text: args.proposal.goalText,
|
|
@@ -152,81 +115,6 @@ export function buildAdvisoryStartupBrief(args: {
|
|
|
152
115
|
};
|
|
153
116
|
}
|
|
154
117
|
|
|
155
|
-
export function buildApprovedStartupPlan(args: {
|
|
156
|
-
proposal: Pick<ContextProposal, "goalText" | "mission" | "scope" | "constraints" | "acceptance" | "source">;
|
|
157
|
-
analysis: ContextProposalAnalysis;
|
|
158
|
-
missionAnchor?: string;
|
|
159
|
-
capturedAt?: string;
|
|
160
|
-
}): ApprovedStartupPlan {
|
|
161
|
-
const capturedAt = args.capturedAt ?? new Date().toISOString();
|
|
162
|
-
const notes = buildAdvisoryStartupBriefNotes(args.analysis);
|
|
163
|
-
return {
|
|
164
|
-
artifact_type: "completion-startup-plan",
|
|
165
|
-
schema_version: 1,
|
|
166
|
-
status: "approved",
|
|
167
|
-
source: startupPlanSourceForProposal(args.proposal.source),
|
|
168
|
-
captured_at: capturedAt,
|
|
169
|
-
mission_anchor: args.missionAnchor ?? args.proposal.mission,
|
|
170
|
-
goal_text: args.proposal.goalText,
|
|
171
|
-
task_type: args.analysis.taskType,
|
|
172
|
-
evaluation_profile: args.analysis.evaluationProfile,
|
|
173
|
-
scope: [...args.proposal.scope],
|
|
174
|
-
constraints: [...args.proposal.constraints],
|
|
175
|
-
acceptance: [...args.proposal.acceptance],
|
|
176
|
-
risks: [...args.analysis.risks],
|
|
177
|
-
notes: [...notes],
|
|
178
|
-
planned_surfaces: extractDelimitedNoteValues(notes, "Implementation surfaces:"),
|
|
179
|
-
verification_intent: extractDelimitedNoteValues(notes, "Verification commands:"),
|
|
180
|
-
sequencing_hints: extractSequencingHints(notes),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function buildApprovedStartupPlanMarkdown(plan: ApprovedStartupPlan): string {
|
|
185
|
-
const lines = [
|
|
186
|
-
"# Approved Startup Plan",
|
|
187
|
-
"",
|
|
188
|
-
`Mission anchor: ${plan.mission_anchor}`,
|
|
189
|
-
`Source: ${plan.source}`,
|
|
190
|
-
`Captured at: ${plan.captured_at}`,
|
|
191
|
-
];
|
|
192
|
-
if (plan.task_type) lines.push(`Task type: ${plan.task_type}`);
|
|
193
|
-
if (plan.evaluation_profile) lines.push(`Evaluation profile: ${plan.evaluation_profile}`);
|
|
194
|
-
lines.push("", "## Goal", "", plan.goal_text);
|
|
195
|
-
if (plan.scope.length > 0) {
|
|
196
|
-
lines.push("", "## Scope", "");
|
|
197
|
-
for (const item of plan.scope) lines.push(`- ${item}`);
|
|
198
|
-
}
|
|
199
|
-
if (plan.constraints.length > 0) {
|
|
200
|
-
lines.push("", "## Constraints", "");
|
|
201
|
-
for (const item of plan.constraints) lines.push(`- ${item}`);
|
|
202
|
-
}
|
|
203
|
-
if (plan.acceptance.length > 0) {
|
|
204
|
-
lines.push("", "## Acceptance", "");
|
|
205
|
-
for (const item of plan.acceptance) lines.push(`- ${item}`);
|
|
206
|
-
}
|
|
207
|
-
if (plan.risks.length > 0) {
|
|
208
|
-
lines.push("", "## Risks", "");
|
|
209
|
-
for (const item of plan.risks) lines.push(`- ${item}`);
|
|
210
|
-
}
|
|
211
|
-
if (plan.planned_surfaces.length > 0) {
|
|
212
|
-
lines.push("", "## Planned surfaces", "");
|
|
213
|
-
for (const item of plan.planned_surfaces) lines.push(`- ${item}`);
|
|
214
|
-
}
|
|
215
|
-
if (plan.verification_intent.length > 0) {
|
|
216
|
-
lines.push("", "## Verification intent", "");
|
|
217
|
-
for (const item of plan.verification_intent) lines.push(`- ${item}`);
|
|
218
|
-
}
|
|
219
|
-
if (plan.sequencing_hints.length > 0) {
|
|
220
|
-
lines.push("", "## Sequencing hints", "");
|
|
221
|
-
for (const item of plan.sequencing_hints) lines.push(`- ${item}`);
|
|
222
|
-
}
|
|
223
|
-
if (plan.notes.length > 0) {
|
|
224
|
-
lines.push("", "## Notes", "");
|
|
225
|
-
for (const item of plan.notes) lines.push(`- ${item}`);
|
|
226
|
-
}
|
|
227
|
-
return `${lines.join("\n")}\n`;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
118
|
export function buildContextProposalCritiqueText(analysis: ContextProposalAnalysis): string {
|
|
231
119
|
const lines: string[] = [];
|
|
232
120
|
if (analysis.critique.length > 0) {
|
|
@@ -259,7 +147,7 @@ export function buildContextProposalCritiqueText(analysis: ContextProposalAnalys
|
|
|
259
147
|
for (const item of analysis.suppressedNegatedTopics) lines.push(`- ${item}`);
|
|
260
148
|
}
|
|
261
149
|
if (lines.length === 0) {
|
|
262
|
-
return "No additional operator notes or risks were derived for this startup
|
|
150
|
+
return "No additional operator notes or risks were derived for this startup brief.";
|
|
263
151
|
}
|
|
264
152
|
return lines.join("\n");
|
|
265
153
|
}
|
|
@@ -308,7 +196,7 @@ export function buildContextProposalConfirmationActions(mainChatRerunGuidance: s
|
|
|
308
196
|
{
|
|
309
197
|
id: "start",
|
|
310
198
|
label: "Start",
|
|
311
|
-
description: "Accept this startup
|
|
199
|
+
description: "Accept this startup brief and let /cook write or refocus canonical workflow state.",
|
|
312
200
|
},
|
|
313
201
|
{
|
|
314
202
|
id: "cancel",
|
|
@@ -328,8 +216,8 @@ export function buildContextProposalConfirmationLayout(args: {
|
|
|
328
216
|
}): ContextProposalConfirmationLayout {
|
|
329
217
|
return {
|
|
330
218
|
title: args.title,
|
|
331
|
-
intro: "Review the startup
|
|
332
|
-
proposalHeading: "Startup
|
|
219
|
+
intro: "Review the startup brief (mission, scope, constraints, acceptance, and notes/risks) plus the routing details before /cook writes canonical workflow state. This gate is approval-only: either Start it as-is or Cancel, discuss changes in the main chat, and rerun /cook.",
|
|
220
|
+
proposalHeading: "Startup brief",
|
|
333
221
|
proposalBody: buildContextProposalDisplayText(args.proposal),
|
|
334
222
|
critiqueHeading: "Notes and risks",
|
|
335
223
|
critiqueBody: buildContextProposalCritiqueText(args.analysis),
|
|
@@ -529,15 +417,6 @@ type CompletionVerificationEvidenceSummary = {
|
|
|
529
417
|
summary: string;
|
|
530
418
|
};
|
|
531
419
|
|
|
532
|
-
type CompletionStartupPlanSummary = {
|
|
533
|
-
path: string;
|
|
534
|
-
status: string;
|
|
535
|
-
source?: string;
|
|
536
|
-
plannedSurfaces: string[];
|
|
537
|
-
verificationIntent: string[];
|
|
538
|
-
summary: string;
|
|
539
|
-
};
|
|
540
|
-
|
|
541
420
|
export function buildSystemReminder(args: {
|
|
542
421
|
missionAnchor?: string;
|
|
543
422
|
taskType?: string;
|
|
@@ -561,12 +440,11 @@ export function buildSystemReminder(args: {
|
|
|
561
440
|
implementationSurfacesLine?: string;
|
|
562
441
|
verificationCommandsLine?: string;
|
|
563
442
|
evidence: CompletionVerificationEvidenceSummary;
|
|
564
|
-
startupPlan: CompletionStartupPlanSummary;
|
|
565
443
|
evaluationRoleReminderText?: string;
|
|
566
444
|
}): string {
|
|
567
445
|
const lines = [
|
|
568
446
|
"Completion workflow detected.",
|
|
569
|
-
"Canonical truth lives in .agent/state.json, .agent/
|
|
447
|
+
"Canonical truth lives in .agent/state.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json.",
|
|
570
448
|
`Mission anchor: ${args.missionAnchor ?? "(unknown)"}`,
|
|
571
449
|
`Task type: ${args.taskType ?? "(missing)"}`,
|
|
572
450
|
`Evaluation profile: ${args.evaluationProfile ?? "(missing)"}`,
|
|
@@ -583,7 +461,6 @@ export function buildSystemReminder(args: {
|
|
|
583
461
|
"Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.",
|
|
584
462
|
"If canonical state is stale, invalid, ambiguous, or missing, route to completion-regrounder.",
|
|
585
463
|
"When recovering from compaction, prefer a deterministic restart from canonical files over conversational inference.",
|
|
586
|
-
`Approved startup plan: ${args.startupPlan.path} (${args.startupPlan.status})`,
|
|
587
464
|
];
|
|
588
465
|
if (args.exactActiveContract) {
|
|
589
466
|
lines.push("Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.");
|
|
@@ -597,14 +474,6 @@ export function buildSystemReminder(args: {
|
|
|
597
474
|
else if (args.implementationSurfaces.length > 0) lines.push(`Active implementation surfaces: ${args.implementationSurfaces.join(", ")}`);
|
|
598
475
|
if (args.verificationCommandsLine) lines.push(args.verificationCommandsLine);
|
|
599
476
|
else if (args.verificationCommands.length > 0) lines.push(`Active verification commands: ${args.verificationCommands.join(" | ")}`);
|
|
600
|
-
if (args.startupPlan.source) lines.push(`Approved startup plan source: ${args.startupPlan.source}`);
|
|
601
|
-
if (args.startupPlan.plannedSurfaces.length > 0) {
|
|
602
|
-
lines.push(`Approved startup plan surfaces: ${args.startupPlan.plannedSurfaces.join(" | ")}`);
|
|
603
|
-
}
|
|
604
|
-
if (args.startupPlan.verificationIntent.length > 0) {
|
|
605
|
-
lines.push(`Approved startup plan verification intent: ${args.startupPlan.verificationIntent.join(" | ")}`);
|
|
606
|
-
}
|
|
607
|
-
lines.push(`Approved startup plan summary: ${args.startupPlan.summary}`);
|
|
608
477
|
lines.push(`Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})`);
|
|
609
478
|
if (args.evidence.subjectType) lines.push(`Verification evidence subject: ${args.evidence.subjectType}`);
|
|
610
479
|
if (args.evidence.outcome) lines.push(`Verification evidence outcome: ${args.evidence.outcome}`);
|
|
@@ -633,7 +502,6 @@ export function buildResumeCapsule(args: {
|
|
|
633
502
|
activeSliceMatchesPlan: "yes" | "no" | "unknown";
|
|
634
503
|
activeSliceContractDrift: string;
|
|
635
504
|
implementerHandoffSnapshot: "present" | "missing_or_unclear";
|
|
636
|
-
startupPlan: CompletionStartupPlanSummary;
|
|
637
505
|
evidence: CompletionVerificationEvidenceSummary;
|
|
638
506
|
activeSlice: {
|
|
639
507
|
sliceId?: string;
|
|
@@ -676,14 +544,6 @@ export function buildResumeCapsule(args: {
|
|
|
676
544
|
`implementer_handoff_snapshot: ${args.implementerHandoffSnapshot}`,
|
|
677
545
|
`history_counts: reviewed=${args.history.reviewed}, audited=${args.history.audited}, accepted=${args.history.accepted}, reopened=${args.history.reopened}, judgments=${args.history.judgments}`,
|
|
678
546
|
"",
|
|
679
|
-
"startup_plan:",
|
|
680
|
-
`- path: ${args.startupPlan.path}`,
|
|
681
|
-
`- status: ${args.startupPlan.status}`,
|
|
682
|
-
`- source: ${args.startupPlan.source ?? "(missing)"}`,
|
|
683
|
-
`- planned_surfaces: ${args.startupPlan.plannedSurfaces.length > 0 ? args.startupPlan.plannedSurfaces.join(" | ") : "(none)"}`,
|
|
684
|
-
`- verification_intent: ${args.startupPlan.verificationIntent.length > 0 ? args.startupPlan.verificationIntent.join(" | ") : "(none)"}`,
|
|
685
|
-
`- summary: ${args.startupPlan.summary}`,
|
|
686
|
-
"",
|
|
687
547
|
"verification_evidence:",
|
|
688
548
|
`- path: ${args.evidence.path}`,
|
|
689
549
|
`- status: ${args.evidence.status}`,
|
|
@@ -731,9 +591,8 @@ export function buildResumeCapsule(args: {
|
|
|
731
591
|
"- Treat this block as continuity support derived from canonical .agent state.",
|
|
732
592
|
"- For selected/in-progress/committed/done slices, .agent/active-slice.json is the canonical implementation contract and the selected plan slice must mirror it exactly.",
|
|
733
593
|
"- Preserve exact slice_id, goal, contract_ids, acceptance criteria, blocked_on, priority, why_now, implementation surfaces, verification commands, locked notes, must-fix findings, basis_commit, and before-slice counters where still true.",
|
|
734
|
-
"- .agent/startup-plan.json is the approved workflow plan captured at /cook entry. completion-regrounder must treat it as planning input, then reconcile canonical slices against repo truth instead of copying it blindly into plan.json.",
|
|
735
594
|
"- When populated, .agent/verification-evidence.json is the durable canonical verification record for the selected slice or current HEAD and should be consumed instead of temp-only artifacts or conversational summaries.",
|
|
736
|
-
"- After compaction, re-read .agent/state.json, .agent/
|
|
595
|
+
"- After compaction, re-read .agent/state.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json before resuming long-running completion work.",
|
|
737
596
|
"- Invoke completion-regrounder before continuing when requires_reground is true or unknown.",
|
|
738
597
|
"- Invoke completion-regrounder before continuing when next_mandatory_role or next_mandatory_action is unknown or ambiguous.",
|
|
739
598
|
"- Invoke completion-regrounder before continuing when active_slice_matches_plan is no, active_slice_contract_drift_fields is not none, or implementer_handoff_snapshot is missing_or_unclear.",
|
|
@@ -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) continue;
|
|
1282
|
+
if (!mission || !firstSliceGoal || !whyThisSliceFirst) 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}`,
|
|
1328
1333
|
];
|
|
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,21 +1363,21 @@ 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
|
-
|
|
1371
|
-
failures.push("first_slice_goal is planning-only instead of a repo-change sequencing hint");
|
|
1372
|
-
}
|
|
1366
|
+
const firstSliceGoal = deps.normalizeMissionAnchorText(capsule.first_slice_goal);
|
|
1367
|
+
if (!firstSliceGoal || deps.isWeakMissionAnchor(firstSliceGoal) || COOK_HANDOFF_NEGATIVE_MISSION_REGEX.test(firstSliceGoal)) {
|
|
1368
|
+
failures.push("first_slice_goal is not a bounded implementation slice");
|
|
1369
|
+
} else if (hasExplicitPlanningOnlyDeliverable([capsule.first_slice_goal]) || hasClearNoImplementationSignal([capsule.first_slice_goal])) {
|
|
1370
|
+
failures.push("first_slice_goal is planning-only instead of a repo-change slice");
|
|
1373
1371
|
}
|
|
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
1377
|
function buildNonStartableCookHandoffMessage(failures: string[]): string {
|
|
1378
1378
|
return [
|
|
1379
|
-
"/cook failed closed because a fresh explicit primary-agent
|
|
1380
|
-
"Tighten the
|
|
1379
|
+
"/cook failed closed because a fresh explicit primary-agent handoff exists, but it is not concrete enough to start implementation workflow yet.",
|
|
1380
|
+
"Tighten the handoff in the main chat so it names a bounded first implementation slice, repo-change-oriented acceptance, implementation_surfaces, and verification_commands, then rerun /cook.",
|
|
1381
1381
|
`Blocking details: ${failures.join("; ")}.`,
|
|
1382
1382
|
].join(" ");
|
|
1383
1383
|
}
|
|
@@ -1435,11 +1435,11 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1435
1435
|
evaluationProfile: capsule.evaluation_profile,
|
|
1436
1436
|
critique: [
|
|
1437
1437
|
...capsule.notes,
|
|
1438
|
-
|
|
1438
|
+
`First slice goal: ${capsule.first_slice_goal}`,
|
|
1439
1439
|
...(capsule.first_slice_non_goals.length > 0 ? [`First slice non-goals: ${capsule.first_slice_non_goals.join(" | ")}`] : []),
|
|
1440
1440
|
...(capsule.implementation_surfaces.length > 0 ? [`Implementation surfaces: ${capsule.implementation_surfaces.join(" | ")}`] : []),
|
|
1441
1441
|
...(capsule.verification_commands.length > 0 ? [`Verification commands: ${capsule.verification_commands.join(" | ")}`] : []),
|
|
1442
|
-
|
|
1442
|
+
`Why this slice first: ${capsule.why_this_slice_first}`,
|
|
1443
1443
|
...(capsule.why_cook_now ? [`Primary-agent /cook handoff rationale: ${capsule.why_cook_now}`] : []),
|
|
1444
1444
|
],
|
|
1445
1445
|
risks: capsule.risks,
|
|
@@ -1455,11 +1455,11 @@ function buildContextProposalFromCookHandoffCapsule(
|
|
|
1455
1455
|
...capsule.scope,
|
|
1456
1456
|
...constraints,
|
|
1457
1457
|
...capsule.acceptance,
|
|
1458
|
-
|
|
1458
|
+
capsule.first_slice_goal,
|
|
1459
1459
|
...capsule.first_slice_non_goals,
|
|
1460
1460
|
...capsule.implementation_surfaces,
|
|
1461
1461
|
...capsule.verification_commands,
|
|
1462
|
-
|
|
1462
|
+
capsule.why_this_slice_first,
|
|
1463
1463
|
],
|
|
1464
1464
|
),
|
|
1465
1465
|
goalText,
|
|
@@ -97,15 +97,14 @@ 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
|
-
"
|
|
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
|
-
"Do not make /cook infer or rediscover the mission from recent discussion later; author the startup plan now from the primary-agent view of the task.",
|
|
100
|
+
"You are the primary agent preparing an explicit /cook handoff 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 handoff can be prepared.",
|
|
102
|
+
"If you can prepare a handoff, the JSON must use kind cook_handoff, source primary_agent, and handoff_kind implementation_workflow_handoff.",
|
|
103
|
+
"When the user has clearly accepted a concrete assistant-proposed slice, carry that slice forward into the handoff instead of broadening or re-guessing the mission.",
|
|
104
|
+
"Do not make /cook infer or rediscover the mission from recent discussion later; author the handoff now from the primary-agent view of the task.",
|
|
107
105
|
"Do not emit markdown commentary before or after the capsule.",
|
|
108
|
-
"If the task is not concrete enough for workflow
|
|
106
|
+
"If the task is not concrete enough for implementation workflow, do not invent the slice.",
|
|
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.",
|
|
109
108
|
].join(" ");
|
|
110
109
|
const PRIMARY_AGENT_HANDOFF_ROLE = "cook-primary-agent-handoff";
|
|
111
110
|
|
|
@@ -343,7 +342,7 @@ function buildPrimaryAgentHandoffPrompt(projectName: string, recentEntries: Rece
|
|
|
343
342
|
lines.push(
|
|
344
343
|
"",
|
|
345
344
|
"Task:",
|
|
346
|
-
"The user explicitly invoked /cook. Prepare the primary-agent
|
|
345
|
+
"The user explicitly invoked /cook. Prepare the primary-agent handoff that /cook should consume immediately for Start/Cancel confirmation.",
|
|
347
346
|
);
|
|
348
347
|
return lines.join("\n");
|
|
349
348
|
}
|
|
@@ -360,7 +359,7 @@ async function runPrimaryAgentHandoffSubprocess(params: GenerateCookHandoffWithA
|
|
|
360
359
|
const invocation = getPiInvocation(args);
|
|
361
360
|
const liveActivity = createLiveRoleActivity(PRIMARY_AGENT_HANDOFF_ROLE);
|
|
362
361
|
liveActivity.progress = "Preparing primary-agent /cook handoff";
|
|
363
|
-
liveActivity.currentAction = "Authoring explicit startup
|
|
362
|
+
liveActivity.currentAction = "Authoring explicit startup handoff from current task context";
|
|
364
363
|
liveActivity.assistantSummary = liveActivity.progress;
|
|
365
364
|
try {
|
|
366
365
|
const output = await new Promise<string | undefined>((resolve) => {
|
|
@@ -410,7 +409,7 @@ export async function generateCookHandoffWithAgent(params: GenerateCookHandoffWi
|
|
|
410
409
|
try {
|
|
411
410
|
return await runPrimaryAgentHandoffSubprocess(params);
|
|
412
411
|
} catch (error) {
|
|
413
|
-
console.warn("[completion] primary-agent
|
|
412
|
+
console.warn("[completion] primary-agent handoff generation failed", error);
|
|
414
413
|
return undefined;
|
|
415
414
|
}
|
|
416
415
|
}
|