@trevonistrevon/pi-loop 0.5.9 → 0.6.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 (152) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +64 -5
  3. package/dist/api.d.ts +11 -0
  4. package/dist/api.js +10 -0
  5. package/dist/commands/loop-command.d.ts +9 -2
  6. package/dist/commands/loop-command.js +81 -51
  7. package/dist/commands/tasks-command.js +3 -3
  8. package/dist/coordinator.d.ts +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.js +89 -19
  11. package/dist/loop-format.d.ts +3 -0
  12. package/dist/loop-format.js +20 -0
  13. package/dist/loop-parse.d.ts +1 -0
  14. package/dist/loop-parse.js +59 -6
  15. package/dist/loop-reducer.d.ts +13 -1
  16. package/dist/loop-reducer.js +26 -0
  17. package/dist/monitor-manager.js +1 -1
  18. package/dist/notification-reducer.d.ts +2 -0
  19. package/dist/reducer-backed-store.d.ts +1 -1
  20. package/dist/reducer-backed-store.js +1 -1
  21. package/dist/rpc/channels.d.ts +73 -0
  22. package/dist/rpc/channels.js +30 -0
  23. package/dist/rpc/cross-extension-rpc.d.ts +59 -0
  24. package/dist/rpc/cross-extension-rpc.js +117 -0
  25. package/dist/runtime/loop-events.d.ts +26 -0
  26. package/dist/runtime/loop-events.js +26 -0
  27. package/dist/runtime/monitor-ondone-runtime.js +1 -1
  28. package/dist/runtime/native-task-rpc.d.ts +22 -0
  29. package/dist/runtime/native-task-rpc.js +50 -0
  30. package/dist/runtime/notification-runtime.d.ts +2 -1
  31. package/dist/runtime/notification-runtime.js +19 -7
  32. package/dist/runtime/task-backlog-runtime.d.ts +5 -1
  33. package/dist/runtime/task-backlog-runtime.js +14 -7
  34. package/dist/runtime/task-mutations.d.ts +39 -0
  35. package/dist/runtime/task-mutations.js +68 -0
  36. package/dist/runtime/task-rpc.d.ts +4 -0
  37. package/dist/runtime/task-rpc.js +40 -58
  38. package/dist/scheduler.js +25 -7
  39. package/dist/store.d.ts +9 -1
  40. package/dist/store.js +57 -0
  41. package/dist/tools/loop-tools.d.ts +8 -0
  42. package/dist/tools/loop-tools.js +105 -15
  43. package/dist/tools/monitor-tools.js +1 -3
  44. package/dist/tools/native-task-tools.d.ts +2 -6
  45. package/dist/tools/native-task-tools.js +25 -55
  46. package/dist/tools/tool-result.d.ts +8 -0
  47. package/dist/tools/tool-result.js +4 -0
  48. package/dist/trigger-system.js +1 -1
  49. package/dist/types.d.ts +24 -1
  50. package/package.json +15 -2
  51. package/src/api.ts +36 -0
  52. package/src/commands/loop-command.ts +100 -55
  53. package/src/commands/tasks-command.ts +3 -3
  54. package/src/coordinator.ts +1 -1
  55. package/src/index.ts +92 -19
  56. package/src/loop-format.ts +22 -0
  57. package/src/loop-parse.ts +62 -8
  58. package/src/loop-reducer.ts +41 -1
  59. package/src/monitor-manager.ts +1 -1
  60. package/src/notification-reducer.ts +3 -0
  61. package/src/reducer-backed-store.ts +3 -3
  62. package/src/rpc/channels.ts +97 -0
  63. package/src/rpc/cross-extension-rpc.ts +152 -0
  64. package/src/runtime/loop-events.ts +60 -0
  65. package/src/runtime/monitor-ondone-runtime.ts +1 -1
  66. package/src/runtime/native-task-rpc.ts +139 -0
  67. package/src/runtime/notification-runtime.ts +21 -8
  68. package/src/runtime/task-backlog-runtime.ts +25 -7
  69. package/src/runtime/task-mutations.ts +103 -0
  70. package/src/runtime/task-rpc.ts +48 -54
  71. package/src/scheduler.ts +25 -8
  72. package/src/store.ts +59 -2
  73. package/src/tools/loop-tools.ts +129 -16
  74. package/src/tools/monitor-tools.ts +1 -4
  75. package/src/tools/native-task-tools.ts +31 -52
  76. package/src/tools/tool-result.ts +4 -0
  77. package/src/trigger-system.ts +5 -5
  78. package/src/types.ts +29 -1
  79. package/.release-please-manifest.json +0 -3
  80. package/AGENTS.md +0 -83
  81. package/CONTRIBUTING.md +0 -69
  82. package/Dockerfile +0 -17
  83. package/benchmarks/experiment-sim.js +0 -30
  84. package/coverage/base.css +0 -224
  85. package/coverage/block-navigation.js +0 -87
  86. package/coverage/coverage-final.json +0 -33
  87. package/coverage/favicon.png +0 -0
  88. package/coverage/index.html +0 -176
  89. package/coverage/prettify.css +0 -1
  90. package/coverage/prettify.js +0 -2
  91. package/coverage/sort-arrow-sprite.png +0 -0
  92. package/coverage/sorter.js +0 -210
  93. package/coverage/src/commands/index.html +0 -131
  94. package/coverage/src/commands/loop-command.ts.html +0 -634
  95. package/coverage/src/commands/tasks-command.ts.html +0 -490
  96. package/coverage/src/coordinator.ts.html +0 -430
  97. package/coverage/src/goal-coordinator.ts.html +0 -259
  98. package/coverage/src/goal-reducer.ts.html +0 -982
  99. package/coverage/src/goal-store.ts.html +0 -742
  100. package/coverage/src/goal-verifier.ts.html +0 -808
  101. package/coverage/src/index.html +0 -386
  102. package/coverage/src/index.ts.html +0 -1111
  103. package/coverage/src/loop-parse.ts.html +0 -574
  104. package/coverage/src/loop-reducer.ts.html +0 -559
  105. package/coverage/src/monitor-completion-coordinator.ts.html +0 -157
  106. package/coverage/src/monitor-manager.ts.html +0 -940
  107. package/coverage/src/monitor-reducer.ts.html +0 -583
  108. package/coverage/src/notification-reducer.ts.html +0 -550
  109. package/coverage/src/reducer-backed-store.ts.html +0 -589
  110. package/coverage/src/runtime/index.html +0 -206
  111. package/coverage/src/runtime/monitor-ondone-runtime.ts.html +0 -310
  112. package/coverage/src/runtime/notification-runtime.ts.html +0 -721
  113. package/coverage/src/runtime/scope.ts.html +0 -196
  114. package/coverage/src/runtime/session-runtime.ts.html +0 -679
  115. package/coverage/src/runtime/task-backlog-runtime.ts.html +0 -574
  116. package/coverage/src/runtime/task-events.ts.html +0 -208
  117. package/coverage/src/runtime/task-rpc.ts.html +0 -541
  118. package/coverage/src/scheduler.ts.html +0 -400
  119. package/coverage/src/store.ts.html +0 -667
  120. package/coverage/src/task-backlog-coordinator.ts.html +0 -181
  121. package/coverage/src/task-reducer.ts.html +0 -550
  122. package/coverage/src/task-store.ts.html +0 -526
  123. package/coverage/src/tools/index.html +0 -146
  124. package/coverage/src/tools/loop-tools.ts.html +0 -1000
  125. package/coverage/src/tools/monitor-tools.ts.html +0 -547
  126. package/coverage/src/tools/native-task-tools.ts.html +0 -556
  127. package/coverage/src/trigger-system.ts.html +0 -547
  128. package/coverage/src/ui/index.html +0 -116
  129. package/coverage/src/ui/widget.ts.html +0 -292
  130. package/dist/goal-coordinator.d.ts +0 -22
  131. package/dist/goal-coordinator.js +0 -28
  132. package/dist/goal-reducer.d.ts +0 -98
  133. package/dist/goal-reducer.js +0 -190
  134. package/dist/goal-store.d.ts +0 -22
  135. package/dist/goal-store.js +0 -188
  136. package/dist/goal-types.d.ts +0 -82
  137. package/dist/goal-types.js +0 -1
  138. package/dist/goal-verifier.d.ts +0 -20
  139. package/dist/goal-verifier.js +0 -198
  140. package/docker-compose.yml +0 -12
  141. package/docs/architecture/goal-state-schema.md +0 -505
  142. package/docs/architecture/state-machine-migration.md +0 -546
  143. package/docs/architecture/state-machine-reducer-event-model.md +0 -823
  144. package/docs/architecture/state-machine-test-matrix.md +0 -249
  145. package/docs/architecture/state-machine-transition-map.md +0 -436
  146. package/release-please-config.json +0 -9
  147. package/src/goal-coordinator.ts +0 -58
  148. package/src/goal-reducer.ts +0 -299
  149. package/src/goal-store.ts +0 -219
  150. package/src/goal-types.ts +0 -104
  151. package/src/goal-verifier.ts +0 -241
  152. package/vitest.config.ts +0 -29
@@ -1,198 +0,0 @@
1
- function uniqueById(items) {
2
- const seen = new Set();
3
- const result = [];
4
- for (const item of items) {
5
- if (seen.has(item.id))
6
- continue;
7
- seen.add(item.id);
8
- result.push(item);
9
- }
10
- return result;
11
- }
12
- function selectTasks(goal, taskState) {
13
- const selected = [];
14
- for (const id of goal.scope.taskIds ?? []) {
15
- const task = taskState.tasksById[id];
16
- if (task)
17
- selected.push(task);
18
- }
19
- for (const prefix of goal.scope.subjectPrefixes ?? []) {
20
- for (const task of Object.values(taskState.tasksById)) {
21
- if (task.subject.startsWith(prefix))
22
- selected.push(task);
23
- }
24
- }
25
- return uniqueById(selected);
26
- }
27
- function selectLoops(goal, loopState) {
28
- const selected = [];
29
- for (const id of goal.scope.loopIds ?? []) {
30
- const loop = loopState.loopsById[id];
31
- if (loop)
32
- selected.push(loop);
33
- }
34
- return uniqueById(selected);
35
- }
36
- function selectMonitors(goal, monitorState) {
37
- const selected = [];
38
- for (const id of goal.scope.monitorIds ?? []) {
39
- const monitor = monitorState.monitorsById[id];
40
- if (monitor)
41
- selected.push(monitor);
42
- }
43
- return uniqueById(selected);
44
- }
45
- export function projectGoalProgress(goal, taskState, loopState, monitorState) {
46
- const tasks = selectTasks(goal, taskState);
47
- const loops = selectLoops(goal, loopState);
48
- const monitors = selectMonitors(goal, monitorState);
49
- const timestamps = [];
50
- for (const task of tasks)
51
- timestamps.push(task.updatedAt);
52
- for (const loop of loops)
53
- timestamps.push(loop.updatedAt);
54
- for (const monitor of monitors)
55
- timestamps.push(monitor.completedAt ?? monitor.startedAt);
56
- return {
57
- totalTasks: tasks.length,
58
- pendingTasks: tasks.filter(task => task.status === "pending").length,
59
- inProgressTasks: tasks.filter(task => task.status === "in_progress").length,
60
- completedTasks: tasks.filter(task => task.status === "completed").length,
61
- activeLoops: loops.filter(loop => loop.status === "active").length,
62
- pausedLoops: loops.filter(loop => loop.status === "paused").length,
63
- runningMonitors: monitors.filter(monitor => monitor.status === "running").length,
64
- completedMonitors: monitors.filter(monitor => monitor.status === "completed").length,
65
- erroredMonitors: monitors.filter(monitor => monitor.status === "error").length,
66
- stoppedMonitors: monitors.filter(monitor => monitor.status === "stopped").length,
67
- lastProgressAt: timestamps.length > 0 ? Math.max(...timestamps) : undefined,
68
- };
69
- }
70
- function dispatchEffect(event) {
71
- return {
72
- type: "DISPATCH_EVENT",
73
- entityType: "goal",
74
- entityId: event.entityId,
75
- payload: { event },
76
- };
77
- }
78
- function buildEffects(goal, at, progress, resultType, reason) {
79
- return [
80
- dispatchEffect({
81
- type: "GOAL_VERIFICATION_STARTED",
82
- at,
83
- source: "coordinator",
84
- entityType: "goal",
85
- entityId: goal.id,
86
- payload: { id: goal.id },
87
- }),
88
- dispatchEffect({
89
- type: "GOAL_PROGRESS_RECORDED",
90
- at,
91
- source: "coordinator",
92
- entityType: "goal",
93
- entityId: goal.id,
94
- payload: { id: goal.id, progress },
95
- }),
96
- dispatchEffect({
97
- type: resultType,
98
- at,
99
- source: "coordinator",
100
- entityType: "goal",
101
- entityId: goal.id,
102
- payload: { id: goal.id, reason, progress },
103
- }),
104
- ];
105
- }
106
- export function verifyGoal(input) {
107
- const { goal, taskState, loopState, monitorState, at } = input;
108
- const progress = projectGoalProgress(goal, taskState, loopState, monitorState);
109
- if (goal.criteria.failure?.maxVerificationFailures !== undefined
110
- && goal.verification.failures >= goal.criteria.failure.maxVerificationFailures) {
111
- return {
112
- progress,
113
- verdict: "failed",
114
- reason: "maximum verification failures reached",
115
- effects: buildEffects(goal, at, progress, "GOAL_FAILED", "maximum verification failures reached"),
116
- };
117
- }
118
- for (const monitorId of goal.criteria.failure?.anyMonitorIdsErrored ?? []) {
119
- if (monitorState.monitorsById[monitorId]?.status === "error") {
120
- return {
121
- progress,
122
- verdict: "failed",
123
- reason: `monitor #${monitorId} errored`,
124
- effects: buildEffects(goal, at, progress, "GOAL_FAILED", `monitor #${monitorId} errored`),
125
- };
126
- }
127
- }
128
- for (const taskId of goal.criteria.failure?.failIfTaskIdsDeleted ?? []) {
129
- if (!taskState.tasksById[taskId]) {
130
- return {
131
- progress,
132
- verdict: "failed",
133
- reason: `task #${taskId} missing`,
134
- effects: buildEffects(goal, at, progress, "GOAL_FAILED", `task #${taskId} missing`),
135
- };
136
- }
137
- }
138
- const requiredTasksDone = (goal.criteria.success.requiredTaskIds ?? [])
139
- .every(taskId => taskState.tasksById[taskId]?.status === "completed");
140
- const requiredMonitorsDone = (goal.criteria.success.requiredMonitorIdsCompleted ?? [])
141
- .every(monitorId => monitorState.monitorsById[monitorId]?.status === "completed");
142
- const requiredLoopsPresent = (goal.criteria.success.requiredLoopIdsPresent ?? [])
143
- .every(loopId => Boolean(loopState.loopsById[loopId]));
144
- const minCompletedTasksMet = progress.completedTasks >= (goal.criteria.success.minCompletedTasks ?? 0);
145
- const noPendingWork = !goal.criteria.success.requireNoPendingTasksInScope
146
- || (progress.pendingTasks === 0 && progress.inProgressTasks === 0);
147
- const latestVerificationPass = !goal.criteria.success.requireLatestVerificationPass
148
- || goal.verificationStatus === "verified";
149
- const success = requiredTasksDone
150
- && requiredMonitorsDone
151
- && requiredLoopsPresent
152
- && minCompletedTasksMet
153
- && noPendingWork
154
- && latestVerificationPass;
155
- if (success) {
156
- return {
157
- progress,
158
- verdict: "passed",
159
- reason: "success criteria satisfied",
160
- effects: buildEffects(goal, at, progress, "GOAL_VERIFICATION_PASSED", "success criteria satisfied"),
161
- };
162
- }
163
- if (goal.criteria.blocked?.blockedIfRequiredLoopMissing && !requiredLoopsPresent) {
164
- return {
165
- progress,
166
- verdict: "blocked",
167
- reason: "required loop missing",
168
- effects: buildEffects(goal, at, progress, "GOAL_BLOCKED", "required loop missing"),
169
- };
170
- }
171
- if (goal.criteria.blocked?.blockedIfNoScopedProgressSinceMs !== undefined
172
- && progress.lastProgressAt !== undefined
173
- && at - progress.lastProgressAt >= goal.criteria.blocked.blockedIfNoScopedProgressSinceMs) {
174
- return {
175
- progress,
176
- verdict: "blocked",
177
- reason: "no scoped progress within configured interval",
178
- effects: buildEffects(goal, at, progress, "GOAL_BLOCKED", "no scoped progress within configured interval"),
179
- };
180
- }
181
- if (goal.criteria.blocked?.blockedIfAllTasksCompletedButVerificationFails
182
- && progress.totalTasks > 0
183
- && progress.pendingTasks === 0
184
- && progress.inProgressTasks === 0) {
185
- return {
186
- progress,
187
- verdict: "blocked",
188
- reason: "all scoped tasks completed but verification has not passed",
189
- effects: buildEffects(goal, at, progress, "GOAL_BLOCKED", "all scoped tasks completed but verification has not passed"),
190
- };
191
- }
192
- return {
193
- progress,
194
- verdict: "failed",
195
- reason: "success criteria not yet satisfied",
196
- effects: buildEffects(goal, at, progress, "GOAL_VERIFICATION_FAILED", "success criteria not yet satisfied"),
197
- };
198
- }
@@ -1,12 +0,0 @@
1
- services:
2
- ci-node20:
3
- build:
4
- context: .
5
- target: node20
6
- command: sh -c "npm run typecheck && npm run lint && npm run test:coverage && npm run build"
7
-
8
- ci-node22:
9
- build:
10
- context: .
11
- target: node22
12
- command: sh -c "npm run typecheck && npm run lint && npm run test:coverage && npm run build"
@@ -1,505 +0,0 @@
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.