@linimin/pi-letscook 0.1.45 → 0.1.47
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 +10 -1
- package/README.md +13 -13
- package/extensions/completion/driver.ts +673 -0
- package/extensions/completion/index.ts +468 -3488
- package/extensions/completion/policy-guards.ts +110 -0
- package/extensions/completion/prompt-surfaces.ts +536 -0
- package/extensions/completion/proposal.ts +1217 -0
- package/extensions/completion/role-runner.ts +460 -0
- package/extensions/completion/state-store.ts +458 -0
- package/extensions/completion/status-surface.ts +516 -0
- package/extensions/completion/transcription.ts +77 -0
- package/extensions/completion/types.ts +87 -0
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +5 -4
- package/scripts/canonical-evidence-artifact-test.sh +4 -4
- package/scripts/context-proposal-test.sh +295 -23
- package/scripts/legacy-cleanup-test.sh +107 -0
- package/scripts/observability-status-test.sh +39 -0
- package/scripts/refocus-test.sh +6 -6
- package/scripts/release-check.sh +17 -16
- package/scripts/role-runner-contract-test.sh +44 -0
- package/scripts/rubric-contract-test.sh +8 -6
- package/scripts/smoke-test.sh +5 -5
|
@@ -75,11 +75,12 @@ assertIncludes('README.md', 'Deterministic active-slice contract regression now
|
|
|
75
75
|
assertIncludes('README.md', 'includes deterministic active-slice contract coverage plus observability coverage');
|
|
76
76
|
assertIncludes('scripts/release-check.sh', 'bash ./scripts/active-slice-contract-test.sh');
|
|
77
77
|
assertIncludes('.agent/verify_completion_stop.sh', 'npm run release-check >/dev/null');
|
|
78
|
-
assertIncludes('extensions/completion/
|
|
79
|
-
assertIncludes('extensions/completion/
|
|
80
|
-
assertIncludes('extensions/completion/
|
|
78
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function buildVerifyControlPlaneScript(');
|
|
79
|
+
assertIncludes('extensions/completion/state-store.ts', 'return fs.readFileSync(trackedScriptPath, "utf8");');
|
|
80
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.');
|
|
81
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Active slice contract drift: ${args.activeContractDrift}');
|
|
81
82
|
assertIncludes('extensions/completion/index.ts', 'Canonical active-slice contract drift is currently: ${activeContractDrift}');
|
|
82
|
-
assertIncludes('extensions/completion/
|
|
83
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`active_slice_contract_drift_fields: ${args.activeSliceContractDrift}`');
|
|
83
84
|
assertIncludes('extensions/completion/index.ts', 'treat .agent/active-slice.json as the canonical implementation contract');
|
|
84
85
|
assertIncludes('.agent/verify_completion_control_plane.sh', "const planMirrorFields = ['locked_notes', 'must_fix_findings', 'implementation_surfaces', 'verification_commands', 'basis_commit', 'remaining_contract_ids_before', 'release_blocker_count_before', 'high_value_gap_count_before'];");
|
|
85
86
|
assertIncludes('.agent/verify_completion_control_plane.sh', 'slice_id must match a slice in .agent/plan.json when status carries an exact handoff');
|
|
@@ -96,11 +96,11 @@ assertSectionIncludes('skills/completion-protocol/references/completion.md', '##
|
|
|
96
96
|
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Canonical Inputs', '- `.agent/verification-evidence.json`');
|
|
97
97
|
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '- `.agent/verification-evidence.json`');
|
|
98
98
|
assertSectionIncludes('skills/completion-protocol/references/completion.md', '## Compaction And Recovery', '`completion-implementer` must also re-read canonical `.agent/state.json`, `.agent/plan.json`, `.agent/active-slice.json`, and `.agent/verification-evidence.json` before resuming work.');
|
|
99
|
-
assertIncludes('extensions/completion/
|
|
100
|
-
assertIncludes('extensions/completion/
|
|
99
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Verification evidence artifact: ${args.evidence.path} (${args.evidence.status})');
|
|
100
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Verification evidence summary: ${args.evidence.summary}');
|
|
101
101
|
assertIncludes('extensions/completion/index.ts', 'Canonical verification evidence artifact is currently: ${evidence.path} (${evidence.status})');
|
|
102
|
-
assertIncludes('extensions/completion/
|
|
103
|
-
assertIncludes('extensions/completion/
|
|
102
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`- verification_evidence_path: ${evidence.path}`');
|
|
103
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`- verification_evidence_summary: ${evidence.summary}`');
|
|
104
104
|
assertIncludes('extensions/completion/index.ts', 'Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.');
|
|
105
105
|
assertIncludes('scripts/release-check.sh', 'bash .agent/verify_completion_control_plane.sh');
|
|
106
106
|
assertIncludes('scripts/release-check.sh', 'bash ./scripts/canonical-evidence-artifact-test.sh');
|
|
@@ -158,6 +158,85 @@ PY
|
|
|
158
158
|
|
|
159
159
|
rm -rf .agent
|
|
160
160
|
|
|
161
|
+
# No workflow yet: when multiple structured discussions exist, bare /cook should prioritize the latest
|
|
162
|
+
# concrete implementation mission instead of failing closed on older structured context.
|
|
163
|
+
SESSION_ZERO_LATEST_WINDOW="$TMPDIR/session-zero-latest-window.jsonl"
|
|
164
|
+
DISCUSSION_ZERO_LATEST_OLDER=$'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- Keep observability regression coverage truthful.'
|
|
165
|
+
DISCUSSION_ZERO_LATEST_NEWER=$'Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic.\nConstraints:\n- Do not refactor the broader auth flow.\nAcceptance:\n- Add a regression test for returning to the requested page.'
|
|
166
|
+
DISCUSSION_SNAPSHOT_ZERO_LATEST_WINDOW="$TMPDIR/context-proposal-latest-window.json"
|
|
167
|
+
python3 - "$SESSION_ZERO_LATEST_WINDOW" "$ROOT" "$DISCUSSION_ZERO_LATEST_OLDER" "$DISCUSSION_ZERO_LATEST_NEWER" <<'PY'
|
|
168
|
+
import json
|
|
169
|
+
import sys
|
|
170
|
+
from pathlib import Path
|
|
171
|
+
|
|
172
|
+
session_path = Path(sys.argv[1])
|
|
173
|
+
cwd = sys.argv[2]
|
|
174
|
+
older = sys.argv[3]
|
|
175
|
+
newer = sys.argv[4]
|
|
176
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
177
|
+
entries = [
|
|
178
|
+
{
|
|
179
|
+
"type": "session",
|
|
180
|
+
"version": 3,
|
|
181
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
182
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
183
|
+
"cwd": cwd,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"type": "message",
|
|
187
|
+
"id": "a1b2c3d4",
|
|
188
|
+
"parentId": None,
|
|
189
|
+
"timestamp": "2026-01-01T00:00:01.000Z",
|
|
190
|
+
"message": {
|
|
191
|
+
"role": "user",
|
|
192
|
+
"content": older,
|
|
193
|
+
"timestamp": 1767225601000,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"type": "message",
|
|
198
|
+
"id": "b2c3d4e5",
|
|
199
|
+
"parentId": "a1b2c3d4",
|
|
200
|
+
"timestamp": "2026-01-01T00:00:02.000Z",
|
|
201
|
+
"message": {
|
|
202
|
+
"role": "user",
|
|
203
|
+
"content": newer,
|
|
204
|
+
"timestamp": 1767225602000,
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
]
|
|
208
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
209
|
+
for entry in entries:
|
|
210
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
211
|
+
PY
|
|
212
|
+
|
|
213
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
214
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
215
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_LATEST_WINDOW" \
|
|
216
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
217
|
+
pi --session "$SESSION_ZERO_LATEST_WINDOW" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-latest-window.out" 2>"$TMPDIR/pi-completion-context-proposal-latest-window.err"
|
|
218
|
+
|
|
219
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_LATEST_WINDOW" <<'PY'
|
|
220
|
+
import json
|
|
221
|
+
import sys
|
|
222
|
+
from pathlib import Path
|
|
223
|
+
|
|
224
|
+
mission = 'Fix login redirect callback behavior.'
|
|
225
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
226
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
227
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
228
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
229
|
+
|
|
230
|
+
assert proposal['mission'] == mission, 'latest structured discussion should win over older structured context'
|
|
231
|
+
assert proposal['scope'] == ['Update the callback redirect decision logic.'], 'latest structured discussion should preserve the latest scope only'
|
|
232
|
+
assert proposal['analysis']['suppressedNegatedTopics'] == ['Do not refactor the broader auth flow.'], 'latest structured discussion should preserve negated implementation topics separately from the mission'
|
|
233
|
+
assert state['mission_anchor'] == mission, 'latest structured discussion should initialize state.json with the latest mission'
|
|
234
|
+
assert plan['mission_anchor'] == mission, 'latest structured discussion should initialize plan.json with the latest mission'
|
|
235
|
+
assert active['mission_anchor'] == mission, 'latest structured discussion should initialize active-slice.json with the latest mission'
|
|
236
|
+
PY
|
|
237
|
+
|
|
238
|
+
rm -rf .agent
|
|
239
|
+
|
|
161
240
|
# No workflow yet: bare /cook should fail closed when a required structured section is missing and analyst output is unavailable.
|
|
162
241
|
SESSION_ZERO_MISSING="$TMPDIR/session-zero-missing-section.jsonl"
|
|
163
242
|
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.'
|
|
@@ -182,26 +261,35 @@ assert '/cook failed closed' in output, 'missing-section structured discussion s
|
|
|
182
261
|
assert 'Mission/Scope/Constraints/Acceptance' in output, 'missing-section structured discussion should explain the strict fallback requirement'
|
|
183
262
|
PY
|
|
184
263
|
|
|
185
|
-
# No workflow yet:
|
|
264
|
+
# No workflow yet: when one structured discussion message contains multiple complete mission blocks,
|
|
265
|
+
# bare /cook should prioritize the latest block and preserve earlier blocks as alternate missions.
|
|
186
266
|
SESSION_ZERO_AMBIG="$TMPDIR/session-zero-ambiguous.jsonl"
|
|
187
|
-
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.'
|
|
267
|
+
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.\nConstraints:\n- Do not modify the completion widget.\nAcceptance:\n- Land the unrelated overhaul changes only.'
|
|
268
|
+
DISCUSSION_SNAPSHOT_ZERO_AMBIG="$TMPDIR/context-proposal-ambiguous-latest-block.json"
|
|
188
269
|
write_session "$SESSION_ZERO_AMBIG" "$ROOT" "$DISCUSSION_ZERO_AMBIG"
|
|
189
270
|
|
|
190
271
|
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
191
272
|
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
273
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_ZERO_AMBIG" \
|
|
192
274
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
193
275
|
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"
|
|
194
276
|
|
|
195
|
-
python3 - "$
|
|
277
|
+
python3 - "$DISCUSSION_SNAPSHOT_ZERO_AMBIG" <<'PY'
|
|
278
|
+
import json
|
|
196
279
|
import sys
|
|
197
280
|
from pathlib import Path
|
|
198
281
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
282
|
+
mission = 'Ship an unrelated widget overhaul.'
|
|
283
|
+
proposal = json.loads(Path(sys.argv[1]).read_text())
|
|
284
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
285
|
+
|
|
286
|
+
assert proposal['mission'] == mission, 'latest complete mission block should win inside a single structured discussion message'
|
|
287
|
+
assert proposal['analysis']['alternateMissions'] == ['Remove the completion status line while keeping the completion widget.'], 'earlier complete mission blocks should be preserved as alternate missions'
|
|
288
|
+
assert state['mission_anchor'] == mission, 'latest complete mission block should initialize canonical mission state'
|
|
203
289
|
PY
|
|
204
290
|
|
|
291
|
+
rm -rf .agent
|
|
292
|
+
|
|
205
293
|
# No workflow yet: bare /cook structured fallback should normalize placeholder planning phrasing
|
|
206
294
|
# into the concrete implementation mission when scope/acceptance clearly describe shipped work.
|
|
207
295
|
SESSION_ZERO_NORMALIZED="$TMPDIR/session-zero-normalized.jsonl"
|
|
@@ -699,6 +787,100 @@ assert plan['mission_anchor'] == mission, 'summary-only active bare /cook should
|
|
|
699
787
|
assert active['mission_anchor'] == mission, 'summary-only active bare /cook should keep active-slice.json unchanged'
|
|
700
788
|
PY
|
|
701
789
|
|
|
790
|
+
# Active workflow: when recent discussion suggests a different implementation goal but the proposal is
|
|
791
|
+
# still incomplete, bare /cook should surface the chooser instead of silently resuming the current workflow.
|
|
792
|
+
SESSION_ONE_AMBIGUOUS_CHOOSER="$TMPDIR/session-one-ambiguous-chooser.jsonl"
|
|
793
|
+
DISCUSSION_ONE_AMBIGUOUS_CHOOSER=$'Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic for the current auth flow.'
|
|
794
|
+
AMBIGUOUS_ROUTING_ONE="$TMPDIR/active-ambiguous-routing.json"
|
|
795
|
+
AMBIGUOUS_CHOOSER_ONE="$TMPDIR/active-ambiguous-chooser.json"
|
|
796
|
+
AMBIGUOUS_RESUME_PROMPT_ONE="$TMPDIR/unexpected-active-ambiguous-resume.txt"
|
|
797
|
+
AMBIGUOUS_PROPOSAL_ONE="$TMPDIR/unexpected-active-ambiguous-proposal.json"
|
|
798
|
+
write_session "$SESSION_ONE_AMBIGUOUS_CHOOSER" "$ROOT" "$DISCUSSION_ONE_AMBIGUOUS_CHOOSER"
|
|
799
|
+
|
|
800
|
+
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=cancel \
|
|
801
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT='{"mission":"Fix login redirect callback behavior.","scope":["Update the callback redirect decision logic for the current auth flow."],"constraints":[],"acceptance":[],"task_type":"completion-workflow","evaluation_profile":"completion-rubric-v1","confidence":0.72,"possible_noise":["older completion widget cleanup"]}' \
|
|
802
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$AMBIGUOUS_ROUTING_ONE" \
|
|
803
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$AMBIGUOUS_CHOOSER_ONE" \
|
|
804
|
+
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$AMBIGUOUS_RESUME_PROMPT_ONE" \
|
|
805
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$AMBIGUOUS_PROPOSAL_ONE" \
|
|
806
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
807
|
+
pi --session "$SESSION_ONE_AMBIGUOUS_CHOOSER" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.out" 2>"$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.err"
|
|
808
|
+
|
|
809
|
+
python3 - "$AMBIGUOUS_ROUTING_ONE" "$AMBIGUOUS_CHOOSER_ONE" "$AMBIGUOUS_RESUME_PROMPT_ONE" "$AMBIGUOUS_PROPOSAL_ONE" "$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.out" "$TMPDIR/pi-completion-context-proposal-active-ambiguous-chooser.err" <<'PY'
|
|
810
|
+
import json
|
|
811
|
+
import sys
|
|
812
|
+
from pathlib import Path
|
|
813
|
+
|
|
814
|
+
mission = 'Remove the completion status line while keeping the completion widget.'
|
|
815
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
816
|
+
chooser = json.loads(Path(sys.argv[2]).read_text())
|
|
817
|
+
resume_path = Path(sys.argv[3])
|
|
818
|
+
proposal_path = Path(sys.argv[4])
|
|
819
|
+
output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
|
|
820
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
821
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
822
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
823
|
+
|
|
824
|
+
assert routing['mode'] == 'bare', 'ambiguous active bare /cook should snapshot bare routing mode'
|
|
825
|
+
assert routing['action'] == 'unclear', 'incomplete replacement discussion should stay ambiguous until the chooser resolves it'
|
|
826
|
+
assert routing['reason'] == 'ambiguous_discussion', 'incomplete replacement discussion should record the ambiguous-discussion reason'
|
|
827
|
+
assert routing['currentMissionAnchor'] == mission, 'ambiguous chooser routing should preserve the current mission anchor'
|
|
828
|
+
assert routing['proposedMissionAnchor'] == 'Fix login redirect callback behavior.', 'ambiguous chooser routing should expose the latest inferred mission'
|
|
829
|
+
assert chooser['title'].startswith('Existing completion workflow found'), 'ambiguous active bare /cook should still open the existing-workflow chooser'
|
|
830
|
+
assert chooser['choices'][0].startswith('Continue current workflow'), 'ambiguous chooser should keep the continue option'
|
|
831
|
+
assert chooser['choices'][1].startswith('Start new workflow from recent discussion'), 'ambiguous chooser should offer the recent-discussion replacement option'
|
|
832
|
+
assert not resume_path.exists(), 'ambiguous active bare /cook should not silently queue a resume prompt before the chooser resolves it'
|
|
833
|
+
assert not proposal_path.exists(), 'ambiguous chooser cancel should not open the final proposal confirmation'
|
|
834
|
+
assert 'Discuss changes in the main chat and rerun /cook.' in output, 'ambiguous chooser cancel should redirect users back to the main chat and rerun /cook'
|
|
835
|
+
assert state['mission_anchor'] == mission, 'ambiguous chooser cancel should keep state.json unchanged'
|
|
836
|
+
assert plan['mission_anchor'] == mission, 'ambiguous chooser cancel should keep plan.json unchanged'
|
|
837
|
+
assert active['mission_anchor'] == mission, 'ambiguous chooser cancel should keep active-slice.json unchanged'
|
|
838
|
+
PY
|
|
839
|
+
|
|
840
|
+
# Active workflow: when recent discussion contains multiple complete replacement missions, bare /cook should
|
|
841
|
+
# surface all candidates and allow the chooser to select a non-primary alternate mission.
|
|
842
|
+
SESSION_ONE_MULTI_CANDIDATE="$TMPDIR/session-one-multi-candidate.jsonl"
|
|
843
|
+
DISCUSSION_ONE_MULTI_CANDIDATE=$'Mission: Fix login redirect callback behavior.\nScope:\n- Update the callback redirect decision logic for the current auth flow.\nConstraints:\n- Do not refactor the broader auth flow.\nAcceptance:\n- Add a regression test for returning to the requested page.\nMission: Add logout redirect regression coverage.\nScope:\n- Add coverage for logout redirect behavior.\nConstraints:\n- Do not change login redirect behavior in this pass.\nAcceptance:\n- Land a dedicated logout redirect regression test.'
|
|
844
|
+
MULTI_ROUTING_ONE="$TMPDIR/active-multi-routing.json"
|
|
845
|
+
MULTI_CHOOSER_ONE="$TMPDIR/active-multi-chooser.json"
|
|
846
|
+
MULTI_PROPOSAL_ONE="$TMPDIR/active-multi-proposal.json"
|
|
847
|
+
write_session "$SESSION_ONE_MULTI_CANDIDATE" "$ROOT" "$DISCUSSION_ONE_MULTI_CANDIDATE"
|
|
848
|
+
|
|
849
|
+
PI_COMPLETION_EXISTING_WORKFLOW_MISSION='Fix login redirect callback behavior.' \
|
|
850
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
851
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
852
|
+
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$MULTI_ROUTING_ONE" \
|
|
853
|
+
PI_COMPLETION_TEST_EXISTING_WORKFLOW_CHOOSER_PATH="$MULTI_CHOOSER_ONE" \
|
|
854
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$MULTI_PROPOSAL_ONE" \
|
|
855
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
856
|
+
pi --session "$SESSION_ONE_MULTI_CANDIDATE" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-active-multi.out" 2>"$TMPDIR/pi-completion-context-proposal-active-multi.err"
|
|
857
|
+
|
|
858
|
+
python3 - "$MULTI_ROUTING_ONE" "$MULTI_CHOOSER_ONE" "$MULTI_PROPOSAL_ONE" <<'PY'
|
|
859
|
+
import json
|
|
860
|
+
import sys
|
|
861
|
+
from pathlib import Path
|
|
862
|
+
|
|
863
|
+
selected = 'Fix login redirect callback behavior.'
|
|
864
|
+
routing = json.loads(Path(sys.argv[1]).read_text())
|
|
865
|
+
chooser = json.loads(Path(sys.argv[2]).read_text())
|
|
866
|
+
proposal = json.loads(Path(sys.argv[3]).read_text())
|
|
867
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
868
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
869
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
870
|
+
|
|
871
|
+
assert routing['action'] == 'unclear', 'multi-candidate replacement discussion should stay ambiguous until the chooser selects one mission'
|
|
872
|
+
assert routing['reason'] == 'ambiguous_discussion', 'multi-candidate replacement discussion should record ambiguous-discussion routing'
|
|
873
|
+
assert routing['alternateMissions'] == ['Fix login redirect callback behavior.'], 'routing snapshot should preserve alternate candidate missions'
|
|
874
|
+
assert chooser['candidateMissions'] == ['Add logout redirect regression coverage.', 'Fix login redirect callback behavior.'], 'chooser snapshot should list the primary and alternate candidate missions'
|
|
875
|
+
assert len(chooser['choices']) == 4, 'chooser should expose continue, primary, alternate, and cancel choices'
|
|
876
|
+
assert 'Scope\n- Add coverage for logout redirect behavior.' in chooser['choices'][1], 'primary chooser option should summarize the candidate scope'
|
|
877
|
+
assert 'Acceptance\n- Add a regression test for returning to the requested page.' in chooser['choices'][2], 'alternate chooser option should summarize the candidate acceptance'
|
|
878
|
+
assert proposal['mission'] == selected, 'selected alternate mission should flow into the final proposal confirmation'
|
|
879
|
+
assert state['mission_anchor'] == selected, 'selected alternate mission should rewrite state.json after approval'
|
|
880
|
+
assert plan['mission_anchor'] == selected, 'selected alternate mission should rewrite plan.json after approval'
|
|
881
|
+
assert active['mission_anchor'] == selected, 'selected alternate mission should rewrite active-slice.json after approval'
|
|
882
|
+
PY
|
|
883
|
+
|
|
702
884
|
# Active workflow: bare /cook with a placeholder planning mission should still route through the existing
|
|
703
885
|
# refocus chooser and final Start/Cancel gate before canonical state is rewritten.
|
|
704
886
|
SESSION_ONE_REFOCUS_NORMALIZED="$TMPDIR/session-one-refocus-normalized.jsonl"
|
|
@@ -747,10 +929,93 @@ assert plan['mission_anchor'] == mission, 'active bare /cook refocus should rewr
|
|
|
747
929
|
assert active['mission_anchor'] == mission, 'active bare /cook refocus should rewrite active-slice.json only after approval'
|
|
748
930
|
PY
|
|
749
931
|
|
|
750
|
-
# Completed workflow: bare /cook should
|
|
751
|
-
#
|
|
932
|
+
# Completed workflow: bare /cook should suppress proposals that simply restate the completed mission
|
|
933
|
+
# without a clear reopen or next-round signal.
|
|
752
934
|
mark_done
|
|
753
935
|
|
|
936
|
+
SESSION_TWO_COMPLETED_SUPPRESS="$TMPDIR/session-two-completed-suppress.jsonl"
|
|
937
|
+
CURRENT_DONE_MISSION="$(python3 - <<'PY'
|
|
938
|
+
import json
|
|
939
|
+
from pathlib import Path
|
|
940
|
+
print(json.loads(Path('.agent/state.json').read_text())['mission_anchor'])
|
|
941
|
+
PY
|
|
942
|
+
)"
|
|
943
|
+
DISCUSSION_TWO_COMPLETED_SUPPRESS="Mission: ${CURRENT_DONE_MISSION}
|
|
944
|
+
Scope:
|
|
945
|
+
- Keep the current completed mission exactly as-is.
|
|
946
|
+
Constraints:
|
|
947
|
+
- Do not start a different workflow from this discussion.
|
|
948
|
+
Acceptance:
|
|
949
|
+
- Keep the finished mission closed and unchanged."
|
|
950
|
+
DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS="$TMPDIR/context-proposal-next-round-completed-suppress.json"
|
|
951
|
+
write_session "$SESSION_TWO_COMPLETED_SUPPRESS" "$ROOT" "$DISCUSSION_TWO_COMPLETED_SUPPRESS"
|
|
952
|
+
|
|
953
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
954
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
955
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS" \
|
|
956
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
957
|
+
pi --session "$SESSION_TWO_COMPLETED_SUPPRESS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.err"
|
|
958
|
+
|
|
959
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.out" "$TMPDIR/pi-completion-context-proposal-next-round-completed-suppress.err" "$DISCUSSION_SNAPSHOT_TWO_COMPLETED_SUPPRESS" "$CURRENT_DONE_MISSION" <<'PY'
|
|
960
|
+
import json
|
|
961
|
+
import sys
|
|
962
|
+
from pathlib import Path
|
|
963
|
+
|
|
964
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
965
|
+
snapshot = Path(sys.argv[3])
|
|
966
|
+
expected = sys.argv[4]
|
|
967
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
968
|
+
|
|
969
|
+
assert state['mission_anchor'] == expected, 'completed-topic suppression should keep the done workflow mission anchor unchanged'
|
|
970
|
+
assert state['continuation_policy'] == 'done', 'completed-topic suppression should keep the workflow closed'
|
|
971
|
+
assert not snapshot.exists(), 'completed-topic suppression should not emit a proposal snapshot when the latest discussion only repeats finished work'
|
|
972
|
+
assert '/cook failed closed' in output, 'completed-topic suppression should fail closed instead of reopening the finished mission'
|
|
973
|
+
PY
|
|
974
|
+
|
|
975
|
+
# Completed workflow: bare /cook should also suppress proposals that merely restate canonical
|
|
976
|
+
# verification evidence for already verified work.
|
|
977
|
+
python3 - <<'PY'
|
|
978
|
+
import json
|
|
979
|
+
from pathlib import Path
|
|
980
|
+
|
|
981
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
982
|
+
state['latest_verified_slice'] = 'verified-logout-redirect'
|
|
983
|
+
Path('.agent/state.json').write_text(json.dumps(state, indent=2) + '\n')
|
|
984
|
+
|
|
985
|
+
evidence = json.loads(Path('.agent/verification-evidence.json').read_text())
|
|
986
|
+
evidence.update({
|
|
987
|
+
'subject_type': 'selected_slice',
|
|
988
|
+
'slice_id': 'verified-logout-redirect',
|
|
989
|
+
'goal': 'Add logout redirect regression coverage.',
|
|
990
|
+
'summary': 'Verified logout redirect regression coverage already matches the selected slice and current HEAD.',
|
|
991
|
+
'outcome': 'pass',
|
|
992
|
+
})
|
|
993
|
+
Path('.agent/verification-evidence.json').write_text(json.dumps(evidence, indent=2) + '\n')
|
|
994
|
+
PY
|
|
995
|
+
|
|
996
|
+
SESSION_TWO_VERIFIED_SUPPRESS="$TMPDIR/session-two-verified-suppress.jsonl"
|
|
997
|
+
DISCUSSION_TWO_VERIFIED_SUPPRESS=$'Mission: Add logout redirect regression coverage.\nScope:\n- Add coverage for logout redirect behavior.\nConstraints:\n- Do not change the verified logout redirect work.\nAcceptance:\n- Keep the verified logout redirect regression coverage unchanged.'
|
|
998
|
+
DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS="$TMPDIR/context-proposal-next-round-verified-suppress.json"
|
|
999
|
+
write_session "$SESSION_TWO_VERIFIED_SUPPRESS" "$ROOT" "$DISCUSSION_TWO_VERIFIED_SUPPRESS"
|
|
1000
|
+
|
|
1001
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
1002
|
+
PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
|
|
1003
|
+
PI_COMPLETION_TEST_CONTEXT_PROPOSAL_PATH="$DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS" \
|
|
1004
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
1005
|
+
pi --session "$SESSION_TWO_VERIFIED_SUPPRESS" -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.out" 2>"$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.err"
|
|
1006
|
+
|
|
1007
|
+
python3 - "$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.out" "$TMPDIR/pi-completion-context-proposal-next-round-verified-suppress.err" "$DISCUSSION_SNAPSHOT_TWO_VERIFIED_SUPPRESS" <<'PY'
|
|
1008
|
+
import sys
|
|
1009
|
+
from pathlib import Path
|
|
1010
|
+
|
|
1011
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
1012
|
+
snapshot = Path(sys.argv[3])
|
|
1013
|
+
assert not snapshot.exists(), 'verification-evidence overlap suppression should not emit a proposal snapshot for already verified work'
|
|
1014
|
+
assert '/cook failed closed' in output, 'verification-evidence overlap suppression should fail closed when the latest discussion only repeats verified work'
|
|
1015
|
+
PY
|
|
1016
|
+
|
|
1017
|
+
# Completed workflow: bare /cook should normalize placeholder planning phrasing for the next workflow
|
|
1018
|
+
# round too, not only for fresh startup.
|
|
754
1019
|
SESSION_TWO_NORMALIZED="$TMPDIR/session-two-normalized.jsonl"
|
|
755
1020
|
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.'
|
|
756
1021
|
DISCUSSION_SNAPSHOT_TWO_NORMALIZED="$TMPDIR/context-proposal-next-round-normalized.json"
|
|
@@ -836,8 +1101,8 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
|
|
|
836
1101
|
assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
|
|
837
1102
|
PY
|
|
838
1103
|
|
|
839
|
-
# Active workflow:
|
|
840
|
-
# and still leave canonical state unchanged
|
|
1104
|
+
# Active workflow: /cook <text> should be rejected before routing or proposal confirmation
|
|
1105
|
+
# and still leave canonical state unchanged.
|
|
841
1106
|
ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
|
|
842
1107
|
ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
|
|
843
1108
|
ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
|
|
@@ -886,15 +1151,16 @@ tracked = [
|
|
|
886
1151
|
Path('.agent/verification-evidence.json'),
|
|
887
1152
|
]
|
|
888
1153
|
|
|
889
|
-
assert
|
|
890
|
-
assert
|
|
891
|
-
assert
|
|
1154
|
+
assert '/cook only supports the bare /cook entrypoint.' in output, 'active /cook <text> rejection should explain the bare-only contract'
|
|
1155
|
+
assert not routing.exists(), 'active /cook <text> rejection should not run active-workflow routing'
|
|
1156
|
+
assert not proposal.exists(), 'active /cook <text> rejection should not open proposal confirmation'
|
|
1157
|
+
assert not chooser.exists(), 'active /cook <text> rejection should not open the existing-workflow chooser'
|
|
892
1158
|
after = {path.name: path.read_text() for path in tracked}
|
|
893
|
-
assert before == after, 'active
|
|
1159
|
+
assert before == after, 'active /cook <text> rejection should leave canonical files unchanged'
|
|
894
1160
|
PY
|
|
895
1161
|
|
|
896
|
-
# Completed workflow:
|
|
897
|
-
# canonical state unchanged
|
|
1162
|
+
# Completed workflow: /cook <text> should be rejected before any next-round proposal is derived
|
|
1163
|
+
# and still leave canonical state unchanged.
|
|
898
1164
|
mark_done
|
|
899
1165
|
|
|
900
1166
|
DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
|
|
@@ -944,13 +1210,14 @@ tracked = [
|
|
|
944
1210
|
Path('.agent/verification-evidence.json'),
|
|
945
1211
|
]
|
|
946
1212
|
state_before = json.loads(before['state.json'])
|
|
947
|
-
assert state_before['current_phase'] == 'done', 'done
|
|
948
|
-
assert state_before['project_done'] is True, 'done
|
|
949
|
-
assert
|
|
950
|
-
assert
|
|
951
|
-
assert not
|
|
1213
|
+
assert state_before['current_phase'] == 'done', 'done /cook <text> rejection should start from a completed workflow'
|
|
1214
|
+
assert state_before['project_done'] is True, 'done /cook <text> rejection should start from project_done=true'
|
|
1215
|
+
assert '/cook only supports the bare /cook entrypoint.' in output, 'done /cook <text> rejection should explain the bare-only contract'
|
|
1216
|
+
assert not routing.exists(), 'done /cook <text> rejection should not run active/done workflow routing'
|
|
1217
|
+
assert not proposal.exists(), 'done /cook <text> rejection should not open next-round proposal confirmation'
|
|
1218
|
+
assert not chooser.exists(), 'done /cook <text> rejection should not open the chooser flow'
|
|
952
1219
|
after = {path.name: path.read_text() for path in tracked}
|
|
953
|
-
assert before == after, 'done
|
|
1220
|
+
assert before == after, 'done /cook <text> rejection should leave canonical files unchanged'
|
|
954
1221
|
PY
|
|
955
1222
|
|
|
956
1223
|
# Completed workflow again: /cook with no goal should be able to use model-assisted
|
|
@@ -1086,4 +1353,9 @@ assert 'Discuss changes in the main chat and rerun /cook.' in output, 'cancel co
|
|
|
1086
1353
|
assert not Path('.agent').exists(), 'cancel action should not write canonical workflow state'
|
|
1087
1354
|
PY
|
|
1088
1355
|
|
|
1356
|
+
grep -q 'export async function deriveCookContextProposalFromRecentDiscussion' "$PKG_ROOT/extensions/completion/proposal.ts"
|
|
1357
|
+
grep -q 'export function parseContextProposalAnalystOutput' "$PKG_ROOT/extensions/completion/proposal.ts"
|
|
1358
|
+
grep -q 'export function buildContextProposalConfirmationLayout' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
|
|
1359
|
+
grep -q 'export function buildEvaluationRoleContextLines' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
|
|
1360
|
+
|
|
1089
1361
|
echo "context proposal test passed: $ROOT"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
cd "$ROOT"
|
|
6
|
+
|
|
7
|
+
node <<'NODE'
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
|
|
10
|
+
const read = (file) => fs.readFileSync(file, 'utf8');
|
|
11
|
+
const assertIncludes = (file, snippet) => {
|
|
12
|
+
const text = read(file);
|
|
13
|
+
if (!text.includes(snippet)) {
|
|
14
|
+
throw new Error(`${file} is missing expected legacy-cleanup ownership text: ${snippet}`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const assertNotIncludes = (file, snippet) => {
|
|
18
|
+
const text = read(file);
|
|
19
|
+
if (text.includes(snippet)) {
|
|
20
|
+
throw new Error(`${file} still contains stale monolith ownership text: ${snippet}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
assertIncludes('extensions/completion/state-store.ts', 'export async function scaffoldCompletionFiles(');
|
|
25
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function buildAgentReadme(');
|
|
26
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function buildVerifyStopScript(');
|
|
27
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function buildVerifyControlPlaneScript(');
|
|
28
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function currentTaskType(');
|
|
29
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function currentEvaluationProfile(');
|
|
30
|
+
|
|
31
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function nowMs(');
|
|
32
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function formatElapsed(');
|
|
33
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function createLiveRoleActivity(');
|
|
34
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function cloneLiveRoleActivity(');
|
|
35
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function applyLiveRoleEvent(');
|
|
36
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function pushRecentActivity(');
|
|
37
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function truncateInline(');
|
|
38
|
+
|
|
39
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function normalizeMissionAnchorText(');
|
|
40
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function isWeakMissionAnchor(');
|
|
41
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function deriveMissionAnchor(');
|
|
42
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function assessMissionAnchor(');
|
|
43
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function stripCodeBlocks(');
|
|
44
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function missionAnchorsStrictlyEquivalent(');
|
|
45
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function missionAnchorsLikelyEquivalent(');
|
|
46
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function collectRecentDiscussionEntries(');
|
|
47
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function serializeRecentDiscussionEntries(');
|
|
48
|
+
assertIncludes('extensions/completion/proposal.ts', 'export function extractJsonObjectFromText(');
|
|
49
|
+
|
|
50
|
+
assertIncludes('extensions/completion/role-runner.ts', 'export async function analyzeContextProposalWithAgent(');
|
|
51
|
+
assertIncludes('extensions/completion/role-runner.ts', 'class StartupAnalystOverlay extends Container');
|
|
52
|
+
assertIncludes('extensions/completion/role-runner.ts', 'async function runContextProposalAnalystSubprocess(');
|
|
53
|
+
|
|
54
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'export function buildSystemReminder(');
|
|
55
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'export function buildResumeCapsule(');
|
|
56
|
+
|
|
57
|
+
assertIncludes('extensions/completion/index.ts', 'scaffoldCompletionFiles as scaffoldCompletionFilesOnDisk');
|
|
58
|
+
assertIncludes('extensions/completion/index.ts', 'return await scaffoldCompletionFilesOnDisk(root, missionAnchor, {');
|
|
59
|
+
assertIncludes('extensions/completion/index.ts', 'applyLiveRoleEvent,');
|
|
60
|
+
assertIncludes('extensions/completion/index.ts', 'cloneLiveRoleActivity,');
|
|
61
|
+
assertIncludes('extensions/completion/index.ts', 'createLiveRoleActivity,');
|
|
62
|
+
assertIncludes('extensions/completion/index.ts', 'formatElapsed,');
|
|
63
|
+
assertIncludes('extensions/completion/index.ts', 'nowMs,');
|
|
64
|
+
|
|
65
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function detectVerifierCommand(');
|
|
66
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildAgentReadme(');
|
|
67
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildMission(');
|
|
68
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildVerifyStopScript(');
|
|
69
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildVerifyControlPlaneScript(');
|
|
70
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function ensureGitignore(');
|
|
71
|
+
assertNotIncludes('extensions/completion/index.ts', 'function currentTaskType(');
|
|
72
|
+
assertNotIncludes('extensions/completion/index.ts', 'function currentEvaluationProfile(');
|
|
73
|
+
assertNotIncludes('extensions/completion/index.ts', 'function formatCount(');
|
|
74
|
+
assertNotIncludes('extensions/completion/index.ts', 'function completionRemainingSummary(');
|
|
75
|
+
assertNotIncludes('extensions/completion/index.ts', 'function envNumber(');
|
|
76
|
+
assertNotIncludes('extensions/completion/index.ts', 'function nowMs(');
|
|
77
|
+
assertNotIncludes('extensions/completion/index.ts', 'type LiveActivitySignal = {');
|
|
78
|
+
assertNotIncludes('extensions/completion/index.ts', 'function cloneLiveRoleActivity(');
|
|
79
|
+
assertNotIncludes('extensions/completion/index.ts', 'function createLiveRoleActivity(');
|
|
80
|
+
assertNotIncludes('extensions/completion/index.ts', 'type RoleMessage = {');
|
|
81
|
+
assertNotIncludes('extensions/completion/index.ts', 'function applyLiveRoleEvent(');
|
|
82
|
+
assertNotIncludes('extensions/completion/index.ts', 'function maybeInjectTestLiveRoleActivity(');
|
|
83
|
+
assertNotIncludes('extensions/completion/index.ts', 'function maybeReplayTestLiveRoleEvents(');
|
|
84
|
+
assertNotIncludes('extensions/completion/index.ts', 'function formatElapsed(');
|
|
85
|
+
assertNotIncludes('extensions/completion/index.ts', 'function truncateInline(');
|
|
86
|
+
assertNotIncludes('extensions/completion/index.ts', 'function formatToolActivity(');
|
|
87
|
+
assertNotIncludes('extensions/completion/index.ts', 'function pushRecentActivity(');
|
|
88
|
+
assertNotIncludes('extensions/completion/index.ts', 'function parseStructuredProgress(');
|
|
89
|
+
assertNotIncludes('extensions/completion/index.ts', 'function lastAssistantText(');
|
|
90
|
+
assertNotIncludes('extensions/completion/index.ts', 'function normalizeMissionAnchorText(');
|
|
91
|
+
assertNotIncludes('extensions/completion/index.ts', 'function isWeakMissionAnchor(');
|
|
92
|
+
assertNotIncludes('extensions/completion/index.ts', 'function assessMissionAnchor(');
|
|
93
|
+
assertNotIncludes('extensions/completion/index.ts', 'function stripCodeBlocks(');
|
|
94
|
+
assertNotIncludes('extensions/completion/index.ts', 'function missionAnchorsStrictlyEquivalent(');
|
|
95
|
+
assertNotIncludes('extensions/completion/index.ts', 'function missionAnchorsLikelyEquivalent(');
|
|
96
|
+
assertNotIncludes('extensions/completion/index.ts', 'function collectRecentDiscussionEntries(');
|
|
97
|
+
assertNotIncludes('extensions/completion/index.ts', 'function serializeRecentDiscussionEntries(');
|
|
98
|
+
assertNotIncludes('extensions/completion/index.ts', 'function extractJsonObjectFromText(');
|
|
99
|
+
assertNotIncludes('extensions/completion/index.ts', 'function contextProposalAnalystModelArg(');
|
|
100
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function runContextProposalAnalystSubprocess(');
|
|
101
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function analyzeContextProposalWithAgent(');
|
|
102
|
+
assertNotIncludes('extensions/completion/index.ts', 'function deriveMissionAnchor(');
|
|
103
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildSystemReminder(');
|
|
104
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildResumeCapsule(');
|
|
105
|
+
NODE
|
|
106
|
+
|
|
107
|
+
echo "legacy cleanup test passed: $ROOT"
|
|
@@ -237,4 +237,43 @@ PI_COMPLETION_TEST_ROLE_EVENT_STREAM_JSON="$LIVE_ROLE_EVENT_STREAM_JSON" \
|
|
|
237
237
|
pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-stalled.out" 2>"$TMPDIR/pi-completion-status-stalled.err"
|
|
238
238
|
assert_status_json "$STALLED_JSON" stalled
|
|
239
239
|
|
|
240
|
+
cd "$PKG_ROOT"
|
|
241
|
+
|
|
242
|
+
node <<'NODE'
|
|
243
|
+
const fs = require('node:fs');
|
|
244
|
+
|
|
245
|
+
const read = (file) => fs.readFileSync(file, 'utf8');
|
|
246
|
+
const assertIncludes = (file, snippet) => {
|
|
247
|
+
const text = read(file);
|
|
248
|
+
if (!text.includes(snippet)) {
|
|
249
|
+
throw new Error(`${file} is missing required status/policy extraction text: ${snippet}`);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const assertNotIncludes = (file, snippet) => {
|
|
253
|
+
const text = read(file);
|
|
254
|
+
if (text.includes(snippet)) {
|
|
255
|
+
throw new Error(`${file} still contains stale inline status/policy text: ${snippet}`);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export async function refreshCompletionStatus(');
|
|
260
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function buildCompletionStatusSurface(');
|
|
261
|
+
assertIncludes('extensions/completion/status-surface.ts', 'export function buildInlineRunningLines(');
|
|
262
|
+
assertIncludes('extensions/completion/policy-guards.ts', 'export function toolCallBlockReason(');
|
|
263
|
+
assertIncludes('extensions/completion/state-store.ts', 'export function completionRootKey(');
|
|
264
|
+
assertIncludes('extensions/completion/index.ts', 'import { toolCallBlockReason } from "./policy-guards";');
|
|
265
|
+
assertIncludes('extensions/completion/index.ts', 'buildInlineRunningLines');
|
|
266
|
+
assertIncludes('extensions/completion/index.ts', 'formatInlineRunningText');
|
|
267
|
+
assertIncludes('extensions/completion/index.ts', 'refreshCompletionStatus');
|
|
268
|
+
assertIncludes('extensions/completion/index.ts', 'const reason = toolCallBlockReason({');
|
|
269
|
+
assertIncludes('extensions/completion/index.ts', 'await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });');
|
|
270
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildCompletionStatusSurface(');
|
|
271
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function writeCompletionStatusProbe(');
|
|
272
|
+
assertNotIncludes('extensions/completion/index.ts', 'function isAllowedControlPlanePath(');
|
|
273
|
+
assertNotIncludes('extensions/completion/index.ts', 'function isMutatingBash(');
|
|
274
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function refreshStatus(');
|
|
275
|
+
assertNotIncludes('extensions/completion/index.ts', 'function buildInlineRunningLines(');
|
|
276
|
+
assertNotIncludes('extensions/completion/index.ts', 'function formatInlineRunningText(');
|
|
277
|
+
NODE
|
|
278
|
+
|
|
240
279
|
echo "observability status test passed: $TMPDIR"
|
package/scripts/refocus-test.sh
CHANGED
|
@@ -131,13 +131,13 @@ tracked = [
|
|
|
131
131
|
Path('.agent/verification-evidence.json'),
|
|
132
132
|
]
|
|
133
133
|
current_state = json.loads(before['state.json'])
|
|
134
|
-
assert current_state['mission_anchor'] == initial_mission, 'active
|
|
135
|
-
assert '
|
|
136
|
-
assert routing.exists(), 'active
|
|
137
|
-
assert not proposal.exists(), 'active
|
|
138
|
-
assert chooser.exists(), 'active
|
|
134
|
+
assert current_state['mission_anchor'] == initial_mission, 'active /cook <text> rejection should start from the current mission anchor'
|
|
135
|
+
assert '/cook only supports the bare /cook entrypoint.' in output, 'active /cook <text> rejection should explain the bare-only contract'
|
|
136
|
+
assert not routing.exists(), 'active /cook <text> rejection should not run active-workflow routing'
|
|
137
|
+
assert not proposal.exists(), 'active /cook <text> rejection should not open final proposal confirmation'
|
|
138
|
+
assert not chooser.exists(), 'active /cook <text> rejection should not open the existing-workflow chooser'
|
|
139
139
|
after = {path.name: path.read_text() for path in tracked}
|
|
140
|
-
assert before == after, 'active
|
|
140
|
+
assert before == after, 'active /cook <text> rejection should leave canonical files unchanged'
|
|
141
141
|
PY
|
|
142
142
|
|
|
143
143
|
SESSION_INITIAL_REFOCUS="$TMPDIR/session-initial-bare-refocus.jsonl"
|