@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
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
|
|
38
|
-
6.
|
|
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
|
|
56
|
-
- a mission and
|
|
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
|
|
60
|
-
- `/cook`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
81
|
-
-
|
|
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`
|
|
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`
|
|
102
|
-
| Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If
|
|
103
|
-
| Previous workflow is `done` | `/cook` can start the next implementation round from a
|
|
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
|
|
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`
|
|
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.
|
|
40
|
-
4.
|
|
41
|
-
5.
|
|
42
|
-
6.
|
|
43
|
-
7.
|
|
44
|
-
8.
|
|
45
|
-
9.
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
| "
|
|
75
|
-
| "
|
|
76
|
-
| "
|
|
77
|
-
| "
|
|
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?: {
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
|
423
|
-
: `${options.alternateChoiceLabel ?? "Start alternate workflow from
|
|
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
|
|
577
|
+
title: "Start a completion workflow from this startup plan?",
|
|
551
578
|
});
|
|
552
579
|
if (!decision) {
|
|
553
|
-
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
665
|
+
const generatedReplacement = assessment.reason === "generated_replacement_startup_plan";
|
|
625
666
|
const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, deps, {
|
|
626
|
-
intro:
|
|
627
|
-
? "A
|
|
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:
|
|
630
|
-
? "Proposed mission from
|
|
670
|
+
proposedMissionLabel: generatedReplacement
|
|
671
|
+
? "Proposed mission from same-entry primary-agent startup plan"
|
|
631
672
|
: "Proposed mission",
|
|
632
|
-
refocusChoiceLabel:
|
|
633
|
-
? "Start new workflow from
|
|
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:
|
|
636
|
-
? "Start alternate workflow from
|
|
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 === "
|
|
651
|
-
? "Start the replacement workflow from this
|
|
652
|
-
: "Start the replacement workflow from this startup
|
|
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
|
-
|
|
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 === "
|
|
673
|
-
? `Refocused completion mission from
|
|
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
|
}
|