@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 CHANGED
@@ -13,6 +13,8 @@ This repository uses the `completion` workflow for long-running coding tasks.
13
13
  ## Ignored canonical execution state
14
14
 
15
15
  - `.agent/state.json`
16
+ - `.agent/startup-plan.json`
17
+ - `.agent/startup-plan.md`
16
18
  - `.agent/plan.json`
17
19
  - `.agent/active-slice.json`
18
20
  - `.agent/slice-history.jsonl`
@@ -21,6 +23,8 @@ This repository uses the `completion` workflow for long-running coding tasks.
21
23
  - `.agent/*.log`
22
24
  - `.agent/tmp/`
23
25
 
26
+ `.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.
27
+
24
28
  `.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.
25
29
 
26
30
  The source of truth for long-running completion work is canonical `.agent/**` state plus current repo truth.
@@ -82,15 +82,23 @@ function trackedDiffFiles(fromCommit, toCommit) {
82
82
 
83
83
  const profile = readJson('.agent/profile.json');
84
84
  const state = readJson('.agent/state.json');
85
+ const startupPlan = readJson('.agent/startup-plan.json');
85
86
  const plan = readJson('.agent/plan.json');
86
87
  const active = readJson('.agent/active-slice.json');
87
88
  const evidence = readJson('.agent/verification-evidence.json');
89
+ let startupPlanMarkdown = '';
90
+ try {
91
+ startupPlanMarkdown = fs.readFileSync('.agent/startup-plan.md', 'utf8');
92
+ } catch (error) {
93
+ fail('.agent/startup-plan.md must be present and readable: ' + error.message);
94
+ }
88
95
 
89
96
  ensureTrackedContractFiles();
90
97
 
91
98
  for (const [file, record] of [
92
99
  ['.agent/profile.json', profile],
93
100
  ['.agent/state.json', state],
101
+ ['.agent/startup-plan.json', startupPlan],
94
102
  ['.agent/plan.json', plan],
95
103
  ['.agent/active-slice.json', active],
96
104
  ]) {
@@ -101,12 +109,38 @@ for (const [file, record] of [
101
109
  const taskType = asString(profile.task_type);
102
110
  const evaluationProfile = asString(profile.evaluation_profile);
103
111
  if (asString(state.task_type) !== taskType) fail('.agent/state.json task_type must match .agent/profile.json task_type');
112
+ if (asString(startupPlan.task_type) !== taskType) fail('.agent/startup-plan.json task_type must match .agent/profile.json task_type');
104
113
  if (asString(plan.task_type) !== taskType) fail('.agent/plan.json task_type must match .agent/profile.json task_type');
105
114
  if (asString(active.task_type) !== taskType) fail('.agent/active-slice.json task_type must match .agent/profile.json task_type');
106
115
  if (asString(state.evaluation_profile) !== evaluationProfile) fail('.agent/state.json evaluation_profile must match .agent/profile.json evaluation_profile');
116
+ if (asString(startupPlan.evaluation_profile) !== evaluationProfile) fail('.agent/startup-plan.json evaluation_profile must match .agent/profile.json evaluation_profile');
107
117
  if (asString(plan.evaluation_profile) !== evaluationProfile) fail('.agent/plan.json evaluation_profile must match .agent/profile.json evaluation_profile');
108
118
  if (asString(active.evaluation_profile) !== evaluationProfile) fail('.agent/active-slice.json evaluation_profile must match .agent/profile.json evaluation_profile');
109
119
 
120
+ if (asString(startupPlan.artifact_type) !== 'completion-startup-plan') {
121
+ fail('.agent/startup-plan.json artifact_type must be completion-startup-plan');
122
+ }
123
+ if (asString(startupPlan.status) !== 'approved') {
124
+ fail('.agent/startup-plan.json status must be approved');
125
+ }
126
+ const startupPlanMissionAnchor = asString(startupPlan.mission_anchor);
127
+ if (!startupPlanMissionAnchor) fail('.agent/startup-plan.json mission_anchor must be present');
128
+ if (startupPlanMissionAnchor !== asString(state.mission_anchor)) fail('.agent/startup-plan.json mission_anchor must match .agent/state.json mission_anchor');
129
+ if (startupPlanMissionAnchor !== asString(plan.mission_anchor)) fail('.agent/startup-plan.json mission_anchor must match .agent/plan.json mission_anchor');
130
+ if (startupPlanMissionAnchor !== asString(active.mission_anchor)) fail('.agent/startup-plan.json mission_anchor must match .agent/active-slice.json mission_anchor');
131
+ if (!asString(startupPlan.goal_text)) fail('.agent/startup-plan.json goal_text must be present');
132
+ for (const field of ['scope', 'constraints', 'acceptance', 'risks', 'notes', 'planned_surfaces', 'verification_intent', 'sequencing_hints']) {
133
+ if (!Array.isArray(startupPlan[field])) fail('.agent/startup-plan.json is missing ' + field);
134
+ }
135
+ if (startupPlanMarkdown.trim().length === 0) fail('.agent/startup-plan.md must not be empty');
136
+ if (startupPlanMissionAnchor && !startupPlanMarkdown.includes(startupPlanMissionAnchor)) {
137
+ fail('.agent/startup-plan.md must mention the startup-plan mission_anchor');
138
+ }
139
+ const startupPlanGoalText = asString(startupPlan.goal_text);
140
+ if (startupPlanGoalText && !startupPlanMarkdown.includes(startupPlanGoalText)) {
141
+ fail('.agent/startup-plan.md must render the startup-plan goal_text');
142
+ }
143
+
110
144
  if (asString(evidence.artifact_type) !== 'completion-verification-evidence') {
111
145
  fail('.agent/verification-evidence.json artifact_type must be completion-verification-evidence');
112
146
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.68
4
+
5
+ ### Changed
6
+
7
+ - simplified `/cook` startup sourcing so workflow proposals now come only from same-entry primary-agent startup-plan synthesis
8
+ - stopped `/cook` from directly adopting old preview capsules or falling back to transcript-derived startup proposals
9
+ - kept preview capsules advisory-only for humans while active-workflow replacement and next-round startup now depend on same-entry primary-agent synthesis from current task context
10
+
11
+ ## 0.1.67
12
+
13
+ ### Changed
14
+
15
+ - rewrote `/cook` startup around an approved startup plan that is captured under `.agent/startup-plan.json` / `.agent/startup-plan.md` after Start instead of leaving startup intent only as advisory intake in `state.json`
16
+ - kept `/cook` confirm-first while handing the approved startup plan to `completion-regrounder`, which now derives canonical slices from repo truth instead of treating startup intake like an implementation-ready first-slice contract
17
+ - relaxed primary-agent `/cook` preview requirements so explicit startup plans may carry optional sequencing hints (`first_slice_goal`, `implementation_surfaces`, `verification_commands`) without requiring them before workflow startup can begin
18
+ - updated workflow reminders, recovery capsules, regrounder/bootstrapper instructions, and public docs so canonical startup-plan persistence and regrounder-owned slice derivation stay truthful
19
+
3
20
  ## 0.1.66
4
21
 
5
22
  ### Changed
package/README.md CHANGED
@@ -34,8 +34,9 @@ Then run `/reload` in Pi.
34
34
  2. Run `/reload` in Pi.
35
35
  3. In the main chat, either implement directly with the agent or refine the concrete repo change you want.
36
36
  4. When you want workflow mode, run `/cook`.
37
- 5. Review the startup brief and choose **Start** or **Cancel**.
38
- 6. Later, run `/cook` again to resume from canonical state or confirm a replacement or next-round handoff.
37
+ 5. Review the startup plan and choose **Start** or **Cancel**.
38
+ 6. After **Start**, `/cook` records the approved startup plan under `.agent/` and hands the workflow to `completion-regrounder` to split canonical slices.
39
+ 7. Later, run `/cook` again to resume from canonical state or confirm a replacement or next-round startup plan.
39
40
 
40
41
  ```text
41
42
  /cook
@@ -52,16 +53,15 @@ Then run `/reload` in Pi.
52
53
 
53
54
  ## What `/cook` expects
54
55
 
55
- - enough current task context for a primary-agent handoff synthesis step to produce a concrete workflow startup handoff
56
- - a mission and first slice concrete enough for the primary-agent handoff step to author a truthful implementation-startable handoff
57
- - acceptance and verification intent that can support a truthful first workflow round
56
+ - enough current task context for a primary-agent startup-plan synthesis step to produce a concrete workflow startup plan
57
+ - a mission, scope, acceptance, and verification intent concrete enough for `completion-regrounder` to derive truthful slices after startup
58
58
  - README/CHANGELOG updates still count as concrete repo changes
59
- - assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts still do not count unless the primary-agent handoff step turns them into a concrete `cook_handoff` capsule
60
- - `/cook` first prefers a fresh explicit `cook_handoff` capsule when one already exists, but otherwise calls the primary-agent handoff synthesis step in the same `/cook` entry
59
+ - assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts still do not count unless the primary-agent startup-plan step turns them into concrete startup intake for `/cook`
60
+ - `/cook` always runs a same-entry primary-agent startup-plan synthesis step from the current task context instead of directly adopting an old preview or transcript-derived proposal
61
61
 
62
- If the primary-agent handoff step still cannot prepare a concrete handoff, `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to refine the mission, first slice, or verification intent in the main chat before rerunning `/cook`.
62
+ If the startup-plan step still cannot prepare a concrete startup plan, `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to refine the mission, scope, acceptance, or verification intent in the main chat before rerunning `/cook`.
63
63
 
64
- If a fresh explicit handoff exists but is still workflow-worthy rather than implementation-startable, `/cook` also fails closed instead of silently treating that capsule as planning support or canonical workflow state.
64
+ If the same-entry synthesized startup plan is still too vague or planning-only to seed workflow planning, `/cook` also fails closed instead of silently treating that output as canonical workflow state.
65
65
 
66
66
  If you pass inline arguments to `/cook`, it also fails closed and tells you to move that intent into the main chat before rerunning bare `/cook`.
67
67
 
@@ -69,16 +69,17 @@ If you pass inline arguments to `/cook`, it also fails closed and tells you to m
69
69
 
70
70
  Only explicit `/cook` enters workflow mode. Ordinary prompts stay in the main chat and go straight to the primary agent.
71
71
 
72
- Ordinary chat can still directly implement repo changes. `/cook` is for the cases where you want workflow control rather than just implementation help, and the primary agent should prepare the handoff before you run it.
72
+ Ordinary chat can still directly implement repo changes. `/cook` is for the cases where you want workflow control rather than just implementation help, and the primary agent should prepare the startup plan before workflow begins.
73
73
 
74
- When you explicitly run `/cook`, it first checks for a fresh explicit primary-agent handoff. If one is missing, it calls a same-entry primary-agent handoff synthesis step from the current task context, then asks you to **Start** or **Cancel** before rewriting canonical workflow state.
74
+ When you explicitly run `/cook`, it always calls a same-entry primary-agent startup-plan synthesis step from the current task context, then asks you to **Start** or **Cancel** before rewriting canonical workflow state.
75
75
 
76
- Explicit `/cook` capsules are still valid startup intake, but they are no longer the only path because `/cook` can synthesize the primary-agent handoff in the same entry when needed.
76
+ Optional preview capsules in ordinary chat are advisory only. `/cook` does not directly consume them as approval-ready workflow state; it synthesizes a fresh startup plan in the `/cook` entry.
77
77
 
78
78
  Important behavior:
79
79
  - `/cook` is an optional workflow boundary and manual entry point
80
- - startup and next-round entry stay confirm-first, using explicit primary-agent handoff data when present and otherwise running the primary-agent handoff synthesis step in the same `/cook` entry
81
- - active workflows resume from canonical `.agent/**` state unless a concrete replacement handoff is available or synthesized in the same `/cook` entry
80
+ - startup and next-round entry stay confirm-first, always using same-entry primary-agent startup-plan synthesis from the current task context
81
+ - after **Start**, `/cook` records the approved startup plan under `.agent/startup-plan.json` / `.agent/startup-plan.md`, then `completion-regrounder` derives canonical slices from repo truth
82
+ - active workflows resume from canonical `.agent/**` state unless same-entry primary-agent startup-plan synthesis produces a concrete replacement mission
82
83
  - explicit slash commands other than `/cook` continue normally in the main chat
83
84
  - ordinary main-chat discussion may clarify, propose, or directly implement repo changes without entering workflow mode
84
85
 
@@ -94,13 +95,13 @@ I want to add login redirect handling and tests.
94
95
 
95
96
  ## What happens when you run `/cook`
96
97
 
97
- `/cook` first checks for a fresh explicit primary-agent handoff capsule. New-workflow entry and done-workflow next-round entry use that handoff when it already exists; otherwise `/cook` calls a same-entry primary-agent handoff synthesis step, then immediately continues to Start / Cancel if the generated handoff is concrete enough. Active workflows still resume canonical state by default unless a concrete replacement handoff is available or synthesized in the same `/cook` entry. None of this prevents ordinary-chat implementation when you choose not to enter workflow mode.
98
+ `/cook` always runs a same-entry primary-agent startup-plan synthesis step from the current task context. If that synthesized plan is concrete enough, `/cook` continues to Start / Cancel confirmation. After **Start**, the approved startup plan is written into `.agent/startup-plan.json` / `.agent/startup-plan.md`, and `completion-regrounder` uses it to derive canonical slices from current repo truth. Active workflows still resume canonical state by default unless same-entry synthesis produces a concrete replacement mission. None of this prevents ordinary-chat implementation when you choose not to enter workflow mode.
98
99
 
99
100
  | Repo state | What you'll see |
100
101
  |---|---|
101
- | No workflow yet | `/cook` consumes a fresh explicit primary-agent handoff when one already exists, or synthesizes one from the primary-agent view in the same entry, then asks you to choose **Start** or **Cancel**. Stale, planning-only, or non-startable handoffs still fail closed. |
102
- | Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If a concrete replacement handoff exists already or is synthesized in the same `/cook` entry and points to a different mission, `/cook` shows a chooser first and only rewrites canonical state after you confirm the replacement. Ambiguous or missing replacement handoff stays conservative. |
103
- | Previous workflow is `done` | `/cook` can start the next implementation round from a fresh explicit primary-agent handoff or from the same-entry primary-agent handoff synthesis step behind **Start** or **Cancel**. Weak or planning-only next-round handoffs still fail closed. |
102
+ | No workflow yet | `/cook` synthesizes a primary-agent startup plan in the same entry, then asks you to choose **Start** or **Cancel**. After **Start**, the approved startup plan is persisted under `.agent/` and `completion-regrounder` derives canonical slices. Weak, planning-only, or non-startable synthesized plans still fail closed. |
103
+ | Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If same-entry primary-agent startup-plan synthesis produces a concrete replacement mission, `/cook` shows a chooser first and only rewrites canonical state after you confirm the replacement. Ambiguous, missing, or non-startable synthesized replacement plans stay conservative. |
104
+ | Previous workflow is `done` | `/cook` can start the next implementation round from a same-entry primary-agent startup-plan synthesis step behind **Start** or **Cancel**. Weak or planning-only synthesized next-round startup plans still fail closed. |
104
105
 
105
106
  ## Confirmation and fail-closed behavior
106
107
 
@@ -114,9 +115,9 @@ I want to add login redirect handling and tests.
114
115
 
115
116
  When you accept startup or refocus, `/cook` persists the chosen workflow state in canonical `.agent/**` files before the re-ground round begins.
116
117
 
117
- The confirmed startup brief is also preserved there as advisory intake for later re-grounding. It does not replace `.agent/plan.json` or `.agent/active-slice.json`, which remain under regrounder authority.
118
+ The confirmed startup plan is preserved under `.agent/startup-plan.json` / `.agent/startup-plan.md` and summarized in `state.json` as advisory intake for later re-grounding. It does not replace `.agent/plan.json` or `.agent/active-slice.json`, which remain under regrounder authority.
118
119
 
119
- The pre-`/cook` handoff capsule itself is not canonical workflow state. It is only startup intake for `/cook`.
120
+ The pre-`/cook` preview capsule itself is not canonical workflow state. It is only an advisory preview for the human; `/cook` still synthesizes a fresh startup plan in the entry where workflow actually begins.
120
121
 
121
122
  ## Observability
122
123
 
@@ -202,6 +203,8 @@ This package stores canonical workflow state under:
202
203
  verify_completion_stop.sh
203
204
  verify_completion_control_plane.sh
204
205
  state.json
206
+ startup-plan.json
207
+ startup-plan.md
205
208
  plan.json
206
209
  active-slice.json
207
210
  slice-history.jsonl
@@ -228,6 +231,8 @@ Tracked repo-contract files:
228
231
  Ignored execution-state files:
229
232
 
230
233
  - `.agent/state.json`
234
+ - `.agent/startup-plan.json`
235
+ - `.agent/startup-plan.md`
231
236
  - `.agent/plan.json`
232
237
  - `.agent/active-slice.json`
233
238
  - `.agent/slice-history.jsonl`
@@ -239,7 +244,7 @@ Ignored execution-state files:
239
244
  In short:
240
245
 
241
246
  - tracked `.agent` files define the repo-level workflow contract
242
- - ignored `.agent` files are the local control-plane state for the current run
247
+ - ignored `.agent` files are the local control-plane state for the current run, including the approved startup plan captured at `/cook`
243
248
 
244
249
  ## Package layout
245
250
 
@@ -14,6 +14,7 @@ You are an onboarding-only control-plane role. You may:
14
14
  - create or repair tracked completion contract files under `.agent/**`
15
15
  - update `.gitignore` so tracked contract files remain tracked while execution artifacts remain ignored
16
16
  - initialize missing or invalid canonical execution-state files only when repair is required for a truthful handoff
17
+ - preserve any approved startup plan already recorded in `.agent/startup-plan.json`
17
18
  - return the exact handoff payload for `completion-regrounder`
18
19
 
19
20
  You must not:
@@ -40,7 +41,7 @@ These lines are for workflow observability, not hidden reasoning. Keep them brie
40
41
  3. If repo intent or validation entrypoint is ambiguous, ask one short clarifying question.
41
42
  4. Create or repair `.agent/README.md`, `.agent/mission.md`, `.agent/profile.json`, `.agent/verify_completion_stop.sh`, and `.agent/verify_completion_control_plane.sh`, keeping them truthful to current repo reality.
42
43
  5. Update `.gitignore` so `.agent/*` remains ignored except the tracked repo-contract files, and keep `.agent/tmp/` ignored as scratch space.
43
- 6. Initialize `.agent/state.json`, `.agent/plan.json`, and `.agent/active-slice.json` only when they are missing, unreadable, or structurally invalid. Preserve any existing truthful execution state.
44
+ 6. Initialize `.agent/state.json`, `.agent/startup-plan.json`, `.agent/startup-plan.md`, `.agent/plan.json`, and `.agent/active-slice.json` only when they are missing, unreadable, or structurally invalid. Preserve any existing truthful execution state.
44
45
  7. Stop after canonical bootstrap or repair is truthful and return the handoff to `completion-regrounder`.
45
46
 
46
47
  Return exactly this fixed report format:
@@ -12,6 +12,7 @@ You are the canonical reconciliation role. You may:
12
12
 
13
13
  - read current repo truth and canonical `.agent` state
14
14
  - write canonical `.agent` state and `.gitignore`
15
+ - read the approved startup plan in `.agent/startup-plan.json` and `.agent/startup-plan.md`
15
16
  - rebuild or reconcile `.agent/plan.json`
16
17
  - confirm or update `.agent/active-slice.json` and `.agent/state.json`
17
18
  - reopen slices whose acceptance criteria no longer hold
@@ -36,25 +37,30 @@ These lines are for workflow observability, not hidden reasoning. Keep them brie
36
37
 
37
38
  1. Read canonical `.agent` inputs before changing canonical state.
38
39
  2. Read current git status, recent git history, and repo surfaces relevant to the locked or remaining contract IDs.
39
- 3. Reconcile `.agent/plan.json` against current repo truth.
40
- 4. Revalidate every slice's `acceptance_criteria` against current repo truth and update `status` plus `evidence` accordingly.
41
- 5. Reopen any previously `done` slice whose acceptance criteria no longer hold.
42
- 6. Keep `.agent/state.json` and `.agent/active-slice.json` truthful, including `current_phase`, `continuation_policy`, `continuation_reason`, `next_mandatory_role`, and any exact implementer handoff snapshot fields.
43
- 7. Reconcile canonical state after review, audit, and final stop verification waves when required.
44
- 8. If the latest committed slice leaves the tracked and unignored worktree dirty, treat that dirty state as a blocker, reopen or continue that latest slice for reconciliation, set `Next role to invoke` to `completion-implementer`, and do not select or hand off any different next slice until it is reconciled.
45
- 9. When reconciling after review, audit, or dirty-worktree follow-up for the latest committed slice, emit an explicit reconciliation record decision:
40
+ 3. Read `.agent/startup-plan.json` / `.agent/startup-plan.md` and extract the approved mission, scope, acceptance, risks, planned surfaces, verification intent, and any sequencing hints.
41
+ 4. Treat that startup plan as planning input only, then derive the smallest truthful canonical slices from current repo truth instead of copying startup-plan structure blindly into `.agent/plan.json`.
42
+ 5. If current repo truth contradicts, narrows, or broadens the approved startup plan, preserve the startup-plan record as historical intake, explain the deviation explicitly, and reconcile `.agent/plan.json` / `.agent/state.json` to the newer truth.
43
+ 6. Emit an explicit startup-plan reconciliation outcome in your report so the workflow driver can see whether canonical slices adopted the startup plan as-is or deviated from it.
44
+ 7. Revalidate every slice's `acceptance_criteria` against current repo truth and update `status` plus `evidence` accordingly.
45
+ 8. Reopen any previously `done` slice whose acceptance criteria no longer hold.
46
+ 9. Keep `.agent/state.json` and `.agent/active-slice.json` truthful, including `current_phase`, `continuation_policy`, `continuation_reason`, `next_mandatory_role`, and any exact implementer handoff snapshot fields.
47
+ 10. Reconcile canonical state after review, audit, and final stop verification waves when required.
48
+ 11. If the latest committed slice leaves the tracked and unignored worktree dirty, treat that dirty state as a blocker, reopen or continue that latest slice for reconciliation, set `Next role to invoke` to `completion-implementer`, and do not select or hand off any different next slice until it is reconciled.
49
+ 12. When reconciling after review, audit, or dirty-worktree follow-up for the latest committed slice, emit an explicit reconciliation record decision:
46
50
  - `accepted` only when the latest committed slice is truthfully accepted as-is
47
51
  - `reopened` only when the latest committed slice must be reopened for follow-up work
48
52
  - `none` when this re-ground was not a post-commit reconciliation decision
49
- 10. If you emit `accepted` or `reopened`, also emit the exact reconciled slice id in the report.
50
- 11. If a slice is already selected, ensure `.agent/active-slice.json` contains the exact implementer handoff snapshot and return that exact handoff payload for `completion-implementer` instead of implementing it yourself.
51
- 12. If no slice is selected, return the exact next recommended slice and why.
53
+ 13. If you emit `accepted` or `reopened`, also emit the exact reconciled slice id in the report.
54
+ 14. If a slice is already selected, ensure `.agent/active-slice.json` contains the exact implementer handoff snapshot and return that exact handoff payload for `completion-implementer` instead of implementing it yourself.
55
+ 15. If no slice is selected, return the exact next recommended slice and why.
52
56
 
53
57
  Output format:
54
58
 
55
59
  - `MISSION ANCHOR: ...`
56
60
  - `Remaining contract IDs: ...`
57
61
  - `Canonical re-ground applied: yes/no - ...`
62
+ - `Startup plan adopted as-is: yes/no - ...`
63
+ - `Startup-plan deviations from repo truth: ...`
58
64
  - `Acceptance criteria revalidated: yes/no - ...`
59
65
  - `Tracked and unignored worktree is clean: yes/no`
60
66
  - `Reopened slices: ...`
@@ -16,7 +16,7 @@ import {
16
16
  loadCompletionSnapshot,
17
17
  writeJsonFile,
18
18
  } from "./state-store";
19
- import { buildAdvisoryStartupBrief } from "./prompt-surfaces";
19
+ import { buildAdvisoryStartupBrief, buildApprovedStartupPlan, buildApprovedStartupPlanMarkdown } from "./prompt-surfaces";
20
20
  import type { CompletionStateSnapshot } from "./types";
21
21
 
22
22
  type ContextProposalAnalysis = {
@@ -38,7 +38,7 @@ type ContextProposalAlternate = {
38
38
  analysis: ContextProposalAnalysis;
39
39
  goalText: string;
40
40
  basisPreview: string;
41
- source: "session" | "analyst" | "handoff_capsule";
41
+ source: "session" | "analyst" | "handoff_capsule" | "deferred_primary_agent_handoff";
42
42
  };
43
43
 
44
44
  type ContextProposal = ContextProposalAlternate & {
@@ -71,10 +71,10 @@ type ActiveWorkflowProposalAssessment = {
71
71
  proposal?: ContextProposal;
72
72
  blockedFailureMessage?: string;
73
73
  reason:
74
- | "matching_mission"
75
- | "missing_explicit_handoff"
76
- | "fresh_explicit_handoff"
77
- | "fresh_explicit_handoff_not_startable";
74
+ | "matching_generated_startup_plan"
75
+ | "no_generated_startup_plan"
76
+ | "generated_replacement_startup_plan"
77
+ | "generated_startup_plan_not_startable";
78
78
  };
79
79
 
80
80
  type ExistingWorkflowChooserOptions = {
@@ -126,7 +126,6 @@ export type CompletionDriverDeps = {
126
126
  ) => string;
127
127
  completionResumePrompt: (taskType: string, evaluationProfile: string) => string;
128
128
  deriveCookContextProposal: (ctx: DriverContext, projectName: string) => Promise<CookContextProposalResult>;
129
- deriveCookStartupProposal: (ctx: DriverContext, projectName: string) => Promise<CookContextProposalResult>;
130
129
  confirmContextProposal: (
131
130
  ctx: { hasUI: boolean; ui: any },
132
131
  proposal: ContextProposal,
@@ -137,7 +136,12 @@ export type CompletionDriverDeps = {
137
136
  scaffoldCompletionFiles: (
138
137
  root: string,
139
138
  missionAnchor: string,
140
- options?: { analysis?: ContextProposalAnalysis; continuationReason?: string; advisoryStartupBrief?: Record<string, unknown> },
139
+ options?: {
140
+ analysis?: ContextProposalAnalysis;
141
+ continuationReason?: string;
142
+ advisoryStartupBrief?: Record<string, unknown>;
143
+ approvedStartupPlan?: Record<string, unknown>;
144
+ },
141
145
  ) => Promise<{ root: string; created: string[] }>;
142
146
  maybeWriteActiveWorkflowRoutingSnapshot: (assessment: ActiveWorkflowProposalAssessment) => void;
143
147
  missionAnchorsLikelyEquivalent: (left: string, right: string) => boolean;
@@ -327,7 +331,7 @@ async function assessActiveWorkflowProposalRouting(
327
331
  action: "blocked",
328
332
  currentMissionAnchor: currentMission,
329
333
  blockedFailureMessage: proposalResult.blockedFailureMessage,
330
- reason: "fresh_explicit_handoff_not_startable",
334
+ reason: "generated_startup_plan_not_startable",
331
335
  };
332
336
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
333
337
  return assessment;
@@ -337,7 +341,7 @@ async function assessActiveWorkflowProposalRouting(
337
341
  const assessment: ActiveWorkflowProposalAssessment = {
338
342
  action: "continue",
339
343
  currentMissionAnchor: currentMission,
340
- reason: "missing_explicit_handoff",
344
+ reason: "no_generated_startup_plan",
341
345
  };
342
346
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
343
347
  return assessment;
@@ -347,7 +351,7 @@ async function assessActiveWorkflowProposalRouting(
347
351
  action: "continue",
348
352
  currentMissionAnchor: currentMission,
349
353
  proposal,
350
- reason: "matching_mission",
354
+ reason: "matching_generated_startup_plan",
351
355
  };
352
356
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
353
357
  return assessment;
@@ -356,7 +360,7 @@ async function assessActiveWorkflowProposalRouting(
356
360
  action: "refocus",
357
361
  currentMissionAnchor: currentMission,
358
362
  proposal,
359
- reason: "fresh_explicit_handoff",
363
+ reason: "generated_replacement_startup_plan",
360
364
  };
361
365
  deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
362
366
  return assessment;
@@ -419,8 +423,8 @@ async function confirmExistingWorkflowProposal(
419
423
  const continueChoice = "Continue current workflow\n\nKeep the current mission and treat the new goal as extra direction only.";
420
424
  const buildRefocusChoice = (candidate: ContextProposalAlternate, variant: "primary" | "alternate") =>
421
425
  variant === "primary"
422
- ? `${options.refocusChoiceLabel ?? "Start new workflow from recent discussion\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."}\n\n${summarizeProposalForChoice(candidate)}`
423
- : `${options.alternateChoiceLabel ?? "Start alternate workflow from recent discussion\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."}\n\n${summarizeProposalForChoice(candidate)}`;
426
+ ? `${options.refocusChoiceLabel ?? "Start new workflow from synthesized startup plan\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."}\n\n${summarizeProposalForChoice(candidate)}`
427
+ : `${options.alternateChoiceLabel ?? "Start alternate workflow from synthesized startup plan\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."}\n\n${summarizeProposalForChoice(candidate)}`;
424
428
  const refocusChoices = candidateProposals.map((candidate, index) => buildRefocusChoice(candidate, index === 0 ? "primary" : "alternate"));
425
429
  const cancelChoice = `Cancel\n\nKeep the current workflow unchanged. ${deps.mainChatRerunGuidance}`;
426
430
  deps.maybeWriteTestSnapshot(
@@ -479,6 +483,7 @@ async function refocusCompletionMission(
479
483
  analysis: ContextProposalAnalysis | undefined,
480
484
  deps: CompletionDriverDeps,
481
485
  advisoryStartupBrief?: Record<string, unknown>,
486
+ approvedStartupPlan?: Record<string, unknown>,
482
487
  ): Promise<void> {
483
488
  const requiredStopJudges = asNumber(snapshot.profile?.required_stop_judges) ?? 3;
484
489
  const root = snapshot.files.root;
@@ -506,10 +511,32 @@ async function refocusCompletionMission(
506
511
  plan_basis: "user_refocus",
507
512
  };
508
513
  const nextActive = defaultActiveSlice(missionAnchor, { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile });
514
+ const nextStartupPlan =
515
+ approvedStartupPlan ?? {
516
+ artifact_type: "completion-startup-plan",
517
+ schema_version: 1,
518
+ status: "approved",
519
+ source: "recent_discussion",
520
+ captured_at: null,
521
+ mission_anchor: missionAnchor,
522
+ goal_text: rawGoal,
523
+ task_type: routing.taskType,
524
+ evaluation_profile: routing.evaluationProfile,
525
+ scope: [],
526
+ constraints: [],
527
+ acceptance: [],
528
+ risks: [],
529
+ notes: ["No approved startup plan summary is available for this refocused workflow."],
530
+ planned_surfaces: [],
531
+ verification_intent: [],
532
+ sequencing_hints: [],
533
+ };
509
534
  await Promise.all([
510
535
  fsp.writeFile(path.join(snapshot.files.agentDir, "mission.md"), buildMission(path.basename(root), missionAnchor), "utf8"),
511
536
  writeJsonFile(snapshot.files.profilePath, nextProfile),
512
537
  writeJsonFile(snapshot.files.statePath, nextState),
538
+ writeJsonFile(snapshot.files.startupPlanPath, nextStartupPlan),
539
+ fsp.writeFile(snapshot.files.startupPlanMarkdownPath, buildApprovedStartupPlanMarkdown(nextStartupPlan as any), "utf8"),
513
540
  writeJsonFile(snapshot.files.planPath, nextPlan),
514
541
  writeJsonFile(snapshot.files.activePath, nextActive),
515
542
  writeJsonFile(snapshot.files.verificationEvidencePath, defaultVerificationEvidence()),
@@ -547,16 +574,22 @@ export async function runCookEntry(
547
574
  return;
548
575
  }
549
576
  const decision = await deps.confirmContextProposal(ctx, proposal, {
550
- title: "Start a completion workflow from this startup brief?",
577
+ title: "Start a completion workflow from this startup plan?",
551
578
  });
552
579
  if (!decision) {
553
- deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled recent-discussion workflow proposal", deps), "info");
580
+ deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled synthesized startup plan", deps), "info");
554
581
  return;
555
582
  }
556
583
  goal = decision.goalText;
557
584
  kickoffMissionAnchor = decision.missionAnchor;
558
585
  kickoffAnalysis = decision.analysis;
559
586
  const startupRouting = deps.finalizeContextProposalAnalysis(kickoffAnalysis, [goal ?? kickoffMissionAnchor ?? projectName]);
587
+ const advisoryStartupBrief = buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis });
588
+ const approvedStartupPlan = buildApprovedStartupPlan({
589
+ proposal,
590
+ analysis: decision.analysis,
591
+ missionAnchor: kickoffMissionAnchor ?? projectName,
592
+ });
560
593
  const created = await deps.scaffoldCompletionFiles(root, kickoffMissionAnchor ?? projectName, {
561
594
  analysis: startupRouting,
562
595
  continuationReason: deps.buildContextProposalContinuationReason(
@@ -564,11 +597,12 @@ export async function runCookEntry(
564
597
  goal ?? kickoffMissionAnchor ?? projectName,
565
598
  startupRouting,
566
599
  ),
567
- advisoryStartupBrief: buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis }),
600
+ advisoryStartupBrief,
601
+ approvedStartupPlan,
568
602
  });
569
603
  deps.emitCommandText(
570
604
  ctx,
571
- `Initialized completion control plane in ${created.root}${created.created.length > 0 ? ` (${created.created.length} files created)` : ""}`,
605
+ `Initialized completion control plane in ${created.root}${created.created.length > 0 ? ` (${created.created.length} files created)` : ""} and recorded the approved startup plan under .agent/startup-plan.json`,
572
606
  "info",
573
607
  );
574
608
  snapshot = await loadCompletionSnapshot(root);
@@ -592,7 +626,7 @@ export async function runCookEntry(
592
626
  return;
593
627
  }
594
628
  const decision = await deps.confirmContextProposal(ctx, proposal, {
595
- title: "The previous completion workflow is done. Start the next workflow round from this startup brief?",
629
+ title: "The previous completion workflow is done. Start the next workflow round from this startup plan?",
596
630
  });
597
631
  if (!decision) {
598
632
  deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled next workflow round proposal", deps), "info");
@@ -601,16 +635,23 @@ export async function runCookEntry(
601
635
  goal = decision.goalText;
602
636
  kickoffIntent = "refocus";
603
637
  kickoffMissionAnchor = decision.missionAnchor;
638
+ const advisoryStartupBrief = buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis });
639
+ const approvedStartupPlan = buildApprovedStartupPlan({
640
+ proposal,
641
+ analysis: decision.analysis,
642
+ missionAnchor: decision.missionAnchor,
643
+ });
604
644
  await refocusCompletionMission(
605
645
  snapshot,
606
646
  decision.missionAnchor,
607
647
  decision.goalText,
608
648
  decision.analysis,
609
649
  deps,
610
- buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis }),
650
+ advisoryStartupBrief,
651
+ approvedStartupPlan,
611
652
  );
612
653
  snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
613
- deps.emitCommandText(ctx, `Started a new completion workflow round from explicit primary-agent handoff: ${decision.missionAnchor}`, "info");
654
+ deps.emitCommandText(ctx, `Started a new completion workflow round and recorded the approved startup plan: ${decision.missionAnchor}`, "info");
614
655
  } else {
615
656
  const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, deps);
616
657
  if (assessment.action === "blocked") {
@@ -621,19 +662,19 @@ export async function runCookEntry(
621
662
  await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
622
663
  return;
623
664
  }
624
- const explicitReplacement = assessment.reason === "fresh_explicit_handoff";
665
+ const generatedReplacement = assessment.reason === "generated_replacement_startup_plan";
625
666
  const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, deps, {
626
- intro: explicitReplacement
627
- ? "A fresh explicit primary-agent handoff proposes replacing the current workflow. Choose how /cook should proceed:"
667
+ intro: generatedReplacement
668
+ ? "A same-entry primary-agent startup plan proposes replacing the current workflow. Choose how /cook should proceed:"
628
669
  : "A replacement workflow is ready. Choose how /cook should proceed:",
629
- proposedMissionLabel: explicitReplacement
630
- ? "Proposed mission from explicit primary-agent handoff"
670
+ proposedMissionLabel: generatedReplacement
671
+ ? "Proposed mission from same-entry primary-agent startup plan"
631
672
  : "Proposed mission",
632
- refocusChoiceLabel: explicitReplacement
633
- ? "Start new workflow from explicit primary-agent handoff\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
673
+ refocusChoiceLabel: generatedReplacement
674
+ ? "Start new workflow from same-entry primary-agent startup plan\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
634
675
  : "Start new workflow\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.",
635
- alternateChoiceLabel: explicitReplacement
636
- ? "Start alternate workflow from explicit primary-agent handoff\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
676
+ alternateChoiceLabel: generatedReplacement
677
+ ? "Start alternate workflow from same-entry primary-agent startup plan\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
637
678
  : undefined,
638
679
  comparison: "strict",
639
680
  });
@@ -647,9 +688,9 @@ export async function runCookEntry(
647
688
  }
648
689
  const selectedProposal = decision.proposal;
649
690
  const proposalDecision = await deps.confirmContextProposal(ctx, selectedProposal, {
650
- title: assessment.reason === "fresh_explicit_handoff"
651
- ? "Start the replacement workflow from this explicit startup brief?"
652
- : "Start the replacement workflow from this startup brief?",
691
+ title: assessment.reason === "generated_replacement_startup_plan"
692
+ ? "Start the replacement workflow from this synthesized startup plan?"
693
+ : "Start the replacement workflow from this startup plan?",
653
694
  });
654
695
  if (!proposalDecision) {
655
696
  deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal", deps), "info");
@@ -658,20 +699,27 @@ export async function runCookEntry(
658
699
  goal = proposalDecision.goalText;
659
700
  kickoffIntent = "refocus";
660
701
  kickoffMissionAnchor = proposalDecision.missionAnchor;
702
+ const advisoryStartupBrief = buildAdvisoryStartupBrief({ proposal: selectedProposal, analysis: proposalDecision.analysis });
703
+ const approvedStartupPlan = buildApprovedStartupPlan({
704
+ proposal: selectedProposal,
705
+ analysis: proposalDecision.analysis,
706
+ missionAnchor: proposalDecision.missionAnchor,
707
+ });
661
708
  await refocusCompletionMission(
662
709
  snapshot,
663
710
  proposalDecision.missionAnchor,
664
711
  proposalDecision.goalText,
665
712
  proposalDecision.analysis,
666
713
  deps,
667
- buildAdvisoryStartupBrief({ proposal: selectedProposal, analysis: proposalDecision.analysis }),
714
+ advisoryStartupBrief,
715
+ approvedStartupPlan,
668
716
  );
669
717
  snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
670
718
  deps.emitCommandText(
671
719
  ctx,
672
- assessment.reason === "fresh_explicit_handoff"
673
- ? `Refocused completion mission from explicit primary-agent handoff to: ${proposalDecision.missionAnchor}`
674
- : `Refocused completion mission to: ${proposalDecision.missionAnchor}`,
720
+ assessment.reason === "generated_replacement_startup_plan"
721
+ ? `Refocused completion mission from same-entry primary-agent startup plan and rewrote the approved startup plan to: ${proposalDecision.missionAnchor}`
722
+ : `Refocused completion mission and rewrote the approved startup plan to: ${proposalDecision.missionAnchor}`,
675
723
  "info",
676
724
  );
677
725
  }