pi-squad 0.16.6 → 0.17.0
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/README.md +26 -3
- package/docs/behavioral-contract-exact-targeting-review-suspended-attention.md +204 -0
- package/docs/file-spec-and-full-read-attestation-contract.md +412 -0
- package/docs/fleet-bridge.md +79 -0
- package/package.json +2 -1
- package/src/agent-pool.ts +16 -4
- package/src/file-spec.ts +451 -0
- package/src/index.ts +209 -83
- package/src/panel/squad-widget.ts +16 -7
- package/src/panel/task-list.ts +10 -0
- package/src/presentation.ts +38 -0
- package/src/protocol.ts +21 -0
- package/src/review.ts +19 -6
- package/src/scheduler.ts +211 -18
- package/src/skills/squad-supervisor/SKILL.md +6 -1
- package/src/store.ts +39 -2
- package/src/types.ts +18 -1
package/README.md
CHANGED
|
@@ -56,6 +56,7 @@ Squad agents—including QA/reviewer agents—produce candidate work and evidenc
|
|
|
56
56
|
- Failed review is reworked in the **same authoritative squad**: use `squad_modify` with that `squadId` and `add_task` or `resume_task` (or `resume` when interrupted work exists). These operations reconstruct the scheduler after restart. `/squad resume <squad-id>` provides the same resume path.
|
|
57
57
|
- When rework begins, the failed attempt moves to `reviewHistory`, the squad returns to `running`, and its evidence remains auditable. After every rework task settles, a fresh pending review becomes the active gate and `squad_review` is required again.
|
|
58
58
|
- Pending and failed review gates survive Pi restarts and are restored on the next session. A separate squad never links to, remediates, or accepts the failed gate.
|
|
59
|
+
- Every UI/status surface keeps execution progress adjacent to an explicit acceptance state: `◆ REVIEW PENDING · independent review required` means no verdict exists yet; `✗ REVIEW FAILED · awaiting same-squad rework` means the candidate was rejected. A truthful `3/3` task count never means a failed candidate was accepted.
|
|
59
60
|
|
|
60
61
|
The completion report is explicitly labeled **untrusted and not yet accepted**. Main Pi must never merely relay it or ask whether verification should be run.
|
|
61
62
|
|
|
@@ -77,6 +78,18 @@ architect → frontend ─┘
|
|
|
77
78
|
|
|
78
79
|
Architect runs first. Backend and frontend run in parallel after architect completes. QA waits for both.
|
|
79
80
|
|
|
81
|
+
### Suspended Work Requires Explicit Action
|
|
82
|
+
|
|
83
|
+
Suspension is an explicit pause, not a retry signal. If every remaining non-cancelled task is suspended or transitively blocked by suspended work, pi-squad durably wakes the main orchestrator once for that exact stall state. The widget shows `⚠ SUSPENDED — explicit resume required`; `squad_status` and the detail panel list every exact suspended task ID and blocked descendant.
|
|
84
|
+
|
|
85
|
+
Nothing resumes automatically. Choose each task intentionally with an exact squad and task ID:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
squad_modify({ action: "resume_task", squadId: "<exact-squad-id>", taskId: "<exact-task-id>" })
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Repeated reconciliation and restart do not create notification storms. A delivered stall remains visible until explicit resumption changes the state; a different suspended/blocked set is a new actionable episode.
|
|
92
|
+
|
|
80
93
|
### QA Rework Loop
|
|
81
94
|
|
|
82
95
|
When a QA agent outputs `## Verdict: FAIL`, the scheduler automatically:
|
|
@@ -156,9 +169,13 @@ When the main agent provides tasks directly (via the `tasks` parameter), unknown
|
|
|
156
169
|
|
|
157
170
|
### Widget (above editor)
|
|
158
171
|
|
|
159
|
-
Shows live
|
|
172
|
+
Shows live execution progress plus a distinct acceptance/attention state. Truncation is viewport-only: no wrapping, deterministic height, with complete IDs and evidence retained for detail/status output.
|
|
160
173
|
|
|
161
174
|
```
|
|
175
|
+
◆ squad Build task API 3/3 · ◆ REVIEW PENDING · independent review required
|
|
176
|
+
✗ squad Build task API 3/3 · ✗ REVIEW FAILED · awaiting same-squad rework
|
|
177
|
+
⚠ SUSPENDED — explicit resume required · ^q detail
|
|
178
|
+
|
|
162
179
|
⏳ squad Build task API 2/3 $0.58 3m12s ^q detail · /squad msg
|
|
163
180
|
✓ api (backend) 2m12s Created CRUD REST API with validation
|
|
164
181
|
⏳ tests (qa) 45s → bash npm test
|
|
@@ -197,7 +214,7 @@ Full overlay with task list, live activity preview, and scrollable message view.
|
|
|
197
214
|
| `/squad msg [task-id\|running-agent] text` | Message an exact task, or use an agent name only when it has one live task |
|
|
198
215
|
| `/squad widget` | Toggle widget |
|
|
199
216
|
| `/squad panel` | Toggle panel |
|
|
200
|
-
| `/squad cancel` | Cancel
|
|
217
|
+
| `/squad cancel` | Cancel the visibly focused squad; the notification names it and the focused widget/status clear |
|
|
201
218
|
| `/squad clear` | Dismiss widget |
|
|
202
219
|
| `/squad cleanup` | Delete squad data |
|
|
203
220
|
| `/squad enable/disable` | Enable/disable the extension |
|
|
@@ -209,7 +226,9 @@ Full overlay with task list, live activity preview, and scrollable message view.
|
|
|
209
226
|
| `squad` | Start a squad with goal + optional tasks/config |
|
|
210
227
|
| `squad_status` | Check progress, costs, task states |
|
|
211
228
|
| `squad_message` | Durably message an exact task; completed tasks reopen on their original session |
|
|
212
|
-
| `squad_modify` | Add/cancel/complete/pause/resume tasks or squads, or replace a task's dependencies with `set_dependencies`;
|
|
229
|
+
| `squad_modify` | Add/cancel/complete/pause/resume tasks or squads, or replace a task's dependencies with `set_dependencies`; exact `squadId` is required for destructive whole-squad `cancel` and recommended for all task actions |
|
|
230
|
+
|
|
231
|
+
Tool-level whole-squad cancellation never infers a target: `squad_modify({ action: "cancel", squadId: "<exact-squad-id>" })` affects only that persisted squad and names it in the result. Omitting `squadId` is rejected without changing any squad. Interactive `/squad cancel` instead uses the squad visibly focused in the current UI and names the affected squad.
|
|
213
232
|
|
|
214
233
|
Dependency repair uses top-level `taskId` and `depends`, for example `squad_modify({ action: "set_dependencies", taskId: "publish", depends: ["build"] })`. The replacement is validated atomically (known IDs, no self-reference, duplicates, or cycles) and is allowed only while the task is not running or done.
|
|
215
234
|
|
|
@@ -376,3 +395,7 @@ src/
|
|
|
376
395
|
## License
|
|
377
396
|
|
|
378
397
|
MIT
|
|
398
|
+
|
|
399
|
+
## File-based squad specifications
|
|
400
|
+
|
|
401
|
+
Large contracts may be started without inlining them: `squad({ specFile, specSha256 })`. The strict v1 JSON contract, size/artifact policy, canonical-byte publication, child-only `squad_spec_read` protocol, tool guard, durable task attestation, and legacy compatibility are specified in [`docs/file-spec-and-full-read-attestation-contract.md`](docs/file-spec-and-full-read-attestation-contract.md). The SHA-256 must be lowercase and match the exact file bytes; malformed, oversized, or artifact-integrity failures are rejected before scheduling.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Exact cancellation, review presentation, and suspended-stall attention contract
|
|
2
|
+
|
|
3
|
+
Status: implementation contract (no implementation in this note)
|
|
4
|
+
|
|
5
|
+
## Non-negotiable invariants
|
|
6
|
+
|
|
7
|
+
1. A destructive tool call never infers its squad target. `squad_modify({ action: "cancel" })` requires a non-empty exact `squadId`.
|
|
8
|
+
2. A scheduler is owned/addressed by its persisted squad ID. Acting on squad A never stops, deletes, focuses, or renders scheduler B.
|
|
9
|
+
3. When the widget is enabled, its target equals the active/focused squad ID, or both are `null`. A cancelled focused squad is never left displayed.
|
|
10
|
+
4. Task execution completion and independent acceptance remain separate. A failed review stays `squad.status === "review"`, retains immutable evidence in `squad.review`, and is not accepted.
|
|
11
|
+
5. Suspension is an explicit pause. Reconciliation, restart, dependency changes, and attention delivery never resume a suspended task. Only an explicit resume operation may do that.
|
|
12
|
+
6. Task-owned sessions, task mailboxes, review evidence/history, and complete message/evidence text are unchanged and must not be truncated semantically.
|
|
13
|
+
7. `resume_task` is idempotent with respect to already-live exact work: it never demotes an `in_progress` task, replaces its child, or queues a duplicate run.
|
|
14
|
+
|
|
15
|
+
## 1. Exact-ID whole-squad cancellation
|
|
16
|
+
|
|
17
|
+
### Tool contract
|
|
18
|
+
|
|
19
|
+
For `squad_modify` with `action: "cancel"`:
|
|
20
|
+
|
|
21
|
+
- `squadId` is required for this action even if the schema remains conditionally optional for other actions.
|
|
22
|
+
- Missing ID returns an error such as: `cancel requires exact squadId; no squad was changed.` It must not consult `activeSquadId`, latest squad, project candidates, or widget state.
|
|
23
|
+
- Unknown ID returns: `Squad '<supplied-id>' not found; no squad was changed.`
|
|
24
|
+
- Validate/load the persisted target before touching any scheduler or focus state.
|
|
25
|
+
- With a live target scheduler: stop only that scheduler, then remove only `schedulers.get(squadId)`.
|
|
26
|
+
- Without a live target scheduler: perform the same persisted cancellation transition safely. If scheduler lifecycle cleanup is reused, construct it for the exact ID without focusing it and without registering it as another squad's active scheduler. Existing `in_progress -> suspended` stop semantics may be preserved.
|
|
27
|
+
- Persist the target squad's existing cancellation terminal representation (`failed`) after cleanup. This contract does not add a new squad status and does not alter task cancellation semantics.
|
|
28
|
+
- Success names the target: `Squad '<squadId>' cancelled.` Any result/error after an ID is supplied also echoes that ID.
|
|
29
|
+
- Repeating cancellation for the same already-terminal target is safe/idempotent and still names that target; it must not affect another squad.
|
|
30
|
+
|
|
31
|
+
Task-level `cancel_task` remains its existing neutral, dependency-safe contract; this section is only whole-squad destructive cancellation.
|
|
32
|
+
|
|
33
|
+
### Focus/widget synchronization
|
|
34
|
+
|
|
35
|
+
After the persisted transition, enforce one helper-level invariant rather than updating globals ad hoc:
|
|
36
|
+
|
|
37
|
+
- If `activeSquadId === cancelledId`: set `activeSquadId = null`, set `widgetState.squadId = null`, and request a widget update so both widget and status line clear.
|
|
38
|
+
- If another valid squad B is focused: preserve B and set/retain `widgetState.squadId = B`; do not focus A merely because an absent scheduler had to be reconstructed.
|
|
39
|
+
- If globals were already inconsistent and either points at A, repair them to the valid focused B or clear both. Never leave `widgetState.squadId` pointing at A or at a squad different from `activeSquadId`.
|
|
40
|
+
- Always request an update after focus repair. Clearing must reset/ignore the prior widget cache key so a same-shaped future squad can render.
|
|
41
|
+
|
|
42
|
+
Interactive `/squad cancel` may use the visibly focused squad, but it must capture that exact ID before awaiting, cancel only it, notify `Squad '<id>' cancelled`, then apply the same focus/widget repair. If there is no visible focus, it is a no-op with `No focused squad to cancel`.
|
|
43
|
+
|
|
44
|
+
## 2. Review-state presentation
|
|
45
|
+
|
|
46
|
+
The persisted lifecycle does not change:
|
|
47
|
+
|
|
48
|
+
| Squad/task execution | Active review | Acceptance meaning |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| all relevant tasks done | `pending` | candidate awaits independent review |
|
|
51
|
+
| all relevant tasks done | `failed` | candidate was rejected; same-squad rework required |
|
|
52
|
+
| rework starts | absent; failed attempt appended unchanged to `reviewHistory` | executing rework |
|
|
53
|
+
| fresh execution settles | fresh `pending` | new candidate awaits review |
|
|
54
|
+
| pass/pass-with-issues | `passed`, squad `done` | accepted |
|
|
55
|
+
|
|
56
|
+
Required presentation in compact widget, status line/tool output, squad list/selection, and detail panel:
|
|
57
|
+
|
|
58
|
+
- Pending: `◆ REVIEW PENDING · independent review required` (warning style).
|
|
59
|
+
- Failed: `✗ REVIEW FAILED · awaiting same-squad rework` (error style).
|
|
60
|
+
- Failed must never be rendered as the generic `review required` label used for pending.
|
|
61
|
+
- Execution progress (for example `3/3`) remains truthful but must be adjacent to the acceptance label; it must not imply acceptance.
|
|
62
|
+
- `squad_status` must render an explicit acceptance line using the labels above. Detail/list surfaces must use distinct pending/failed icon/label text.
|
|
63
|
+
- Failed detail may show actionable issues, but if it does it must preserve every issue in the detail view; do not slice the semantic list or issue bodies. Width/viewport clipping may be visual only, with full data still navigable.
|
|
64
|
+
|
|
65
|
+
Widget render invalidation must include every persisted value that changes visible output, at minimum `squad.status` and `squad.review?.status ?? "none"` (and issue/verdict fields if rendered). A review `pending -> failed` update must repaint even when task statuses, turns, and progress are unchanged. Clearing/focus changes also invalidate independently of this cache.
|
|
66
|
+
|
|
67
|
+
## 3. Durable level-triggered suspended-stall attention
|
|
68
|
+
|
|
69
|
+
### Derived stall predicate
|
|
70
|
+
|
|
71
|
+
Evaluate after normal mailbox recovery and blocked/pending normalization, and before/with completion derivation. Ignore cancelled tasks for scheduling and stall denominators, consistent with current lifecycle rules.
|
|
72
|
+
|
|
73
|
+
Definitions for one persisted squad DAG:
|
|
74
|
+
|
|
75
|
+
- `S`: all exact task IDs whose status is `suspended`, sorted lexically.
|
|
76
|
+
- A task is runnable/live if it is `in_progress`, or `pending` with every dependency `done`.
|
|
77
|
+
- `suspensionBlocked(t)`: `t.status === "blocked"` and at least one currently unsatisfied dependency has a transitive dependency path ending at a task in `S`.
|
|
78
|
+
- Terminal for this predicate: `done`, `failed`, or `cancelled` (cancelled is excluded from the relevant set, but remains visible historically).
|
|
79
|
+
|
|
80
|
+
A **suspended stall** is active iff:
|
|
81
|
+
|
|
82
|
+
1. `S` is non-empty;
|
|
83
|
+
2. there is no runnable/live non-cancelled task; and
|
|
84
|
+
3. every non-cancelled task not in `S` is terminal or `suspensionBlocked`.
|
|
85
|
+
|
|
86
|
+
This covers suspended-only squads and DAGs whose remaining nonterminal work is blocked directly or transitively by suspended ancestors. It does not fire while independent runnable work exists. A blocked task with no transitive suspended ancestor is a different stall and must not be mislabeled.
|
|
87
|
+
|
|
88
|
+
### Persisted attention/outbox state
|
|
89
|
+
|
|
90
|
+
Add optional squad-owned metadata (names may vary only if semantics remain exact):
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
interface SuspendedStallAttention {
|
|
94
|
+
kind: "suspended_stall";
|
|
95
|
+
fingerprint: string; // canonical hash/string of sorted S + sorted blockedTaskIds
|
|
96
|
+
suspendedTaskIds: string[]; // sorted, complete, never abbreviated
|
|
97
|
+
blockedTaskIds: string[]; // sorted suspension-blocked descendants, complete
|
|
98
|
+
detectedAt: string;
|
|
99
|
+
delivery: "pending" | "delivered";
|
|
100
|
+
deliveredAt: string | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface Squad {
|
|
104
|
+
suspendedStallAttention?: SuspendedStallAttention;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Rules:
|
|
109
|
+
|
|
110
|
+
- On false -> true, atomically persist a new `pending` record before emitting an in-memory scheduler event.
|
|
111
|
+
- Repeated reconcile with the same canonical fingerprint is a no-op: do not rewrite timestamps and do not emit again.
|
|
112
|
+
- If the exact suspended/blocked sets change while still stalled, persist one new semantic episode/fingerprint and permit one updated wake containing the new exact IDs.
|
|
113
|
+
- On predicate false (including explicit resume making progress possible), clear the active record. If the same tasks are explicitly suspended again later, that is a new episode and may notify once.
|
|
114
|
+
- Delivery handling is an outbox: only a `pending` record emits `suspended_stall`; the extension sends the main-session follow-up and then marks that exact fingerprint `delivered`. Marking must compare the fingerprint so an older callback cannot acknowledge a newer episode.
|
|
115
|
+
- Scheduler reconstruction/start calls reconcile and therefore recreates missing attention or emits a still-pending persisted record. A delivered record is still rendered/injected as active attention but is not re-emitted on restart or timer reconcile.
|
|
116
|
+
- The unavoidable host boundary is handled as an outbox: a process failure between host acceptance and acknowledgement can retry once after restart; stable `customType` plus squad/fingerprint should be supplied if the host supports dedupe. Normal transitions/reconcile/restart must never create duplicate wakeups.
|
|
117
|
+
|
|
118
|
+
### Wake content and durable visibility
|
|
119
|
+
|
|
120
|
+
The wake must name the exact squad and include the complete sorted suspended IDs and complete blocked-descendant IDs. Required actionable text:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
[squad] SUSPENDED WORK NEEDS ACTION in '<squadId>'.
|
|
124
|
+
Suspended task IDs: <all IDs>
|
|
125
|
+
Blocked by suspended work: <all IDs, or none>
|
|
126
|
+
No task was resumed automatically.
|
|
127
|
+
Resume intentionally with squad_modify { action: "resume_task", squadId: "<squadId>", taskId: "<exact-task-id>" } for each task you choose.
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
No `slice`, `... +N`, or shortened semantic payload is allowed in the wake, injected context, or status/detail data. Terminal-width clipping is presentation-only.
|
|
131
|
+
|
|
132
|
+
While the record is active:
|
|
133
|
+
|
|
134
|
+
- `before_agent_start` injects it project-wide even if another squad is focused, so durable attention survives a missed edge notification.
|
|
135
|
+
- `squad_status` and the detail panel show `SUSPENDED — explicit resume required` plus all exact IDs.
|
|
136
|
+
- The compact widget shows an attention indicator; it need not fit all IDs, but detail/status must preserve them. Its cache key includes the attention fingerprint and delivery-independent active state.
|
|
137
|
+
- Attention never calls `resume()` or `resumeTask()`.
|
|
138
|
+
|
|
139
|
+
### Explicit resumption
|
|
140
|
+
|
|
141
|
+
The recommended recovery is one exact call per intended task:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{ "action": "resume_task", "squadId": "<exact squad>", "taskId": "<exact suspended task>" }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`resume_task` retains the task-owned session/mailbox behavior and immutable failed-review history. It changes only the exact task plus existing descendant invalidation/scheduling rules. Reconcile then clears or replaces attention from current persisted state. If another suspended cut still prevents progress, a new exact-set episode is allowed; otherwise normal scheduling resumes. No descendant is marked successful merely because its ancestor resumed.
|
|
148
|
+
|
|
149
|
+
Before any status write, descendant invalidation, or scheduling, `Scheduler.resumeTask(taskId)` must load the exact task and check both durable and live state. If `task.status === "in_progress"` and `pool.isRunning(taskId)`, it is an idempotent no-op: preserve `in_progress`, the same child process, session, mailbox, messages, and active review/rework state; do not enqueue/spawn another run. The tool must return truthful text naming both identities, for example `Task '<taskId>' is already running in squad '<squadId>'; no duplicate resume was started.` A safe explicit rejection with the same names is also conforming. A contradictory state (`in_progress` without a live child, or live child with non-`in_progress` durable status) must be reconciled by the existing recovery policy, never by blindly writing `pending` under a live child. This guard specifically covers a completed task reopened by `squad_message` on its original session followed immediately by redundant `resume_task`.
|
|
150
|
+
|
|
151
|
+
## 4. Edge-case matrix
|
|
152
|
+
|
|
153
|
+
| Case | Required outcome |
|
|
154
|
+
|---|---|
|
|
155
|
+
| A focused/running, cancel tool omits ID | Reject; A and every other squad unchanged; widget remains A. |
|
|
156
|
+
| A focused, cancel tool explicitly targets B | Stop/persist only B; A scheduler/focus/widget remain A; result names B. |
|
|
157
|
+
| Explicit B has no live scheduler | Persist cancellation for B without focusing/stopping A; result names B. |
|
|
158
|
+
| Explicit focused A cancelled | Only A stops; focus/widget/status clear; result names A. |
|
|
159
|
+
| Interactive cancel on visible A | Cancel captured A, name A, clear/refresh correctly. |
|
|
160
|
+
| Unknown explicit ID | No scheduler/focus/store mutation; error echoes supplied ID. |
|
|
161
|
+
| Review pending -> failed with tasks still 3/3 | Compact, status, list, detail repaint to `REVIEW FAILED`; progress may remain 3/3. |
|
|
162
|
+
| Repeated widget update after failed review | Cache does not suppress first changed render; later identical updates may coalesce. |
|
|
163
|
+
| One suspended task, all others done | Persist/wake once; no auto-resume. |
|
|
164
|
+
| Suspended root -> blocked child -> blocked grandchild | Wake once with root in suspended IDs and both descendants in blocked IDs. |
|
|
165
|
+
| Suspended root plus unrelated runnable task | No suspended-stall wake until unrelated work ceases to be runnable/live. |
|
|
166
|
+
| Suspended task plus unrelated irreducible blocked component | Predicate false for suspended-stall; existing generic failure/stall handling may apply. |
|
|
167
|
+
| Reconcile timer repeats unchanged stall | Same persisted fingerprint; zero additional wakeups. |
|
|
168
|
+
| Scheduler reconstructed/restarted in unchanged delivered stall | Attention remains visible/injected; no duplicate wake. |
|
|
169
|
+
| Restart with pending outbox record | Emit it once, acknowledge matching fingerprint after send. |
|
|
170
|
+
| Resume one of several suspended tasks | Never resume the others; clear/update attention by the newly derived exact sets. |
|
|
171
|
+
| Resume enables work | Scheduler runs normally; active attention clears. |
|
|
172
|
+
| Message reopens completed task and its child is live; redundant exact `resume_task` follows | Idempotent already-running result names squad/task; durable state stays `in_progress`; same child/session/mailbox continues; no duplicate spawn/queue. |
|
|
173
|
+
| Same task explicitly suspended again later | New false->true episode; one new wake is valid. |
|
|
174
|
+
| Cancelled tasks coexist with suspension | Cancelled tasks remain visible but do not satisfy dependencies or affect the suspended-stall denominator. |
|
|
175
|
+
|
|
176
|
+
## 5. Verification plan
|
|
177
|
+
|
|
178
|
+
### Focused unit tests
|
|
179
|
+
|
|
180
|
+
1. Predicate tests: suspended-only; suspended with all done; direct/transitive blocked descendants; unrelated runnable suppression; unrelated blocked-component suppression; cancelled exclusion.
|
|
181
|
+
2. Attention reducer/outbox tests: persist-before-emit; canonical sorting; unchanged reconcile dedupe; changed exact set creates one new episode; false clears; stale acknowledgement cannot acknowledge a newer fingerprint.
|
|
182
|
+
3. Widget/render tests: pending and failed labels differ; `pending -> failed` repaints with unchanged task state; focus clear removes widget/status; attention fingerprint invalidates cache.
|
|
183
|
+
|
|
184
|
+
### Extension integration/regression tests
|
|
185
|
+
|
|
186
|
+
1. Create concurrent squads A and B with independent fake schedulers. Focus A; omitted tool cancellation rejects; explicit B cancellation changes only B and names B; A remains displayed/running.
|
|
187
|
+
2. Repeat with B persisted but no scheduler. Assert no implicit focus and no stop/delete of A.
|
|
188
|
+
3. Cancel focused A via tool and via `/squad cancel`; assert exact named notification and widget/status clear.
|
|
189
|
+
4. Record a failed review after a pending widget render; assert compact/status/detail/list say `REVIEW FAILED · awaiting same-squad rework`, evidence remains byte-for-byte in active review, and rework archives it unchanged.
|
|
190
|
+
5. Live transition into suspended-only and suspended-blocking DAG stalls sends exactly one follow-up with all exact IDs and exact-squad `resume_task` guidance.
|
|
191
|
+
6. Call reconcile repeatedly and advance the 60-second path; assert no duplicate wake for the same fingerprint.
|
|
192
|
+
7. Reconstruct extension/scheduler from persisted pending attention; assert one delivery. Reconstruct from delivered attention; assert durable prompt/status visibility and no duplicate follow-up.
|
|
193
|
+
8. Explicitly resume one exact task; assert its original session/mailbox is reused, other suspended tasks remain suspended, attention updates/clears correctly, and blocked descendants schedule only under existing dependency rules.
|
|
194
|
+
9. Reopen a completed task through `squad_message`, wait until its original-session child is live and persisted `in_progress`, then immediately call exact `resume_task`. Assert truthful already-running text names squad/task, status never becomes `pending`, child spawn count remains one, session/mailbox identity is unchanged, and one eventual `agent_settled` produces exactly one completion/review transition.
|
|
195
|
+
|
|
196
|
+
Run the full native suite after focused tests (`npm test`), focused TypeScript checks used by the repository, extension-load smoke if available, and `git diff --check`. No test may rely only on emitted text: assert persisted squad/task state, scheduler map/focus state, widget/status calls, and notification counts.
|
|
197
|
+
|
|
198
|
+
## 6. Implementation boundaries
|
|
199
|
+
|
|
200
|
+
- Do not change task-owned session identity or mailbox routing/retention.
|
|
201
|
+
- Do not mutate, replace, or truncate failed review evidence; archive only through existing same-squad rework behavior.
|
|
202
|
+
- Do not auto-unpause or infer consent from restart/reconcile/dependency edits.
|
|
203
|
+
- Do not add cascading task cancellation or make cancelled dependencies successful.
|
|
204
|
+
- Prefer small shared helpers for exact target resolution, focus/widget synchronization, review presentation, and the pure suspended-stall derivation; avoid unrelated lifecycle refactors.
|