@trevonistrevon/pi-loop 0.4.11 → 0.5.1
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 +20 -9
- package/dist/commands/loop-command.d.ts +22 -0
- package/dist/commands/loop-command.js +148 -0
- package/dist/commands/tasks-command.d.ts +15 -0
- package/dist/commands/tasks-command.js +117 -0
- package/dist/coordinator.d.ts +35 -0
- package/dist/coordinator.js +56 -0
- package/dist/goal-coordinator.d.ts +22 -0
- package/dist/goal-coordinator.js +28 -0
- package/dist/goal-reducer.d.ts +89 -0
- package/dist/goal-reducer.js +181 -0
- package/dist/goal-store.d.ts +31 -0
- package/dist/goal-store.js +298 -0
- package/dist/goal-types.d.ts +82 -0
- package/dist/goal-types.js +1 -0
- package/dist/goal-verifier.d.ts +20 -0
- package/dist/goal-verifier.js +198 -0
- package/dist/index.js +130 -1087
- package/dist/loop-reducer.d.ts +63 -0
- package/dist/loop-reducer.js +67 -0
- package/dist/monitor-completion-coordinator.d.ts +10 -0
- package/dist/monitor-completion-coordinator.js +13 -0
- package/dist/monitor-manager.d.ts +2 -0
- package/dist/monitor-manager.js +107 -29
- package/dist/monitor-reducer.d.ts +82 -0
- package/dist/monitor-reducer.js +69 -0
- package/dist/notification-reducer.d.ts +81 -0
- package/dist/notification-reducer.js +65 -0
- package/dist/runtime/monitor-ondone-runtime.d.ts +13 -0
- package/dist/runtime/monitor-ondone-runtime.js +49 -0
- package/dist/runtime/notification-runtime.d.ts +34 -0
- package/dist/runtime/notification-runtime.js +152 -0
- package/dist/runtime/scope.d.ts +8 -0
- package/dist/runtime/scope.js +33 -0
- package/dist/runtime/session-runtime.d.ts +39 -0
- package/dist/runtime/session-runtime.js +110 -0
- package/dist/runtime/task-backlog-runtime.d.ts +36 -0
- package/dist/runtime/task-backlog-runtime.js +105 -0
- package/dist/runtime/task-rpc.d.ts +19 -0
- package/dist/runtime/task-rpc.js +118 -0
- package/dist/store.d.ts +7 -4
- package/dist/store.js +129 -49
- package/dist/task-backlog-coordinator.d.ts +12 -0
- package/dist/task-backlog-coordinator.js +22 -0
- package/dist/task-reducer.d.ts +66 -0
- package/dist/task-reducer.js +76 -0
- package/dist/task-store.d.ts +8 -4
- package/dist/task-store.js +102 -33
- package/dist/tools/loop-tools.d.ts +41 -0
- package/dist/tools/loop-tools.js +241 -0
- package/dist/tools/monitor-tools.d.ts +25 -0
- package/dist/tools/monitor-tools.js +110 -0
- package/dist/tools/native-task-tools.d.ts +15 -0
- package/dist/tools/native-task-tools.js +127 -0
- package/docs/architecture/goal-state-schema.md +505 -0
- package/docs/architecture/state-machine-migration.md +546 -0
- package/docs/architecture/state-machine-reducer-event-model.md +823 -0
- package/docs/architecture/state-machine-test-matrix.md +249 -0
- package/docs/architecture/state-machine-transition-map.md +436 -0
- package/package.json +1 -1
- package/src/commands/loop-command.ts +184 -0
- package/src/commands/tasks-command.ts +135 -0
- package/src/coordinator.ts +115 -0
- package/src/goal-coordinator.ts +58 -0
- package/src/goal-reducer.ts +280 -0
- package/src/goal-store.ts +315 -0
- package/src/goal-types.ts +104 -0
- package/src/goal-verifier.ts +241 -0
- package/src/index.ts +134 -1147
- package/src/loop-reducer.ts +148 -0
- package/src/monitor-completion-coordinator.ts +24 -0
- package/src/monitor-manager.ts +115 -27
- package/src/monitor-reducer.ts +166 -0
- package/src/notification-reducer.ts +155 -0
- package/src/runtime/monitor-ondone-runtime.ts +75 -0
- package/src/runtime/notification-runtime.ts +212 -0
- package/src/runtime/scope.ts +37 -0
- package/src/runtime/session-runtime.ts +168 -0
- package/src/runtime/task-backlog-runtime.ts +163 -0
- package/src/runtime/task-rpc.ts +150 -0
- package/src/store.ts +132 -50
- package/src/task-backlog-coordinator.ts +32 -0
- package/src/task-reducer.ts +152 -0
- package/src/task-store.ts +103 -31
- package/src/tools/loop-tools.ts +304 -0
- package/src/tools/monitor-tools.ts +145 -0
- package/src/tools/native-task-tools.ts +144 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# pi-loop State Tracking Test Matrix
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This matrix turns the state inventory in `docs/architecture/state-machine-transition-map.md`
|
|
6
|
+
into a concrete test plan for the reducer/state-machine migration.
|
|
7
|
+
|
|
8
|
+
The goal is to:
|
|
9
|
+
|
|
10
|
+
- lock down current behavior before refactoring
|
|
11
|
+
- separate entity transition tests from coordination scenario tests
|
|
12
|
+
- make hidden lifecycle phases observable via assertions
|
|
13
|
+
- ensure future Goal orchestration builds on a reliable state core
|
|
14
|
+
|
|
15
|
+
## Test layers
|
|
16
|
+
|
|
17
|
+
We will test state tracking at three layers:
|
|
18
|
+
|
|
19
|
+
1. **Entity transition tests**
|
|
20
|
+
- task, loop, monitor, and notification transitions in isolation
|
|
21
|
+
2. **Invariant tests**
|
|
22
|
+
- properties that must always hold across transitions
|
|
23
|
+
3. **Scenario tests**
|
|
24
|
+
- cross-entity coordination paths and lifecycle races
|
|
25
|
+
|
|
26
|
+
## 1. Entity transition tests
|
|
27
|
+
|
|
28
|
+
### 1.1 Task entity transitions
|
|
29
|
+
|
|
30
|
+
| ID | Case | Start | Event | Expected end state | Current coverage | Target file |
|
|
31
|
+
|---|---|---|---|---|---|---|
|
|
32
|
+
| T-01 | create native task | none | `TaskCreate` | `pending` | partial | `test/index.test.ts` |
|
|
33
|
+
| T-02 | quick-create native task | none | `/tasks some text` | `pending` | partial | `test/index.test.ts` |
|
|
34
|
+
| T-03 | start task | `pending` | `TaskUpdate(status=in_progress)` | `in_progress` | missing direct test | `test/index.test.ts` |
|
|
35
|
+
| T-04 | complete pending task | `pending` | `TaskUpdate(status=completed)` | `completed` with `completedAt` | partial | `test/index.test.ts` |
|
|
36
|
+
| T-05 | complete in-progress task | `in_progress` | `TaskUpdate(status=completed)` | `completed` with `completedAt` | missing direct test | `test/index.test.ts` |
|
|
37
|
+
| T-06 | reopen completed task | `completed` | `TaskUpdate(status=pending)` | `pending` | missing | `test/index.test.ts` |
|
|
38
|
+
| T-07 | delete task | any existing | `TaskDelete` | removed | partial | `test/index.test.ts` |
|
|
39
|
+
| T-08 | prune completed tasks | mixed task set | `cleanDoneTasks()` / `sweepCompleted()` | completed removed, active retained | partial | `test/index.test.ts` / `test/task-store.test.ts` if added |
|
|
40
|
+
| T-09 | monotonic ids after prune | completed tasks swept | `TaskCreate` | next id strictly increases | covered | `test/index.test.ts` |
|
|
41
|
+
| T-10 | completedAt semantics on reopen | completed then reopened | `TaskUpdate(status=pending)` | define whether `completedAt` persists or clears | currently unspecified | state-model decision |
|
|
42
|
+
|
|
43
|
+
### 1.2 Loop entity transitions
|
|
44
|
+
|
|
45
|
+
| ID | Case | Start | Event | Expected end state | Current coverage | Target file |
|
|
46
|
+
|---|---|---|---|---|---|---|
|
|
47
|
+
| L-01 | create cron loop | none | `LoopCreate(trigger=cron)` | `active` and armed | partial | `test/index.test.ts` / `test/store.test.ts` |
|
|
48
|
+
| L-02 | create event loop | none | `LoopCreate(trigger=event)` | `active` and subscribed | partial | `test/index.test.ts` / `test/trigger-system.test.ts` |
|
|
49
|
+
| L-03 | create hybrid loop | none | `LoopCreate(trigger=hybrid)` | `active`, armed, subscribed | partial | `test/index.test.ts` / `test/trigger-system.test.ts` |
|
|
50
|
+
| L-04 | pause active loop | `active` | `LoopDelete(action=pause)` | `paused`, unsubscribed, disarmed | covered wrapper only | `test/index.test.ts` |
|
|
51
|
+
| L-05 | resume paused loop | `paused` | interactive resume / direct store+trigger path | `active`, re-armed, re-subscribed | missing | `test/index.test.ts` |
|
|
52
|
+
| L-06 | delete loop | `active` or `paused` | `LoopDelete(delete)` | removed from store and trigger system | covered | `test/index.test.ts` |
|
|
53
|
+
| L-07 | fire recurring loop | `active` recurring | `onLoopFire` | `fireCount++`, remains active | partial | `test/scheduler.test.ts` / `test/trigger-system.test.ts` |
|
|
54
|
+
| L-08 | fire one-shot loop | `active` non-recurring | `fire` | removed after delivery path | partial | `test/trigger-system.test.ts` / `test/injection.test.ts` |
|
|
55
|
+
| L-09 | maxFires terminal delete | recurring + limit | final fire | removed immediately | covered | `test/index.test.ts` / `test/trigger-system.test.ts` |
|
|
56
|
+
| L-10 | expiry delete | expired active loop | scheduler/session prune | removed | partial | `test/scheduler.test.ts` / `test/store.test.ts` |
|
|
57
|
+
| L-11 | resume-time event loop cleanup | persisted event/hybrid loop from prior session | `expireEventLoops()` | removed | covered store-level only | `test/store.test.ts` |
|
|
58
|
+
| L-12 | backlog loop zero-pending delete | `taskBacklog` loop active | pending count -> 0 | removed | covered | `test/index.test.ts` |
|
|
59
|
+
|
|
60
|
+
### 1.3 Monitor entity transitions
|
|
61
|
+
|
|
62
|
+
| ID | Case | Start | Event | Expected end state | Current coverage | Target file |
|
|
63
|
+
|---|---|---|---|---|---|---|
|
|
64
|
+
| M-01 | create monitor | none | `MonitorCreate` | `running` | covered | `test/index.test.ts` |
|
|
65
|
+
| M-02 | normal completion | `running` | child close code 0 | `completed` retained briefly | covered indirectly | `test/index.test.ts` / `test/monitor-manager.test.ts` |
|
|
66
|
+
| M-03 | error completion | `running` | child close nonzero | `error` retained briefly | partial | `test/monitor-manager.test.ts` |
|
|
67
|
+
| M-04 | child error path | `running` | child `error` | `error` retained, callbacks cleared | missing direct test | `test/monitor-manager.test.ts` |
|
|
68
|
+
| M-05 | stop monitor | `running` | `MonitorStop` | `stopped` | covered | `test/index.test.ts` |
|
|
69
|
+
| M-06 | retain completed monitor | `completed` | before 30s retention expiry | still listed | partial | `test/index.test.ts` |
|
|
70
|
+
| M-07 | prune completed monitor | `completed` | after retention timeout | removed | missing direct test | `test/monitor-manager.test.ts` |
|
|
71
|
+
| M-08 | onDone callback registration | `running` | `onComplete()` | callback stored | missing direct test | `test/monitor-manager.test.ts` |
|
|
72
|
+
| M-09 | onDone direct delivery after completion | `completed` | registered callback or immediate completed registration | wake delivered once | covered | `test/index.test.ts` |
|
|
73
|
+
| M-10 | onDone does not depend on event dispatch | `completed` | event suppressed | direct wake still delivered | covered | `test/index.test.ts` |
|
|
74
|
+
|
|
75
|
+
### 1.4 Notification / wake entity transitions
|
|
76
|
+
|
|
77
|
+
| ID | Case | Start | Event | Expected end state | Current coverage | Target file |
|
|
78
|
+
|---|---|---|---|---|---|---|
|
|
79
|
+
| N-01 | idle immediate delivery | none | `loop:fire` while idle | sent immediately | covered | `test/injection.test.ts` |
|
|
80
|
+
| N-02 | active-agent buffering | none | `loop:fire` while agent active | queued, not sent yet | covered | `test/injection.test.ts` |
|
|
81
|
+
| N-03 | flush on idle | queued | `agent_end` / idle flush | delivered | covered | `test/injection.test.ts` |
|
|
82
|
+
| N-04 | recurring dedupe | queued recurring | newer same-loop fire | prior pending replaced | covered | `test/injection.test.ts` |
|
|
83
|
+
| N-05 | one-shot isolation | queued one-shot | separate one-shot fire | both preserved independently | missing explicit test | `test/injection.test.ts` |
|
|
84
|
+
| N-06 | autoTask drop on zero pending at delivery | queued autoTask wake | pending count -> 0 | dropped and cleanup requested | covered | `test/injection.test.ts` |
|
|
85
|
+
| N-07 | force flush on agent_end even with pending messages | queued wake | `agent_end` with `hasPendingMessages()` true | delivered | covered | `test/index.test.ts` |
|
|
86
|
+
| N-08 | clear on session switch | queued wake | `session_switch` | cleared, not delivered | missing direct test | `test/injection.test.ts` |
|
|
87
|
+
| N-09 | clear on shutdown | queued wake | `session_shutdown` | cleared, not delivered | missing direct test | `test/injection.test.ts` |
|
|
88
|
+
|
|
89
|
+
## 2. Invariant tests
|
|
90
|
+
|
|
91
|
+
These are properties that should remain true across many transitions.
|
|
92
|
+
|
|
93
|
+
### 2.1 Task invariants
|
|
94
|
+
|
|
95
|
+
| ID | Invariant | Assertion |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| TI-01 | ids are monotonic | creating after prune never reuses an old id |
|
|
98
|
+
| TI-02 | at most one status per task | task cannot be both `completed` and counted as pending/in-progress |
|
|
99
|
+
| TI-03 | pending count excludes completed | `pendingCount()` only counts `pending` and `in_progress` |
|
|
100
|
+
| TI-04 | pruning preserves active work | sweeping completed tasks never removes pending/in-progress tasks |
|
|
101
|
+
| TI-05 | retention policy is event-driven, not implicit | completed tasks remain listed until an explicit cleanup trigger occurs |
|
|
102
|
+
|
|
103
|
+
### 2.2 Loop invariants
|
|
104
|
+
|
|
105
|
+
| ID | Invariant | Assertion |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| LI-01 | terminal loops do not remain active | non-recurring, expired, and maxFires-complete loops are removed |
|
|
108
|
+
| LI-02 | paused loops do not fire | paused loops have no active scheduler/event behavior |
|
|
109
|
+
| LI-03 | backlog loops only self-delete on zero pending | ordinary `tasks:created` watchers stay alive |
|
|
110
|
+
| LI-04 | recurring fire count is monotonic | `fireCount` never decreases |
|
|
111
|
+
| LI-05 | max loop count enforced | cannot exceed 25 active loops |
|
|
112
|
+
|
|
113
|
+
### 2.3 Monitor invariants
|
|
114
|
+
|
|
115
|
+
| ID | Invariant | Assertion |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| MI-01 | monitor has one terminal status | running cannot transition to multiple terminal statuses |
|
|
118
|
+
| MI-02 | onDone wake delivered at most once | direct callback + event path do not duplicate the wake |
|
|
119
|
+
| MI-03 | stopped monitors do not later complete | stop path cannot also emit completion wake |
|
|
120
|
+
| MI-04 | retention timeout eventually prunes completed/error monitors | terminal monitors do not linger indefinitely |
|
|
121
|
+
|
|
122
|
+
### 2.4 Notification invariants
|
|
123
|
+
|
|
124
|
+
| ID | Invariant | Assertion |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| NI-01 | recurring pending wake key is unique per loop | one recurring loop has at most one buffered wake |
|
|
127
|
+
| NI-02 | one-shot wake key uniqueness | one-shot deliveries do not overwrite each other |
|
|
128
|
+
| NI-03 | no delivery while agent active | queued wakes do not send until flush conditions are met |
|
|
129
|
+
| NI-04 | dropped autoTask wake requests cleanup | zero-pending autoTask drops trigger cleanup side effect |
|
|
130
|
+
| NI-05 | session switch clears buffered wakes | pending notifications are not delivered into a new session unexpectedly |
|
|
131
|
+
|
|
132
|
+
## 3. Scenario tests
|
|
133
|
+
|
|
134
|
+
These validate cross-entity coordination and races.
|
|
135
|
+
|
|
136
|
+
### 3.1 Backlog orchestration scenarios
|
|
137
|
+
|
|
138
|
+
| ID | Scenario | Expected outcome | Current coverage |
|
|
139
|
+
|---|---|---|---|
|
|
140
|
+
| S-01 | create 5 native tasks | worker loop auto-created once | covered |
|
|
141
|
+
| S-02 | create 6th+ task | no duplicate worker loop | covered |
|
|
142
|
+
| S-03 | agent busy during worker bootstrap | wake buffered then flushed at `agent_end` | covered |
|
|
143
|
+
| S-04 | queue clears | worker loop auto-deletes | covered |
|
|
144
|
+
| S-05 | manual `taskBacklog` loop queue clears | manual backlog loop auto-deletes | covered |
|
|
145
|
+
| S-06 | plain `tasks:created` watcher queue clears | ordinary watcher stays active | covered |
|
|
146
|
+
|
|
147
|
+
### 3.2 Monitor completion scenarios
|
|
148
|
+
|
|
149
|
+
| ID | Scenario | Expected outcome | Current coverage |
|
|
150
|
+
|---|---|---|---|
|
|
151
|
+
| S-07 | monitor completes while idle | model receives onDone wake | covered |
|
|
152
|
+
| S-08 | monitor completes while agent busy | wake buffered and later delivered | partial |
|
|
153
|
+
| S-09 | monitor event dispatch missed | direct callback path still wakes model | covered |
|
|
154
|
+
| S-10 | onDone loop created after monitor already finished | immediate direct delivery or expiry behavior is correct | partial |
|
|
155
|
+
|
|
156
|
+
### 3.3 Task cleanup scenarios
|
|
157
|
+
|
|
158
|
+
| ID | Scenario | Expected outcome | Current coverage |
|
|
159
|
+
|---|---|---|---|
|
|
160
|
+
| S-11 | successful `git commit` | completed tasks pruned, ids preserved | covered |
|
|
161
|
+
| S-12 | failed commit | completed tasks retained | covered |
|
|
162
|
+
| S-13 | non-commit bash tool | completed tasks retained | covered |
|
|
163
|
+
| S-14 | zero-pending autoTask drop | completed tasks swept and wake not delivered | covered |
|
|
164
|
+
|
|
165
|
+
### 3.4 Session lifecycle scenarios
|
|
166
|
+
|
|
167
|
+
| ID | Scenario | Expected outcome | Current coverage |
|
|
168
|
+
|---|---|---|---|
|
|
169
|
+
| S-15 | session switch with queued wakes | queued notifications cleared | missing |
|
|
170
|
+
| S-16 | session shutdown with queued wakes | queued notifications cleared | missing |
|
|
171
|
+
| S-17 | resume with stale event/hybrid loops | expired event/hybrid loops pruned | partial |
|
|
172
|
+
| S-18 | memory scope new non-resume session | loops cleared | missing direct integration |
|
|
173
|
+
| S-19 | session-scoped storage path | tasks/loops isolated by session id | covered for tasks, partial for loops |
|
|
174
|
+
|
|
175
|
+
## 4. Priority order for implementation
|
|
176
|
+
|
|
177
|
+
### Phase A — Close the highest-value gaps first
|
|
178
|
+
|
|
179
|
+
These gaps most directly reduce refactor risk:
|
|
180
|
+
|
|
181
|
+
1. `T-03`, `T-05`, `T-06`, `T-10`
|
|
182
|
+
2. `L-05`, `L-08`
|
|
183
|
+
3. `M-04`, `M-07`, `M-08`
|
|
184
|
+
4. `N-05`, `N-08`, `N-09`
|
|
185
|
+
5. `S-08`, `S-10`, `S-15`, `S-16`, `S-18`
|
|
186
|
+
|
|
187
|
+
### Phase B — Extract testable helpers before reducer migration
|
|
188
|
+
|
|
189
|
+
Once Phase A closes, we should be able to extract helper functions or reducers for:
|
|
190
|
+
|
|
191
|
+
- task transition application
|
|
192
|
+
- loop terminal-deletion rules
|
|
193
|
+
- notification queue keying and flush eligibility
|
|
194
|
+
- monitor completion side effects
|
|
195
|
+
|
|
196
|
+
### Phase C — Add reducer-specific tests
|
|
197
|
+
|
|
198
|
+
After reducer extraction, add pure transition tests with no Pi mocks:
|
|
199
|
+
|
|
200
|
+
- `reduceTask(state, event)`
|
|
201
|
+
- `reduceLoop(state, event)`
|
|
202
|
+
- `reduceMonitor(state, event)`
|
|
203
|
+
- `reduceNotification(state, event)`
|
|
204
|
+
|
|
205
|
+
Each should assert:
|
|
206
|
+
|
|
207
|
+
- next state
|
|
208
|
+
- emitted effects
|
|
209
|
+
- no hidden mutation of unrelated entities
|
|
210
|
+
|
|
211
|
+
## 5. Goal feature prerequisites
|
|
212
|
+
|
|
213
|
+
The Goal feature should not land until these test conditions are met:
|
|
214
|
+
|
|
215
|
+
- all Phase A gaps above are closed
|
|
216
|
+
- notification delivery is deterministic under idle/active/session-switch conditions
|
|
217
|
+
- backlog loop ownership and cleanup are explicit and tested
|
|
218
|
+
- monitor completion semantics are single-delivery and race-resistant
|
|
219
|
+
- task pruning policy is stable and monotonic-id safe
|
|
220
|
+
|
|
221
|
+
## 6. Minimum deliverables for the next subtasks
|
|
222
|
+
|
|
223
|
+
### Task #3 — Add entity transition tests
|
|
224
|
+
|
|
225
|
+
Deliver:
|
|
226
|
+
|
|
227
|
+
- direct tests for missing task, loop, monitor, and notification transitions
|
|
228
|
+
- no production behavior changes required
|
|
229
|
+
|
|
230
|
+
### Task #4 — Add scenario state tests
|
|
231
|
+
|
|
232
|
+
Deliver:
|
|
233
|
+
|
|
234
|
+
- end-to-end race and lifecycle scenarios from section 3
|
|
235
|
+
- especially session-switch and monitor-busy completion paths
|
|
236
|
+
|
|
237
|
+
### Task #5 — Design reducer event model
|
|
238
|
+
|
|
239
|
+
Must align reducer event names to the transition cases above so the matrix remains the stable contract.
|
|
240
|
+
|
|
241
|
+
## 7. Summary
|
|
242
|
+
|
|
243
|
+
This matrix turns the current behavior into a test contract.
|
|
244
|
+
|
|
245
|
+
The most important principle for the refactor is:
|
|
246
|
+
|
|
247
|
+
> Preserve behavior first, then simplify representation.
|
|
248
|
+
|
|
249
|
+
If a future reducer/state-machine change cannot be mapped back to one of the transition or scenario rows in this document, it is probably changing semantics, not just structure.
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# pi-loop State and Transition Inventory
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
This document inventories the current explicit and implicit state for:
|
|
6
|
+
|
|
7
|
+
- native tasks
|
|
8
|
+
- loops
|
|
9
|
+
- monitors
|
|
10
|
+
- pending notifications / wake delivery
|
|
11
|
+
- cross-entity coordination paths
|
|
12
|
+
|
|
13
|
+
It is the baseline for the planned reducer/state-machine refactor.
|
|
14
|
+
|
|
15
|
+
## 1. Native task state
|
|
16
|
+
|
|
17
|
+
### Explicit task states
|
|
18
|
+
|
|
19
|
+
Defined in `src/task-types.ts`:
|
|
20
|
+
|
|
21
|
+
- `pending`
|
|
22
|
+
- `in_progress`
|
|
23
|
+
- `completed`
|
|
24
|
+
|
|
25
|
+
### Persisted task fields
|
|
26
|
+
|
|
27
|
+
Defined in `TaskEntry`:
|
|
28
|
+
|
|
29
|
+
- `id`
|
|
30
|
+
- `subject`
|
|
31
|
+
- `description`
|
|
32
|
+
- `status`
|
|
33
|
+
- `createdAt`
|
|
34
|
+
- `updatedAt`
|
|
35
|
+
- `completedAt?`
|
|
36
|
+
- `metadata?`
|
|
37
|
+
|
|
38
|
+
### Persisted task container state
|
|
39
|
+
|
|
40
|
+
Defined in `TaskStore`:
|
|
41
|
+
|
|
42
|
+
- `nextId`
|
|
43
|
+
- `tasks: Map<string, TaskEntry>`
|
|
44
|
+
- optional `filePath` / `lockPath`
|
|
45
|
+
|
|
46
|
+
### Task transitions
|
|
47
|
+
|
|
48
|
+
| From | Event | To | Notes |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| none | `TaskCreate` / `/tasks` quick-create / autoTask fallback create | `pending` | increments monotonic `nextId` |
|
|
51
|
+
| `pending` | `TaskUpdate(status=in_progress)` / interactive `> Start` | `in_progress` | active work starts |
|
|
52
|
+
| `pending` | `TaskUpdate(status=completed)` / interactive `ok Complete` | `completed` | `completedAt` set |
|
|
53
|
+
| `in_progress` | `TaskUpdate(status=completed)` / interactive `ok Complete` | `completed` | `completedAt` set |
|
|
54
|
+
| `in_progress` | `TaskUpdate(status=pending)` / interactive `* Return to pending` | `pending` | re-queued |
|
|
55
|
+
| `completed` | `TaskUpdate(status=pending)` / interactive `* Reopen` | `pending` | currently clears state semantically, but leaves `completedAt` populated |
|
|
56
|
+
| any existing | `TaskDelete` / interactive delete | deleted | removed from store |
|
|
57
|
+
| `completed` | `cleanDoneTasks()` / `sweepCompleted()` | pruned | removed from store, `nextId` preserved |
|
|
58
|
+
|
|
59
|
+
### Hidden / implicit task state
|
|
60
|
+
|
|
61
|
+
These are not encoded as task status values, but materially affect behavior:
|
|
62
|
+
|
|
63
|
+
- task provider mode: `tasksAvailable` vs `nativeTaskStore`
|
|
64
|
+
- storage scope: `memory` / `session` / `project`
|
|
65
|
+
- backlog summary state derived in widget: `count`, `active`, `next`
|
|
66
|
+
- completed-task retention policy is event-driven, not time-driven
|
|
67
|
+
- `completedAt` remains populated if a completed task is reopened
|
|
68
|
+
|
|
69
|
+
### Task sharp edges
|
|
70
|
+
|
|
71
|
+
- `completed` and `pruned` are separate lifecycle phases but only one is explicit.
|
|
72
|
+
- Reopening a completed task does not clear `completedAt`; this is harmless now but muddies semantics.
|
|
73
|
+
- Provider mode (`pi-tasks` vs native fallback) is global session state, not entity state.
|
|
74
|
+
- Cleanup policy is distributed across commit hooks, loop zero-pending paths, and explicit delete/sweep flows.
|
|
75
|
+
|
|
76
|
+
## 2. Loop state
|
|
77
|
+
|
|
78
|
+
### Explicit loop states
|
|
79
|
+
|
|
80
|
+
Defined in `src/types.ts`:
|
|
81
|
+
|
|
82
|
+
- `active`
|
|
83
|
+
- `paused`
|
|
84
|
+
|
|
85
|
+
### Persisted loop fields
|
|
86
|
+
|
|
87
|
+
Defined in `LoopEntry`:
|
|
88
|
+
|
|
89
|
+
- `id`
|
|
90
|
+
- `prompt`
|
|
91
|
+
- `trigger`
|
|
92
|
+
- `status`
|
|
93
|
+
- `recurring`
|
|
94
|
+
- `createdAt`
|
|
95
|
+
- `updatedAt`
|
|
96
|
+
- `expiresAt`
|
|
97
|
+
- `autoTask?`
|
|
98
|
+
- `taskBacklog?`
|
|
99
|
+
- `readOnly?`
|
|
100
|
+
- `maxFires?`
|
|
101
|
+
- `fireCount?`
|
|
102
|
+
|
|
103
|
+
### Trigger variants
|
|
104
|
+
|
|
105
|
+
- `cron`
|
|
106
|
+
- `event`
|
|
107
|
+
- `hybrid`
|
|
108
|
+
|
|
109
|
+
### Loop transitions
|
|
110
|
+
|
|
111
|
+
| From | Event | To | Notes |
|
|
112
|
+
|---|---|---|---|
|
|
113
|
+
| none | `LoopCreate` / auto worker creation / monitor onDone helper | `active` | persisted in store |
|
|
114
|
+
| `active` | `LoopDelete(action=pause)` / interactive pause | `paused` | subscriptions/timers removed |
|
|
115
|
+
| `paused` | `LoopDelete(action=resume)` / interactive resume | `active` | subscriptions/timers re-added |
|
|
116
|
+
| `active` | `LoopDelete(delete)` / interactive delete | deleted | removed from store and trigger system |
|
|
117
|
+
| `active` | `onLoopFire` with recurring loop | `active` | `fireCount++`; may queue wake |
|
|
118
|
+
| `active` | `onLoopFire` and `maxFires` reached | deleted | removed immediately |
|
|
119
|
+
| `active` | one-shot event or one-shot monitor completion loop fired | deleted | trigger system removes after fire |
|
|
120
|
+
| `active` | `cleanupTaskBacklogLoops()` and zero pending tasks | deleted | only for `taskBacklog` or auto worker loop |
|
|
121
|
+
| `active` | `clearExpired()` / expiry boundary | deleted | recurring lifetime cap |
|
|
122
|
+
| `active` | `expireEventLoops()` on resumed session | deleted | old event/hybrid loops from prior session pruned |
|
|
123
|
+
|
|
124
|
+
### Hidden / implicit loop state
|
|
125
|
+
|
|
126
|
+
These states are currently distributed across multiple structures:
|
|
127
|
+
|
|
128
|
+
- scheduler armed/not-armed: `CronScheduler.fireTimes`
|
|
129
|
+
- event subscribed/not-subscribed: `TriggerSystem.eventSubscriptions`
|
|
130
|
+
- hybrid debounce timer pending/not-pending: `TriggerSystem.hybridTimers`
|
|
131
|
+
- last fire timestamp for debounce: `TriggerSystem.lastFireTime`
|
|
132
|
+
- wake queued/not-queued: `pendingNotifications`
|
|
133
|
+
- wake dedupe keying: recurring loops use `loop:<id>`, one-shot uses `loop:<id>:<timestamp>`
|
|
134
|
+
- worker-loop classification is partly semantic (`prompt` string match) and partly structural (`taskBacklog`)
|
|
135
|
+
- bootstrapped-against-backlog is an effect, not persisted state
|
|
136
|
+
|
|
137
|
+
### Loop sharp edges
|
|
138
|
+
|
|
139
|
+
- `active` covers multiple sub-states: armed, waiting, queued, delivering, debounced.
|
|
140
|
+
- Auto worker loop detection still depends partly on prompt equality (`AUTO_TASK_WORKER_PROMPT`).
|
|
141
|
+
- Task backlog semantics are split between `taskBacklog`, `tasks:created` trigger shape, and prompt heuristics.
|
|
142
|
+
- Monitor `onDone` loops are stored as generic loops, but delivered by a separate direct completion path.
|
|
143
|
+
|
|
144
|
+
## 3. Monitor state
|
|
145
|
+
|
|
146
|
+
### Explicit monitor states
|
|
147
|
+
|
|
148
|
+
Defined in `src/types.ts`:
|
|
149
|
+
|
|
150
|
+
- `running`
|
|
151
|
+
- `completed`
|
|
152
|
+
- `error`
|
|
153
|
+
- `stopped`
|
|
154
|
+
|
|
155
|
+
### Persisted/in-memory monitor fields
|
|
156
|
+
|
|
157
|
+
Defined in `MonitorEntry` / `MonitorProcess`:
|
|
158
|
+
|
|
159
|
+
- `id`
|
|
160
|
+
- `command`
|
|
161
|
+
- `description?`
|
|
162
|
+
- `timeout`
|
|
163
|
+
- `status`
|
|
164
|
+
- `startedAt`
|
|
165
|
+
- `completedAt?`
|
|
166
|
+
- `exitCode?`
|
|
167
|
+
- `outputLines`
|
|
168
|
+
- `outputBuffer`
|
|
169
|
+
- `pid`
|
|
170
|
+
- `proc`
|
|
171
|
+
- `abortController`
|
|
172
|
+
- `waiters`
|
|
173
|
+
- `completionCallbacks`
|
|
174
|
+
|
|
175
|
+
### Monitor transitions
|
|
176
|
+
|
|
177
|
+
| From | Event | To | Notes |
|
|
178
|
+
|---|---|---|---|
|
|
179
|
+
| none | `MonitorCreate` | `running` | child process spawned |
|
|
180
|
+
| `running` | child `close` exit code 0 | `completed` | emits `monitor:done` and direct completion callbacks |
|
|
181
|
+
| `running` | child `close` nonzero | `error` | emits `monitor:error` |
|
|
182
|
+
| `running` | child `error` | `error` | emits `monitor:error` |
|
|
183
|
+
| `running` | `MonitorStop` | `stopped` | SIGTERM then SIGKILL fallback |
|
|
184
|
+
| `completed/error` | 30s retention timeout | pruned | removed from `processes` map |
|
|
185
|
+
|
|
186
|
+
### Hidden / implicit monitor state
|
|
187
|
+
|
|
188
|
+
- completion callbacks registered/not-registered for `onDone`
|
|
189
|
+
- monitor retained-for-UI vs logically finished
|
|
190
|
+
- event-based completion signaling and direct callback signaling both exist
|
|
191
|
+
- process liveness is split between child process reality and `entry.status`
|
|
192
|
+
|
|
193
|
+
### Monitor sharp edges
|
|
194
|
+
|
|
195
|
+
- Completion has two signaling channels: event bus and direct callback.
|
|
196
|
+
- `stopped` bypasses the `finish()` helper and therefore differs from `completed/error` paths.
|
|
197
|
+
- Retention window is time-based but only in memory; no persisted monitor store exists.
|
|
198
|
+
|
|
199
|
+
## 4. Pending notification / wake delivery state
|
|
200
|
+
|
|
201
|
+
This is the largest implicit state area today.
|
|
202
|
+
|
|
203
|
+
### Structures
|
|
204
|
+
|
|
205
|
+
- `agentRunning: boolean`
|
|
206
|
+
- `_latestCtx?.hasPendingMessages()`
|
|
207
|
+
- `pendingNotifications: Map<string, PendingNotification>`
|
|
208
|
+
- `flushPromise?: Promise<void>`
|
|
209
|
+
|
|
210
|
+
### Wake pipeline states
|
|
211
|
+
|
|
212
|
+
These are not explicit enum values today, but they exist behaviorally:
|
|
213
|
+
|
|
214
|
+
- generated (`buildLoopFireMessage`)
|
|
215
|
+
- queued (`pendingNotifications.set(...)`)
|
|
216
|
+
- deduped/replaced (same recurring loop key)
|
|
217
|
+
- blocked by active agent
|
|
218
|
+
- blocked by pending messages
|
|
219
|
+
- delivered via `pi.sendMessage(..., { deliverAs: "steer", triggerTurn: true })`
|
|
220
|
+
- dropped due to zero pending tasks at delivery time
|
|
221
|
+
- cleared on `session_shutdown` / `session_switch`
|
|
222
|
+
|
|
223
|
+
### Notification transitions
|
|
224
|
+
|
|
225
|
+
| From | Event | To | Notes |
|
|
226
|
+
|---|---|---|---|
|
|
227
|
+
| none | `onLoopFire` / monitor completion / backlog bootstrap | generated | message built |
|
|
228
|
+
| generated | `queueOrDeliverNotification` | queued | stored in map |
|
|
229
|
+
| queued | `flushPendingNotifications` while idle | delivered | sends custom message |
|
|
230
|
+
| queued recurring | newer fire same key | replaced | latest prompt wins |
|
|
231
|
+
| queued | delivery-time pending tasks == 0 and `autoTask` true | dropped | triggers completed-task cleanup |
|
|
232
|
+
| queued | `session_shutdown` / `session_switch` | cleared | dropped without delivery |
|
|
233
|
+
|
|
234
|
+
### Hidden / implicit notification state
|
|
235
|
+
|
|
236
|
+
- one-shot vs recurring dedupe is encoded in key generation
|
|
237
|
+
- delivery eligibility is a conjunction of agent idle + pending-message policy + autoTask pending count
|
|
238
|
+
- some wake sources are event-driven, some direct-callback driven
|
|
239
|
+
|
|
240
|
+
## 5. Cross-entity coordination state
|
|
241
|
+
|
|
242
|
+
These are coordination rules that span multiple entities and should likely become explicit reducer events.
|
|
243
|
+
|
|
244
|
+
### Existing coordination rules
|
|
245
|
+
|
|
246
|
+
1. **Task backlog threshold -> auto worker loop creation**
|
|
247
|
+
- when native pending count reaches `>= 5`
|
|
248
|
+
- creates a hybrid task-backlog loop
|
|
249
|
+
|
|
250
|
+
2. **Zero pending tasks -> backlog loop deletion**
|
|
251
|
+
- backlog worker loops are deleted when pending count reaches `0`
|
|
252
|
+
|
|
253
|
+
3. **Successful git commit -> completed task pruning**
|
|
254
|
+
- `tool_execution_end` for successful `bash` `git commit` calls `cleanDoneTasks()`
|
|
255
|
+
|
|
256
|
+
4. **Monitor completion -> onDone wake**
|
|
257
|
+
- direct callback path for `MonitorCreate(..., onDone)`
|
|
258
|
+
- generic event path remains for manual `LoopCreate trigger='monitor:done'`
|
|
259
|
+
|
|
260
|
+
5. **Session lifecycle -> store/scheduler/reset behavior**
|
|
261
|
+
- `turn_start`, `before_agent_start`, `agent_start`, `agent_end`, `session_switch`, `session_shutdown`
|
|
262
|
+
|
|
263
|
+
### Hidden coordinator state
|
|
264
|
+
|
|
265
|
+
- whether `pi-tasks` is available is global mutable session state
|
|
266
|
+
- whether native task tools are registered is global mutable session state
|
|
267
|
+
- current session id affects storage paths but is not part of entity state
|
|
268
|
+
- worker loop ownership is inferred rather than strongly modeled
|
|
269
|
+
|
|
270
|
+
## 6. Duplicate / overlapping state encodings
|
|
271
|
+
|
|
272
|
+
### Loop status is spread across four places
|
|
273
|
+
|
|
274
|
+
A loop's real status is currently determined by all of:
|
|
275
|
+
|
|
276
|
+
- `LoopEntry.status`
|
|
277
|
+
- `CronScheduler.fireTimes`
|
|
278
|
+
- `TriggerSystem.eventSubscriptions`
|
|
279
|
+
- `pendingNotifications`
|
|
280
|
+
|
|
281
|
+
A loop can be `active` in the store while simultaneously:
|
|
282
|
+
|
|
283
|
+
- not armed in scheduler
|
|
284
|
+
- unsubscribed from events
|
|
285
|
+
- queued for delivery
|
|
286
|
+
- already logically terminal but not yet deleted
|
|
287
|
+
|
|
288
|
+
### Monitor completion is dual-path
|
|
289
|
+
|
|
290
|
+
Completion signaling uses both:
|
|
291
|
+
|
|
292
|
+
- `monitor:done` / `monitor:error` events
|
|
293
|
+
- `completionCallbacks`
|
|
294
|
+
|
|
295
|
+
This is correct for reliability today, but it means state transitions are duplicated conceptually.
|
|
296
|
+
|
|
297
|
+
### Task completion and pruning are separate lifecycles
|
|
298
|
+
|
|
299
|
+
Task lifecycle currently has at least:
|
|
300
|
+
|
|
301
|
+
- active work state (`pending` / `in_progress`)
|
|
302
|
+
- finished state (`completed`)
|
|
303
|
+
- retention state (still listed vs swept)
|
|
304
|
+
|
|
305
|
+
Only the first two are explicit.
|
|
306
|
+
|
|
307
|
+
## 7. Suggested normalization targets for the reducer refactor
|
|
308
|
+
|
|
309
|
+
### Task machine
|
|
310
|
+
|
|
311
|
+
Recommended explicit states:
|
|
312
|
+
|
|
313
|
+
- `pending`
|
|
314
|
+
- `in_progress`
|
|
315
|
+
- `completed_retained`
|
|
316
|
+
- `pruned` (terminal/effect boundary, may remain implicit in storage)
|
|
317
|
+
|
|
318
|
+
Recommended explicit events:
|
|
319
|
+
|
|
320
|
+
- `TASK_CREATED`
|
|
321
|
+
- `TASK_STARTED`
|
|
322
|
+
- `TASK_COMPLETED`
|
|
323
|
+
- `TASK_REOPENED`
|
|
324
|
+
- `TASK_DELETED`
|
|
325
|
+
- `TASKS_PRUNED`
|
|
326
|
+
|
|
327
|
+
### Loop machine
|
|
328
|
+
|
|
329
|
+
Recommended explicit states:
|
|
330
|
+
|
|
331
|
+
- `active_idle`
|
|
332
|
+
- `active_debounced`
|
|
333
|
+
- `queued_for_delivery`
|
|
334
|
+
- `paused`
|
|
335
|
+
- `completed_terminal`
|
|
336
|
+
- `deleted`
|
|
337
|
+
|
|
338
|
+
Recommended explicit events:
|
|
339
|
+
|
|
340
|
+
- `LOOP_CREATED`
|
|
341
|
+
- `LOOP_ARMED`
|
|
342
|
+
- `LOOP_FIRED`
|
|
343
|
+
- `LOOP_QUEUED`
|
|
344
|
+
- `LOOP_DELIVERED`
|
|
345
|
+
- `LOOP_PAUSED`
|
|
346
|
+
- `LOOP_RESUMED`
|
|
347
|
+
- `LOOP_DELETED`
|
|
348
|
+
- `LOOP_EXPIRED`
|
|
349
|
+
- `LOOP_MAX_FIRES_REACHED`
|
|
350
|
+
|
|
351
|
+
### Monitor machine
|
|
352
|
+
|
|
353
|
+
Recommended explicit states:
|
|
354
|
+
|
|
355
|
+
- `running`
|
|
356
|
+
- `completed_retained`
|
|
357
|
+
- `error_retained`
|
|
358
|
+
- `stopped_retained`
|
|
359
|
+
- `pruned`
|
|
360
|
+
|
|
361
|
+
Recommended explicit events:
|
|
362
|
+
|
|
363
|
+
- `MONITOR_CREATED`
|
|
364
|
+
- `MONITOR_OUTPUT`
|
|
365
|
+
- `MONITOR_COMPLETED`
|
|
366
|
+
- `MONITOR_ERRORED`
|
|
367
|
+
- `MONITOR_STOPPED`
|
|
368
|
+
- `MONITOR_PRUNED`
|
|
369
|
+
- `MONITOR_ONDONE_REGISTERED`
|
|
370
|
+
|
|
371
|
+
### Notification machine
|
|
372
|
+
|
|
373
|
+
Recommended explicit states:
|
|
374
|
+
|
|
375
|
+
- `queued`
|
|
376
|
+
- `blocked_by_agent`
|
|
377
|
+
- `blocked_by_pending_messages`
|
|
378
|
+
- `ready_to_deliver`
|
|
379
|
+
- `delivered`
|
|
380
|
+
- `dropped`
|
|
381
|
+
- `cleared`
|
|
382
|
+
|
|
383
|
+
Recommended explicit events:
|
|
384
|
+
|
|
385
|
+
- `NOTIFICATION_QUEUED`
|
|
386
|
+
- `NOTIFICATION_REPLACED`
|
|
387
|
+
- `NOTIFICATION_DELIVERED`
|
|
388
|
+
- `NOTIFICATION_DROPPED`
|
|
389
|
+
- `NOTIFICATION_CLEARED`
|
|
390
|
+
|
|
391
|
+
## 8. Refactor hotspots
|
|
392
|
+
|
|
393
|
+
These are the highest-value places to extract into reducer-driven logic:
|
|
394
|
+
|
|
395
|
+
1. `src/index.ts`
|
|
396
|
+
- pending notification logic
|
|
397
|
+
- task backlog loop creation/cleanup
|
|
398
|
+
- commit-driven completed-task pruning
|
|
399
|
+
- session lifecycle hooks
|
|
400
|
+
|
|
401
|
+
2. `src/trigger-system.ts`
|
|
402
|
+
- event subscription vs fire vs terminal deletion
|
|
403
|
+
|
|
404
|
+
3. `src/scheduler.ts`
|
|
405
|
+
- active/armed/expired/maxFires scheduling transitions
|
|
406
|
+
|
|
407
|
+
4. `src/monitor-manager.ts`
|
|
408
|
+
- unify retained terminal states and completion effect dispatch
|
|
409
|
+
|
|
410
|
+
5. `src/task-store.ts` + native task tool handlers
|
|
411
|
+
- completion retention vs pruning policy
|
|
412
|
+
|
|
413
|
+
## 9. Minimum next test matrix enabled by this inventory
|
|
414
|
+
|
|
415
|
+
The next test pass should assert:
|
|
416
|
+
|
|
417
|
+
- a loop cannot remain `active` after terminal deletion conditions
|
|
418
|
+
- a monitor `onDone` wake is delivered exactly once
|
|
419
|
+
- a task backlog loop with zero pending tasks is eventually deleted
|
|
420
|
+
- completed tasks can be pruned without resetting `nextId`
|
|
421
|
+
- recurring wake dedupe preserves only the latest queued prompt
|
|
422
|
+
- session switch clears pending notifications but preserves the intended persisted entities
|
|
423
|
+
|
|
424
|
+
## 10. Summary
|
|
425
|
+
|
|
426
|
+
Current behavior works, but state is encoded across:
|
|
427
|
+
|
|
428
|
+
- persisted entity fields
|
|
429
|
+
- scheduler maps
|
|
430
|
+
- event subscription maps
|
|
431
|
+
- callback arrays
|
|
432
|
+
- booleans
|
|
433
|
+
- pending wake queues
|
|
434
|
+
- provider-mode globals
|
|
435
|
+
|
|
436
|
+
The reducer/state-machine refactor should make these lifecycle phases explicit, move hidden coordination state into named transitions, and separate pure transition logic from effect execution.
|
package/package.json
CHANGED