@linimin/pi-letscook 0.1.36 → 0.1.39
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/CHANGELOG.md +22 -2
- package/README.md +27 -27
- package/extensions/completion/index.ts +465 -206
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +45 -1
- package/scripts/canonical-evidence-artifact-test.sh +45 -1
- package/scripts/context-proposal-test.sh +462 -131
- package/scripts/refocus-test.sh +260 -28
- package/scripts/release-check.sh +63 -1
- package/scripts/smoke-test.sh +88 -2
|
@@ -101,20 +101,251 @@ mkdir -p "$ROOT"
|
|
|
101
101
|
cd "$ROOT"
|
|
102
102
|
git init -q
|
|
103
103
|
|
|
104
|
-
# No workflow yet: /cook
|
|
104
|
+
# No workflow yet: bare /cook should use strict structured discussion fallback when analyst output is unavailable.
|
|
105
105
|
SESSION_ZERO="$TMPDIR/session-zero.jsonl"
|
|
106
106
|
DISCUSSION_ZERO=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\n- Suppress the widget while a completion role is active.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Update README to match the shipped behavior.\n- Keep observability regression coverage truthful.'
|
|
107
|
+
DISCUSSION_SNAPSHOT_ZERO="$TMPDIR/context-proposal-structured-fallback.json"
|
|
107
108
|
write_session "$SESSION_ZERO" "$ROOT" "$DISCUSSION_ZERO"
|
|
108
109
|
|
|
109
110
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
110
111
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
112
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO" \
|
|
111
113
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
112
|
-
pi --session "$SESSION_ZERO" -e "$PKG_ROOT" -p "/cook"
|
|
114
|
+
pi --session "$SESSION_ZERO" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-structured-fallback.out" 2>"$TMPDIR/pi-completion-context-proposal-structured-fallback.err"
|
|
113
115
|
|
|
114
|
-
python3 - <<'PY'
|
|
116
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO" <<'PY'
|
|
117
|
+
import json
|
|
118
|
+
import sys
|
|
119
|
+
from pathlib import Path
|
|
120
|
+
|
|
121
|
+
mission = 'Remove the completion status line while keeping the completion widget.'
|
|
122
|
+
expected_task_type = 'completion-workflow'
|
|
123
|
+
expected_eval_profile = 'completion-rubric-v1'
|
|
124
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
125
|
+
profile = json.loads(Path('.agent/profile.json').read_text())
|
|
126
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
127
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
128
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
129
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
130
|
+
|
|
131
|
+
assert Path('.agent').exists(), 'strict structured fallback should only create canonical state after Start is accepted'
|
|
132
|
+
assert mission in mission_text, '.agent/mission.md did not record the structured-fallback mission anchor'
|
|
133
|
+
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after structured-fallback bootstrap'
|
|
134
|
+
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after structured-fallback bootstrap'
|
|
135
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after structured-fallback bootstrap'
|
|
136
|
+
assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after structured-fallback bootstrap'
|
|
137
|
+
assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after structured-fallback bootstrap'
|
|
138
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after structured-fallback bootstrap'
|
|
139
|
+
assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after structured-fallback bootstrap'
|
|
140
|
+
assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after structured-fallback bootstrap'
|
|
141
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after structured-fallback bootstrap'
|
|
142
|
+
assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after structured-fallback bootstrap'
|
|
143
|
+
assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after structured-fallback bootstrap'
|
|
144
|
+
assert proposal['mission'] == mission, 'structured-fallback proposal snapshot should preserve the discussion mission anchor'
|
|
145
|
+
assert proposal['source'] == 'session', 'structured-fallback proposal snapshot should record the strict session fallback source'
|
|
146
|
+
assert proposal['scope'] == ['Keep the non-running completion widget.', 'Suppress the widget while a completion role is active.'], 'structured-fallback proposal snapshot should preserve discussion scope'
|
|
147
|
+
assert proposal['constraints'] == ['Do not reintroduce any other completion status surface.'], 'structured-fallback proposal snapshot should preserve discussion constraints'
|
|
148
|
+
assert proposal['acceptance'] == ['Update README to match the shipped behavior.', 'Keep observability regression coverage truthful.'], 'structured-fallback proposal snapshot should preserve discussion acceptance'
|
|
149
|
+
assert state['current_phase'] == 'reground', 'state.json current_phase should start at reground after structured-fallback bootstrap'
|
|
150
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should start at completion-regrounder after structured-fallback bootstrap'
|
|
151
|
+
assert state['continuation_reason'].startswith('User started workflow via /cook:'), 'structured-fallback startup should record the accepted startup routing in continuation_reason'
|
|
152
|
+
assert 'task_type=completion-workflow' in state['continuation_reason'], 'structured-fallback startup should persist the selected task_type in continuation_reason'
|
|
153
|
+
assert 'evaluation_profile=completion-rubric-v1' in state['continuation_reason'], 'structured-fallback startup should persist the selected evaluation_profile in continuation_reason'
|
|
154
|
+
PY
|
|
155
|
+
|
|
156
|
+
rm -rf .agent
|
|
157
|
+
|
|
158
|
+
# No workflow yet: bare /cook should fail closed when a required structured section is missing and analyst output is unavailable.
|
|
159
|
+
SESSION_ZERO_MISSING="$TMPDIR/session-zero-missing-section.jsonl"
|
|
160
|
+
DISCUSSION_ZERO_MISSING=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\n- Suppress the widget while a completion role is active.\nConstraints:\n- Do not reintroduce any other completion status surface.'
|
|
161
|
+
DISCUSSION_SNAPSHOT_ZERO_MISSING="$TMPDIR/context-proposal-missing-section.json"
|
|
162
|
+
write_session "$SESSION_ZERO_MISSING" "$ROOT" "$DISCUSSION_ZERO_MISSING"
|
|
163
|
+
|
|
164
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
165
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
166
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_MISSING" \
|
|
167
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
168
|
+
pi --session "$SESSION_ZERO_MISSING" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-missing-section.out" 2>"$TMPDIR/pi-completion-context-proposal-missing-section.err"
|
|
169
|
+
|
|
170
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-missing-section.out" "$TMPDIR/pi-completion-context-proposal-missing-section.err" "$DISCUSSION_SNAPSHOT_ZERO_MISSING" <<'PY'
|
|
171
|
+
import sys
|
|
172
|
+
from pathlib import Path
|
|
173
|
+
|
|
174
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
175
|
+
snapshot = Path(sys.argv[3])
|
|
176
|
+
assert not Path('.agent').exists(), 'missing-section structured discussion should fail closed without writing canonical state'
|
|
177
|
+
assert not snapshot.exists(), 'missing-section structured discussion should not emit a proposal snapshot when bare /cook fails closed'
|
|
178
|
+
assert 'Bare /cook failed closed' in output, 'missing-section structured discussion should explain the fail-closed startup outcome'
|
|
179
|
+
assert 'Mission/Scope/Constraints/Acceptance' in output, 'missing-section structured discussion should explain the strict fallback requirement'
|
|
180
|
+
PY
|
|
181
|
+
|
|
182
|
+
# No workflow yet: bare /cook should fail closed on ambiguous structured discussion when analyst output is unavailable.
|
|
183
|
+
SESSION_ZERO_AMBIG="$TMPDIR/session-zero-ambiguous.jsonl"
|
|
184
|
+
DISCUSSION_ZERO_AMBIG=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the non-running completion widget.\nConstraints:\n- Do not reintroduce any other completion status surface.\nAcceptance:\n- Update README to match the shipped behavior.\nMission: Ship an unrelated widget overhaul.\nScope:\n- Replace the widget entirely.'
|
|
185
|
+
write_session "$SESSION_ZERO_AMBIG" "$ROOT" "$DISCUSSION_ZERO_AMBIG"
|
|
186
|
+
|
|
187
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
188
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
189
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
190
|
+
pi --session "$SESSION_ZERO_AMBIG" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-ambiguous.out" 2>"$TMPDIR/pi-completion-context-proposal-ambiguous.err"
|
|
191
|
+
|
|
192
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-ambiguous.out" "$TMPDIR/pi-completion-context-proposal-ambiguous.err" <<'PY'
|
|
193
|
+
import sys
|
|
194
|
+
from pathlib import Path
|
|
195
|
+
|
|
196
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
197
|
+
assert not Path('.agent').exists(), 'ambiguous structured discussion should fail closed without writing canonical state'
|
|
198
|
+
assert 'Bare /cook failed closed' in output, 'ambiguous structured discussion should explain the fail-closed startup outcome'
|
|
199
|
+
assert 'Mission/Scope/Constraints/Acceptance' in output, 'ambiguous structured discussion should explain the strict fallback requirement'
|
|
200
|
+
PY
|
|
201
|
+
|
|
202
|
+
# No workflow yet: bare /cook structured fallback should normalize placeholder planning phrasing
|
|
203
|
+
# into the concrete implementation mission when scope/acceptance clearly describe shipped work.
|
|
204
|
+
SESSION_ZERO_NORMALIZED="$TMPDIR/session-zero-normalized.jsonl"
|
|
205
|
+
DISCUSSION_ZERO_NORMALIZED=$'Mission: 開始實作這個方案\nScope:\n- Normalize bare /cook planning phrasing into shipped implementation missions.\n- Keep analyst-derived and structured-fallback proposals aligned.\nConstraints:\n- Do not rewrite the supported bare-discussion mission anchor once it is clear.\nAcceptance:\n- Add deterministic regression coverage for startup normalization and refocus gating.\n- Keep the approval-only Start/Cancel rewrite gate.'
|
|
206
|
+
DISCUSSION_SNAPSHOT_ZERO_NORMALIZED="$TMPDIR/context-proposal-normalized-fallback.json"
|
|
207
|
+
write_session "$SESSION_ZERO_NORMALIZED" "$ROOT" "$DISCUSSION_ZERO_NORMALIZED"
|
|
208
|
+
|
|
209
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
210
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
211
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_NORMALIZED" \
|
|
212
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
213
|
+
pi --session "$SESSION_ZERO_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-normalized-fallback.out" 2>"$TMPDIR/pi-completion-context-proposal-normalized-fallback.err"
|
|
214
|
+
|
|
215
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_NORMALIZED" <<'PY'
|
|
216
|
+
import json
|
|
217
|
+
import sys
|
|
218
|
+
from pathlib import Path
|
|
219
|
+
|
|
220
|
+
mission = 'Normalize bare /cook planning phrasing into shipped implementation missions.'
|
|
221
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
222
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
223
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
224
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
225
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
226
|
+
|
|
227
|
+
assert mission in mission_text, 'normalized structured-fallback startup should update .agent/mission.md to the implementation mission'
|
|
228
|
+
assert proposal['mission'] == mission, 'structured-fallback startup should normalize the placeholder planning mission'
|
|
229
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor should use the normalized implementation mission'
|
|
230
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor should use the normalized implementation mission'
|
|
231
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor should use the normalized implementation mission'
|
|
232
|
+
assert proposal['source'] == 'session', 'normalized structured-fallback startup should still record session fallback as the proposal source'
|
|
233
|
+
assert proposal['scope'][0] == mission, 'normalized structured-fallback startup should derive the mission from shipped-work scope'
|
|
234
|
+
PY
|
|
235
|
+
|
|
236
|
+
rm -rf .agent
|
|
237
|
+
|
|
238
|
+
# No workflow yet: analyst-derived and strict structured fallback proposals should converge on the same
|
|
239
|
+
# normalized implementation mission for the same planning-phrased discussion.
|
|
240
|
+
SESSION_ZERO_ANALYST_NORMALIZED="$TMPDIR/session-zero-analyst-normalized.jsonl"
|
|
241
|
+
ANALYST_OUTPUT_ZERO_NORMALIZED='{"mission":"開始實作這個方案","scope":["Normalize bare /cook planning phrasing into shipped implementation missions.","Keep analyst-derived and structured-fallback proposals aligned."],"constraints":["Do not rewrite the supported bare-discussion mission anchor once it is clear."],"acceptance":["Add deterministic regression coverage for startup normalization and refocus gating.","Keep the approval-only Start/Cancel rewrite gate."],"task_type":"completion-workflow","evaluation_profile":"completion-rubric-v1","confidence":0.93}'
|
|
242
|
+
DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED="$TMPDIR/context-proposal-normalized-analyst.json"
|
|
243
|
+
write_session "$SESSION_ZERO_ANALYST_NORMALIZED" "$ROOT" "$DISCUSSION_ZERO_NORMALIZED"
|
|
244
|
+
|
|
245
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
246
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_ZERO_NORMALIZED" \
|
|
247
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED" \
|
|
248
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
249
|
+
pi --session "$SESSION_ZERO_ANALYST_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-normalized-analyst.out" 2>"$TMPDIR/pi-completion-context-proposal-normalized-analyst.err"
|
|
250
|
+
|
|
251
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_ANALYST_NORMALIZED" <<'PY'
|
|
252
|
+
import json
|
|
253
|
+
import sys
|
|
115
254
|
from pathlib import Path
|
|
116
255
|
|
|
117
|
-
|
|
256
|
+
mission = 'Normalize bare /cook planning phrasing into shipped implementation missions.'
|
|
257
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
258
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
259
|
+
|
|
260
|
+
assert proposal['mission'] == mission, 'analyst-derived startup should normalize the same placeholder planning mission to the same implementation mission'
|
|
261
|
+
assert state['mission_anchor'] == mission, 'analyst-derived startup should converge on the same canonical mission anchor as structured fallback'
|
|
262
|
+
assert proposal['analysis']['taskType'] == 'completion-workflow', 'analyst-derived normalization should preserve task_type hints'
|
|
263
|
+
assert proposal['analysis']['evaluationProfile'] == 'completion-rubric-v1', 'analyst-derived normalization should preserve evaluation_profile hints'
|
|
264
|
+
PY
|
|
265
|
+
|
|
266
|
+
rm -rf .agent
|
|
267
|
+
|
|
268
|
+
# No workflow yet: planning-only deliverables should preserve planning missions when discussion clearly says
|
|
269
|
+
# docs-only / no-code plan output instead of shipped code, test, or runtime work.
|
|
270
|
+
SESSION_ZERO_PLANNING_ONLY="$TMPDIR/session-zero-planning-only.jsonl"
|
|
271
|
+
DISCUSSION_ZERO_PLANNING_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Draft the migration plan for the /cook mission-normalization rollout.\nConstraints:\n- Docs only; do not implement runtime changes.\nAcceptance:\n- Produce the proposal text for review.'
|
|
272
|
+
DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY="$TMPDIR/context-proposal-planning-only.json"
|
|
273
|
+
write_session "$SESSION_ZERO_PLANNING_ONLY" "$ROOT" "$DISCUSSION_ZERO_PLANNING_ONLY"
|
|
274
|
+
|
|
275
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
276
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
277
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" \
|
|
278
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
279
|
+
pi --session "$SESSION_ZERO_PLANNING_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-planning-only.out" 2>"$TMPDIR/pi-completion-context-proposal-planning-only.err"
|
|
280
|
+
|
|
281
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_PLANNING_ONLY" <<'PY'
|
|
282
|
+
import json
|
|
283
|
+
import sys
|
|
284
|
+
from pathlib import Path
|
|
285
|
+
|
|
286
|
+
mission = '開始實作這個方案.'
|
|
287
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
288
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
289
|
+
|
|
290
|
+
assert proposal['mission'] == mission, 'planning-only startup should preserve the planning mission when the deliverable is explicitly a plan'
|
|
291
|
+
assert state['mission_anchor'] == mission, 'planning-only startup should keep the planning mission anchor in canonical state'
|
|
292
|
+
PY
|
|
293
|
+
|
|
294
|
+
rm -rf .agent
|
|
295
|
+
|
|
296
|
+
# No workflow yet: support-docs-only deliverables should also preserve planning missions even without
|
|
297
|
+
# an explicit docs-only/no-code phrase, as long as the deliverable stays limited to support documentation.
|
|
298
|
+
SESSION_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/session-zero-support-docs-only.jsonl"
|
|
299
|
+
DISCUSSION_ZERO_SUPPORT_DOCS_ONLY=$'Mission: 開始實作這個方案\nScope:\n- Update README for the /cook mission-normalization behavior.\nConstraints:\n- Keep the approval-only Start/Cancel gate unchanged.\nAcceptance:\n- Add documentation for the operator-facing refocus flow.'
|
|
300
|
+
DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY="$TMPDIR/context-proposal-support-docs-only.json"
|
|
301
|
+
write_session "$SESSION_ZERO_SUPPORT_DOCS_ONLY" "$ROOT" "$DISCUSSION_ZERO_SUPPORT_DOCS_ONLY"
|
|
302
|
+
|
|
303
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
304
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
305
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY" \
|
|
306
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
307
|
+
pi --session "$SESSION_ZERO_SUPPORT_DOCS_ONLY" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-support-docs-only.out" 2>"$TMPDIR/pi-completion-context-proposal-support-docs-only.err"
|
|
308
|
+
|
|
309
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_SUPPORT_DOCS_ONLY" <<'PY'
|
|
310
|
+
import json
|
|
311
|
+
import sys
|
|
312
|
+
from pathlib import Path
|
|
313
|
+
|
|
314
|
+
mission = '開始實作這個方案.'
|
|
315
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
316
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
317
|
+
|
|
318
|
+
assert proposal['mission'] == mission, 'support-docs-only startup should preserve the planning mission even without an explicit docs-only phrase'
|
|
319
|
+
assert state['mission_anchor'] == mission, 'support-docs-only startup should keep the planning mission anchor in canonical state'
|
|
320
|
+
assert proposal['scope'] == ['Update README for the /cook mission-normalization behavior.'], 'support-docs-only startup should keep the documentation scope item'
|
|
321
|
+
assert proposal['acceptance'] == ['Add documentation for the operator-facing refocus flow.'], 'support-docs-only startup should keep the documentation acceptance item'
|
|
322
|
+
PY
|
|
323
|
+
|
|
324
|
+
rm -rf .agent
|
|
325
|
+
|
|
326
|
+
# No workflow yet: analyst-derived generic planning missions should still fail closed when discussion
|
|
327
|
+
# never provides a clear implementation anchor, instead of promoting vague non-doc scope.
|
|
328
|
+
SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC="$TMPDIR/session-zero-analyst-ambiguous-generic.jsonl"
|
|
329
|
+
DISCUSSION_ZERO_ANALYST_AMBIGUOUS_GENERIC=$'We should revisit the completion widget while roles are active and make the outcome easier to follow without deciding the exact implementation yet.'
|
|
330
|
+
ANALYST_OUTPUT_ZERO_AMBIGUOUS_GENERIC='{"mission":"開始實作這個方案","scope":["The completion widget during active roles."],"constraints":["Keep the approval-only Start/Cancel gate unchanged."],"acceptance":["Current behavior stays understandable."],"task_type":"completion-workflow","evaluation_profile":"completion-rubric-v1","confidence":0.74}'
|
|
331
|
+
DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC="$TMPDIR/context-proposal-analyst-ambiguous-generic.json"
|
|
332
|
+
write_session "$SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC" "$ROOT" "$DISCUSSION_ZERO_ANALYST_AMBIGUOUS_GENERIC"
|
|
333
|
+
|
|
334
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
335
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT="$ANALYST_OUTPUT_ZERO_AMBIGUOUS_GENERIC" \
|
|
336
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC" \
|
|
337
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
338
|
+
pi --session "$SESSION_ZERO_ANALYST_AMBIGUOUS_GENERIC" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.out" 2>"$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.err"
|
|
339
|
+
|
|
340
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.out" "$TMPDIR/pi-completion-context-proposal-analyst-ambiguous-generic.err" "$DISCUSSION_SNAPSHOT_ZERO_ANALYST_AMBIGUOUS_GENERIC" <<'PY'
|
|
341
|
+
import sys
|
|
342
|
+
from pathlib import Path
|
|
343
|
+
|
|
344
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
345
|
+
snapshot = Path(sys.argv[3])
|
|
346
|
+
assert not Path('.agent').exists(), 'analyst-derived ambiguous generic discussion should fail closed without writing canonical state'
|
|
347
|
+
assert not snapshot.exists(), 'analyst-derived ambiguous generic discussion should not emit a proposal snapshot when bare /cook fails closed'
|
|
348
|
+
assert 'Bare /cook failed closed' in output, 'analyst-derived ambiguous generic discussion should explain the fail-closed startup outcome'
|
|
118
349
|
PY
|
|
119
350
|
|
|
120
351
|
# No workflow yet: /cook with no goal should infer from recent discussion through analyst output.
|
|
@@ -173,21 +404,147 @@ assert 'evaluation_profile=completion-rubric-v1' in state['continuation_reason']
|
|
|
173
404
|
assert 'Keep critique separate from the mission anchor so startup analysis does not rewrite the workflow goal.' in state['continuation_reason'], 'initial startup should persist the accepted critique outcome in continuation_reason'
|
|
174
405
|
PY
|
|
175
406
|
|
|
176
|
-
#
|
|
407
|
+
# Active workflow: bare /cook with matching structured discussion should classify as continue
|
|
408
|
+
# and resume the current workflow without opening the chooser or rewriting canonical state.
|
|
409
|
+
SESSION_ONE_CONTINUE="$TMPDIR/session-one-continue.jsonl"
|
|
410
|
+
DISCUSSION_ONE_CONTINUE=$'Mission: Remove the completion status line while keeping the completion widget.\nScope:\n- Keep the current mission focused on the non-running completion widget.\nConstraints:\n- Do not start a different workflow from this discussion.\nAcceptance:\n- Resume the current workflow from canonical state without rewriting it.'
|
|
411
|
+
CONTINUE_ROUTING_ONE="$TMPDIR/active-continue-routing.json"
|
|
412
|
+
CONTINUE_RESUME_PROMPT_ONE="$TMPDIR/active-continue-resume.txt"
|
|
413
|
+
CONTINUE_CHOOSER_ONE="$TMPDIR/unexpected-active-continue-chooser.json"
|
|
414
|
+
write_session "$SESSION_ONE_CONTINUE" "$ROOT" "$DISCUSSION_ONE_CONTINUE"
|
|
415
|
+
|
|
416
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
417
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$CONTINUE_ROUTING_ONE" \
|
|
418
|
+
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$CONTINUE_RESUME_PROMPT_ONE" \
|
|
419
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$CONTINUE_CHOOSER_ONE" \
|
|
420
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
421
|
+
pi --session "$SESSION_ONE_CONTINUE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-continue.out" 2>"$TMPDIR/pi-completion-context-proposal-active-continue.err"
|
|
422
|
+
|
|
423
|
+
python3 - "$CONTINUE_ROUTING_ONE" "$CONTINUE_RESUME_PROMPT_ONE" "$CONTINUE_CHOOSER_ONE" <<'PY'
|
|
424
|
+
import json
|
|
425
|
+
import sys
|
|
426
|
+
from pathlib import Path
|
|
427
|
+
|
|
428
|
+
mission = 'Remove the completion status line while keeping the completion widget.'
|
|
429
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
430
|
+
resume = Path(sys.argv[2]).read_text()
|
|
431
|
+
chooser_path = Path(sys.argv[3])
|
|
432
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
433
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
434
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
435
|
+
|
|
436
|
+
assert routing['mode'] == 'bare', 'active bare /cook continue regression should snapshot bare routing mode'
|
|
437
|
+
assert 'explicitGoal' not in routing, 'active bare /cook continue routing should not expose removed explicit-goal shim fields'
|
|
438
|
+
assert 'explicitGoalProvided' not in routing, 'active bare /cook continue routing should not expose removed explicit-goal shim fields'
|
|
439
|
+
assert routing['action'] == 'continue', 'matching structured discussion should classify active bare /cook as continue'
|
|
440
|
+
assert routing['reason'] == 'matching_mission', 'matching structured discussion should keep the current mission rather than refocus'
|
|
441
|
+
assert routing['currentMissionAnchor'] == mission, 'continue routing should preserve the current mission anchor'
|
|
442
|
+
assert routing['proposedMissionAnchor'] == mission, 'continue routing should keep the proposed mission anchored to the current mission'
|
|
443
|
+
assert 'Resume the completion workflow from canonical state.' in resume, 'active bare /cook continue should still use the canonical resume prompt'
|
|
444
|
+
assert not chooser_path.exists(), 'active bare /cook continue should not open the refocus chooser'
|
|
445
|
+
assert state['mission_anchor'] == mission, 'active bare /cook continue should keep state.json unchanged'
|
|
446
|
+
assert plan['mission_anchor'] == mission, 'active bare /cook continue should keep plan.json unchanged'
|
|
447
|
+
assert active['mission_anchor'] == mission, 'active bare /cook continue should keep active-slice.json unchanged'
|
|
448
|
+
PY
|
|
449
|
+
|
|
450
|
+
# Active workflow: bare /cook with a placeholder planning mission should still route through the existing
|
|
451
|
+
# refocus chooser and final Start/Cancel gate before canonical state is rewritten.
|
|
452
|
+
SESSION_ONE_REFOCUS_NORMALIZED="$TMPDIR/session-one-refocus-normalized.jsonl"
|
|
453
|
+
DISCUSSION_ONE_REFOCUS_NORMALIZED=$'Mission: 開始實作這個方案\nScope:\n- Normalize bare /cook planning phrasing into implementation-result missions.\n- Keep the approval-only Start/Cancel gate before rewriting canonical state.\nConstraints:\n- Do not resume the current widget mission.\nAcceptance:\n- Route through chooser-driven refocus before rewriting canonical state.'
|
|
454
|
+
REFOCUS_ROUTING_ONE="$TMPDIR/active-refocus-routing.json"
|
|
455
|
+
REFOCUS_CHOOSER_ONE="$TMPDIR/active-refocus-chooser.json"
|
|
456
|
+
REFOCUS_PROPOSAL_ONE="$TMPDIR/active-refocus-proposal.json"
|
|
457
|
+
REFOCUS_UI_ONE="$TMPDIR/active-refocus-ui.json"
|
|
458
|
+
write_session "$SESSION_ONE_REFOCUS_NORMALIZED" "$ROOT" "$DISCUSSION_ONE_REFOCUS_NORMALIZED"
|
|
459
|
+
|
|
460
|
+
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
461
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_ACTION=start \
|
|
462
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
463
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$REFOCUS_ROUTING_ONE" \
|
|
464
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$REFOCUS_CHOOSER_ONE" \
|
|
465
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$REFOCUS_PROPOSAL_ONE" \
|
|
466
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_UI_PATH="$REFOCUS_UI_ONE" \
|
|
467
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
468
|
+
pi --session "$SESSION_ONE_REFOCUS_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-refocus-normalized.out" 2>"$TMPDIR/pi-completion-context-proposal-active-refocus-normalized.err"
|
|
469
|
+
|
|
470
|
+
python3 - "$REFOCUS_ROUTING_ONE" "$REFOCUS_CHOOSER_ONE" "$REFOCUS_PROPOSAL_ONE" "$REFOCUS_UI_ONE" <<'PY'
|
|
471
|
+
import json
|
|
472
|
+
import sys
|
|
473
|
+
from pathlib import Path
|
|
474
|
+
|
|
475
|
+
mission = 'Normalize bare /cook planning phrasing into implementation-result missions.'
|
|
476
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
477
|
+
chooser = json.loads(Path(sys.argv[2]).read_text())
|
|
478
|
+
proposal = json.loads(Path(sys.argv[3]).read_text())
|
|
479
|
+
ui = json.loads(Path(sys.argv[4]).read_text())
|
|
480
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
481
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
482
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
483
|
+
|
|
484
|
+
assert routing['mode'] == 'bare', 'active bare /cook refocus normalization should snapshot bare routing mode'
|
|
485
|
+
assert 'explicitGoal' not in routing, 'active bare /cook refocus routing should not expose removed explicit-goal shim fields'
|
|
486
|
+
assert 'explicitGoalProvided' not in routing, 'active bare /cook refocus routing should not expose removed explicit-goal shim fields'
|
|
487
|
+
assert routing['action'] == 'refocus', 'placeholder planning mission should still classify active bare /cook as refocus when the normalized mission changes'
|
|
488
|
+
assert routing['reason'] == 'clear_refocus', 'active bare /cook refocus normalization should keep the clear_refocus routing reason'
|
|
489
|
+
assert routing['proposedMissionAnchor'] == mission, 'active bare /cook refocus should normalize the proposed mission before canonical rewrite'
|
|
490
|
+
assert chooser['choices'][1].startswith('Start new workflow from recent discussion'), 'active bare /cook refocus should still route through the existing chooser copy before rewrite'
|
|
491
|
+
assert [action['id'] for action in ui['actions']] == ['start', 'cancel'], 'active bare /cook refocus should still end at the approval-only Start/Cancel gate'
|
|
492
|
+
assert proposal['mission'] == mission, 'active bare /cook refocus proposal snapshot should expose the normalized implementation mission'
|
|
493
|
+
assert state['mission_anchor'] == mission, 'active bare /cook refocus should rewrite canonical state to the normalized mission only after approval'
|
|
494
|
+
assert plan['mission_anchor'] == mission, 'active bare /cook refocus should rewrite plan.json only after approval'
|
|
495
|
+
assert active['mission_anchor'] == mission, 'active bare /cook refocus should rewrite active-slice.json only after approval'
|
|
496
|
+
PY
|
|
497
|
+
|
|
498
|
+
# Completed workflow: bare /cook should normalize placeholder planning phrasing for the next workflow
|
|
499
|
+
# round too, not only for fresh startup.
|
|
500
|
+
mark_done
|
|
501
|
+
|
|
502
|
+
SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
|
|
503
|
+
DISCUSSION_TWO_NORMALIZED=$'Mission: 開始實作這個方案\nScope:\n- Normalize bare /cook planning phrasing for the next workflow round.\n- Reset canonical state for the new implementation mission.\nConstraints:\n- Do not resume the completed workflow when the new round is clearly different.\nAcceptance:\n- Start a new round with the normalized mission anchor.'
|
|
504
|
+
DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
|
|
505
|
+
write_session "$SESSION_TWO_NORMALIZED" "$ROOT" "$DISCUSSION_TWO_NORMALIZED"
|
|
506
|
+
|
|
507
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
508
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
509
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" \
|
|
510
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
511
|
+
pi --session "$SESSION_TWO_NORMALIZED" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-normalized.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-normalized.err"
|
|
512
|
+
|
|
513
|
+
python3 - "$DISCUSSION_SNAPSHOT_TWO_NORMALIZED" <<'PY'
|
|
514
|
+
import json
|
|
515
|
+
import sys
|
|
516
|
+
from pathlib import Path
|
|
517
|
+
|
|
518
|
+
mission = 'Normalize bare /cook planning phrasing for the next workflow round.'
|
|
519
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
520
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
521
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
522
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
523
|
+
|
|
524
|
+
assert proposal['mission'] == mission, 'done-workflow structured fallback should normalize the placeholder planning mission for the next round'
|
|
525
|
+
assert state['mission_anchor'] == mission, 'done-workflow startup should rewrite canonical state to the normalized next-round mission'
|
|
526
|
+
assert plan['mission_anchor'] == mission, 'done-workflow startup should rewrite plan.json to the normalized next-round mission'
|
|
527
|
+
assert active['mission_anchor'] == mission, 'done-workflow startup should rewrite active-slice.json to the normalized next-round mission'
|
|
528
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'done-workflow normalization should still route through refocus semantics for the next round'
|
|
529
|
+
PY
|
|
530
|
+
|
|
531
|
+
# Completed workflow: bare /cook should use the same strict structured fallback for the next workflow round when analyst output is unavailable.
|
|
177
532
|
mark_done
|
|
178
533
|
|
|
179
534
|
SESSION_TWO="$TMPDIR/session-two.jsonl"
|
|
180
535
|
DISCUSSION_TWO=$'Mission: Ship the next workflow round for richer context-derived /cook startup.\nScope:\n- Start a new workflow round from recent discussion after the previous one is done.\n- Keep using canonical .agent state after confirmation.\nConstraints:\n- Do not resume the completed workflow when the new round is clearly different.\nAcceptance:\n- Reset canonical state back to reground for the new mission.\n- Preserve the tracked completion control-plane files.'
|
|
181
|
-
|
|
536
|
+
DISCUSSION_SNAPSHOT_TWO="$TMPDIR/context-proposal-next-round-structured-fallback.json"
|
|
182
537
|
write_session "$SESSION_TWO" "$ROOT" "$DISCUSSION_TWO"
|
|
183
538
|
|
|
184
539
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
185
|
-
|
|
540
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
541
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO" \
|
|
186
542
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
187
|
-
pi --session "$SESSION_TWO" -e "$PKG_ROOT" -p "/cook"
|
|
543
|
+
pi --session "$SESSION_TWO" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round.err"
|
|
188
544
|
|
|
189
|
-
python3 - <<'PY'
|
|
545
|
+
python3 - "$DISCUSSION_SNAPSHOT_TWO" <<'PY'
|
|
190
546
|
import json
|
|
547
|
+
import sys
|
|
191
548
|
from pathlib import Path
|
|
192
549
|
|
|
193
550
|
mission = 'Ship the next workflow round for richer context-derived /cook startup.'
|
|
@@ -198,6 +555,7 @@ profile = json.loads(Path('.agent/profile.json').read_text())
|
|
|
198
555
|
state = json.loads(Path('.agent/state.json').read_text())
|
|
199
556
|
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
200
557
|
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
558
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
201
559
|
|
|
202
560
|
assert mission in mission_text, '.agent/mission.md did not update to the next-round context-derived mission anchor'
|
|
203
561
|
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after next-round startup'
|
|
@@ -211,6 +569,8 @@ assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluatio
|
|
|
211
569
|
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after starting the next workflow round'
|
|
212
570
|
assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after starting the next workflow round'
|
|
213
571
|
assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after starting the next workflow round'
|
|
572
|
+
assert proposal['mission'] == mission, 'next-round structured-fallback proposal snapshot should preserve the discussion mission anchor'
|
|
573
|
+
assert proposal['source'] == 'session', 'next-round structured-fallback proposal snapshot should record the strict session fallback source'
|
|
214
574
|
assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground for the next workflow round'
|
|
215
575
|
assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue for the next workflow round'
|
|
216
576
|
assert state['requires_reground'] is True, 'requires_reground should reset to true for the next workflow round'
|
|
@@ -224,153 +584,124 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
|
|
|
224
584
|
assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
|
|
225
585
|
PY
|
|
226
586
|
|
|
227
|
-
# Active workflow: /cook
|
|
228
|
-
#
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
235
|
-
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
236
|
-
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
237
|
-
pi --session "$SESSION_THREE" -e "$PKG_ROOT" -p "/cook Explicit replacement mission for the active workflow" >/tmp/pi-completion-context-proposal-active-goal.out 2>/tmp/pi-completion-context-proposal-active-goal.err
|
|
238
|
-
|
|
239
|
-
python3 - <<'PY'
|
|
587
|
+
# Active workflow: inline /cook args should be rejected before proposal/routing helpers run
|
|
588
|
+
# and should leave canonical state unchanged.
|
|
589
|
+
ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
|
|
590
|
+
ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
|
|
591
|
+
ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
|
|
592
|
+
ACTIVE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-active-inline-before.json"
|
|
593
|
+
python3 - "$ACTIVE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
240
594
|
import json
|
|
595
|
+
import sys
|
|
241
596
|
from pathlib import Path
|
|
242
597
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
assert mission in mission_text, '.agent/mission.md did not update to the explicit replacement mission anchor'
|
|
253
|
-
assert profile['task_type'] == expected_task_type, 'profile.json task_type mismatch after explicit-goal replacement'
|
|
254
|
-
assert profile['evaluation_profile'] == expected_eval_profile, 'profile.json evaluation_profile mismatch after explicit-goal replacement'
|
|
255
|
-
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after explicit-goal replacement'
|
|
256
|
-
assert state['task_type'] == expected_task_type, 'state.json task_type mismatch after explicit-goal replacement'
|
|
257
|
-
assert state['evaluation_profile'] == expected_eval_profile, 'state.json evaluation_profile mismatch after explicit-goal replacement'
|
|
258
|
-
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after explicit-goal replacement'
|
|
259
|
-
assert plan['task_type'] == expected_task_type, 'plan.json task_type mismatch after explicit-goal replacement'
|
|
260
|
-
assert plan['evaluation_profile'] == expected_eval_profile, 'plan.json evaluation_profile mismatch after explicit-goal replacement'
|
|
261
|
-
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after explicit-goal replacement'
|
|
262
|
-
assert active['task_type'] == expected_task_type, 'active-slice.json task_type mismatch after explicit-goal replacement'
|
|
263
|
-
assert active['evaluation_profile'] == expected_eval_profile, 'active-slice.json evaluation_profile mismatch after explicit-goal replacement'
|
|
264
|
-
assert state['current_phase'] == 'reground', 'current_phase should reset to reground after explicit-goal replacement'
|
|
265
|
-
assert state['continuation_policy'] == 'continue', 'continuation_policy should stay continue after explicit-goal replacement'
|
|
266
|
-
assert state['next_mandatory_role'] == 'completion-regrounder', 'next role should reset to completion-regrounder after explicit-goal replacement'
|
|
267
|
-
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the explicit-goal replacement'
|
|
268
|
-
assert 'task_type=completion-workflow' in state['continuation_reason'], 'explicit-goal replacement should persist the selected task_type'
|
|
269
|
-
assert 'evaluation_profile=completion-rubric-v1' in state['continuation_reason'], 'explicit-goal replacement should persist the selected evaluation_profile'
|
|
270
|
-
assert 'critique outcome=accepted critique=none' in state['continuation_reason'], 'explicit-goal replacement should persist the accepted critique outcome even when no critique was derived'
|
|
271
|
-
assert 'Preserve the richer proposal structure from discussion.' not in state['continuation_reason'], 'session scope should not be merged when analyst output is unavailable'
|
|
272
|
-
assert 'Keep explicit goals as the mission anchor when they conflict with earlier text.' not in state['continuation_reason'], 'session constraints should not be merged when analyst output is unavailable'
|
|
273
|
-
assert 'Refresh canonical state from the replacement mission.' not in state['continuation_reason'], 'session acceptance should not be merged when analyst output is unavailable'
|
|
274
|
-
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should be user_refocus after explicit-goal replacement'
|
|
275
|
-
assert active['status'] == 'idle', 'active slice should reset to idle after explicit-goal replacement'
|
|
598
|
+
tracked = [
|
|
599
|
+
Path('.agent/mission.md'),
|
|
600
|
+
Path('.agent/profile.json'),
|
|
601
|
+
Path('.agent/state.json'),
|
|
602
|
+
Path('.agent/plan.json'),
|
|
603
|
+
Path('.agent/active-slice.json'),
|
|
604
|
+
Path('.agent/verification-evidence.json'),
|
|
605
|
+
]
|
|
606
|
+
Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
|
|
276
607
|
PY
|
|
277
608
|
|
|
278
|
-
# Active workflow: cancelling the replacement proposal should keep the current workflow and redirect
|
|
279
|
-
# the user back to the main chat before rerunning /cook.
|
|
280
|
-
SESSION_THREE_CANCEL="$TMPDIR/session-three-cancel.jsonl"
|
|
281
|
-
DISCUSSION_THREE_CANCEL=$'Scope:\n- Keep the current workflow unchanged when replacement confirmation is cancelled.\nConstraints:\n- Do not rewrite canonical state after cancel.\nAcceptance:\n- Print rerun guidance.'
|
|
282
|
-
write_session "$SESSION_THREE_CANCEL" "$ROOT" "$DISCUSSION_THREE_CANCEL"
|
|
283
|
-
|
|
284
609
|
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
285
|
-
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=
|
|
610
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
286
611
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
612
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$ACTIVE_INLINE_REJECTION_PROPOSAL" \
|
|
613
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$ACTIVE_INLINE_REJECTION_ROUTING" \
|
|
614
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$ACTIVE_INLINE_REJECTION_CHOOSER" \
|
|
287
615
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
288
|
-
pi
|
|
616
|
+
pi -e "$PKG_ROOT" -p "/cook Replacement mission for the active workflow" >"$TMPDIR/pi-completion-context-proposal-active-inline-arg.out" 2>"$TMPDIR/pi-completion-context-proposal-active-inline-arg.err"
|
|
289
617
|
|
|
290
|
-
python3 - "$TMPDIR/pi-completion-context-proposal-
|
|
618
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-active-inline-arg.out" "$TMPDIR/pi-completion-context-proposal-active-inline-arg.err" "$ACTIVE_INLINE_REJECTION_ROUTING" "$ACTIVE_INLINE_REJECTION_PROPOSAL" "$ACTIVE_INLINE_REJECTION_CHOOSER" "$ACTIVE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
291
619
|
import json
|
|
292
620
|
import sys
|
|
293
621
|
from pathlib import Path
|
|
294
622
|
|
|
295
|
-
mission = 'Explicit replacement mission for the active workflow.'
|
|
296
|
-
state = json.loads(Path('.agent/state.json').read_text())
|
|
297
|
-
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
298
|
-
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
299
623
|
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
624
|
+
routing = Path(sys.argv[3])
|
|
625
|
+
proposal = Path(sys.argv[4])
|
|
626
|
+
chooser = Path(sys.argv[5])
|
|
627
|
+
before = json.loads(Path(sys.argv[6]).read_text())
|
|
628
|
+
tracked = [
|
|
629
|
+
Path('.agent/mission.md'),
|
|
630
|
+
Path('.agent/profile.json'),
|
|
631
|
+
Path('.agent/state.json'),
|
|
632
|
+
Path('.agent/plan.json'),
|
|
633
|
+
Path('.agent/active-slice.json'),
|
|
634
|
+
Path('.agent/verification-evidence.json'),
|
|
635
|
+
]
|
|
300
636
|
|
|
301
|
-
assert
|
|
302
|
-
assert
|
|
303
|
-
assert
|
|
304
|
-
assert
|
|
637
|
+
assert 'Inline /cook arguments are no longer supported.' in output, 'active inline /cook args should explain the hard rejection'
|
|
638
|
+
assert 'Clarify the mission in the main chat and rerun bare /cook.' in output, 'active inline /cook args should redirect users back to main chat plus bare /cook'
|
|
639
|
+
assert not routing.exists(), 'active inline /cook args should not run active-workflow routing'
|
|
640
|
+
assert not proposal.exists(), 'active inline /cook args should not open proposal confirmation'
|
|
641
|
+
assert not chooser.exists(), 'active inline /cook args should not open the existing-workflow chooser'
|
|
642
|
+
after = {path.name: path.read_text() for path in tracked}
|
|
643
|
+
assert before == after, 'active inline /cook args should leave canonical files unchanged'
|
|
305
644
|
PY
|
|
306
645
|
|
|
307
|
-
# Completed workflow
|
|
308
|
-
# even when analyst output is unavailable, without merging session-derived scope, constraints, or acceptance.
|
|
646
|
+
# Completed workflow: inline /cook args should also fail closed without starting the next round.
|
|
309
647
|
mark_done
|
|
310
648
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
649
|
+
DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
|
|
650
|
+
DONE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-done-inline-arg-proposal.json"
|
|
651
|
+
DONE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-done-inline-arg-chooser.json"
|
|
652
|
+
DONE_INLINE_REJECTION_BASELINE="$TMPDIR/context-proposal-done-inline-before.json"
|
|
653
|
+
python3 - "$DONE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
654
|
+
import json
|
|
655
|
+
import sys
|
|
656
|
+
from pathlib import Path
|
|
657
|
+
|
|
658
|
+
tracked = [
|
|
659
|
+
Path('.agent/mission.md'),
|
|
660
|
+
Path('.agent/profile.json'),
|
|
661
|
+
Path('.agent/state.json'),
|
|
662
|
+
Path('.agent/plan.json'),
|
|
663
|
+
Path('.agent/active-slice.json'),
|
|
664
|
+
Path('.agent/verification-evidence.json'),
|
|
665
|
+
]
|
|
666
|
+
Path(sys.argv[1]).write_text(json.dumps({path.name: path.read_text() for path in tracked}, indent=2) + '\n')
|
|
667
|
+
PY
|
|
316
668
|
|
|
317
669
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
318
670
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
319
|
-
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$
|
|
671
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DONE_INLINE_REJECTION_PROPOSAL" \
|
|
672
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$DONE_INLINE_REJECTION_ROUTING" \
|
|
673
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$DONE_INLINE_REJECTION_CHOOSER" \
|
|
320
674
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
321
|
-
pi
|
|
675
|
+
pi -e "$PKG_ROOT" -p "/cook done-workflow replacement mission" >"$TMPDIR/pi-completion-context-proposal-done-inline-arg.out" 2>"$TMPDIR/pi-completion-context-proposal-done-inline-arg.err"
|
|
322
676
|
|
|
323
|
-
python3 - "$
|
|
677
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-done-inline-arg.out" "$TMPDIR/pi-completion-context-proposal-done-inline-arg.err" "$DONE_INLINE_REJECTION_ROUTING" "$DONE_INLINE_REJECTION_PROPOSAL" "$DONE_INLINE_REJECTION_CHOOSER" "$DONE_INLINE_REJECTION_BASELINE" <<'PY'
|
|
324
678
|
import json
|
|
325
679
|
import sys
|
|
326
680
|
from pathlib import Path
|
|
327
681
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
assert
|
|
343
|
-
assert
|
|
344
|
-
assert
|
|
345
|
-
assert
|
|
346
|
-
assert
|
|
347
|
-
assert
|
|
348
|
-
assert
|
|
349
|
-
|
|
350
|
-
assert
|
|
351
|
-
assert proposal['mission'] == mission, 'explicit-goal proposal snapshot should preserve the explicit mission anchor'
|
|
352
|
-
assert proposal['analysis']['taskType'] == expected_task_type, 'explicit-goal proposal snapshot should preserve task_type hints from the goal text'
|
|
353
|
-
assert proposal['analysis']['evaluationProfile'] == expected_eval_profile, 'explicit-goal proposal snapshot should preserve evaluation_profile hints from the goal text'
|
|
354
|
-
assert proposal['analysis']['critique'] == ['Keep critique notes separate from the mission anchor.'], 'explicit-goal proposal snapshot should preserve critique hints from the goal text'
|
|
355
|
-
assert proposal['analysis']['risks'] == ['Session-only scope could leak into the next workflow round.'], 'explicit-goal proposal snapshot should preserve risk hints from the goal text'
|
|
356
|
-
assert 'Critique:' not in proposal['goalText'], 'goalText should keep critique notes separate from mission/scope/constraints/acceptance'
|
|
357
|
-
assert 'Task type:' not in proposal['goalText'], 'goalText should keep task_type hints separate from the mission body'
|
|
358
|
-
assert state['current_phase'] == 'reground', 'current_phase should reset to reground after explicit-goal next-round start'
|
|
359
|
-
assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue after explicit-goal next-round start'
|
|
360
|
-
assert state['project_done'] is False, 'project_done should reset to false after explicit-goal next-round start'
|
|
361
|
-
assert state['requires_reground'] is True, 'requires_reground should reset to true after explicit-goal next-round start'
|
|
362
|
-
assert state['next_mandatory_role'] == 'completion-regrounder', 'next role should reset to completion-regrounder after explicit-goal next-round start'
|
|
363
|
-
assert continuation_reason.startswith('User refocused workflow via /cook:'), 'continuation_reason should record the explicit-goal next-round start'
|
|
364
|
-
assert 'task_type=completion-workflow' in continuation_reason, 'explicit-goal next-round start should persist the selected task_type'
|
|
365
|
-
assert 'evaluation_profile=completion-rubric-v1' in continuation_reason, 'explicit-goal next-round start should persist the selected evaluation_profile'
|
|
366
|
-
assert 'Keep critique notes separate from the mission anchor.' in continuation_reason, 'explicit-goal next-round start should persist the accepted critique outcome'
|
|
367
|
-
assert 'Keep explicit scope.' in continuation_reason, 'explicit scope should remain in the explicit-goal proposal'
|
|
368
|
-
assert 'Add session-only scope.' not in continuation_reason, 'session-derived scope should not be merged when analyst output is unavailable'
|
|
369
|
-
assert 'Restyle widget.' not in continuation_reason, 'unrelated session-derived scope should not be merged when analyst output is unavailable'
|
|
370
|
-
assert 'Keep rules.' not in continuation_reason, 'session-derived constraints should not merge when analyst output is unavailable'
|
|
371
|
-
assert 'Add test.' not in continuation_reason, 'session-derived acceptance should not merge when analyst output is unavailable'
|
|
372
|
-
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should be user_refocus after explicit-goal next-round start'
|
|
373
|
-
assert active['status'] == 'idle', 'active slice should reset to idle after explicit-goal next-round start'
|
|
682
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
683
|
+
routing = Path(sys.argv[3])
|
|
684
|
+
proposal = Path(sys.argv[4])
|
|
685
|
+
chooser = Path(sys.argv[5])
|
|
686
|
+
before = json.loads(Path(sys.argv[6]).read_text())
|
|
687
|
+
tracked = [
|
|
688
|
+
Path('.agent/mission.md'),
|
|
689
|
+
Path('.agent/profile.json'),
|
|
690
|
+
Path('.agent/state.json'),
|
|
691
|
+
Path('.agent/plan.json'),
|
|
692
|
+
Path('.agent/active-slice.json'),
|
|
693
|
+
Path('.agent/verification-evidence.json'),
|
|
694
|
+
]
|
|
695
|
+
state_before = json.loads(before['state.json'])
|
|
696
|
+
assert state_before['current_phase'] == 'done', 'done inline /cook rejection should start from a completed workflow'
|
|
697
|
+
assert state_before['project_done'] is True, 'done inline /cook rejection should start from project_done=true'
|
|
698
|
+
assert 'Inline /cook arguments are no longer supported.' in output, 'done inline /cook args should explain the hard rejection'
|
|
699
|
+
assert 'Clarify the mission in the main chat and rerun bare /cook.' in output, 'done inline /cook args should redirect users back to main chat plus bare /cook'
|
|
700
|
+
assert not routing.exists(), 'done inline /cook args should not run active/done workflow routing'
|
|
701
|
+
assert not proposal.exists(), 'done inline /cook args should not open next-round proposal confirmation'
|
|
702
|
+
assert not chooser.exists(), 'done inline /cook args should not open the chooser flow'
|
|
703
|
+
after = {path.name: path.read_text() for path in tracked}
|
|
704
|
+
assert before == after, 'done inline /cook args should leave canonical files unchanged'
|
|
374
705
|
PY
|
|
375
706
|
|
|
376
707
|
# Completed workflow again: /cook with no goal should be able to use model-assisted
|
|
@@ -378,8 +709,8 @@ PY
|
|
|
378
709
|
mark_done
|
|
379
710
|
|
|
380
711
|
SESSION_FIVE="$TMPDIR/session-five.jsonl"
|
|
381
|
-
DISCUSSION_FIVE=$'I do not want to rewrite the parser. The safer path is to let /cook analyze the discussion first, keep the
|
|
382
|
-
ANALYST_OUTPUT_FIVE='{"mission":"Use a proposal analyst to summarize natural discussion before /cook writes canonical state.","scope":["Keep
|
|
712
|
+
DISCUSSION_FIVE=$'I do not want to rewrite the parser. The safer path is to let /cook analyze the discussion first, keep the discussion-derived mission anchored once it is clear, and ignore stale scope that drifted in from earlier turns. We should still prove it with a regression test before writing canonical state.'
|
|
713
|
+
ANALYST_OUTPUT_FIVE='{"mission":"Use a proposal analyst to summarize natural discussion before /cook writes canonical state.","scope":["Keep the discussion-derived mission anchored once it is clear.","Drop stale scope from earlier turns."],"constraints":["Do not rewrite the parser."],"acceptance":["Add a regression test."],"confidence":0.91,"possible_noise":["old unrelated scope"]}'
|
|
383
714
|
write_session "$SESSION_FIVE" "$ROOT" "$DISCUSSION_FIVE"
|
|
384
715
|
|
|
385
716
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
@@ -419,7 +750,7 @@ assert continuation_reason.startswith('User refocused workflow via /cook:'), 'co
|
|
|
419
750
|
assert 'task_type=completion-workflow' in continuation_reason, 'analyst-derived restart should persist the selected task_type'
|
|
420
751
|
assert 'evaluation_profile=completion-rubric-v1' in continuation_reason, 'analyst-derived restart should persist the selected evaluation_profile'
|
|
421
752
|
assert 'critique outcome=accepted critique=none' in continuation_reason, 'analyst-derived restart should persist that no critique notes were accepted'
|
|
422
|
-
assert 'Keep
|
|
753
|
+
assert 'Keep the discussion-derived mission anchored once it is clear.' in continuation_reason, 'analyst-derived scope should be preserved'
|
|
423
754
|
PY
|
|
424
755
|
|
|
425
756
|
# Custom confirmation UI: start should render proposal content separately from approval-only Start/Cancel actions.
|