@linimin/pi-letscook 0.1.66 → 0.1.67
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 +9 -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 +66 -17
- package/extensions/completion/index.ts +96 -21
- package/extensions/completion/prompt-surfaces.ts +156 -15
- package/extensions/completion/proposal.ts +21 -21
- package/extensions/completion/role-runner.ts +11 -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 +1 -1
- package/scripts/canonical-evidence-artifact-test.sh +49 -4
- package/scripts/context-proposal-test.sh +57 -58
- package/scripts/refocus-test.sh +17 -17
- package/scripts/release-check.sh +11 -11
- package/scripts/smoke-test.sh +69 -9
- package/skills/completion-protocol/SKILL.md +9 -3
- package/skills/completion-protocol/references/completion.md +37 -2
- package/skills/cook-handoff-boundary/SKILL.md +16 -16
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,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.67
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- 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`
|
|
8
|
+
- 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
|
|
9
|
+
- 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
|
|
10
|
+
- updated workflow reminders, recovery capsules, regrounder/bootstrapper instructions, and public docs so canonical startup-plan persistence and regrounder-owned slice derivation stay truthful
|
|
11
|
+
|
|
3
12
|
## 0.1.66
|
|
4
13
|
|
|
5
14
|
### 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` first prefers a fresh explicit `cook_handoff`
|
|
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` first prefers a fresh explicit `cook_handoff` preview when one already exists, but otherwise calls the primary-agent startup-plan synthesis step in the same `/cook` entry
|
|
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 a fresh explicit
|
|
64
|
+
If a fresh explicit preview exists but is still workflow-worthy rather than concrete enough to seed workflow planning, `/cook` also fails closed instead of silently treating that capsule 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 first checks for a fresh explicit primary-agent
|
|
74
|
+
When you explicitly run `/cook`, it first checks for a fresh explicit primary-agent startup-plan preview. If one is missing, it 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
|
|
76
|
+
Explicit `/cook` capsules are still valid startup intake, but they are no longer the only path because `/cook` can synthesize the primary-agent startup plan in the same entry when needed.
|
|
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
|
|
81
|
-
-
|
|
80
|
+
- startup and next-round entry stay confirm-first, using explicit primary-agent startup-plan data when present and otherwise running the primary-agent startup-plan synthesis step in the same `/cook` entry
|
|
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 a concrete replacement startup plan is available or synthesized in the same `/cook` entry
|
|
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
|
|
98
|
+
`/cook` first checks for a fresh explicit primary-agent startup-plan preview. New-workflow entry and done-workflow next-round entry use that plan when it already exists; otherwise `/cook` calls a same-entry primary-agent startup-plan synthesis step, then immediately continues to Start / Cancel if the generated plan is concrete enough. 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 a concrete replacement startup plan 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
99
|
|
|
99
100
|
| Repo state | What you'll see |
|
|
100
101
|
|---|---|
|
|
101
|
-
| No workflow yet | `/cook` consumes a fresh explicit primary-agent
|
|
102
|
-
| Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If a concrete replacement
|
|
103
|
-
| Previous workflow is `done` | `/cook` can start the next implementation round from a fresh explicit primary-agent
|
|
102
|
+
| No workflow yet | `/cook` consumes a fresh explicit primary-agent startup-plan preview when one already exists, or synthesizes one from the primary-agent view 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. Stale, planning-only, or non-startable startup plans still fail closed. |
|
|
103
|
+
| Active workflow exists | Usually a resume of the current workflow from canonical `.agent/**` state. If a concrete replacement startup plan 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 startup plans stay conservative. |
|
|
104
|
+
| Previous workflow is `done` | `/cook` can start the next implementation round from a fresh explicit primary-agent startup plan or from the same-entry primary-agent startup-plan synthesis step behind **Start** or **Cancel**. Weak or planning-only 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 startup intake for `/cook`.
|
|
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 = {
|
|
@@ -137,7 +137,12 @@ export type CompletionDriverDeps = {
|
|
|
137
137
|
scaffoldCompletionFiles: (
|
|
138
138
|
root: string,
|
|
139
139
|
missionAnchor: string,
|
|
140
|
-
options?: {
|
|
140
|
+
options?: {
|
|
141
|
+
analysis?: ContextProposalAnalysis;
|
|
142
|
+
continuationReason?: string;
|
|
143
|
+
advisoryStartupBrief?: Record<string, unknown>;
|
|
144
|
+
approvedStartupPlan?: Record<string, unknown>;
|
|
145
|
+
},
|
|
141
146
|
) => Promise<{ root: string; created: string[] }>;
|
|
142
147
|
maybeWriteActiveWorkflowRoutingSnapshot: (assessment: ActiveWorkflowProposalAssessment) => void;
|
|
143
148
|
missionAnchorsLikelyEquivalent: (left: string, right: string) => boolean;
|
|
@@ -479,6 +484,7 @@ async function refocusCompletionMission(
|
|
|
479
484
|
analysis: ContextProposalAnalysis | undefined,
|
|
480
485
|
deps: CompletionDriverDeps,
|
|
481
486
|
advisoryStartupBrief?: Record<string, unknown>,
|
|
487
|
+
approvedStartupPlan?: Record<string, unknown>,
|
|
482
488
|
): Promise<void> {
|
|
483
489
|
const requiredStopJudges = asNumber(snapshot.profile?.required_stop_judges) ?? 3;
|
|
484
490
|
const root = snapshot.files.root;
|
|
@@ -506,10 +512,32 @@ async function refocusCompletionMission(
|
|
|
506
512
|
plan_basis: "user_refocus",
|
|
507
513
|
};
|
|
508
514
|
const nextActive = defaultActiveSlice(missionAnchor, { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile });
|
|
515
|
+
const nextStartupPlan =
|
|
516
|
+
approvedStartupPlan ?? {
|
|
517
|
+
artifact_type: "completion-startup-plan",
|
|
518
|
+
schema_version: 1,
|
|
519
|
+
status: "approved",
|
|
520
|
+
source: "recent_discussion",
|
|
521
|
+
captured_at: null,
|
|
522
|
+
mission_anchor: missionAnchor,
|
|
523
|
+
goal_text: rawGoal,
|
|
524
|
+
task_type: routing.taskType,
|
|
525
|
+
evaluation_profile: routing.evaluationProfile,
|
|
526
|
+
scope: [],
|
|
527
|
+
constraints: [],
|
|
528
|
+
acceptance: [],
|
|
529
|
+
risks: [],
|
|
530
|
+
notes: ["No approved startup plan summary is available for this refocused workflow."],
|
|
531
|
+
planned_surfaces: [],
|
|
532
|
+
verification_intent: [],
|
|
533
|
+
sequencing_hints: [],
|
|
534
|
+
};
|
|
509
535
|
await Promise.all([
|
|
510
536
|
fsp.writeFile(path.join(snapshot.files.agentDir, "mission.md"), buildMission(path.basename(root), missionAnchor), "utf8"),
|
|
511
537
|
writeJsonFile(snapshot.files.profilePath, nextProfile),
|
|
512
538
|
writeJsonFile(snapshot.files.statePath, nextState),
|
|
539
|
+
writeJsonFile(snapshot.files.startupPlanPath, nextStartupPlan),
|
|
540
|
+
fsp.writeFile(snapshot.files.startupPlanMarkdownPath, buildApprovedStartupPlanMarkdown(nextStartupPlan as any), "utf8"),
|
|
513
541
|
writeJsonFile(snapshot.files.planPath, nextPlan),
|
|
514
542
|
writeJsonFile(snapshot.files.activePath, nextActive),
|
|
515
543
|
writeJsonFile(snapshot.files.verificationEvidencePath, defaultVerificationEvidence()),
|
|
@@ -547,7 +575,7 @@ export async function runCookEntry(
|
|
|
547
575
|
return;
|
|
548
576
|
}
|
|
549
577
|
const decision = await deps.confirmContextProposal(ctx, proposal, {
|
|
550
|
-
title: "Start a completion workflow from this startup
|
|
578
|
+
title: "Start a completion workflow from this startup plan?",
|
|
551
579
|
});
|
|
552
580
|
if (!decision) {
|
|
553
581
|
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled recent-discussion workflow proposal", deps), "info");
|
|
@@ -557,6 +585,12 @@ export async function runCookEntry(
|
|
|
557
585
|
kickoffMissionAnchor = decision.missionAnchor;
|
|
558
586
|
kickoffAnalysis = decision.analysis;
|
|
559
587
|
const startupRouting = deps.finalizeContextProposalAnalysis(kickoffAnalysis, [goal ?? kickoffMissionAnchor ?? projectName]);
|
|
588
|
+
const advisoryStartupBrief = buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis });
|
|
589
|
+
const approvedStartupPlan = buildApprovedStartupPlan({
|
|
590
|
+
proposal,
|
|
591
|
+
analysis: decision.analysis,
|
|
592
|
+
missionAnchor: kickoffMissionAnchor ?? projectName,
|
|
593
|
+
});
|
|
560
594
|
const created = await deps.scaffoldCompletionFiles(root, kickoffMissionAnchor ?? projectName, {
|
|
561
595
|
analysis: startupRouting,
|
|
562
596
|
continuationReason: deps.buildContextProposalContinuationReason(
|
|
@@ -564,11 +598,12 @@ export async function runCookEntry(
|
|
|
564
598
|
goal ?? kickoffMissionAnchor ?? projectName,
|
|
565
599
|
startupRouting,
|
|
566
600
|
),
|
|
567
|
-
advisoryStartupBrief
|
|
601
|
+
advisoryStartupBrief,
|
|
602
|
+
approvedStartupPlan,
|
|
568
603
|
});
|
|
569
604
|
deps.emitCommandText(
|
|
570
605
|
ctx,
|
|
571
|
-
`Initialized completion control plane in ${created.root}${created.created.length > 0 ? ` (${created.created.length} files created)` : ""}`,
|
|
606
|
+
`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
607
|
"info",
|
|
573
608
|
);
|
|
574
609
|
snapshot = await loadCompletionSnapshot(root);
|
|
@@ -592,7 +627,7 @@ export async function runCookEntry(
|
|
|
592
627
|
return;
|
|
593
628
|
}
|
|
594
629
|
const decision = await deps.confirmContextProposal(ctx, proposal, {
|
|
595
|
-
title: "The previous completion workflow is done. Start the next workflow round from this startup
|
|
630
|
+
title: "The previous completion workflow is done. Start the next workflow round from this startup plan?",
|
|
596
631
|
});
|
|
597
632
|
if (!decision) {
|
|
598
633
|
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled next workflow round proposal", deps), "info");
|
|
@@ -601,16 +636,23 @@ export async function runCookEntry(
|
|
|
601
636
|
goal = decision.goalText;
|
|
602
637
|
kickoffIntent = "refocus";
|
|
603
638
|
kickoffMissionAnchor = decision.missionAnchor;
|
|
639
|
+
const advisoryStartupBrief = buildAdvisoryStartupBrief({ proposal, analysis: decision.analysis });
|
|
640
|
+
const approvedStartupPlan = buildApprovedStartupPlan({
|
|
641
|
+
proposal,
|
|
642
|
+
analysis: decision.analysis,
|
|
643
|
+
missionAnchor: decision.missionAnchor,
|
|
644
|
+
});
|
|
604
645
|
await refocusCompletionMission(
|
|
605
646
|
snapshot,
|
|
606
647
|
decision.missionAnchor,
|
|
607
648
|
decision.goalText,
|
|
608
649
|
decision.analysis,
|
|
609
650
|
deps,
|
|
610
|
-
|
|
651
|
+
advisoryStartupBrief,
|
|
652
|
+
approvedStartupPlan,
|
|
611
653
|
);
|
|
612
654
|
snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
|
|
613
|
-
deps.emitCommandText(ctx, `Started a new completion workflow round
|
|
655
|
+
deps.emitCommandText(ctx, `Started a new completion workflow round and recorded the approved startup plan: ${decision.missionAnchor}`, "info");
|
|
614
656
|
} else {
|
|
615
657
|
const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, deps);
|
|
616
658
|
if (assessment.action === "blocked") {
|
|
@@ -624,16 +666,16 @@ export async function runCookEntry(
|
|
|
624
666
|
const explicitReplacement = assessment.reason === "fresh_explicit_handoff";
|
|
625
667
|
const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, deps, {
|
|
626
668
|
intro: explicitReplacement
|
|
627
|
-
? "A fresh explicit primary-agent
|
|
669
|
+
? "A fresh explicit primary-agent startup plan proposes replacing the current workflow. Choose how /cook should proceed:"
|
|
628
670
|
: "A replacement workflow is ready. Choose how /cook should proceed:",
|
|
629
671
|
proposedMissionLabel: explicitReplacement
|
|
630
|
-
? "Proposed mission from explicit primary-agent
|
|
672
|
+
? "Proposed mission from explicit primary-agent startup plan"
|
|
631
673
|
: "Proposed mission",
|
|
632
674
|
refocusChoiceLabel: explicitReplacement
|
|
633
|
-
? "Start new workflow from explicit primary-agent
|
|
675
|
+
? "Start new workflow from explicit primary-agent startup plan\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
|
|
634
676
|
: "Start new workflow\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.",
|
|
635
677
|
alternateChoiceLabel: explicitReplacement
|
|
636
|
-
? "Start alternate workflow from explicit primary-agent
|
|
678
|
+
? "Start alternate workflow from explicit primary-agent startup plan\n\nReview this alternate replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state."
|
|
637
679
|
: undefined,
|
|
638
680
|
comparison: "strict",
|
|
639
681
|
});
|
|
@@ -648,8 +690,8 @@ export async function runCookEntry(
|
|
|
648
690
|
const selectedProposal = decision.proposal;
|
|
649
691
|
const proposalDecision = await deps.confirmContextProposal(ctx, selectedProposal, {
|
|
650
692
|
title: assessment.reason === "fresh_explicit_handoff"
|
|
651
|
-
? "Start the replacement workflow from this explicit startup
|
|
652
|
-
: "Start the replacement workflow from this startup
|
|
693
|
+
? "Start the replacement workflow from this explicit startup plan?"
|
|
694
|
+
: "Start the replacement workflow from this startup plan?",
|
|
653
695
|
});
|
|
654
696
|
if (!proposalDecision) {
|
|
655
697
|
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal", deps), "info");
|
|
@@ -658,20 +700,27 @@ export async function runCookEntry(
|
|
|
658
700
|
goal = proposalDecision.goalText;
|
|
659
701
|
kickoffIntent = "refocus";
|
|
660
702
|
kickoffMissionAnchor = proposalDecision.missionAnchor;
|
|
703
|
+
const advisoryStartupBrief = buildAdvisoryStartupBrief({ proposal: selectedProposal, analysis: proposalDecision.analysis });
|
|
704
|
+
const approvedStartupPlan = buildApprovedStartupPlan({
|
|
705
|
+
proposal: selectedProposal,
|
|
706
|
+
analysis: proposalDecision.analysis,
|
|
707
|
+
missionAnchor: proposalDecision.missionAnchor,
|
|
708
|
+
});
|
|
661
709
|
await refocusCompletionMission(
|
|
662
710
|
snapshot,
|
|
663
711
|
proposalDecision.missionAnchor,
|
|
664
712
|
proposalDecision.goalText,
|
|
665
713
|
proposalDecision.analysis,
|
|
666
714
|
deps,
|
|
667
|
-
|
|
715
|
+
advisoryStartupBrief,
|
|
716
|
+
approvedStartupPlan,
|
|
668
717
|
);
|
|
669
718
|
snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
|
|
670
719
|
deps.emitCommandText(
|
|
671
720
|
ctx,
|
|
672
721
|
assessment.reason === "fresh_explicit_handoff"
|
|
673
|
-
? `Refocused completion mission from explicit primary-agent
|
|
674
|
-
: `Refocused completion mission to: ${proposalDecision.missionAnchor}`,
|
|
722
|
+
? `Refocused completion mission from explicit primary-agent startup plan and rewrote the approved startup plan to: ${proposalDecision.missionAnchor}`
|
|
723
|
+
: `Refocused completion mission and rewrote the approved startup plan to: ${proposalDecision.missionAnchor}`,
|
|
675
724
|
"info",
|
|
676
725
|
);
|
|
677
726
|
}
|