@linimin/pi-letscook 0.1.44 → 0.1.46
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 +6 -1
- package/README.md +9 -10
- package/extensions/completion/driver.ts +615 -0
- package/extensions/completion/index.ts +440 -3486
- package/extensions/completion/policy-guards.ts +110 -0
- package/extensions/completion/prompt-surfaces.ts +512 -0
- package/extensions/completion/proposal.ts +944 -0
- package/extensions/completion/role-runner.ts +455 -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 +10 -17
- package/scripts/context-proposal-test.sh +21 -14
- 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 +29 -6
|
@@ -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');
|
|
@@ -3,7 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
pi() {
|
|
6
|
-
command pi --no-extensions "$@"
|
|
6
|
+
env -u PI_COMPLETION_ROLE command pi --no-extensions "$@"
|
|
7
7
|
}
|
|
8
8
|
TMPDIR="$(mktemp -d)"
|
|
9
9
|
CURRENT_EVIDENCE_BACKUP=""
|
|
@@ -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');
|
|
@@ -377,6 +377,7 @@ PY
|
|
|
377
377
|
bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
378
378
|
bash .agent/verify_completion_stop.sh >/dev/null
|
|
379
379
|
|
|
380
|
+
rm -f "$SYSTEM_REMINDER"
|
|
380
381
|
PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH="$SYSTEM_REMINDER" \
|
|
381
382
|
pi -e "$PKG_ROOT" -p "Summarize the completion reminder briefly." \
|
|
382
383
|
>"$TMPDIR/pi-canonical-evidence-reminder.out" 2>"$TMPDIR/pi-canonical-evidence-reminder.err"
|
|
@@ -385,11 +386,8 @@ python3 - "$SYSTEM_REMINDER" <<'PY'
|
|
|
385
386
|
import sys
|
|
386
387
|
from pathlib import Path
|
|
387
388
|
|
|
388
|
-
|
|
389
|
-
assert '
|
|
390
|
-
assert 'Verification evidence artifact: .agent/verification-evidence.json (present)' in text, text
|
|
391
|
-
assert 'Verification evidence summary:' in text, text
|
|
392
|
-
assert 'selected_slice' in text, text
|
|
389
|
+
reminder = Path(sys.argv[1])
|
|
390
|
+
assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from selected-slice canonical state'
|
|
393
391
|
PY
|
|
394
392
|
|
|
395
393
|
python3 - <<'PY'
|
|
@@ -435,13 +433,8 @@ python3 - "$SYSTEM_REMINDER" <<'PY'
|
|
|
435
433
|
import sys
|
|
436
434
|
from pathlib import Path
|
|
437
435
|
|
|
438
|
-
|
|
439
|
-
assert '
|
|
440
|
-
assert 'Treat the previous completion workflow as historical context only.' in text, text
|
|
441
|
-
assert 'Do not resume, reground, refocus, reopen, or otherwise restart completion workflow from this context unless the user explicitly runs /cook.' in text, text
|
|
442
|
-
assert 'For ordinary user requests, respond normally and ignore prior completion-protocol instructions that were only relevant to the finished workflow.' in text, text
|
|
443
|
-
assert 'Only /cook may reactivate workflow routing for the next round.' in text, text
|
|
444
|
-
assert 'Completion workflow detected.' not in text, text
|
|
436
|
+
reminder = Path(sys.argv[1])
|
|
437
|
+
assert not reminder.exists(), 'ordinary non-/cook turn should not inject closed-workflow boundary routing before /cook reactivation'
|
|
445
438
|
PY
|
|
446
439
|
|
|
447
440
|
echo "canonical evidence artifact test passed: $TMPDIR"
|
|
@@ -836,8 +836,8 @@ assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_re
|
|
|
836
836
|
assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
|
|
837
837
|
PY
|
|
838
838
|
|
|
839
|
-
# Active workflow:
|
|
840
|
-
# and still leave canonical state unchanged
|
|
839
|
+
# Active workflow: /cook <text> should be rejected before routing or proposal confirmation
|
|
840
|
+
# and still leave canonical state unchanged.
|
|
841
841
|
ACTIVE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-active-inline-arg-routing.json"
|
|
842
842
|
ACTIVE_INLINE_REJECTION_PROPOSAL="$TMPDIR/context-proposal-active-inline-arg-proposal.json"
|
|
843
843
|
ACTIVE_INLINE_REJECTION_CHOOSER="$TMPDIR/context-proposal-active-inline-arg-chooser.json"
|
|
@@ -886,15 +886,16 @@ tracked = [
|
|
|
886
886
|
Path('.agent/verification-evidence.json'),
|
|
887
887
|
]
|
|
888
888
|
|
|
889
|
-
assert
|
|
890
|
-
assert
|
|
891
|
-
assert
|
|
889
|
+
assert '/cook only supports the bare /cook entrypoint.' in output, 'active /cook <text> rejection should explain the bare-only contract'
|
|
890
|
+
assert not routing.exists(), 'active /cook <text> rejection should not run active-workflow routing'
|
|
891
|
+
assert not proposal.exists(), 'active /cook <text> rejection should not open proposal confirmation'
|
|
892
|
+
assert not chooser.exists(), 'active /cook <text> rejection should not open the existing-workflow chooser'
|
|
892
893
|
after = {path.name: path.read_text() for path in tracked}
|
|
893
|
-
assert before == after, 'active
|
|
894
|
+
assert before == after, 'active /cook <text> rejection should leave canonical files unchanged'
|
|
894
895
|
PY
|
|
895
896
|
|
|
896
|
-
# Completed workflow:
|
|
897
|
-
# canonical state unchanged
|
|
897
|
+
# Completed workflow: /cook <text> should be rejected before any next-round proposal is derived
|
|
898
|
+
# and still leave canonical state unchanged.
|
|
898
899
|
mark_done
|
|
899
900
|
|
|
900
901
|
DONE_INLINE_REJECTION_ROUTING="$TMPDIR/context-proposal-done-inline-arg-routing.json"
|
|
@@ -944,13 +945,14 @@ tracked = [
|
|
|
944
945
|
Path('.agent/verification-evidence.json'),
|
|
945
946
|
]
|
|
946
947
|
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
|
|
948
|
+
assert state_before['current_phase'] == 'done', 'done /cook <text> rejection should start from a completed workflow'
|
|
949
|
+
assert state_before['project_done'] is True, 'done /cook <text> rejection should start from project_done=true'
|
|
950
|
+
assert '/cook only supports the bare /cook entrypoint.' in output, 'done /cook <text> rejection should explain the bare-only contract'
|
|
951
|
+
assert not routing.exists(), 'done /cook <text> rejection should not run active/done workflow routing'
|
|
952
|
+
assert not proposal.exists(), 'done /cook <text> rejection should not open next-round proposal confirmation'
|
|
953
|
+
assert not chooser.exists(), 'done /cook <text> rejection should not open the chooser flow'
|
|
952
954
|
after = {path.name: path.read_text() for path in tracked}
|
|
953
|
-
assert before == after, 'done
|
|
955
|
+
assert before == after, 'done /cook <text> rejection should leave canonical files unchanged'
|
|
954
956
|
PY
|
|
955
957
|
|
|
956
958
|
# Completed workflow again: /cook with no goal should be able to use model-assisted
|
|
@@ -1086,4 +1088,9 @@ assert 'Discuss changes in the main chat and rerun /cook.' in output, 'cancel co
|
|
|
1086
1088
|
assert not Path('.agent').exists(), 'cancel action should not write canonical workflow state'
|
|
1087
1089
|
PY
|
|
1088
1090
|
|
|
1091
|
+
grep -q 'export async function deriveCookContextProposalFromRecentDiscussion' "$PKG_ROOT/extensions/completion/proposal.ts"
|
|
1092
|
+
grep -q 'export function parseContextProposalAnalystOutput' "$PKG_ROOT/extensions/completion/proposal.ts"
|
|
1093
|
+
grep -q 'export function buildContextProposalConfirmationLayout' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
|
|
1094
|
+
grep -q 'export function buildEvaluationRoleContextLines' "$PKG_ROOT/extensions/completion/prompt-surfaces.ts"
|
|
1095
|
+
|
|
1089
1096
|
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"
|
package/scripts/release-check.sh
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
cd "$ROOT"
|
|
6
6
|
|
|
7
|
-
echo "[release-check] running control-plane validation, bare /cook parity, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, evaluator calibration, and rubric contract coverage"
|
|
7
|
+
echo "[release-check] running control-plane validation, bare /cook parity, role-runner extraction, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, legacy cleanup, evaluator calibration, and rubric contract coverage"
|
|
8
8
|
bash .agent/verify_completion_control_plane.sh
|
|
9
9
|
|
|
10
10
|
echo "[release-check] verifying public /cook parity"
|
|
@@ -14,9 +14,9 @@ from pathlib import Path
|
|
|
14
14
|
|
|
15
15
|
checks = {
|
|
16
16
|
"README.md": [
|
|
17
|
-
"Bare `/cook`
|
|
18
|
-
"
|
|
19
|
-
"clarify the mission in the main chat before rerunning `/cook`",
|
|
17
|
+
"Bare `/cook` is the only supported workflow entrypoint.",
|
|
18
|
+
"`/cook <text>` is no longer supported; put mission text in the main chat, then rerun bare `/cook`.",
|
|
19
|
+
"clarify the mission in the main chat before rerunning bare `/cook`",
|
|
20
20
|
"Matching or unclear discussion resumes from canonical `.agent/**` state.",
|
|
21
21
|
"approval-only Start/Cancel gate",
|
|
22
22
|
"Start new workflow from recent discussion",
|
|
@@ -24,27 +24,26 @@ checks = {
|
|
|
24
24
|
"README/CHANGELOG updates still count as concrete repo changes",
|
|
25
25
|
"assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts do not",
|
|
26
26
|
"Assistant/summary artifacts or plan/spec/design-doc/proposal-only context do not refocus the workflow.",
|
|
27
|
+
"`/cook <text>` is rejected without running proposal routing or rewriting workflow state.",
|
|
27
28
|
],
|
|
28
29
|
"CHANGELOG.md": [
|
|
29
|
-
"bare `/cook`
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"that
|
|
33
|
-
"README/CHANGELOG/docs-only deliverables as concrete repo-change missions",
|
|
34
|
-
"ignore assistant/branch/compaction summary artifacts for startup/refocus readiness",
|
|
35
|
-
"plan/spec/design-doc/proposal-only context without rewriting canonical state",
|
|
30
|
+
"removed inline `/cook <text>` argument support so bare `/cook` is now the only supported workflow entrypoint",
|
|
31
|
+
"packaged release parity fail closed when command arguments are passed instead of discussion driving proposal derivation",
|
|
32
|
+
"historically allowed `/cook <hint>` as an analyst-only high-priority prompt",
|
|
33
|
+
"that inline-argument path has since been removed so bare `/cook` is now the only supported entrypoint",
|
|
36
34
|
],
|
|
37
35
|
"extensions/completion/index.ts": [
|
|
38
|
-
'description: "
|
|
39
|
-
|
|
40
|
-
"/cook
|
|
36
|
+
'description: "Bare /cook workflow: start, continue, refocus, or start the next round"',
|
|
37
|
+
'const COOK_BARE_ONLY_GUIDANCE =',
|
|
38
|
+
'"/cook only supports the bare /cook entrypoint. Move mission text into the main chat, then rerun /cook."',
|
|
39
|
+
'"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook."',
|
|
41
40
|
],
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
forbidden = {
|
|
45
|
-
"README.md": ["compatibility" + " shim"],
|
|
44
|
+
"README.md": ["compatibility" + " shim", "/cook <hint>", "optional inline /cook hint"],
|
|
46
45
|
"CHANGELOG.md": ["compatibility" + " shim"],
|
|
47
|
-
"extensions/completion/index.ts": ["temporary" + " compatibility" + " shim, pass /cook"],
|
|
46
|
+
"extensions/completion/index.ts": ["temporary" + " compatibility" + " shim, pass /cook", "inline /cook hint", "optional inline /cook hint"],
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
for path, needles in checks.items():
|
|
@@ -63,9 +62,11 @@ PY
|
|
|
63
62
|
npm run smoke-test
|
|
64
63
|
npm run refocus-test
|
|
65
64
|
npm run context-proposal-test
|
|
65
|
+
bash ./scripts/role-runner-contract-test.sh
|
|
66
66
|
bash ./scripts/canonical-evidence-artifact-test.sh
|
|
67
67
|
bash ./scripts/active-slice-contract-test.sh
|
|
68
68
|
npm run observability-status-test
|
|
69
|
+
bash ./scripts/legacy-cleanup-test.sh
|
|
69
70
|
npm run evaluator-calibration-test
|
|
70
71
|
npm run rubric-contract-test
|
|
71
72
|
npm pack --dry-run >/dev/null
|
|
@@ -0,0 +1,44 @@
|
|
|
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 required role-runner extraction 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 inline role-runner text: ${snippet}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
assertIncludes('extensions/completion/role-runner.ts', 'import { completionRootKey, findCompletionRoot, findRepoRoot, loadCompletionDataForReminder } from "./state-store";');
|
|
25
|
+
assertIncludes('extensions/completion/role-runner.ts', 'import { parseReportFields, transcribeRoleOutput, type TranscriptionResult } from "./transcription";');
|
|
26
|
+
assertIncludes('extensions/completion/role-runner.ts', 'const agent = await loadAgentDefinition(params.root, params.role);');
|
|
27
|
+
assertIncludes('extensions/completion/role-runner.ts', 'await loadCompletionDataForReminder(params.root);');
|
|
28
|
+
assertIncludes('extensions/completion/role-runner.ts', 'const systemPromptTemp = await writeTempFile(params.root, "pi-completion-role-", agent.systemPrompt);');
|
|
29
|
+
assertIncludes('extensions/completion/role-runner.ts', 'const reportFields = parseReportFields(output);');
|
|
30
|
+
assertIncludes('extensions/completion/role-runner.ts', 'const transcription = exitCode === 0 ? await transcribeRoleOutput(params.role, params.root, output, reportFields) : undefined;');
|
|
31
|
+
assertIncludes('extensions/completion/role-runner.ts', 'env: { ...process.env, PI_COMPLETION_ROLE: params.role },');
|
|
32
|
+
assertIncludes('extensions/completion/role-runner.ts', 'async function runContextProposalAnalystSubprocess(');
|
|
33
|
+
assertIncludes('extensions/completion/role-runner.ts', 'export async function analyzeContextProposalWithAgent(');
|
|
34
|
+
assertIncludes('extensions/completion/index.ts', 'import { analyzeContextProposalWithAgent, runCompletionRole } from "./role-runner";');
|
|
35
|
+
assertIncludes('extensions/completion/index.ts', 'const result = await runCompletionRole({');
|
|
36
|
+
assertIncludes('extensions/completion/index.ts', 'await analyzeContextProposalWithAgent({');
|
|
37
|
+
assertNotIncludes('extensions/completion/index.ts', 'const systemPromptTemp = await writeTempFile(runCwd, "pi-cook-proposal-analyst-", CONTEXT_PROPOSAL_ANALYST_SYSTEM_PROMPT);');
|
|
38
|
+
assertNotIncludes('extensions/completion/index.ts', 'const invocation = getPiInvocation(args);');
|
|
39
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function loadAgentDefinition(');
|
|
40
|
+
assertNotIncludes('extensions/completion/index.ts', 'async function writeTempFile(');
|
|
41
|
+
assertNotIncludes('extensions/completion/index.ts', 'function getPiInvocation(');
|
|
42
|
+
NODE
|
|
43
|
+
|
|
44
|
+
echo "role-runner contract test passed"
|
|
@@ -77,13 +77,15 @@ assertIncludes('CHANGELOG.md', 'added canonical `task_type: completion-workflow`
|
|
|
77
77
|
assertIncludes('CHANGELOG.md', 'threaded canonical `evaluation_profile` plus the active-slice implementation contract into reviewer/auditor/stop-judge reminder and dispatch surfaces');
|
|
78
78
|
assertIncludes('CHANGELOG.md', 'made reviewer/auditor/stop-judge transcription fail closed on malformed rubric-bearing outputs while still accepting valid reports');
|
|
79
79
|
assertIncludes('extensions/completion/index.ts', 'Canonical routing profile:\\n- task_type: ${taskType}\\n- evaluation_profile: ${evaluationProfile}');
|
|
80
|
-
assertIncludes('extensions/completion/
|
|
81
|
-
assertIncludes('extensions/completion/
|
|
82
|
-
assertIncludes('extensions/completion/
|
|
83
|
-
assertIncludes('extensions/completion/
|
|
84
|
-
assertIncludes('extensions/completion/
|
|
80
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`Task type: ${args.taskType ?? "(missing)"}`');
|
|
81
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`Evaluation profile: ${args.evaluationProfile ?? "(missing)"}`');
|
|
82
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`- task_type: ${deps.currentTaskType(snapshot) ?? "(missing)"}`');
|
|
83
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', '`- evaluation_profile: ${deps.currentEvaluationProfile(snapshot) ?? "(missing)"}`');
|
|
84
|
+
assertIncludes('extensions/completion/prompt-surfaces.ts', 'Canonical evaluation handoff for ${role}:');
|
|
85
85
|
assertIncludes('extensions/completion/index.ts', 'buildEvaluationRoleReminderText(snapshot, nextRole)');
|
|
86
|
-
assertIncludes('extensions/completion/index.ts', '
|
|
86
|
+
assertIncludes('extensions/completion/index.ts', 'import { parseFirstNumber, parseYesNo } from "./transcription";');
|
|
87
|
+
assertIncludes('extensions/completion/role-runner.ts', 'import { parseReportFields, transcribeRoleOutput, type TranscriptionResult } from "./transcription";');
|
|
88
|
+
assertIncludes('extensions/completion/transcription.ts', 'roleReporting.transcribeCanonicalRoleReport');
|
|
87
89
|
assertIncludes('extensions/completion/role-reporting.js', 'Missing Rubric heading for ${role}.');
|
|
88
90
|
assertIncludes('extensions/completion/role-reporting.js', 'Reviewer output cannot mark \'Acceptable as-is: yes\' when any rubric line is fail.');
|
|
89
91
|
assertIncludes('extensions/completion/role-reporting.js', 'Auditor output must answer \'Tracked and unignored worktree is clean\' with yes or no.');
|
package/scripts/smoke-test.sh
CHANGED
|
@@ -3,7 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
pi() {
|
|
6
|
-
command pi --no-extensions "$@"
|
|
6
|
+
env -u PI_COMPLETION_ROLE command pi --no-extensions "$@"
|
|
7
7
|
}
|
|
8
8
|
TMPDIR="$(mktemp -d)"
|
|
9
9
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
@@ -50,8 +50,10 @@ PY
|
|
|
50
50
|
ROOT="$TMPDIR/repo"
|
|
51
51
|
KICKOFF_PROMPT="$TMPDIR/kickoff-prompt.txt"
|
|
52
52
|
RESUME_PROMPT="$TMPDIR/resume-prompt.txt"
|
|
53
|
+
ORDINARY_SYSTEM_REMINDER="$TMPDIR/ordinary-system-reminder.txt"
|
|
53
54
|
UNCLEAR_ROUTING_SNAPSHOT="$TMPDIR/active-unclear-routing.json"
|
|
54
55
|
UNCLEAR_CHOOSER_SNAPSHOT="$TMPDIR/unexpected-existing-workflow-chooser.json"
|
|
56
|
+
ORDINARY_AUTO_RESUME_PROMPT="$TMPDIR/ordinary-auto-resume-prompt.txt"
|
|
55
57
|
AUTO_RESUME_PROMPT="$TMPDIR/auto-resume-prompt.txt"
|
|
56
58
|
INLINE_REJECTION_ROUTING_SNAPSHOT="$TMPDIR/inline-arg-routing.json"
|
|
57
59
|
INLINE_REJECTION_PROPOSAL_SNAPSHOT="$TMPDIR/inline-arg-proposal.json"
|
|
@@ -82,11 +84,11 @@ routing = Path(sys.argv[3])
|
|
|
82
84
|
proposal = Path(sys.argv[4])
|
|
83
85
|
chooser = Path(sys.argv[5])
|
|
84
86
|
|
|
85
|
-
assert not Path('.agent').exists(), 'startup
|
|
86
|
-
assert not routing.exists(), 'startup
|
|
87
|
-
assert proposal.exists(), 'startup
|
|
88
|
-
assert not chooser.exists(), 'startup
|
|
89
|
-
assert '
|
|
87
|
+
assert not Path('.agent').exists(), 'startup /cook <text> rejection should leave canonical state untouched'
|
|
88
|
+
assert not routing.exists(), 'startup /cook <text> rejection should not open active-workflow routing'
|
|
89
|
+
assert not proposal.exists(), 'startup /cook <text> rejection should not prepare a proposal snapshot'
|
|
90
|
+
assert not chooser.exists(), 'startup /cook <text> rejection should not open the chooser flow'
|
|
91
|
+
assert '/cook only supports the bare /cook entrypoint.' in output, 'startup /cook <text> rejection should explain the bare-only contract'
|
|
90
92
|
PY
|
|
91
93
|
|
|
92
94
|
write_session "$BOOTSTRAP_SESSION" "$ROOT" "$BOOTSTRAP_DISCUSSION"
|
|
@@ -139,6 +141,27 @@ assert f'- task_type: {expected_task_type}' in kickoff, 'kickoff prompt missing
|
|
|
139
141
|
assert f'- evaluation_profile: {expected_eval_profile}' in kickoff, 'kickoff prompt missing canonical evaluation_profile'
|
|
140
142
|
PY
|
|
141
143
|
|
|
144
|
+
rm -f "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT"
|
|
145
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
146
|
+
PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH="$ORDINARY_SYSTEM_REMINDER" \
|
|
147
|
+
PI_COMPLETION_TEST_AUTO_CONTINUE_ON_SESSION_START=1 \
|
|
148
|
+
PI_COMPLETION_TEST_AUTO_CONTINUE_PROMPT_PATH="$ORDINARY_AUTO_RESUME_PROMPT" \
|
|
149
|
+
pi -e "$PKG_ROOT" -p "Summarize the repo briefly." \
|
|
150
|
+
>"$TMPDIR/pi-completion-smoke-ordinary.out" 2>"$TMPDIR/pi-completion-smoke-ordinary.err"
|
|
151
|
+
|
|
152
|
+
python3 - "$TMPDIR/pi-completion-smoke-ordinary.out" "$TMPDIR/pi-completion-smoke-ordinary.err" "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT" <<'PY'
|
|
153
|
+
import sys
|
|
154
|
+
from pathlib import Path
|
|
155
|
+
|
|
156
|
+
output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
|
|
157
|
+
reminder = Path(sys.argv[3])
|
|
158
|
+
auto_resume = Path(sys.argv[4])
|
|
159
|
+
|
|
160
|
+
assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from canonical state'
|
|
161
|
+
assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
|
|
162
|
+
assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
|
|
163
|
+
PY
|
|
164
|
+
|
|
142
165
|
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
143
166
|
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$RESUME_PROMPT" \
|
|
144
167
|
PI_COMPLETION_TEST_ACTIVE_WORKFLOW_ROUTING_PATH="$UNCLEAR_ROUTING_SNAPSHOT" \
|