@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.
Files changed (87) hide show
  1. package/README.md +20 -9
  2. package/dist/commands/loop-command.d.ts +22 -0
  3. package/dist/commands/loop-command.js +148 -0
  4. package/dist/commands/tasks-command.d.ts +15 -0
  5. package/dist/commands/tasks-command.js +117 -0
  6. package/dist/coordinator.d.ts +35 -0
  7. package/dist/coordinator.js +56 -0
  8. package/dist/goal-coordinator.d.ts +22 -0
  9. package/dist/goal-coordinator.js +28 -0
  10. package/dist/goal-reducer.d.ts +89 -0
  11. package/dist/goal-reducer.js +181 -0
  12. package/dist/goal-store.d.ts +31 -0
  13. package/dist/goal-store.js +298 -0
  14. package/dist/goal-types.d.ts +82 -0
  15. package/dist/goal-types.js +1 -0
  16. package/dist/goal-verifier.d.ts +20 -0
  17. package/dist/goal-verifier.js +198 -0
  18. package/dist/index.js +130 -1087
  19. package/dist/loop-reducer.d.ts +63 -0
  20. package/dist/loop-reducer.js +67 -0
  21. package/dist/monitor-completion-coordinator.d.ts +10 -0
  22. package/dist/monitor-completion-coordinator.js +13 -0
  23. package/dist/monitor-manager.d.ts +2 -0
  24. package/dist/monitor-manager.js +107 -29
  25. package/dist/monitor-reducer.d.ts +82 -0
  26. package/dist/monitor-reducer.js +69 -0
  27. package/dist/notification-reducer.d.ts +81 -0
  28. package/dist/notification-reducer.js +65 -0
  29. package/dist/runtime/monitor-ondone-runtime.d.ts +13 -0
  30. package/dist/runtime/monitor-ondone-runtime.js +49 -0
  31. package/dist/runtime/notification-runtime.d.ts +34 -0
  32. package/dist/runtime/notification-runtime.js +152 -0
  33. package/dist/runtime/scope.d.ts +8 -0
  34. package/dist/runtime/scope.js +33 -0
  35. package/dist/runtime/session-runtime.d.ts +39 -0
  36. package/dist/runtime/session-runtime.js +110 -0
  37. package/dist/runtime/task-backlog-runtime.d.ts +36 -0
  38. package/dist/runtime/task-backlog-runtime.js +105 -0
  39. package/dist/runtime/task-rpc.d.ts +19 -0
  40. package/dist/runtime/task-rpc.js +118 -0
  41. package/dist/store.d.ts +7 -4
  42. package/dist/store.js +129 -49
  43. package/dist/task-backlog-coordinator.d.ts +12 -0
  44. package/dist/task-backlog-coordinator.js +22 -0
  45. package/dist/task-reducer.d.ts +66 -0
  46. package/dist/task-reducer.js +76 -0
  47. package/dist/task-store.d.ts +8 -4
  48. package/dist/task-store.js +102 -33
  49. package/dist/tools/loop-tools.d.ts +41 -0
  50. package/dist/tools/loop-tools.js +241 -0
  51. package/dist/tools/monitor-tools.d.ts +25 -0
  52. package/dist/tools/monitor-tools.js +110 -0
  53. package/dist/tools/native-task-tools.d.ts +15 -0
  54. package/dist/tools/native-task-tools.js +127 -0
  55. package/docs/architecture/goal-state-schema.md +505 -0
  56. package/docs/architecture/state-machine-migration.md +546 -0
  57. package/docs/architecture/state-machine-reducer-event-model.md +823 -0
  58. package/docs/architecture/state-machine-test-matrix.md +249 -0
  59. package/docs/architecture/state-machine-transition-map.md +436 -0
  60. package/package.json +1 -1
  61. package/src/commands/loop-command.ts +184 -0
  62. package/src/commands/tasks-command.ts +135 -0
  63. package/src/coordinator.ts +115 -0
  64. package/src/goal-coordinator.ts +58 -0
  65. package/src/goal-reducer.ts +280 -0
  66. package/src/goal-store.ts +315 -0
  67. package/src/goal-types.ts +104 -0
  68. package/src/goal-verifier.ts +241 -0
  69. package/src/index.ts +134 -1147
  70. package/src/loop-reducer.ts +148 -0
  71. package/src/monitor-completion-coordinator.ts +24 -0
  72. package/src/monitor-manager.ts +115 -27
  73. package/src/monitor-reducer.ts +166 -0
  74. package/src/notification-reducer.ts +155 -0
  75. package/src/runtime/monitor-ondone-runtime.ts +75 -0
  76. package/src/runtime/notification-runtime.ts +212 -0
  77. package/src/runtime/scope.ts +37 -0
  78. package/src/runtime/session-runtime.ts +168 -0
  79. package/src/runtime/task-backlog-runtime.ts +163 -0
  80. package/src/runtime/task-rpc.ts +150 -0
  81. package/src/store.ts +132 -50
  82. package/src/task-backlog-coordinator.ts +32 -0
  83. package/src/task-reducer.ts +152 -0
  84. package/src/task-store.ts +103 -31
  85. package/src/tools/loop-tools.ts +304 -0
  86. package/src/tools/monitor-tools.ts +145 -0
  87. package/src/tools/native-task-tools.ts +144 -0
@@ -0,0 +1,127 @@
1
+ import { Type } from "typebox";
2
+ function textResult(msg) {
3
+ return { content: [{ type: "text", text: msg }], details: undefined };
4
+ }
5
+ export function registerNativeTaskTools(options) {
6
+ const { pi, taskStore, evaluateTaskBacklog, updateWidget } = options;
7
+ pi.registerTool({
8
+ name: "TaskCreate",
9
+ label: "TaskCreate",
10
+ description: `Create a task for tracking work across turns. Use when you need to track progress on complex multi-step tasks.
11
+
12
+ Fields:
13
+ - subject: brief actionable title
14
+ - description: detailed requirements
15
+ - metadata: optional tags/metadata`,
16
+ promptGuidelines: [
17
+ "Use TaskCreate to track complex multi-step work across turns.",
18
+ "Break work into small, independently completable tasks. A task should be finishable in one focused session — if a task would take multiple turns, split it further.",
19
+ "TaskCreate accepts `subject` and `description` parameters only — do not invent extra fields unless the schema explicitly adds them.",
20
+ ],
21
+ parameters: Type.Object({
22
+ subject: Type.String({ description: "Brief actionable title for the task" }),
23
+ description: Type.String({ description: "Detailed description of what needs to be done" }),
24
+ }),
25
+ async execute(_toolCallId, params) {
26
+ const entry = taskStore.create(params.subject, params.description);
27
+ pi.events.emit("tasks:created", {
28
+ taskId: entry.id,
29
+ subject: entry.subject,
30
+ description: entry.description,
31
+ status: entry.status,
32
+ });
33
+ const backlog = await evaluateTaskBacklog(taskStore, taskStore.pendingCount());
34
+ updateWidget();
35
+ const autoLoopMsg = backlog.created && backlog.entry
36
+ ? `\nWorker loop #${backlog.entry.id} auto-created`
37
+ : "";
38
+ return Promise.resolve(textResult(`Task #${entry.id} created: ${entry.subject}${autoLoopMsg}`));
39
+ },
40
+ });
41
+ pi.registerTool({
42
+ name: "TaskList",
43
+ label: "TaskList",
44
+ description: "List all tasks with status. Use to check progress and find available work.",
45
+ parameters: Type.Object({}),
46
+ execute() {
47
+ const tasks = taskStore.list();
48
+ if (tasks.length === 0)
49
+ return Promise.resolve(textResult("No tasks."));
50
+ const lines = [];
51
+ const statuses = {
52
+ pending: 0,
53
+ in_progress: 0,
54
+ completed: 0,
55
+ };
56
+ for (const t of tasks) {
57
+ statuses[t.status]++;
58
+ const icon = t.status === "completed" ? "ok" : t.status === "in_progress" ? ">" : "*";
59
+ lines.push(`${icon} #${t.id} [${t.status}] ${t.subject.slice(0, 80)}`);
60
+ }
61
+ lines.unshift(`${tasks.length} tasks (${statuses.pending} pending, ${statuses.in_progress} in progress, ${statuses.completed} done)`);
62
+ return Promise.resolve(textResult(lines.join("\n")));
63
+ },
64
+ });
65
+ pi.registerTool({
66
+ name: "TaskUpdate",
67
+ label: "TaskUpdate",
68
+ description: `Update task status or details. Set status to "in_progress" before starting work, "completed" when done.
69
+
70
+ Statuses: pending → in_progress → completed
71
+ Parameters: id (required), status, subject, description`,
72
+ promptGuidelines: [
73
+ "TaskUpdate uses parameter `id`, not `taskId`.",
74
+ "Accepted parameters: `id` (required), `status`, `subject`, `description`.",
75
+ "When validation fails with 'must have required properties id', you passed `taskId` instead of `id`. Correct silently and retry.",
76
+ ],
77
+ parameters: Type.Object({
78
+ id: Type.String({ description: "Task ID to update" }),
79
+ status: Type.Optional(Type.String({ description: "New status", enum: ["pending", "in_progress", "completed"] })),
80
+ subject: Type.Optional(Type.String({ description: "New title" })),
81
+ description: Type.Optional(Type.String({ description: "New description" })),
82
+ }),
83
+ async execute(_toolCallId, params) {
84
+ const { id, status, subject, description } = params;
85
+ let entry = taskStore.get(id);
86
+ if (!entry)
87
+ return Promise.resolve(textResult(`Task #${id} not found`));
88
+ if (status === "in_progress")
89
+ entry = taskStore.start(id);
90
+ else if (status === "completed")
91
+ entry = taskStore.complete(id);
92
+ else if (status === "pending")
93
+ entry = taskStore.reopen(id);
94
+ if (!entry)
95
+ return Promise.resolve(textResult(`Task #${id} not found`));
96
+ if (subject !== undefined || description !== undefined) {
97
+ entry = taskStore.updateDetails(id, { subject, description });
98
+ }
99
+ if (!entry)
100
+ return Promise.resolve(textResult(`Task #${id} not found`));
101
+ updateWidget();
102
+ const backlog = await evaluateTaskBacklog(taskStore, taskStore.pendingCount());
103
+ const statusMsg = status ? ` → ${status}` : "";
104
+ const autoLoopMsg = backlog.created && backlog.entry
105
+ ? `\nWorker loop #${backlog.entry.id} auto-created`
106
+ : "";
107
+ return Promise.resolve(textResult(`Task #${id} updated${statusMsg}${autoLoopMsg}`));
108
+ },
109
+ });
110
+ pi.registerTool({
111
+ name: "TaskDelete",
112
+ label: "TaskDelete",
113
+ description: "Delete a task by ID. Use for cleaning up completed or irrelevant tasks.",
114
+ parameters: Type.Object({
115
+ id: Type.String({ description: "Task ID to delete" }),
116
+ }),
117
+ async execute(_toolCallId, params) {
118
+ const deleted = taskStore.delete(params.id);
119
+ updateWidget();
120
+ if (deleted) {
121
+ await evaluateTaskBacklog(taskStore, taskStore.pendingCount());
122
+ return Promise.resolve(textResult(`Task #${params.id} deleted`));
123
+ }
124
+ return Promise.resolve(textResult(`Task #${params.id} not found`));
125
+ },
126
+ });
127
+ }
@@ -0,0 +1,505 @@
1
+ # pi-loop Goal State Schema
2
+
3
+ ## Purpose
4
+
5
+ This document defines the reducer-owned state shape for Goals.
6
+
7
+ It builds on:
8
+
9
+ - `docs/architecture/state-machine-transition-map.md`
10
+ - `docs/architecture/state-machine-test-matrix.md`
11
+ - `docs/architecture/state-machine-reducer-event-model.md`
12
+
13
+ The objective is to let Goals compose:
14
+
15
+ - tasks
16
+ - loops
17
+ - monitors
18
+ - verification status
19
+
20
+ without reaching into runtime internals such as child-process handles, scheduler timer maps, event unsubscriptions, or Pi UI state.
21
+
22
+ ---
23
+
24
+ ## 1. Design constraints
25
+
26
+ The Goal schema should satisfy five constraints.
27
+
28
+ 1. It must be reducer-owned data, not ad hoc runtime state.
29
+ 2. It must compose existing entities rather than duplicate them.
30
+ 3. It must support read-only verification before any autonomous mutation logic is added.
31
+ 4. It must separate desired outcomes from execution mechanisms.
32
+ 5. It must remain serializable and testable without Pi mocks.
33
+
34
+ In practice, this means a Goal should reference entities and interpret their state, rather than own live runtime objects.
35
+
36
+ ---
37
+
38
+ ## 2. Conceptual model
39
+
40
+ A Goal is a durable statement of intended progress over a bounded set of work.
41
+
42
+ A Goal should answer four questions:
43
+
44
+ 1. **What are we trying to achieve?**
45
+ 2. **What entities count as evidence of progress?**
46
+ 3. **What counts as success, failure, or blocked state?**
47
+ 4. **What verification should occur next?**
48
+
49
+ A Goal is therefore not just a label. It is a small stateful contract tying execution evidence to a desired outcome.
50
+
51
+ ---
52
+
53
+ ## 3. Core types
54
+
55
+ ### 3.1 Goal lifecycle status
56
+
57
+ ```ts
58
+ export type GoalStatus =
59
+ | "pending"
60
+ | "active"
61
+ | "satisfied"
62
+ | "blocked"
63
+ | "failed"
64
+ | "archived";
65
+ ```
66
+
67
+ ### Semantics
68
+
69
+ - `pending` — defined but not yet activated
70
+ - `active` — currently being pursued and still incomplete
71
+ - `satisfied` — completion criteria have been met
72
+ - `blocked` — progress is stalled on an unmet dependency or failed precondition
73
+ - `failed` — terminal negative outcome under current criteria
74
+ - `archived` — intentionally closed without further verification
75
+
76
+ `pending`, `active`, and `blocked` are the main nonterminal states.
77
+
78
+ ---
79
+
80
+ ### 3.2 Goal verification status
81
+
82
+ Goal lifecycle status should be separate from the current verification result.
83
+
84
+ ```ts
85
+ export type GoalVerificationStatus =
86
+ | "unknown"
87
+ | "checking"
88
+ | "verified"
89
+ | "unverified"
90
+ | "inconclusive";
91
+ ```
92
+
93
+ ### Why separate these?
94
+
95
+ A Goal may be `active` while its verification is `unknown` or `checking`.
96
+ A Goal may be `blocked` while its latest verification is `unverified`.
97
+ A Goal may become `satisfied` only after a `verified` result is observed.
98
+
99
+ This avoids collapsing operational progress and evidence quality into one field.
100
+
101
+ ---
102
+
103
+ ### 3.3 Goal entry
104
+
105
+ ```ts
106
+ export interface GoalEntry {
107
+ id: string;
108
+ title: string;
109
+ description: string;
110
+ status: GoalStatus;
111
+ verificationStatus: GoalVerificationStatus;
112
+
113
+ createdAt: number;
114
+ updatedAt: number;
115
+ activatedAt?: number;
116
+ resolvedAt?: number;
117
+
118
+ scope: GoalScope;
119
+ criteria: GoalCriteria;
120
+ progress: GoalProgressSnapshot;
121
+ verification: GoalVerificationState;
122
+
123
+ metadata?: Record<string, unknown>;
124
+ }
125
+ ```
126
+
127
+ This is the reducer-owned unit of record.
128
+
129
+ ---
130
+
131
+ ## 4. Goal scope
132
+
133
+ A Goal must explicitly declare which entities it cares about.
134
+
135
+ ```ts
136
+ export interface GoalScope {
137
+ taskIds?: string[];
138
+ loopIds?: string[];
139
+ monitorIds?: string[];
140
+
141
+ tags?: string[];
142
+ subjectPrefixes?: string[];
143
+
144
+ includeFutureMatchingTasks?: boolean;
145
+ includeFutureMatchingLoops?: boolean;
146
+ includeFutureMatchingMonitors?: boolean;
147
+ }
148
+ ```
149
+
150
+ ### Guidance
151
+
152
+ Prefer explicit ids first.
153
+
154
+ Use broader matching such as `tags` or `subjectPrefixes` only when the goal is intended to absorb future work items dynamically.
155
+
156
+ ### Rationale
157
+
158
+ The first prototype should remain conservative. Goals should not silently claim unrelated entities.
159
+
160
+ ---
161
+
162
+ ## 5. Goal criteria
163
+
164
+ Criteria define what the Goal considers success, failure, or blocked state.
165
+
166
+ ```ts
167
+ export interface GoalCriteria {
168
+ success: GoalSuccessCriteria;
169
+ failure?: GoalFailureCriteria;
170
+ blocked?: GoalBlockedCriteria;
171
+ }
172
+ ```
173
+
174
+ ### 5.1 Success criteria
175
+
176
+ ```ts
177
+ export interface GoalSuccessCriteria {
178
+ minCompletedTasks?: number;
179
+ requiredTaskIds?: string[];
180
+ requiredMonitorIdsCompleted?: string[];
181
+ requiredLoopIdsPresent?: string[];
182
+ requireNoPendingTasksInScope?: boolean;
183
+ requireLatestVerificationPass?: boolean;
184
+ }
185
+ ```
186
+
187
+ ### 5.2 Failure criteria
188
+
189
+ ```ts
190
+ export interface GoalFailureCriteria {
191
+ anyMonitorIdsErrored?: string[];
192
+ maxVerificationFailures?: number;
193
+ failIfTaskIdsDeleted?: string[];
194
+ }
195
+ ```
196
+
197
+ ### 5.3 Blocked criteria
198
+
199
+ ```ts
200
+ export interface GoalBlockedCriteria {
201
+ blockedIfAllTasksCompletedButVerificationFails?: boolean;
202
+ blockedIfNoScopedProgressSinceMs?: number;
203
+ blockedIfRequiredLoopMissing?: boolean;
204
+ }
205
+ ```
206
+
207
+ ### Notes
208
+
209
+ These criteria are intentionally simple. The first version should use deterministic declarative rules rather than a scripting language.
210
+
211
+ ---
212
+
213
+ ## 6. Progress snapshot
214
+
215
+ Goals should cache a reducer-owned snapshot of relevant progress so verification can be explained, not merely asserted.
216
+
217
+ ```ts
218
+ export interface GoalProgressSnapshot {
219
+ totalTasks: number;
220
+ pendingTasks: number;
221
+ inProgressTasks: number;
222
+ completedTasks: number;
223
+
224
+ activeLoops: number;
225
+ pausedLoops: number;
226
+
227
+ runningMonitors: number;
228
+ completedMonitors: number;
229
+ erroredMonitors: number;
230
+ stoppedMonitors: number;
231
+
232
+ lastProgressAt?: number;
233
+ }
234
+ ```
235
+
236
+ ### Why cache this?
237
+
238
+ Because a Goal should be able to say why it is still active, blocked, or satisfied. The snapshot makes verification output auditable and stable in tests.
239
+
240
+ ---
241
+
242
+ ## 7. Verification state
243
+
244
+ Verification should retain the latest evidence, not just a boolean.
245
+
246
+ ```ts
247
+ export interface GoalVerificationState {
248
+ attempts: number;
249
+ passes: number;
250
+ failures: number;
251
+ lastCheckedAt?: number;
252
+ lastPassedAt?: number;
253
+ lastFailedAt?: number;
254
+ lastReason?: string;
255
+ nextCheckAfter?: number;
256
+ }
257
+ ```
258
+
259
+ ### Interpretation
260
+
261
+ - `attempts` counts all verifier passes
262
+ - `passes` and `failures` support simple policy thresholds
263
+ - `lastReason` is human-readable justification for the last result
264
+ - `nextCheckAfter` supports future pacing without embedding scheduler internals in the goal itself
265
+
266
+ ---
267
+
268
+ ## 8. Reducer state shape
269
+
270
+ ```ts
271
+ export interface GoalReducerState {
272
+ nextId: number;
273
+ goalsById: Record<string, GoalEntry>;
274
+ }
275
+ ```
276
+
277
+ This follows the same pattern already adopted by task, loop, monitor, and notification reducers.
278
+
279
+ ---
280
+
281
+ ## 9. Event vocabulary
282
+
283
+ The Goal reducer should start with a small event set.
284
+
285
+ ```ts
286
+ export type GoalEventType =
287
+ | "GOAL_CREATED"
288
+ | "GOAL_ACTIVATED"
289
+ | "GOAL_PROGRESS_RECORDED"
290
+ | "GOAL_VERIFICATION_STARTED"
291
+ | "GOAL_VERIFICATION_PASSED"
292
+ | "GOAL_VERIFICATION_FAILED"
293
+ | "GOAL_BLOCKED"
294
+ | "GOAL_UNBLOCKED"
295
+ | "GOAL_SATISFIED"
296
+ | "GOAL_FAILED"
297
+ | "GOAL_ARCHIVED"
298
+ | "GOAL_UPDATED";
299
+ ```
300
+
301
+ ### Event roles
302
+
303
+ - `GOAL_CREATED` — create the entry in `pending`
304
+ - `GOAL_ACTIVATED` — move `pending -> active`
305
+ - `GOAL_PROGRESS_RECORDED` — update derived progress snapshot
306
+ - `GOAL_VERIFICATION_STARTED` — set `verificationStatus=checking`
307
+ - `GOAL_VERIFICATION_PASSED` — update verification fields and potentially satisfy the goal
308
+ - `GOAL_VERIFICATION_FAILED` — update verification fields and potentially block/fail the goal
309
+ - `GOAL_BLOCKED` — explicit blocked transition
310
+ - `GOAL_UNBLOCKED` — blocked -> active
311
+ - `GOAL_SATISFIED` — terminal success
312
+ - `GOAL_FAILED` — terminal failure
313
+ - `GOAL_ARCHIVED` — terminal closure without claiming success
314
+ - `GOAL_UPDATED` — title/description/criteria/scope edits
315
+
316
+ ---
317
+
318
+ ## 10. Effect vocabulary
319
+
320
+ The Goal reducer should emit a small effect set.
321
+
322
+ ```ts
323
+ export type GoalEffectType =
324
+ | "PERSIST_GOAL"
325
+ | "DELETE_GOAL"
326
+ | "DISPATCH_EVENT"
327
+ | "REQUEST_GOAL_VERIFICATION";
328
+ ```
329
+
330
+ ### Intent
331
+
332
+ - `PERSIST_GOAL` — save updated goal state
333
+ - `DELETE_GOAL` — only if we later support physical deletion
334
+ - `DISPATCH_EVENT` — derive follow-up events from verification logic
335
+ - `REQUEST_GOAL_VERIFICATION` — ask the coordinator/effect layer to run a read-only verification pass
336
+
337
+ The reducer should not directly schedule loops or inspect child processes.
338
+
339
+ ---
340
+
341
+ ## 11. Derived progress rules
342
+
343
+ Goals should interpret reducer-owned entity state through projection rules rather than ad hoc queries.
344
+
345
+ ### 11.1 Task projection
346
+
347
+ Tasks in scope contribute:
348
+
349
+ - `pendingTasks`
350
+ - `inProgressTasks`
351
+ - `completedTasks`
352
+ - `lastProgressAt` from latest `updatedAt`
353
+
354
+ ### 11.2 Loop projection
355
+
356
+ Loops in scope contribute:
357
+
358
+ - `activeLoops`
359
+ - `pausedLoops`
360
+ - loop existence checks for required loop criteria
361
+
362
+ ### 11.3 Monitor projection
363
+
364
+ Monitors in scope contribute:
365
+
366
+ - `runningMonitors`
367
+ - `completedMonitors`
368
+ - `erroredMonitors`
369
+ - `stoppedMonitors`
370
+
371
+ ### Rule
372
+
373
+ Projection should be a pure helper over reducer-owned snapshots, not a runtime query API.
374
+
375
+ ---
376
+
377
+ ## 12. Initial verification algorithm
378
+
379
+ The first goal verifier should remain intentionally modest.
380
+
381
+ ### Input
382
+
383
+ - one `GoalEntry`
384
+ - current task reducer state
385
+ - current loop reducer state
386
+ - current monitor reducer state
387
+
388
+ ### Output
389
+
390
+ - updated `GoalProgressSnapshot`
391
+ - one of:
392
+ - `GOAL_VERIFICATION_PASSED`
393
+ - `GOAL_VERIFICATION_FAILED`
394
+ - `GOAL_BLOCKED`
395
+ - no terminal transition yet
396
+
397
+ ### Suggested order
398
+
399
+ 1. project scoped entities into a progress snapshot
400
+ 2. update `lastCheckedAt`
401
+ 3. check hard failure criteria first
402
+ 4. check success criteria second
403
+ 5. check blocked criteria third
404
+ 6. otherwise remain `active` with `verificationStatus=unverified` or `inconclusive`
405
+
406
+ This ordering keeps failure and success decisions deterministic.
407
+
408
+ ---
409
+
410
+ ## 13. Allowed and forbidden dependencies
411
+
412
+ ### Goal reducer may depend on
413
+
414
+ - goal reducer state
415
+ - serializable event payloads
416
+ - projected reducer-owned task/loop/monitor snapshots provided by the coordinator
417
+
418
+ ### Goal reducer may not depend on
419
+
420
+ - `ChildProcess` handles
421
+ - scheduler timer maps
422
+ - event unsubscriber handles
423
+ - `AbortController`
424
+ - Pi UI context
425
+ - direct `sendMessage()` or eventbus calls
426
+ - the raw `MonitorProcess` map
427
+
428
+ This boundary is essential if goals are to remain testable and replayable.
429
+
430
+ ---
431
+
432
+ ## 14. Initial invariants
433
+
434
+ The first Goal implementation should enforce these invariants.
435
+
436
+ 1. Goal ids are monotonic.
437
+ 2. Terminal goals (`satisfied`, `failed`, `archived`) do not transition back to `active` without an explicit reopen event if such an event is later added.
438
+ 3. `resolvedAt` is set exactly when entering a terminal lifecycle state.
439
+ 4. `verificationStatus=verified` implies the last verification result was a pass.
440
+ 5. `verificationStatus=checking` is nonterminal.
441
+ 6. Goal progress is derived from scoped reducer-owned entity state, not from runtime handles.
442
+ 7. A Goal may reference future matching tasks only when that intent is explicit in `GoalScope`.
443
+
444
+ ---
445
+
446
+ ## 15. Suggested storage shape
447
+
448
+ If and when goals are persisted independently, the storage shape should mirror the existing reducer-backed stores.
449
+
450
+ ```ts
451
+ export interface GoalStoreData {
452
+ nextId: number;
453
+ goals: GoalEntry[];
454
+ }
455
+ ```
456
+
457
+ No separate runtime-only goal fields should be serialized.
458
+
459
+ ---
460
+
461
+ ## 16. Relationship to tasks #12 and #13
462
+
463
+ ### Task #12 — Prototype goal verifier loop
464
+
465
+ This schema gives `#12` a narrow target:
466
+
467
+ - implement projection helpers
468
+ - implement read-only verification
469
+ - emit `REQUEST_GOAL_VERIFICATION` or derived goal events
470
+ - do not yet mutate unrelated loop/task/monitor runtime behavior
471
+
472
+ ### Task #13 — Document state machine migration
473
+
474
+ This schema also gives `#13` a stable anchor for the final architecture narrative:
475
+
476
+ - reducers own state
477
+ - coordinator owns cross-entity dispatch
478
+ - verifiers consume reducer-owned snapshots
479
+ - goals remain declarative rather than runtime-coupled
480
+
481
+ ---
482
+
483
+ ## 17. Recommended next implementation order
484
+
485
+ 1. add `GoalEntry` types and reducer state definitions
486
+ 2. add pure goal reducer tests for create/activate/update/block/satisfy/fail/archive
487
+ 3. add projection helpers over task/loop/monitor reducer states
488
+ 4. implement a read-only verifier that emits goal events
489
+ 5. only afterward consider autonomous follow-up behavior
490
+
491
+ ---
492
+
493
+ ## 18. Summary
494
+
495
+ We define a Goal as a reducer-owned contract over scoped task, loop, and monitor evidence.
496
+
497
+ Its state should remain:
498
+
499
+ - declarative
500
+ - serializable
501
+ - projection-driven
502
+ - verification-oriented
503
+ - independent of runtime internals
504
+
505
+ This gives the next stage of work a disciplined base for adding goal verification without collapsing back into ad hoc control flow.