@lumenflow/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +119 -0
  3. package/dist/active-wu-detector.d.ts +33 -0
  4. package/dist/active-wu-detector.js +106 -0
  5. package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
  6. package/dist/adapters/filesystem-metrics.adapter.js +519 -0
  7. package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
  8. package/dist/adapters/terminal-renderer.adapter.js +337 -0
  9. package/dist/arg-parser.d.ts +63 -0
  10. package/dist/arg-parser.js +560 -0
  11. package/dist/backlog-editor.d.ts +98 -0
  12. package/dist/backlog-editor.js +179 -0
  13. package/dist/backlog-generator.d.ts +111 -0
  14. package/dist/backlog-generator.js +381 -0
  15. package/dist/backlog-parser.d.ts +45 -0
  16. package/dist/backlog-parser.js +102 -0
  17. package/dist/backlog-sync-validator.d.ts +78 -0
  18. package/dist/backlog-sync-validator.js +294 -0
  19. package/dist/branch-drift.d.ts +34 -0
  20. package/dist/branch-drift.js +51 -0
  21. package/dist/cleanup-install-config.d.ts +33 -0
  22. package/dist/cleanup-install-config.js +37 -0
  23. package/dist/cleanup-lock.d.ts +139 -0
  24. package/dist/cleanup-lock.js +313 -0
  25. package/dist/code-path-validator.d.ts +146 -0
  26. package/dist/code-path-validator.js +537 -0
  27. package/dist/code-paths-overlap.d.ts +55 -0
  28. package/dist/code-paths-overlap.js +245 -0
  29. package/dist/commands-logger.d.ts +77 -0
  30. package/dist/commands-logger.js +254 -0
  31. package/dist/commit-message-utils.d.ts +25 -0
  32. package/dist/commit-message-utils.js +41 -0
  33. package/dist/compliance-parser.d.ts +150 -0
  34. package/dist/compliance-parser.js +507 -0
  35. package/dist/constants/backlog-patterns.d.ts +20 -0
  36. package/dist/constants/backlog-patterns.js +23 -0
  37. package/dist/constants/dora-constants.d.ts +49 -0
  38. package/dist/constants/dora-constants.js +53 -0
  39. package/dist/constants/gate-constants.d.ts +15 -0
  40. package/dist/constants/gate-constants.js +15 -0
  41. package/dist/constants/linter-constants.d.ts +16 -0
  42. package/dist/constants/linter-constants.js +16 -0
  43. package/dist/constants/tokenizer-constants.d.ts +15 -0
  44. package/dist/constants/tokenizer-constants.js +15 -0
  45. package/dist/core/scope-checker.d.ts +97 -0
  46. package/dist/core/scope-checker.js +163 -0
  47. package/dist/core/tool-runner.d.ts +161 -0
  48. package/dist/core/tool-runner.js +393 -0
  49. package/dist/core/tool.constants.d.ts +105 -0
  50. package/dist/core/tool.constants.js +101 -0
  51. package/dist/core/tool.schemas.d.ts +226 -0
  52. package/dist/core/tool.schemas.js +226 -0
  53. package/dist/core/worktree-guard.d.ts +130 -0
  54. package/dist/core/worktree-guard.js +242 -0
  55. package/dist/coverage-gate.d.ts +108 -0
  56. package/dist/coverage-gate.js +196 -0
  57. package/dist/date-utils.d.ts +75 -0
  58. package/dist/date-utils.js +140 -0
  59. package/dist/dependency-graph.d.ts +142 -0
  60. package/dist/dependency-graph.js +550 -0
  61. package/dist/dependency-guard.d.ts +54 -0
  62. package/dist/dependency-guard.js +142 -0
  63. package/dist/dependency-validator.d.ts +105 -0
  64. package/dist/dependency-validator.js +154 -0
  65. package/dist/docs-path-validator.d.ts +36 -0
  66. package/dist/docs-path-validator.js +95 -0
  67. package/dist/domain/orchestration.constants.d.ts +99 -0
  68. package/dist/domain/orchestration.constants.js +97 -0
  69. package/dist/domain/orchestration.schemas.d.ts +280 -0
  70. package/dist/domain/orchestration.schemas.js +211 -0
  71. package/dist/domain/orchestration.types.d.ts +133 -0
  72. package/dist/domain/orchestration.types.js +12 -0
  73. package/dist/error-handler.d.ts +116 -0
  74. package/dist/error-handler.js +136 -0
  75. package/dist/file-classifiers.d.ts +62 -0
  76. package/dist/file-classifiers.js +108 -0
  77. package/dist/gates-agent-mode.d.ts +81 -0
  78. package/dist/gates-agent-mode.js +94 -0
  79. package/dist/generate-traceability.d.ts +107 -0
  80. package/dist/generate-traceability.js +411 -0
  81. package/dist/git-adapter.d.ts +395 -0
  82. package/dist/git-adapter.js +649 -0
  83. package/dist/git-staged-validator.d.ts +32 -0
  84. package/dist/git-staged-validator.js +48 -0
  85. package/dist/hardcoded-strings.d.ts +61 -0
  86. package/dist/hardcoded-strings.js +270 -0
  87. package/dist/incremental-lint.d.ts +78 -0
  88. package/dist/incremental-lint.js +129 -0
  89. package/dist/incremental-test.d.ts +39 -0
  90. package/dist/incremental-test.js +61 -0
  91. package/dist/index.d.ts +42 -0
  92. package/dist/index.js +61 -0
  93. package/dist/invariants/check-automated-tests.d.ts +50 -0
  94. package/dist/invariants/check-automated-tests.js +166 -0
  95. package/dist/invariants-runner.d.ts +103 -0
  96. package/dist/invariants-runner.js +527 -0
  97. package/dist/lane-checker.d.ts +50 -0
  98. package/dist/lane-checker.js +319 -0
  99. package/dist/lane-inference.d.ts +39 -0
  100. package/dist/lane-inference.js +195 -0
  101. package/dist/lane-lock.d.ts +211 -0
  102. package/dist/lane-lock.js +474 -0
  103. package/dist/lane-validator.d.ts +48 -0
  104. package/dist/lane-validator.js +114 -0
  105. package/dist/logs-lib.d.ts +104 -0
  106. package/dist/logs-lib.js +207 -0
  107. package/dist/lumenflow-config-schema.d.ts +272 -0
  108. package/dist/lumenflow-config-schema.js +207 -0
  109. package/dist/lumenflow-config.d.ts +95 -0
  110. package/dist/lumenflow-config.js +236 -0
  111. package/dist/manual-test-validator.d.ts +80 -0
  112. package/dist/manual-test-validator.js +200 -0
  113. package/dist/merge-lock.d.ts +115 -0
  114. package/dist/merge-lock.js +251 -0
  115. package/dist/micro-worktree.d.ts +159 -0
  116. package/dist/micro-worktree.js +427 -0
  117. package/dist/migration-deployer.d.ts +69 -0
  118. package/dist/migration-deployer.js +151 -0
  119. package/dist/orchestration-advisory-loader.d.ts +28 -0
  120. package/dist/orchestration-advisory-loader.js +87 -0
  121. package/dist/orchestration-advisory.d.ts +58 -0
  122. package/dist/orchestration-advisory.js +94 -0
  123. package/dist/orchestration-di.d.ts +48 -0
  124. package/dist/orchestration-di.js +57 -0
  125. package/dist/orchestration-rules.d.ts +57 -0
  126. package/dist/orchestration-rules.js +201 -0
  127. package/dist/orphan-detector.d.ts +131 -0
  128. package/dist/orphan-detector.js +226 -0
  129. package/dist/path-classifiers.d.ts +57 -0
  130. package/dist/path-classifiers.js +93 -0
  131. package/dist/piped-command-detector.d.ts +34 -0
  132. package/dist/piped-command-detector.js +64 -0
  133. package/dist/ports/dashboard-renderer.port.d.ts +112 -0
  134. package/dist/ports/dashboard-renderer.port.js +25 -0
  135. package/dist/ports/metrics-collector.port.d.ts +132 -0
  136. package/dist/ports/metrics-collector.port.js +26 -0
  137. package/dist/process-detector.d.ts +84 -0
  138. package/dist/process-detector.js +172 -0
  139. package/dist/prompt-linter.d.ts +72 -0
  140. package/dist/prompt-linter.js +312 -0
  141. package/dist/prompt-monitor.d.ts +15 -0
  142. package/dist/prompt-monitor.js +205 -0
  143. package/dist/rebase-artifact-cleanup.d.ts +145 -0
  144. package/dist/rebase-artifact-cleanup.js +433 -0
  145. package/dist/retry-strategy.d.ts +189 -0
  146. package/dist/retry-strategy.js +283 -0
  147. package/dist/risk-detector.d.ts +108 -0
  148. package/dist/risk-detector.js +252 -0
  149. package/dist/rollback-utils.d.ts +76 -0
  150. package/dist/rollback-utils.js +104 -0
  151. package/dist/section-headings.d.ts +43 -0
  152. package/dist/section-headings.js +49 -0
  153. package/dist/spawn-escalation.d.ts +90 -0
  154. package/dist/spawn-escalation.js +253 -0
  155. package/dist/spawn-monitor.d.ts +229 -0
  156. package/dist/spawn-monitor.js +672 -0
  157. package/dist/spawn-recovery.d.ts +82 -0
  158. package/dist/spawn-recovery.js +298 -0
  159. package/dist/spawn-registry-schema.d.ts +98 -0
  160. package/dist/spawn-registry-schema.js +108 -0
  161. package/dist/spawn-registry-store.d.ts +146 -0
  162. package/dist/spawn-registry-store.js +273 -0
  163. package/dist/spawn-tree.d.ts +121 -0
  164. package/dist/spawn-tree.js +285 -0
  165. package/dist/stamp-status-validator.d.ts +84 -0
  166. package/dist/stamp-status-validator.js +134 -0
  167. package/dist/stamp-utils.d.ts +100 -0
  168. package/dist/stamp-utils.js +229 -0
  169. package/dist/state-machine.d.ts +26 -0
  170. package/dist/state-machine.js +83 -0
  171. package/dist/system-map-validator.d.ts +80 -0
  172. package/dist/system-map-validator.js +272 -0
  173. package/dist/telemetry.d.ts +80 -0
  174. package/dist/telemetry.js +213 -0
  175. package/dist/token-counter.d.ts +51 -0
  176. package/dist/token-counter.js +145 -0
  177. package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
  178. package/dist/usecases/get-dashboard-data.usecase.js +61 -0
  179. package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
  180. package/dist/usecases/get-suggestions.usecase.js +153 -0
  181. package/dist/user-normalizer.d.ts +41 -0
  182. package/dist/user-normalizer.js +141 -0
  183. package/dist/validators/phi-constants.d.ts +97 -0
  184. package/dist/validators/phi-constants.js +152 -0
  185. package/dist/validators/phi-scanner.d.ts +58 -0
  186. package/dist/validators/phi-scanner.js +215 -0
  187. package/dist/worktree-ownership.d.ts +50 -0
  188. package/dist/worktree-ownership.js +74 -0
  189. package/dist/worktree-scanner.d.ts +103 -0
  190. package/dist/worktree-scanner.js +168 -0
  191. package/dist/worktree-symlink.d.ts +99 -0
  192. package/dist/worktree-symlink.js +359 -0
  193. package/dist/wu-backlog-updater.d.ts +17 -0
  194. package/dist/wu-backlog-updater.js +37 -0
  195. package/dist/wu-checkpoint.d.ts +124 -0
  196. package/dist/wu-checkpoint.js +233 -0
  197. package/dist/wu-claim-helpers.d.ts +26 -0
  198. package/dist/wu-claim-helpers.js +63 -0
  199. package/dist/wu-claim-resume.d.ts +106 -0
  200. package/dist/wu-claim-resume.js +276 -0
  201. package/dist/wu-consistency-checker.d.ts +95 -0
  202. package/dist/wu-consistency-checker.js +567 -0
  203. package/dist/wu-constants.d.ts +1275 -0
  204. package/dist/wu-constants.js +1382 -0
  205. package/dist/wu-create-validators.d.ts +42 -0
  206. package/dist/wu-create-validators.js +93 -0
  207. package/dist/wu-done-branch-only.d.ts +63 -0
  208. package/dist/wu-done-branch-only.js +191 -0
  209. package/dist/wu-done-messages.d.ts +119 -0
  210. package/dist/wu-done-messages.js +185 -0
  211. package/dist/wu-done-pr.d.ts +72 -0
  212. package/dist/wu-done-pr.js +174 -0
  213. package/dist/wu-done-retry-helpers.d.ts +85 -0
  214. package/dist/wu-done-retry-helpers.js +172 -0
  215. package/dist/wu-done-ui.d.ts +37 -0
  216. package/dist/wu-done-ui.js +69 -0
  217. package/dist/wu-done-validators.d.ts +411 -0
  218. package/dist/wu-done-validators.js +1229 -0
  219. package/dist/wu-done-worktree.d.ts +182 -0
  220. package/dist/wu-done-worktree.js +1097 -0
  221. package/dist/wu-helpers.d.ts +128 -0
  222. package/dist/wu-helpers.js +248 -0
  223. package/dist/wu-lint.d.ts +70 -0
  224. package/dist/wu-lint.js +234 -0
  225. package/dist/wu-paths.d.ts +171 -0
  226. package/dist/wu-paths.js +178 -0
  227. package/dist/wu-preflight-validators.d.ts +86 -0
  228. package/dist/wu-preflight-validators.js +251 -0
  229. package/dist/wu-recovery.d.ts +138 -0
  230. package/dist/wu-recovery.js +341 -0
  231. package/dist/wu-repair-core.d.ts +131 -0
  232. package/dist/wu-repair-core.js +669 -0
  233. package/dist/wu-schema-normalization.d.ts +17 -0
  234. package/dist/wu-schema-normalization.js +82 -0
  235. package/dist/wu-schema.d.ts +793 -0
  236. package/dist/wu-schema.js +881 -0
  237. package/dist/wu-spawn-helpers.d.ts +121 -0
  238. package/dist/wu-spawn-helpers.js +271 -0
  239. package/dist/wu-spawn.d.ts +158 -0
  240. package/dist/wu-spawn.js +1306 -0
  241. package/dist/wu-state-schema.d.ts +213 -0
  242. package/dist/wu-state-schema.js +156 -0
  243. package/dist/wu-state-store.d.ts +264 -0
  244. package/dist/wu-state-store.js +691 -0
  245. package/dist/wu-status-transition.d.ts +63 -0
  246. package/dist/wu-status-transition.js +382 -0
  247. package/dist/wu-status-updater.d.ts +25 -0
  248. package/dist/wu-status-updater.js +116 -0
  249. package/dist/wu-transaction-collectors.d.ts +116 -0
  250. package/dist/wu-transaction-collectors.js +272 -0
  251. package/dist/wu-transaction.d.ts +170 -0
  252. package/dist/wu-transaction.js +273 -0
  253. package/dist/wu-validation-constants.d.ts +60 -0
  254. package/dist/wu-validation-constants.js +66 -0
  255. package/dist/wu-validation.d.ts +118 -0
  256. package/dist/wu-validation.js +243 -0
  257. package/dist/wu-validator.d.ts +62 -0
  258. package/dist/wu-validator.js +325 -0
  259. package/dist/wu-yaml-fixer.d.ts +97 -0
  260. package/dist/wu-yaml-fixer.js +264 -0
  261. package/dist/wu-yaml.d.ts +86 -0
  262. package/dist/wu-yaml.js +222 -0
  263. package/package.json +114 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Spawn Registry Store (WU-1944)
3
+ *
4
+ * Event-sourced state store for tracking sub-agent spawns.
5
+ * Stores events in .beacon/state/spawn-registry.jsonl (append-only, git-friendly).
6
+ *
7
+ * Features:
8
+ * - Event sourcing with replay for current state
9
+ * - Atomic append operations
10
+ * - O(1) queries by parent WU, target WU, and status
11
+ *
12
+ * @see {@link tools/lib/__tests__/spawn-registry-store.test.mjs} - Tests
13
+ * @see {@link tools/lib/spawn-registry-schema.mjs} - Schema definitions
14
+ */
15
+ import { type SpawnEvent } from './spawn-registry-schema.js';
16
+ /**
17
+ * Spawn registry file name constant
18
+ */
19
+ export declare const SPAWN_REGISTRY_FILE_NAME = "spawn-registry.jsonl";
20
+ /**
21
+ * Spawn Registry Store class
22
+ *
23
+ * Manages spawn registry state via event sourcing pattern.
24
+ * Events are appended to JSONL file, state is rebuilt by replaying events.
25
+ */
26
+ export declare class SpawnRegistryStore {
27
+ private readonly baseDir;
28
+ private readonly registryFilePath;
29
+ private readonly spawns;
30
+ private readonly byParent;
31
+ private readonly byTarget;
32
+ /**
33
+ * @param {string} baseDir - Directory containing .beacon/state/
34
+ */
35
+ constructor(baseDir: string);
36
+ /**
37
+ * Loads and replays events from JSONL file into current state.
38
+ *
39
+ * Handles:
40
+ * - Missing file: returns empty state
41
+ * - Empty file: returns empty state
42
+ * - Empty lines: skipped gracefully
43
+ * - Malformed JSON: throws error with line info
44
+ * - Invalid events: throws validation error
45
+ *
46
+ * @returns {Promise<void>}
47
+ * @throws {Error} If file contains malformed JSON or invalid events
48
+ *
49
+ * @example
50
+ * const store = new SpawnRegistryStore('/path/to/project');
51
+ * await store.load();
52
+ * const pending = store.getPending();
53
+ */
54
+ load(): Promise<void>;
55
+ /**
56
+ * Applies an event to the in-memory state.
57
+ * If event for same spawn ID exists, updates it (latest wins).
58
+ *
59
+ * @private
60
+ */
61
+ private _applyEvent;
62
+ /**
63
+ * Appends an event to the registry file.
64
+ *
65
+ * Uses append mode to avoid full file rewrite.
66
+ * Creates file and parent directories if they don't exist.
67
+ * Validates event before appending.
68
+ *
69
+ * @private
70
+ * @throws {Error} If event fails validation
71
+ */
72
+ private _appendEvent;
73
+ /**
74
+ * Records a new spawn event with pending status.
75
+ *
76
+ * @param {string} parentWuId - Parent WU ID (orchestrator)
77
+ * @param {string} targetWuId - Target WU ID (spawned work)
78
+ * @param {string} lane - Lane for the spawned work
79
+ * @returns {Promise<string>} The generated spawn ID
80
+ * @throws {Error} If validation fails
81
+ *
82
+ * @example
83
+ * const spawnId = await store.record('WU-1000', 'WU-1001', 'Operations: Tooling');
84
+ */
85
+ record(parentWuId: string, targetWuId: string, lane: string): Promise<string>;
86
+ /**
87
+ * Updates the status of a spawn.
88
+ *
89
+ * @param {string} spawnId - Spawn ID to update
90
+ * @param {string} status - New status
91
+ * @returns {Promise<void>}
92
+ * @throws {Error} If spawn ID not found
93
+ *
94
+ * @example
95
+ * await store.updateStatus('spawn-a1b2', 'completed');
96
+ */
97
+ updateStatus(spawnId: string, status: string): Promise<void>;
98
+ /**
99
+ * Gets all spawns for a parent WU.
100
+ *
101
+ * @param {string} parentWuId - Parent WU ID
102
+ * @returns {SpawnEvent[]} Array of spawn events
103
+ *
104
+ * @example
105
+ * const spawns = store.getByParent('WU-1000');
106
+ */
107
+ getByParent(parentWuId: string): SpawnEvent[];
108
+ /**
109
+ * Gets spawn for a target WU.
110
+ *
111
+ * @param {string} targetWuId - Target WU ID
112
+ * @returns {SpawnEvent | null} Spawn event or null
113
+ *
114
+ * @example
115
+ * const spawn = store.getByTarget('WU-1001');
116
+ */
117
+ getByTarget(targetWuId: string): SpawnEvent | null;
118
+ /**
119
+ * Gets all pending spawns.
120
+ *
121
+ * @returns {SpawnEvent[]} Array of pending spawn events
122
+ *
123
+ * @example
124
+ * const pending = store.getPending();
125
+ */
126
+ getPending(): SpawnEvent[];
127
+ /**
128
+ * Gets all spawns as an array.
129
+ *
130
+ * @returns {SpawnEvent[]} Array of all spawn events
131
+ *
132
+ * @example
133
+ * const allSpawns = store.getAllSpawns();
134
+ */
135
+ getAllSpawns(): SpawnEvent[];
136
+ /**
137
+ * Gets spawn by ID.
138
+ *
139
+ * @param {string} spawnId - Spawn ID
140
+ * @returns {SpawnEvent | null} Spawn event or null
141
+ *
142
+ * @example
143
+ * const spawn = store.getById('spawn-a1b2');
144
+ */
145
+ getById(spawnId: string): SpawnEvent | null;
146
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Spawn Registry Store (WU-1944)
3
+ *
4
+ * Event-sourced state store for tracking sub-agent spawns.
5
+ * Stores events in .beacon/state/spawn-registry.jsonl (append-only, git-friendly).
6
+ *
7
+ * Features:
8
+ * - Event sourcing with replay for current state
9
+ * - Atomic append operations
10
+ * - O(1) queries by parent WU, target WU, and status
11
+ *
12
+ * @see {@link tools/lib/__tests__/spawn-registry-store.test.mjs} - Tests
13
+ * @see {@link tools/lib/spawn-registry-schema.mjs} - Schema definitions
14
+ */
15
+ import fs from 'node:fs/promises';
16
+ import path from 'node:path';
17
+ import { validateSpawnEvent, generateSpawnId, SpawnStatus, } from './spawn-registry-schema.js';
18
+ /**
19
+ * Spawn registry file name constant
20
+ */
21
+ export const SPAWN_REGISTRY_FILE_NAME = 'spawn-registry.jsonl';
22
+ /**
23
+ * Spawn Registry Store class
24
+ *
25
+ * Manages spawn registry state via event sourcing pattern.
26
+ * Events are appended to JSONL file, state is rebuilt by replaying events.
27
+ */
28
+ export class SpawnRegistryStore {
29
+ baseDir;
30
+ registryFilePath;
31
+ spawns;
32
+ byParent;
33
+ byTarget;
34
+ /**
35
+ * @param {string} baseDir - Directory containing .beacon/state/
36
+ */
37
+ constructor(baseDir) {
38
+ this.baseDir = baseDir;
39
+ this.registryFilePath = path.join(baseDir, SPAWN_REGISTRY_FILE_NAME);
40
+ // In-memory state (rebuilt from events)
41
+ this.spawns = new Map();
42
+ // Index: parentWuId -> spawnIds[]
43
+ this.byParent = new Map();
44
+ // Index: targetWuId -> spawnId
45
+ this.byTarget = new Map();
46
+ }
47
+ /**
48
+ * Loads and replays events from JSONL file into current state.
49
+ *
50
+ * Handles:
51
+ * - Missing file: returns empty state
52
+ * - Empty file: returns empty state
53
+ * - Empty lines: skipped gracefully
54
+ * - Malformed JSON: throws error with line info
55
+ * - Invalid events: throws validation error
56
+ *
57
+ * @returns {Promise<void>}
58
+ * @throws {Error} If file contains malformed JSON or invalid events
59
+ *
60
+ * @example
61
+ * const store = new SpawnRegistryStore('/path/to/project');
62
+ * await store.load();
63
+ * const pending = store.getPending();
64
+ */
65
+ async load() {
66
+ // Reset state
67
+ this.spawns.clear();
68
+ this.byParent.clear();
69
+ this.byTarget.clear();
70
+ // Check if file exists
71
+ let content;
72
+ try {
73
+ content = await fs.readFile(this.registryFilePath, 'utf-8');
74
+ }
75
+ catch (error) {
76
+ if (error.code === 'ENOENT') {
77
+ // File doesn't exist - return empty state
78
+ return;
79
+ }
80
+ throw error;
81
+ }
82
+ // Parse JSONL content
83
+ const lines = content.split('\n');
84
+ for (let i = 0; i < lines.length; i++) {
85
+ const line = lines[i].trim();
86
+ // Skip empty lines
87
+ if (!line) {
88
+ continue;
89
+ }
90
+ // Parse JSON line
91
+ let parsed;
92
+ try {
93
+ parsed = JSON.parse(line);
94
+ }
95
+ catch (error) {
96
+ throw new Error(`Malformed JSON on line ${i + 1}: ${error.message}`);
97
+ }
98
+ // Validate against schema
99
+ const validation = validateSpawnEvent(parsed);
100
+ if (!validation.success) {
101
+ const issues = validation.error.issues
102
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
103
+ .join(', ');
104
+ throw new Error(`Validation error on line ${i + 1}: ${issues}`);
105
+ }
106
+ const event = validation.data;
107
+ // Apply event to state (latest event for same ID wins)
108
+ this._applyEvent(event);
109
+ }
110
+ }
111
+ /**
112
+ * Applies an event to the in-memory state.
113
+ * If event for same spawn ID exists, updates it (latest wins).
114
+ *
115
+ * @private
116
+ */
117
+ _applyEvent(event) {
118
+ const { id, parentWuId, targetWuId } = event;
119
+ // Update main state map
120
+ this.spawns.set(id, event);
121
+ // Update parent index
122
+ if (!this.byParent.has(parentWuId)) {
123
+ this.byParent.set(parentWuId, []);
124
+ }
125
+ const parentSpawns = this.byParent.get(parentWuId);
126
+ if (!parentSpawns.includes(id)) {
127
+ parentSpawns.push(id);
128
+ }
129
+ // Update target index
130
+ this.byTarget.set(targetWuId, id);
131
+ }
132
+ /**
133
+ * Appends an event to the registry file.
134
+ *
135
+ * Uses append mode to avoid full file rewrite.
136
+ * Creates file and parent directories if they don't exist.
137
+ * Validates event before appending.
138
+ *
139
+ * @private
140
+ * @throws {Error} If event fails validation
141
+ */
142
+ async _appendEvent(event) {
143
+ // Validate event before appending
144
+ const validation = validateSpawnEvent(event);
145
+ if (!validation.success) {
146
+ const issues = validation.error.issues
147
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
148
+ .join(', ');
149
+ throw new Error(`Validation error: ${issues}`);
150
+ }
151
+ const line = JSON.stringify(event) + '\n';
152
+ // Ensure parent directory exists before appending
153
+ await fs.mkdir(this.baseDir, { recursive: true });
154
+ // Use append flag to avoid rewriting the file
155
+ await fs.appendFile(this.registryFilePath, line, 'utf-8');
156
+ }
157
+ /**
158
+ * Records a new spawn event with pending status.
159
+ *
160
+ * @param {string} parentWuId - Parent WU ID (orchestrator)
161
+ * @param {string} targetWuId - Target WU ID (spawned work)
162
+ * @param {string} lane - Lane for the spawned work
163
+ * @returns {Promise<string>} The generated spawn ID
164
+ * @throws {Error} If validation fails
165
+ *
166
+ * @example
167
+ * const spawnId = await store.record('WU-1000', 'WU-1001', 'Operations: Tooling');
168
+ */
169
+ async record(parentWuId, targetWuId, lane) {
170
+ const id = generateSpawnId(parentWuId, targetWuId);
171
+ const event = {
172
+ id,
173
+ parentWuId,
174
+ targetWuId,
175
+ lane,
176
+ spawnedAt: new Date().toISOString(),
177
+ status: SpawnStatus.PENDING,
178
+ completedAt: null,
179
+ };
180
+ await this._appendEvent(event);
181
+ this._applyEvent(event);
182
+ return id;
183
+ }
184
+ /**
185
+ * Updates the status of a spawn.
186
+ *
187
+ * @param {string} spawnId - Spawn ID to update
188
+ * @param {string} status - New status
189
+ * @returns {Promise<void>}
190
+ * @throws {Error} If spawn ID not found
191
+ *
192
+ * @example
193
+ * await store.updateStatus('spawn-a1b2', 'completed');
194
+ */
195
+ async updateStatus(spawnId, status) {
196
+ const existing = this.spawns.get(spawnId);
197
+ if (!existing) {
198
+ throw new Error(`Spawn ID ${spawnId} not found`);
199
+ }
200
+ const event = {
201
+ ...existing,
202
+ status: status,
203
+ completedAt: new Date().toISOString(),
204
+ };
205
+ await this._appendEvent(event);
206
+ this._applyEvent(event);
207
+ }
208
+ /**
209
+ * Gets all spawns for a parent WU.
210
+ *
211
+ * @param {string} parentWuId - Parent WU ID
212
+ * @returns {SpawnEvent[]} Array of spawn events
213
+ *
214
+ * @example
215
+ * const spawns = store.getByParent('WU-1000');
216
+ */
217
+ getByParent(parentWuId) {
218
+ const spawnIds = this.byParent.get(parentWuId) ?? [];
219
+ return spawnIds
220
+ .map((id) => this.spawns.get(id))
221
+ .filter((event) => event !== undefined);
222
+ }
223
+ /**
224
+ * Gets spawn for a target WU.
225
+ *
226
+ * @param {string} targetWuId - Target WU ID
227
+ * @returns {SpawnEvent | null} Spawn event or null
228
+ *
229
+ * @example
230
+ * const spawn = store.getByTarget('WU-1001');
231
+ */
232
+ getByTarget(targetWuId) {
233
+ const spawnId = this.byTarget.get(targetWuId);
234
+ if (!spawnId) {
235
+ return null;
236
+ }
237
+ return this.spawns.get(spawnId) ?? null;
238
+ }
239
+ /**
240
+ * Gets all pending spawns.
241
+ *
242
+ * @returns {SpawnEvent[]} Array of pending spawn events
243
+ *
244
+ * @example
245
+ * const pending = store.getPending();
246
+ */
247
+ getPending() {
248
+ return Array.from(this.spawns.values()).filter((spawn) => spawn.status === SpawnStatus.PENDING);
249
+ }
250
+ /**
251
+ * Gets all spawns as an array.
252
+ *
253
+ * @returns {SpawnEvent[]} Array of all spawn events
254
+ *
255
+ * @example
256
+ * const allSpawns = store.getAllSpawns();
257
+ */
258
+ getAllSpawns() {
259
+ return Array.from(this.spawns.values());
260
+ }
261
+ /**
262
+ * Gets spawn by ID.
263
+ *
264
+ * @param {string} spawnId - Spawn ID
265
+ * @returns {SpawnEvent | null} Spawn event or null
266
+ *
267
+ * @example
268
+ * const spawn = store.getById('spawn-a1b2');
269
+ */
270
+ getById(spawnId) {
271
+ return this.spawns.get(spawnId) ?? null;
272
+ }
273
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Spawn Tree Builder (WU-1950)
3
+ *
4
+ * Builds and formats spawn trees for visualization.
5
+ * Used by spawn:list command to display parent-child relationships.
6
+ *
7
+ * Note: Domain-specific tree visualization for spawn registry.
8
+ * Integrates with spawn-registry-store and spawn-registry-schema.
9
+ * No external tree library needed - logic is tightly coupled to spawn data model.
10
+ *
11
+ * @see {@link tools/__tests__/spawn-list.test.mjs} - Tests
12
+ * @see {@link tools/spawn-list.mjs} - CLI command
13
+ * @see {@link tools/lib/spawn-registry-store.mjs} - Data source
14
+ */
15
+ /**
16
+ * Status indicators for terminal output.
17
+ * Using unicode symbols for clear visual distinction.
18
+ */
19
+ export declare const STATUS_INDICATORS: Readonly<{
20
+ pending: "○";
21
+ completed: "✓";
22
+ timeout: "⏱";
23
+ crashed: "✗";
24
+ }>;
25
+ /**
26
+ * Tree node structure
27
+ * @typedef {object} SpawnTreeNode
28
+ * @property {string} wuId - WU ID for this node
29
+ * @property {string|null} spawnId - Spawn ID (null for root)
30
+ * @property {string|null} status - Spawn status (null for root)
31
+ * @property {string|null} lane - Lane (null for root)
32
+ * @property {string|null} spawnedAt - Spawn timestamp (null for root)
33
+ * @property {SpawnTreeNode[]} children - Child nodes
34
+ */
35
+ /**
36
+ * Builds a spawn tree from flat spawn events.
37
+ *
38
+ * @param {import('./spawn-registry-schema.js').SpawnEvent[]} spawns - Array of spawn events
39
+ * @param {string} rootWuId - Root WU ID to build tree from
40
+ * @returns {SpawnTreeNode} Tree rooted at rootWuId
41
+ *
42
+ * @example
43
+ * const tree = buildSpawnTree(spawns, 'WU-1000');
44
+ * // { wuId: 'WU-1000', children: [{ wuId: 'WU-1001', ... }] }
45
+ */
46
+ export declare function buildSpawnTree(spawns: any, rootWuId: any): {
47
+ wuId: any;
48
+ spawnId: any;
49
+ status: any;
50
+ lane: any;
51
+ spawnedAt: any;
52
+ children: any[];
53
+ };
54
+ /**
55
+ * Formats a spawn tree for terminal display with indentation and tree characters.
56
+ *
57
+ * @param {SpawnTreeNode} tree - Tree to format
58
+ * @returns {string} Formatted tree string
59
+ *
60
+ * @example
61
+ * const formatted = formatSpawnTree(tree);
62
+ * // WU-1000 (root)
63
+ * // ├── ○ WU-1001 [spawn-1111] (Operations: Tooling)
64
+ * // │ └── ✓ WU-1002 [spawn-2222] (Core: Backend)
65
+ * // └── ○ WU-1003 [spawn-3333] (Experience: Web)
66
+ */
67
+ export declare function formatSpawnTree(tree: any): string;
68
+ /**
69
+ * Gets all spawns for a WU (both where WU is parent and descendants).
70
+ *
71
+ * Returns all spawns needed to build the full tree from this WU.
72
+ *
73
+ * @param {string} wuId - WU ID to get spawns for
74
+ * @param {string} baseDir - Directory containing spawn-registry.jsonl
75
+ * @returns {Promise<import('./spawn-registry-schema.js').SpawnEvent[]>} Array of spawn events
76
+ *
77
+ * @example
78
+ * const spawns = await getSpawnsByWU('WU-1000', '.beacon/state');
79
+ */
80
+ export declare function getSpawnsByWU(wuId: any, baseDir: any): Promise<any[]>;
81
+ /**
82
+ * Gets all spawns for an initiative.
83
+ *
84
+ * Reads WU YAML files to find which WUs belong to the initiative,
85
+ * then returns all spawns where parent or target WU belongs to initiative.
86
+ *
87
+ * @param {string} initiativeId - Initiative ID (e.g., 'INIT-001')
88
+ * @param {string} registryDir - Directory containing spawn-registry.jsonl
89
+ * @param {string} wuDir - Directory containing WU YAML files
90
+ * @returns {Promise<import('./spawn-registry-schema.js').SpawnEvent[]>} Array of spawn events
91
+ *
92
+ * @example
93
+ * const spawns = await getSpawnsByInitiative('INIT-001', '.beacon/state', 'docs/04-operations/tasks/wu');
94
+ */
95
+ export declare function getSpawnsByInitiative(initiativeId: any, registryDir: any, wuDir: any): Promise<{
96
+ id: string;
97
+ parentWuId: string;
98
+ targetWuId: string;
99
+ lane: string;
100
+ spawnedAt: string;
101
+ status: "completed" | "timeout" | "pending" | "crashed" | "escalated";
102
+ completedAt?: string;
103
+ }[]>;
104
+ /**
105
+ * Converts a spawn tree to JSON format.
106
+ *
107
+ * @param {SpawnTreeNode} tree - Tree to convert
108
+ * @returns {object} JSON-serializable tree
109
+ *
110
+ * @example
111
+ * const json = treeToJSON(tree);
112
+ * console.log(JSON.stringify(json, null, 2));
113
+ */
114
+ export declare function treeToJSON(tree: any): {
115
+ wuId: any;
116
+ spawnId: any;
117
+ status: any;
118
+ lane: any;
119
+ spawnedAt: any;
120
+ children: any;
121
+ };