@linimin/pi-letscook 0.1.26
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 +196 -0
- package/LICENSE +21 -0
- package/PUBLISHING.md +60 -0
- package/README.md +219 -0
- package/agents/completion-auditor.md +43 -0
- package/agents/completion-bootstrapper.md +56 -0
- package/agents/completion-implementer.md +83 -0
- package/agents/completion-regrounder.md +66 -0
- package/agents/completion-reviewer.md +46 -0
- package/agents/completion-stop-judge.md +50 -0
- package/extensions/completion/index.ts +2572 -0
- package/package.json +38 -0
- package/scripts/context-proposal-test.sh +235 -0
- package/scripts/observability-status-test.sh +237 -0
- package/scripts/refocus-test.sh +77 -0
- package/scripts/release-check.sh +13 -0
- package/scripts/smoke-test.sh +74 -0
- package/skills/completion-protocol/SKILL.md +168 -0
- package/skills/completion-protocol/references/completion.md +287 -0
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@linimin/pi-letscook",
|
|
3
|
+
"version": "0.1.26",
|
|
4
|
+
"description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"keywords": ["pi-package", "pi", "workflow", "completion", "agent", "subagent"],
|
|
8
|
+
"files": [
|
|
9
|
+
"extensions",
|
|
10
|
+
"skills",
|
|
11
|
+
"agents",
|
|
12
|
+
"scripts",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"PUBLISHING.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"smoke-test": "bash ./scripts/smoke-test.sh",
|
|
20
|
+
"refocus-test": "bash ./scripts/refocus-test.sh",
|
|
21
|
+
"context-proposal-test": "bash ./scripts/context-proposal-test.sh",
|
|
22
|
+
"observability-status-test": "bash ./scripts/observability-status-test.sh",
|
|
23
|
+
"release-check": "bash ./scripts/release-check.sh"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@mariozechner/pi-ai": "*",
|
|
30
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
31
|
+
"@mariozechner/pi-tui": "*",
|
|
32
|
+
"typebox": "*"
|
|
33
|
+
},
|
|
34
|
+
"pi": {
|
|
35
|
+
"extensions": ["./extensions/completion"],
|
|
36
|
+
"skills": ["./skills"]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
TMPDIR="$(mktemp -d)"
|
|
6
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
7
|
+
|
|
8
|
+
write_session() {
|
|
9
|
+
local session_path="$1"
|
|
10
|
+
local cwd="$2"
|
|
11
|
+
local text="$3"
|
|
12
|
+
python3 - "$session_path" "$cwd" "$text" <<'PY'
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
session_path = Path(sys.argv[1])
|
|
18
|
+
cwd = sys.argv[2]
|
|
19
|
+
text = sys.argv[3]
|
|
20
|
+
session_path.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
entries = [
|
|
22
|
+
{
|
|
23
|
+
"type": "session",
|
|
24
|
+
"version": 3,
|
|
25
|
+
"id": "11111111-1111-4111-8111-111111111111",
|
|
26
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
27
|
+
"cwd": cwd,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"type": "message",
|
|
31
|
+
"id": "a1b2c3d4",
|
|
32
|
+
"parentId": None,
|
|
33
|
+
"timestamp": "2026-01-01T00:00:01.000Z",
|
|
34
|
+
"message": {
|
|
35
|
+
"role": "user",
|
|
36
|
+
"content": text,
|
|
37
|
+
"timestamp": 1767225601000,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
]
|
|
41
|
+
with session_path.open('w', encoding='utf-8') as fh:
|
|
42
|
+
for entry in entries:
|
|
43
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
44
|
+
PY
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
mark_done() {
|
|
48
|
+
python3 - <<'PY'
|
|
49
|
+
import json
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
|
|
52
|
+
state_path = Path('.agent/state.json')
|
|
53
|
+
plan_path = Path('.agent/plan.json')
|
|
54
|
+
active_path = Path('.agent/active-slice.json')
|
|
55
|
+
|
|
56
|
+
state = json.loads(state_path.read_text())
|
|
57
|
+
state.update({
|
|
58
|
+
'current_phase': 'done',
|
|
59
|
+
'continuation_policy': 'done',
|
|
60
|
+
'continuation_reason': 'Previous workflow completed.',
|
|
61
|
+
'project_done': True,
|
|
62
|
+
'requires_reground': False,
|
|
63
|
+
'next_mandatory_action': None,
|
|
64
|
+
'next_mandatory_role': None,
|
|
65
|
+
'remaining_stop_judges': 0,
|
|
66
|
+
'last_reground_at': '2026-01-01T00:10:00.000Z',
|
|
67
|
+
'contract_status': 'satisfied',
|
|
68
|
+
})
|
|
69
|
+
state_path.write_text(json.dumps(state, indent=2) + '\n')
|
|
70
|
+
|
|
71
|
+
plan = json.loads(plan_path.read_text())
|
|
72
|
+
plan.update({
|
|
73
|
+
'plan_basis': 'completed_round_fixture',
|
|
74
|
+
'candidate_slices': [],
|
|
75
|
+
})
|
|
76
|
+
plan_path.write_text(json.dumps(plan, indent=2) + '\n')
|
|
77
|
+
|
|
78
|
+
active = json.loads(active_path.read_text())
|
|
79
|
+
active.update({
|
|
80
|
+
'status': 'idle',
|
|
81
|
+
'slice_id': None,
|
|
82
|
+
'goal': None,
|
|
83
|
+
'contract_ids': [],
|
|
84
|
+
'acceptance_criteria': [],
|
|
85
|
+
'priority': None,
|
|
86
|
+
'why_now': None,
|
|
87
|
+
'blocked_on': [],
|
|
88
|
+
'locked_notes': [],
|
|
89
|
+
'must_fix_findings': [],
|
|
90
|
+
'basis_commit': None,
|
|
91
|
+
'remaining_contract_ids_before': [],
|
|
92
|
+
'release_blocker_count_before': None,
|
|
93
|
+
'high_value_gap_count_before': None,
|
|
94
|
+
})
|
|
95
|
+
active_path.write_text(json.dumps(active, indent=2) + '\n')
|
|
96
|
+
PY
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
ROOT="$TMPDIR/repo"
|
|
100
|
+
mkdir -p "$ROOT"
|
|
101
|
+
cd "$ROOT"
|
|
102
|
+
git init -q
|
|
103
|
+
|
|
104
|
+
# No workflow yet: /cook with no goal should infer from recent discussion.
|
|
105
|
+
SESSION_ONE="$TMPDIR/session-one.jsonl"
|
|
106
|
+
DISCUSSION_ONE=$'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
|
+
write_session "$SESSION_ONE" "$ROOT" "$DISCUSSION_ONE"
|
|
108
|
+
|
|
109
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
110
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
111
|
+
pi --session "$SESSION_ONE" -e "$PKG_ROOT" -p "/cook" >/tmp/pi-completion-context-proposal-bootstrap.out 2>/tmp/pi-completion-context-proposal-bootstrap.err
|
|
112
|
+
|
|
113
|
+
python3 - <<'PY'
|
|
114
|
+
import json
|
|
115
|
+
from pathlib import Path
|
|
116
|
+
|
|
117
|
+
mission = 'Remove the completion status line while keeping the completion widget.'
|
|
118
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
119
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
120
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
121
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
122
|
+
|
|
123
|
+
assert mission in mission_text, '.agent/mission.md did not record the context-derived mission anchor'
|
|
124
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after context-derived bootstrap'
|
|
125
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after context-derived bootstrap'
|
|
126
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after context-derived bootstrap'
|
|
127
|
+
assert state['current_phase'] == 'reground', 'state.json current_phase should start at reground after context-derived bootstrap'
|
|
128
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should start at completion-regrounder after context-derived bootstrap'
|
|
129
|
+
PY
|
|
130
|
+
|
|
131
|
+
# Completed workflow: /cook with no goal should infer the next round from recent discussion.
|
|
132
|
+
mark_done
|
|
133
|
+
|
|
134
|
+
SESSION_TWO="$TMPDIR/session-two.jsonl"
|
|
135
|
+
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.'
|
|
136
|
+
write_session "$SESSION_TWO" "$ROOT" "$DISCUSSION_TWO"
|
|
137
|
+
|
|
138
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
139
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
140
|
+
pi --session "$SESSION_TWO" -e "$PKG_ROOT" -p "/cook" >/tmp/pi-completion-context-proposal-next-round.out 2>/tmp/pi-completion-context-proposal-next-round.err
|
|
141
|
+
|
|
142
|
+
python3 - <<'PY'
|
|
143
|
+
import json
|
|
144
|
+
from pathlib import Path
|
|
145
|
+
|
|
146
|
+
mission = 'Ship the next workflow round for richer context-derived /cook startup.'
|
|
147
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
148
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
149
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
150
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
151
|
+
|
|
152
|
+
assert mission in mission_text, '.agent/mission.md did not update to the next-round context-derived mission anchor'
|
|
153
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after starting the next workflow round'
|
|
154
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after starting the next workflow round'
|
|
155
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after starting the next workflow round'
|
|
156
|
+
assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground for the next workflow round'
|
|
157
|
+
assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue for the next workflow round'
|
|
158
|
+
assert state['requires_reground'] is True, 'requires_reground should reset to true for the next workflow round'
|
|
159
|
+
assert state['project_done'] is False, 'project_done should reset to false for the next workflow round'
|
|
160
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should reset to completion-regrounder for the next workflow round'
|
|
161
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the next-round refocus'
|
|
162
|
+
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should reset to user_refocus for the next workflow round'
|
|
163
|
+
assert active['status'] == 'idle', 'active-slice should reset to idle for the next workflow round'
|
|
164
|
+
PY
|
|
165
|
+
|
|
166
|
+
# Active workflow: /cook <goal> plus refocus should use the explicit goal as the mission anchor,
|
|
167
|
+
# while still allowing recent discussion to enrich the proposal before confirmation.
|
|
168
|
+
SESSION_THREE="$TMPDIR/session-three.jsonl"
|
|
169
|
+
DISCUSSION_THREE=$'Scope:\n- Preserve the richer proposal structure from discussion.\nConstraints:\n- Keep explicit goals as the mission anchor when they conflict with earlier text.\nAcceptance:\n- Refresh canonical state from the replacement mission.'
|
|
170
|
+
write_session "$SESSION_THREE" "$ROOT" "$DISCUSSION_THREE"
|
|
171
|
+
|
|
172
|
+
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
173
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
174
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
175
|
+
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
|
|
176
|
+
|
|
177
|
+
python3 - <<'PY'
|
|
178
|
+
import json
|
|
179
|
+
from pathlib import Path
|
|
180
|
+
|
|
181
|
+
mission = 'Explicit replacement mission for the active workflow.'
|
|
182
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
183
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
184
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
185
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
186
|
+
|
|
187
|
+
assert mission in mission_text, '.agent/mission.md did not update to the explicit replacement mission anchor'
|
|
188
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after explicit-goal replacement'
|
|
189
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after explicit-goal replacement'
|
|
190
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after explicit-goal replacement'
|
|
191
|
+
assert state['current_phase'] == 'reground', 'current_phase should reset to reground after explicit-goal replacement'
|
|
192
|
+
assert state['continuation_policy'] == 'continue', 'continuation_policy should stay continue after explicit-goal replacement'
|
|
193
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next role should reset to completion-regrounder after explicit-goal replacement'
|
|
194
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the explicit-goal replacement'
|
|
195
|
+
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should be user_refocus after explicit-goal replacement'
|
|
196
|
+
assert active['status'] == 'idle', 'active slice should reset to idle after explicit-goal replacement'
|
|
197
|
+
PY
|
|
198
|
+
|
|
199
|
+
# Completed workflow again: /cook <goal> should start the next round directly from the explicit goal
|
|
200
|
+
# without requiring existing-workflow continue/refocus confirmation.
|
|
201
|
+
mark_done
|
|
202
|
+
|
|
203
|
+
SESSION_FOUR="$TMPDIR/session-four.jsonl"
|
|
204
|
+
DISCUSSION_FOUR=$'Mission: This older discussion should not override the explicit next-round goal.\nScope:\n- Reuse discussion details only as supplemental proposal context.\nAcceptance:\n- Start the next round from the explicit goal.'
|
|
205
|
+
write_session "$SESSION_FOUR" "$ROOT" "$DISCUSSION_FOUR"
|
|
206
|
+
|
|
207
|
+
PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
|
|
208
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
209
|
+
pi --session "$SESSION_FOUR" -e "$PKG_ROOT" -p "/cook Explicit goal for the next completed-workflow round" >/tmp/pi-completion-context-proposal-done-goal.out 2>/tmp/pi-completion-context-proposal-done-goal.err
|
|
210
|
+
|
|
211
|
+
python3 - <<'PY'
|
|
212
|
+
import json
|
|
213
|
+
from pathlib import Path
|
|
214
|
+
|
|
215
|
+
mission = 'Explicit goal for the next completed-workflow round.'
|
|
216
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
217
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
218
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
219
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
220
|
+
|
|
221
|
+
assert mission in mission_text, '.agent/mission.md did not update to the explicit next-round mission anchor'
|
|
222
|
+
assert state['mission_anchor'] == mission, 'state.json mission_anchor mismatch after explicit-goal next-round start'
|
|
223
|
+
assert plan['mission_anchor'] == mission, 'plan.json mission_anchor mismatch after explicit-goal next-round start'
|
|
224
|
+
assert active['mission_anchor'] == mission, 'active-slice.json mission_anchor mismatch after explicit-goal next-round start'
|
|
225
|
+
assert state['current_phase'] == 'reground', 'current_phase should reset to reground after explicit-goal next-round start'
|
|
226
|
+
assert state['continuation_policy'] == 'continue', 'continuation_policy should reset to continue after explicit-goal next-round start'
|
|
227
|
+
assert state['project_done'] is False, 'project_done should reset to false after explicit-goal next-round start'
|
|
228
|
+
assert state['requires_reground'] is True, 'requires_reground should reset to true after explicit-goal next-round start'
|
|
229
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next role should reset to completion-regrounder after explicit-goal next-round start'
|
|
230
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the explicit-goal next-round start'
|
|
231
|
+
assert plan['plan_basis'] == 'user_refocus', 'plan_basis should be user_refocus after explicit-goal next-round start'
|
|
232
|
+
assert active['status'] == 'idle', 'active slice should reset to idle after explicit-goal next-round start'
|
|
233
|
+
PY
|
|
234
|
+
|
|
235
|
+
echo "context proposal test passed: $ROOT"
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
TMPDIR="$(mktemp -d)"
|
|
6
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
7
|
+
|
|
8
|
+
assert_status_json() {
|
|
9
|
+
local json_path="$1"
|
|
10
|
+
local mode="$2"
|
|
11
|
+
python3 - "$json_path" "$mode" <<'PY'
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
json_path = Path(sys.argv[1])
|
|
17
|
+
mode = sys.argv[2]
|
|
18
|
+
assert json_path.exists(), f"missing status probe output: {json_path}"
|
|
19
|
+
data = json.loads(json_path.read_text())
|
|
20
|
+
|
|
21
|
+
if mode == 'none':
|
|
22
|
+
assert data['snapshotPresent'] is False, data
|
|
23
|
+
assert not data.get('statusText'), data
|
|
24
|
+
assert data['widgetLines'] == [], data
|
|
25
|
+
elif mode == 'static':
|
|
26
|
+
assert data['snapshotPresent'] is True, data
|
|
27
|
+
assert data['currentPhase'] == 'implement', data
|
|
28
|
+
assert data['sliceId'] == 'fixture-status-surface', data
|
|
29
|
+
assert data['nextMandatoryRole'] == 'completion-implementer', data
|
|
30
|
+
assert data['remainingContractCount'] == 2, data
|
|
31
|
+
assert data['releaseBlockerCount'] == 1, data
|
|
32
|
+
assert data['highValueGapCount'] == 4, data
|
|
33
|
+
assert data['remainingStopJudgeCount'] == 2, data
|
|
34
|
+
assert not data.get('statusText'), data
|
|
35
|
+
widget = data['widgetLines']
|
|
36
|
+
assert 'phase: implement' in widget, widget
|
|
37
|
+
assert 'slice: fixture-status-surface' in widget, widget
|
|
38
|
+
assert 'next: completion-implementer' in widget, widget
|
|
39
|
+
assert 'remaining: 2 contracts · 1 blocker · 4 gaps · 2 stop judges' in widget, widget
|
|
40
|
+
elif mode == 'live':
|
|
41
|
+
assert data['snapshotPresent'] is True, data
|
|
42
|
+
assert data['activeRole'] == 'completion-implementer', data
|
|
43
|
+
assert data['livePreview'] == 'Loading canonical completion state', data
|
|
44
|
+
assert data['liveState'] == 'active', data
|
|
45
|
+
assert data['liveToolActivity'] == 'read .agent/state.json', data
|
|
46
|
+
assert data['liveAssistantSummary'] == 'Loading canonical completion state', data
|
|
47
|
+
assert data['liveProgress'] == 'Loading canonical completion state', data
|
|
48
|
+
assert data['liveRationale'] == 'verifying selected slice handoff', data
|
|
49
|
+
assert data['liveNextStep'] == 'inspect extensions/completion/index.ts', data
|
|
50
|
+
assert data['liveVerifying'] == 'canonical slice handoff matches plan', data
|
|
51
|
+
assert data['liveStateDeltas'] == [
|
|
52
|
+
'tool activity separated from role judgment',
|
|
53
|
+
'waiting threshold uses updatedAt timestamps',
|
|
54
|
+
], data
|
|
55
|
+
assert not data.get('statusText'), data
|
|
56
|
+
widget = data['widgetLines']
|
|
57
|
+
assert widget == [], widget
|
|
58
|
+
live_details = data['liveDetailsLines']
|
|
59
|
+
assert live_details[0] == 'running completion role completion-implementer', live_details
|
|
60
|
+
assert 'tool: read .agent/state.json' in live_details, live_details
|
|
61
|
+
elif mode == 'waiting':
|
|
62
|
+
assert data['liveState'] == 'waiting', data
|
|
63
|
+
assert data['liveIdleMs'] == 20000, data
|
|
64
|
+
assert not data.get('statusText'), data
|
|
65
|
+
widget = data['widgetLines']
|
|
66
|
+
assert widget == [], widget
|
|
67
|
+
elif mode == 'stalled':
|
|
68
|
+
assert data['liveState'] == 'stalled', data
|
|
69
|
+
assert data['liveIdleMs'] == 46000, data
|
|
70
|
+
assert not data.get('statusText'), data
|
|
71
|
+
widget = data['widgetLines']
|
|
72
|
+
assert widget == [], widget
|
|
73
|
+
else:
|
|
74
|
+
raise AssertionError(f'unknown assertion mode: {mode}')
|
|
75
|
+
PY
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
NO_SNAPSHOT_ROOT="$TMPDIR/no-snapshot"
|
|
79
|
+
mkdir -p "$NO_SNAPSHOT_ROOT"
|
|
80
|
+
cd "$NO_SNAPSHOT_ROOT"
|
|
81
|
+
git init -q
|
|
82
|
+
NO_SNAPSHOT_JSON="$TMPDIR/no-snapshot-status.json"
|
|
83
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
84
|
+
PI_COMPLETION_STATUS_SNAPSHOT_FILE="$NO_SNAPSHOT_JSON" \
|
|
85
|
+
pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-none.out" 2>"$TMPDIR/pi-completion-status-none.err" || true
|
|
86
|
+
assert_status_json "$NO_SNAPSHOT_JSON" none
|
|
87
|
+
|
|
88
|
+
FIXTURE_ROOT="$TMPDIR/fixture"
|
|
89
|
+
mkdir -p "$FIXTURE_ROOT/.agent"
|
|
90
|
+
cd "$FIXTURE_ROOT"
|
|
91
|
+
git init -q
|
|
92
|
+
|
|
93
|
+
cat > .agent/profile.json <<'JSON'
|
|
94
|
+
{
|
|
95
|
+
"schema_version": 1,
|
|
96
|
+
"protocol_id": "completion",
|
|
97
|
+
"project_name": "status-surface-fixture",
|
|
98
|
+
"required_stop_judges": 3,
|
|
99
|
+
"priority_policy_id": "completion-default",
|
|
100
|
+
"docs_surfaces": ["README.md"]
|
|
101
|
+
}
|
|
102
|
+
JSON
|
|
103
|
+
|
|
104
|
+
cat > .agent/state.json <<'JSON'
|
|
105
|
+
{
|
|
106
|
+
"schema_version": 1,
|
|
107
|
+
"mission_anchor": "Verify persistent completion observability status surfaces.",
|
|
108
|
+
"current_phase": "implement",
|
|
109
|
+
"continuation_policy": "continue",
|
|
110
|
+
"continuation_reason": "Status surface regression fixture.",
|
|
111
|
+
"project_done": false,
|
|
112
|
+
"requires_reground": false,
|
|
113
|
+
"slices_since_last_reground": 0,
|
|
114
|
+
"remaining_release_blockers": 1,
|
|
115
|
+
"remaining_high_value_gaps": 4,
|
|
116
|
+
"unsatisfied_contract_ids": ["OBS-STATUS-SURFACE", "OBS-ACTIVITY-SEPARATION"],
|
|
117
|
+
"release_blocker_ids": ["RB-STATUS-FIXTURE"],
|
|
118
|
+
"next_mandatory_action": "Implement selected slice fixture-status-surface.",
|
|
119
|
+
"next_mandatory_role": "completion-implementer",
|
|
120
|
+
"remaining_stop_judges": 2,
|
|
121
|
+
"last_reground_at": "2026-04-30T00:00:00Z",
|
|
122
|
+
"last_auditor_verdict": null,
|
|
123
|
+
"contract_status": "gaps_identified",
|
|
124
|
+
"latest_completed_slice": null,
|
|
125
|
+
"latest_verified_slice": null
|
|
126
|
+
}
|
|
127
|
+
JSON
|
|
128
|
+
|
|
129
|
+
cat > .agent/plan.json <<'JSON'
|
|
130
|
+
{
|
|
131
|
+
"schema_version": 1,
|
|
132
|
+
"mission_anchor": "Verify persistent completion observability status surfaces.",
|
|
133
|
+
"last_reground_at": "2026-04-30T00:00:00Z",
|
|
134
|
+
"plan_basis": "observability_status_fixture",
|
|
135
|
+
"candidate_slices": [
|
|
136
|
+
{
|
|
137
|
+
"slice_id": "fixture-status-surface",
|
|
138
|
+
"goal": "Render the remaining completion widget surface from canonical state.",
|
|
139
|
+
"acceptance_criteria": [
|
|
140
|
+
"No completion status text is rendered.",
|
|
141
|
+
"Persistent widget lines are rendered when no role is running."
|
|
142
|
+
],
|
|
143
|
+
"contract_ids": ["OBS-STATUS-SURFACE"],
|
|
144
|
+
"priority": 100,
|
|
145
|
+
"status": "selected",
|
|
146
|
+
"why_now": "Fixture for observability status regression coverage.",
|
|
147
|
+
"blocked_on": [],
|
|
148
|
+
"evidence": []
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
JSON
|
|
153
|
+
|
|
154
|
+
cat > .agent/active-slice.json <<'JSON'
|
|
155
|
+
{
|
|
156
|
+
"schema_version": 1,
|
|
157
|
+
"mission_anchor": "Verify persistent completion observability status surfaces.",
|
|
158
|
+
"status": "selected",
|
|
159
|
+
"slice_id": "fixture-status-surface",
|
|
160
|
+
"goal": "Render the remaining completion widget surface from canonical state.",
|
|
161
|
+
"contract_ids": ["OBS-STATUS-SURFACE"],
|
|
162
|
+
"acceptance_criteria": [
|
|
163
|
+
"No completion status text is rendered.",
|
|
164
|
+
"Persistent widget lines are rendered when no role is running."
|
|
165
|
+
],
|
|
166
|
+
"blocked_on": [],
|
|
167
|
+
"locked_notes": [],
|
|
168
|
+
"must_fix_findings": [],
|
|
169
|
+
"basis_commit": "fixturebasis",
|
|
170
|
+
"remaining_contract_ids_before": ["OBS-STATUS-SURFACE", "OBS-ACTIVITY-SEPARATION"],
|
|
171
|
+
"release_blocker_count_before": 1,
|
|
172
|
+
"high_value_gap_count_before": 4,
|
|
173
|
+
"priority": 100,
|
|
174
|
+
"why_now": "Fixture for observability status regression coverage."
|
|
175
|
+
}
|
|
176
|
+
JSON
|
|
177
|
+
|
|
178
|
+
STATIC_JSON="$TMPDIR/static-status.json"
|
|
179
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
180
|
+
PI_COMPLETION_STATUS_SNAPSHOT_FILE="$STATIC_JSON" \
|
|
181
|
+
pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-static.out" 2>"$TMPDIR/pi-completion-status-static.err"
|
|
182
|
+
assert_status_json "$STATIC_JSON" static
|
|
183
|
+
|
|
184
|
+
LIVE_ROLE_EVENT_STREAM_JSON="$(cat <<'JSON'
|
|
185
|
+
{
|
|
186
|
+
"role": "completion-implementer",
|
|
187
|
+
"startedAt": 1000,
|
|
188
|
+
"events": [
|
|
189
|
+
{
|
|
190
|
+
"type": "tool_execution_start",
|
|
191
|
+
"toolName": "read",
|
|
192
|
+
"args": {"path": ".agent/state.json"},
|
|
193
|
+
"at": 2000
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"type": "message_update",
|
|
197
|
+
"message": {
|
|
198
|
+
"role": "assistant",
|
|
199
|
+
"content": [
|
|
200
|
+
{
|
|
201
|
+
"type": "text",
|
|
202
|
+
"text": "PROGRESS: Loading canonical completion state\nRATIONALE: verifying selected slice handoff\nNEXT: inspect extensions/completion/index.ts\nVERIFYING: canonical slice handoff matches plan\nSTATE-DELTA: tool activity separated from role judgment\nSTATE-DELTA: waiting threshold uses updatedAt timestamps"
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
"at": 2000
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
JSON
|
|
211
|
+
)"
|
|
212
|
+
|
|
213
|
+
LIVE_JSON="$TMPDIR/live-status.json"
|
|
214
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
215
|
+
PI_COMPLETION_STATUS_SNAPSHOT_FILE="$LIVE_JSON" \
|
|
216
|
+
PI_COMPLETION_TEST_NOW=2500 \
|
|
217
|
+
PI_COMPLETION_TEST_ROLE_EVENT_STREAM_JSON="$LIVE_ROLE_EVENT_STREAM_JSON" \
|
|
218
|
+
pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-live.out" 2>"$TMPDIR/pi-completion-status-live.err"
|
|
219
|
+
assert_status_json "$LIVE_JSON" live
|
|
220
|
+
|
|
221
|
+
WAITING_JSON="$TMPDIR/waiting-status.json"
|
|
222
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
223
|
+
PI_COMPLETION_STATUS_SNAPSHOT_FILE="$WAITING_JSON" \
|
|
224
|
+
PI_COMPLETION_TEST_NOW=22000 \
|
|
225
|
+
PI_COMPLETION_TEST_ROLE_EVENT_STREAM_JSON="$LIVE_ROLE_EVENT_STREAM_JSON" \
|
|
226
|
+
pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-waiting.out" 2>"$TMPDIR/pi-completion-status-waiting.err"
|
|
227
|
+
assert_status_json "$WAITING_JSON" waiting
|
|
228
|
+
|
|
229
|
+
STALLED_JSON="$TMPDIR/stalled-status.json"
|
|
230
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
231
|
+
PI_COMPLETION_STATUS_SNAPSHOT_FILE="$STALLED_JSON" \
|
|
232
|
+
PI_COMPLETION_TEST_NOW=48000 \
|
|
233
|
+
PI_COMPLETION_TEST_ROLE_EVENT_STREAM_JSON="$LIVE_ROLE_EVENT_STREAM_JSON" \
|
|
234
|
+
pi -e "$PKG_ROOT" -p "/cook" >"$TMPDIR/pi-completion-status-stalled.out" 2>"$TMPDIR/pi-completion-status-stalled.err"
|
|
235
|
+
assert_status_json "$STALLED_JSON" stalled
|
|
236
|
+
|
|
237
|
+
echo "observability status test passed: $TMPDIR"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
TMPDIR="$(mktemp -d)"
|
|
6
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
7
|
+
|
|
8
|
+
cd "$TMPDIR"
|
|
9
|
+
git init -q
|
|
10
|
+
|
|
11
|
+
pi -e "$PKG_ROOT" -p "/cook smoke-test mission" >/tmp/pi-completion-refocus-bootstrap.out 2>/tmp/pi-completion-refocus-bootstrap.err &
|
|
12
|
+
PI_PID=$!
|
|
13
|
+
for _ in $(seq 1 60); do
|
|
14
|
+
if [[ -f .agent/profile.json && -f .agent/state.json && -f .agent/plan.json && -f .agent/active-slice.json ]]; then
|
|
15
|
+
break
|
|
16
|
+
fi
|
|
17
|
+
sleep 1
|
|
18
|
+
done
|
|
19
|
+
if [[ ! -f .agent/profile.json || ! -f .agent/state.json || ! -f .agent/plan.json || ! -f .agent/active-slice.json ]]; then
|
|
20
|
+
echo "completion bootstrap did not materialize canonical files in time" >&2
|
|
21
|
+
cat /tmp/pi-completion-refocus-bootstrap.err >&2 || true
|
|
22
|
+
kill "$PI_PID" >/dev/null 2>&1 || true
|
|
23
|
+
wait "$PI_PID" >/dev/null 2>&1 || true
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
kill "$PI_PID" >/dev/null 2>&1 || true
|
|
27
|
+
wait "$PI_PID" >/dev/null 2>&1 || true
|
|
28
|
+
|
|
29
|
+
INITIAL_MISSION="$(python3 - <<'PY'
|
|
30
|
+
import json
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
33
|
+
print(state['mission_anchor'])
|
|
34
|
+
PY
|
|
35
|
+
)"
|
|
36
|
+
|
|
37
|
+
PI_COMPLETION_EXISTING_WORKFLOW_ACTION=refocus \
|
|
38
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
39
|
+
pi -e "$PKG_ROOT" -p "/cook refocused smoke-test mission with tests and docs" \
|
|
40
|
+
>/tmp/pi-completion-refocus.out 2>/tmp/pi-completion-refocus.err
|
|
41
|
+
|
|
42
|
+
python3 - <<'PY'
|
|
43
|
+
import json
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
new_anchor = 'refocused smoke-test mission with tests and docs parity.'
|
|
47
|
+
mission_text = Path('.agent/mission.md').read_text()
|
|
48
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
49
|
+
plan = json.loads(Path('.agent/plan.json').read_text())
|
|
50
|
+
active = json.loads(Path('.agent/active-slice.json').read_text())
|
|
51
|
+
|
|
52
|
+
assert new_anchor in mission_text, '.agent/mission.md did not update to the refocused mission anchor'
|
|
53
|
+
assert state['mission_anchor'] == new_anchor, 'state.json mission_anchor mismatch after refocus'
|
|
54
|
+
assert plan['mission_anchor'] == new_anchor, 'plan.json mission_anchor mismatch after refocus'
|
|
55
|
+
assert active['mission_anchor'] == new_anchor, 'active-slice.json mission_anchor mismatch after refocus'
|
|
56
|
+
assert state['current_phase'] == 'reground', 'state.json current_phase should reset to reground after refocus'
|
|
57
|
+
assert state['requires_reground'] is True, 'state.json requires_reground should be true after refocus'
|
|
58
|
+
assert state['next_mandatory_role'] == 'completion-regrounder', 'next_mandatory_role should reset to completion-regrounder'
|
|
59
|
+
assert state['continuation_reason'].startswith('User refocused workflow via /cook:'), 'continuation_reason should record the refocus'
|
|
60
|
+
assert plan['plan_basis'] == 'user_refocus', 'plan.json plan_basis should be user_refocus after refocus'
|
|
61
|
+
assert active['status'] == 'idle', 'active-slice.json status should reset to idle after refocus'
|
|
62
|
+
PY
|
|
63
|
+
|
|
64
|
+
UPDATED_MISSION="$(python3 - <<'PY'
|
|
65
|
+
import json
|
|
66
|
+
from pathlib import Path
|
|
67
|
+
state = json.loads(Path('.agent/state.json').read_text())
|
|
68
|
+
print(state['mission_anchor'])
|
|
69
|
+
PY
|
|
70
|
+
)"
|
|
71
|
+
|
|
72
|
+
if [[ "$INITIAL_MISSION" == "$UPDATED_MISSION" ]]; then
|
|
73
|
+
echo "expected mission anchor to change during refocus" >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
echo "refocus test passed: $TMPDIR"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
cd "$ROOT"
|
|
6
|
+
|
|
7
|
+
npm run smoke-test
|
|
8
|
+
npm run refocus-test
|
|
9
|
+
npm run context-proposal-test
|
|
10
|
+
npm run observability-status-test
|
|
11
|
+
npm pack --dry-run >/dev/null
|
|
12
|
+
|
|
13
|
+
echo "release check passed"
|