@alisio/plugin-swarm 0.0.0-stage → 0.1.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 (266) hide show
  1. package/.agents/agents/swarm-architect.md +20 -0
  2. package/.agents/agents/swarm-cleaner.md +20 -0
  3. package/.agents/agents/swarm-coder.md +20 -0
  4. package/.agents/agents/swarm-hardener.md +20 -0
  5. package/.agents/agents/swarm-lieutenant.md +20 -0
  6. package/.agents/agents/swarm-qa.md +20 -0
  7. package/.agents/agents/swarm-refactorer.md +20 -0
  8. package/.agents/agents/swarm-specifier.md +20 -0
  9. package/.agents/skills/swarm-architecture-rules/SKILL.md +43 -0
  10. package/.agents/skills/swarm-clarification/SKILL.md +42 -0
  11. package/.agents/skills/swarm-cleaner-metrics/SKILL.md +44 -0
  12. package/.agents/skills/swarm-engineering-constitution/SKILL.md +44 -0
  13. package/.agents/skills/swarm-gherkin-spec/SKILL.md +43 -0
  14. package/.agents/skills/swarm-handoff-protocol/SKILL.md +45 -0
  15. package/.agents/skills/swarm-lieutenant/SKILL.md +41 -0
  16. package/.agents/skills/swarm-mutation-hardening/SKILL.md +43 -0
  17. package/.agents/skills/swarm-qa-acceptance/SKILL.md +42 -0
  18. package/.agents/skills/swarm-tdd-slice/SKILL.md +44 -0
  19. package/.agents/skills/swarm-toolchain-profile/SKILL.md +41 -0
  20. package/LICENSE +21 -0
  21. package/README.es.md +337 -0
  22. package/README.md +322 -2
  23. package/assets/approval-flow.svg +1 -0
  24. package/assets/architecture.svg +1 -0
  25. package/assets/audit-states.svg +1 -0
  26. package/assets/dashboard.css +964 -0
  27. package/assets/dashboard.html +123 -0
  28. package/assets/dashboard.js +965 -0
  29. package/assets/dashboard.svg +1 -0
  30. package/assets/handoff-sequence.svg +1 -0
  31. package/assets/pack-pipelines.svg +1 -0
  32. package/assets/packs/four-pack/pack.json +18 -0
  33. package/assets/packs/six-pack/pack.json +22 -0
  34. package/assets/packs/two-pack/pack.json +14 -0
  35. package/assets/screenshots/dashboard-board-dark.png +0 -0
  36. package/assets/screenshots/dashboard-board-light.png +0 -0
  37. package/assets/screenshots/dashboard-documents-diff.png +0 -0
  38. package/assets/screenshots/dashboard-empty-state.png +0 -0
  39. package/assets/screenshots/dashboard-phone.png +0 -0
  40. package/assets/screenshots/dashboard-reject-dialog.png +0 -0
  41. package/assets/screenshots/dashboard-transcript-tail.png +0 -0
  42. package/assets/screenshots/dashboard-work-queue.png +0 -0
  43. package/assets/task-states.svg +1 -0
  44. package/assets/toolchains/node-ts.json +61 -0
  45. package/cover.svg +46 -0
  46. package/dist/adapters/child-session-runner.d.ts +36 -0
  47. package/dist/adapters/child-session-runner.d.ts.map +1 -0
  48. package/dist/adapters/child-session-runner.js +156 -0
  49. package/dist/adapters/child-session-runner.js.map +1 -0
  50. package/dist/adapters/fs-board-store.d.ts +11 -0
  51. package/dist/adapters/fs-board-store.d.ts.map +1 -0
  52. package/dist/adapters/fs-board-store.js +27 -0
  53. package/dist/adapters/fs-board-store.js.map +1 -0
  54. package/dist/adapters/fs-handoff-store.d.ts +36 -0
  55. package/dist/adapters/fs-handoff-store.d.ts.map +1 -0
  56. package/dist/adapters/fs-handoff-store.js +254 -0
  57. package/dist/adapters/fs-handoff-store.js.map +1 -0
  58. package/dist/adapters/fs-task-state-store.d.ts +16 -0
  59. package/dist/adapters/fs-task-state-store.d.ts.map +1 -0
  60. package/dist/adapters/fs-task-state-store.js +66 -0
  61. package/dist/adapters/fs-task-state-store.js.map +1 -0
  62. package/dist/adapters/git-worktree.d.ts +31 -0
  63. package/dist/adapters/git-worktree.d.ts.map +1 -0
  64. package/dist/adapters/git-worktree.js +209 -0
  65. package/dist/adapters/git-worktree.js.map +1 -0
  66. package/dist/adapters/process-exec.d.ts +36 -0
  67. package/dist/adapters/process-exec.d.ts.map +1 -0
  68. package/dist/adapters/process-exec.js +138 -0
  69. package/dist/adapters/process-exec.js.map +1 -0
  70. package/dist/adapters/process-gate-runner.d.ts +39 -0
  71. package/dist/adapters/process-gate-runner.d.ts.map +1 -0
  72. package/dist/adapters/process-gate-runner.js +242 -0
  73. package/dist/adapters/process-gate-runner.js.map +1 -0
  74. package/dist/adapters/unavailable-runner.d.ts +4 -0
  75. package/dist/adapters/unavailable-runner.d.ts.map +1 -0
  76. package/dist/adapters/unavailable-runner.js +12 -0
  77. package/dist/adapters/unavailable-runner.js.map +1 -0
  78. package/dist/app/audit.d.ts +23 -0
  79. package/dist/app/audit.d.ts.map +1 -0
  80. package/dist/app/audit.js +9 -0
  81. package/dist/app/audit.js.map +1 -0
  82. package/dist/app/budget.d.ts +29 -0
  83. package/dist/app/budget.d.ts.map +1 -0
  84. package/dist/app/budget.js +78 -0
  85. package/dist/app/budget.js.map +1 -0
  86. package/dist/app/chat-store.d.ts +18 -0
  87. package/dist/app/chat-store.d.ts.map +1 -0
  88. package/dist/app/chat-store.js +48 -0
  89. package/dist/app/chat-store.js.map +1 -0
  90. package/dist/app/documents.d.ts +33 -0
  91. package/dist/app/documents.d.ts.map +1 -0
  92. package/dist/app/documents.js +63 -0
  93. package/dist/app/documents.js.map +1 -0
  94. package/dist/app/forge.d.ts +106 -0
  95. package/dist/app/forge.d.ts.map +1 -0
  96. package/dist/app/forge.js +420 -0
  97. package/dist/app/forge.js.map +1 -0
  98. package/dist/app/operator.d.ts +46 -0
  99. package/dist/app/operator.d.ts.map +1 -0
  100. package/dist/app/operator.js +168 -0
  101. package/dist/app/operator.js.map +1 -0
  102. package/dist/app/pidfile.d.ts +18 -0
  103. package/dist/app/pidfile.d.ts.map +1 -0
  104. package/dist/app/pidfile.js +56 -0
  105. package/dist/app/pidfile.js.map +1 -0
  106. package/dist/app/project.d.ts +93 -0
  107. package/dist/app/project.d.ts.map +1 -0
  108. package/dist/app/project.js +291 -0
  109. package/dist/app/project.js.map +1 -0
  110. package/dist/app/prompts.d.ts +48 -0
  111. package/dist/app/prompts.d.ts.map +1 -0
  112. package/dist/app/prompts.js +88 -0
  113. package/dist/app/prompts.js.map +1 -0
  114. package/dist/app/pump.d.ts +144 -0
  115. package/dist/app/pump.d.ts.map +1 -0
  116. package/dist/app/pump.js +707 -0
  117. package/dist/app/pump.js.map +1 -0
  118. package/dist/app/services.d.ts +112 -0
  119. package/dist/app/services.d.ts.map +1 -0
  120. package/dist/app/services.js +323 -0
  121. package/dist/app/services.js.map +1 -0
  122. package/dist/app/state.d.ts +70 -0
  123. package/dist/app/state.d.ts.map +1 -0
  124. package/dist/app/state.js +96 -0
  125. package/dist/app/state.js.map +1 -0
  126. package/dist/args.d.ts +10 -0
  127. package/dist/args.d.ts.map +1 -0
  128. package/dist/args.js +33 -0
  129. package/dist/args.js.map +1 -0
  130. package/dist/coordinator.d.ts +80 -0
  131. package/dist/coordinator.d.ts.map +1 -0
  132. package/dist/coordinator.js +481 -0
  133. package/dist/coordinator.js.map +1 -0
  134. package/dist/dashboard/api.d.ts +28 -0
  135. package/dist/dashboard/api.d.ts.map +1 -0
  136. package/dist/dashboard/api.js +178 -0
  137. package/dist/dashboard/api.js.map +1 -0
  138. package/dist/dashboard/auth.d.ts +13 -0
  139. package/dist/dashboard/auth.d.ts.map +1 -0
  140. package/dist/dashboard/auth.js +36 -0
  141. package/dist/dashboard/auth.js.map +1 -0
  142. package/dist/dashboard/open.d.ts +7 -0
  143. package/dist/dashboard/open.d.ts.map +1 -0
  144. package/dist/dashboard/open.js +17 -0
  145. package/dist/dashboard/open.js.map +1 -0
  146. package/dist/dashboard/server.d.ts +28 -0
  147. package/dist/dashboard/server.d.ts.map +1 -0
  148. package/dist/dashboard/server.js +176 -0
  149. package/dist/dashboard/server.js.map +1 -0
  150. package/dist/doctor.d.ts +28 -0
  151. package/dist/doctor.d.ts.map +1 -0
  152. package/dist/doctor.js +108 -0
  153. package/dist/doctor.js.map +1 -0
  154. package/dist/domain/attention.d.ts +20 -0
  155. package/dist/domain/attention.d.ts.map +1 -0
  156. package/dist/domain/attention.js +41 -0
  157. package/dist/domain/attention.js.map +1 -0
  158. package/dist/domain/envelope.d.ts +37 -0
  159. package/dist/domain/envelope.d.ts.map +1 -0
  160. package/dist/domain/envelope.js +106 -0
  161. package/dist/domain/envelope.js.map +1 -0
  162. package/dist/domain/handoff.d.ts +57 -0
  163. package/dist/domain/handoff.d.ts.map +1 -0
  164. package/dist/domain/handoff.js +191 -0
  165. package/dist/domain/handoff.js.map +1 -0
  166. package/dist/domain/identifiers.d.ts +18 -0
  167. package/dist/domain/identifiers.d.ts.map +1 -0
  168. package/dist/domain/identifiers.js +70 -0
  169. package/dist/domain/identifiers.js.map +1 -0
  170. package/dist/domain/pack.d.ts +45 -0
  171. package/dist/domain/pack.d.ts.map +1 -0
  172. package/dist/domain/pack.js +207 -0
  173. package/dist/domain/pack.js.map +1 -0
  174. package/dist/domain/pipeline.d.ts +33 -0
  175. package/dist/domain/pipeline.d.ts.map +1 -0
  176. package/dist/domain/pipeline.js +50 -0
  177. package/dist/domain/pipeline.js.map +1 -0
  178. package/dist/domain/task.d.ts +26 -0
  179. package/dist/domain/task.d.ts.map +1 -0
  180. package/dist/domain/task.js +154 -0
  181. package/dist/domain/task.js.map +1 -0
  182. package/dist/domain/taskstate.d.ts +37 -0
  183. package/dist/domain/taskstate.d.ts.map +1 -0
  184. package/dist/domain/taskstate.js +98 -0
  185. package/dist/domain/taskstate.js.map +1 -0
  186. package/dist/gates/dry.d.ts +14 -0
  187. package/dist/gates/dry.d.ts.map +1 -0
  188. package/dist/gates/dry.js +71 -0
  189. package/dist/gates/dry.js.map +1 -0
  190. package/dist/gates/parsers.d.ts +34 -0
  191. package/dist/gates/parsers.d.ts.map +1 -0
  192. package/dist/gates/parsers.js +120 -0
  193. package/dist/gates/parsers.js.map +1 -0
  194. package/dist/index.d.ts +45 -0
  195. package/dist/index.d.ts.map +1 -0
  196. package/dist/index.js +207 -0
  197. package/dist/index.js.map +1 -0
  198. package/dist/options.d.ts +13 -0
  199. package/dist/options.d.ts.map +1 -0
  200. package/dist/options.js +27 -0
  201. package/dist/options.js.map +1 -0
  202. package/dist/panel.d.ts +5 -0
  203. package/dist/panel.d.ts.map +1 -0
  204. package/dist/panel.js +52 -0
  205. package/dist/panel.js.map +1 -0
  206. package/dist/ports/agent-runner.d.ts +51 -0
  207. package/dist/ports/agent-runner.d.ts.map +1 -0
  208. package/dist/ports/agent-runner.js +2 -0
  209. package/dist/ports/agent-runner.js.map +1 -0
  210. package/dist/ports/board-store.d.ts +7 -0
  211. package/dist/ports/board-store.d.ts.map +1 -0
  212. package/dist/ports/board-store.js +2 -0
  213. package/dist/ports/board-store.js.map +1 -0
  214. package/dist/ports/clock.d.ts +5 -0
  215. package/dist/ports/clock.d.ts.map +1 -0
  216. package/dist/ports/clock.js +2 -0
  217. package/dist/ports/clock.js.map +1 -0
  218. package/dist/ports/gate-runner.d.ts +34 -0
  219. package/dist/ports/gate-runner.d.ts.map +1 -0
  220. package/dist/ports/gate-runner.js +2 -0
  221. package/dist/ports/gate-runner.js.map +1 -0
  222. package/dist/ports/handoff-store.d.ts +39 -0
  223. package/dist/ports/handoff-store.d.ts.map +1 -0
  224. package/dist/ports/handoff-store.js +2 -0
  225. package/dist/ports/handoff-store.js.map +1 -0
  226. package/dist/ports/isolation.d.ts +43 -0
  227. package/dist/ports/isolation.d.ts.map +1 -0
  228. package/dist/ports/isolation.js +2 -0
  229. package/dist/ports/isolation.js.map +1 -0
  230. package/dist/ports/notifier.d.ts +28 -0
  231. package/dist/ports/notifier.d.ts.map +1 -0
  232. package/dist/ports/notifier.js +2 -0
  233. package/dist/ports/notifier.js.map +1 -0
  234. package/dist/ports/task-state-store.d.ts +10 -0
  235. package/dist/ports/task-state-store.d.ts.map +1 -0
  236. package/dist/ports/task-state-store.js +2 -0
  237. package/dist/ports/task-state-store.js.map +1 -0
  238. package/dist/refs.d.ts +10 -0
  239. package/dist/refs.d.ts.map +1 -0
  240. package/dist/refs.js +19 -0
  241. package/dist/refs.js.map +1 -0
  242. package/dist/resources.d.ts +55 -0
  243. package/dist/resources.d.ts.map +1 -0
  244. package/dist/resources.js +255 -0
  245. package/dist/resources.js.map +1 -0
  246. package/dist/status.d.ts +18 -0
  247. package/dist/status.d.ts.map +1 -0
  248. package/dist/status.js +60 -0
  249. package/dist/status.js.map +1 -0
  250. package/dist/storage.d.ts +18 -0
  251. package/dist/storage.d.ts.map +1 -0
  252. package/dist/storage.js +79 -0
  253. package/dist/storage.js.map +1 -0
  254. package/dist/toolchains/profile.d.ts +21 -0
  255. package/dist/toolchains/profile.d.ts.map +1 -0
  256. package/dist/toolchains/profile.js +60 -0
  257. package/dist/toolchains/profile.js.map +1 -0
  258. package/dist/toolchains/thresholds.d.ts +29 -0
  259. package/dist/toolchains/thresholds.d.ts.map +1 -0
  260. package/dist/toolchains/thresholds.js +45 -0
  261. package/dist/toolchains/thresholds.js.map +1 -0
  262. package/dist/version.d.ts +2 -0
  263. package/dist/version.d.ts.map +1 -0
  264. package/dist/version.js +3 -0
  265. package/dist/version.js.map +1 -0
  266. package/package.json +51 -3
package/dist/doctor.js ADDED
@@ -0,0 +1,108 @@
1
+ import { execFile } from "node:child_process";
2
+ import { access, constants } from "node:fs/promises";
3
+ import { promisify } from "node:util";
4
+ const execFileAsync = promisify(execFile);
5
+ /** Minimum git: `git init --initial-branch` needs 2.28; worktrees and merge-base need less. */
6
+ const MIN_GIT = [2, 28];
7
+ const MIN_NODE = [22, 16];
8
+ export const defaultDoctorDeps = {
9
+ async exec(command, args) {
10
+ const { stdout } = await execFileAsync(command, args, { encoding: "utf8", timeout: 5000 });
11
+ return stdout;
12
+ },
13
+ async writable(path) {
14
+ try {
15
+ await access(path, constants.W_OK);
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ },
22
+ };
23
+ export function parseGitVersion(text) {
24
+ const match = /git version (\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
25
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3] ?? 0)] : undefined;
26
+ }
27
+ const atLeast = (version, minimum) => version[0] > minimum[0] ||
28
+ (version[0] === minimum[0] && version[1] >= minimum[1]);
29
+ export async function runDoctor(deps) {
30
+ const items = [];
31
+ const node = deps.nodeVersion.split(".").map(Number);
32
+ items.push({
33
+ id: "node",
34
+ label: "Node.js",
35
+ status: atLeast(node, MIN_NODE) ? "ok" : "missing",
36
+ detail: atLeast(node, MIN_NODE)
37
+ ? deps.nodeVersion
38
+ : `${deps.nodeVersion} found; Node ${MIN_NODE.join(".")} or newer is required`,
39
+ });
40
+ try {
41
+ const version = parseGitVersion(await deps.exec("git", ["--version"]));
42
+ if (!version) {
43
+ items.push({
44
+ id: "git",
45
+ label: "git",
46
+ status: "missing",
47
+ detail: "could not read the git version",
48
+ });
49
+ }
50
+ else if (!atLeast(version, MIN_GIT)) {
51
+ items.push({
52
+ id: "git",
53
+ label: "git",
54
+ status: "missing",
55
+ detail: `${version.join(".")} found; git ${MIN_GIT.join(".")} or newer is required for worktrees and --initial-branch`,
56
+ });
57
+ }
58
+ else {
59
+ items.push({ id: "git", label: "git", status: "ok", detail: version.join(".") });
60
+ }
61
+ }
62
+ catch {
63
+ items.push({ id: "git", label: "git", status: "missing", detail: "git was not found on PATH" });
64
+ }
65
+ const writable = await deps.writable(deps.workspace);
66
+ items.push({
67
+ id: "workspace",
68
+ label: "Workspace",
69
+ status: writable ? "ok" : "missing",
70
+ detail: writable ? "writable" : "the workspace directory is not writable",
71
+ });
72
+ items.push({
73
+ id: "forge",
74
+ label: "Forge",
75
+ status: deps.initialised ? "ok" : "warn",
76
+ detail: deps.initialised ? "initialised" : "not initialised; run /swarm:init",
77
+ });
78
+ const commands = [
79
+ ...new Set(Object.values(deps.toolchain.commands).map((command) => command?.argv[0])),
80
+ ];
81
+ for (const command of commands) {
82
+ try {
83
+ await deps.exec(command, ["--version"]);
84
+ items.push({
85
+ id: `toolchain:${command}`,
86
+ label: `${deps.toolchain.id}: ${command}`,
87
+ status: "ok",
88
+ detail: "found",
89
+ });
90
+ }
91
+ catch {
92
+ items.push({
93
+ id: `toolchain:${command}`,
94
+ label: `${deps.toolchain.id}: ${command}`,
95
+ status: "warn",
96
+ detail: "not found; gates that use it cannot run",
97
+ });
98
+ }
99
+ }
100
+ return { items, ok: items.every((item) => item.status !== "missing") };
101
+ }
102
+ export function formatDoctor(report) {
103
+ return [
104
+ report.ok ? "Swarm doctor: ready" : "Swarm doctor: problems found",
105
+ ...report.items.map((item) => `[${item.status}] ${item.label}: ${item.detail}`),
106
+ ].join("\n");
107
+ }
108
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAwB1C,+FAA+F;AAC/F,MAAM,OAAO,GAAqB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1C,MAAM,QAAQ,GAAqB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAE5C,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,IAAc;QACxC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3F,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;YACnC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzF,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,OAAiB,EAAE,OAAyB,EAAW,EAAE,CACvE,OAAO,CAAC,CAAC,CAAY,GAAG,OAAO,CAAC,CAAC,CAAC;IACnC,CAAE,OAAO,CAAC,CAAC,CAAY,KAAK,OAAO,CAAC,CAAC,CAAC,IAAK,OAAO,CAAC,CAAC,CAAY,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAElF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAgB;IAC9C,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrD,KAAK,CAAC,IAAI,CAAC;QACT,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAClD,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC7B,CAAC,CAAC,IAAI,CAAC,WAAW;YAClB,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,gBAAgB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,uBAAuB;KACjF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,gCAAgC;aACzC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,0DAA0D;aACvH,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrD,KAAK,CAAC,IAAI,CAAC;QACT,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QACnC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,yCAAyC;KAC1E,CAAC,CAAC;IAEH,KAAK,CAAC,IAAI,CAAC;QACT,EAAE,EAAE,OAAO;QACX,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QACxC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,kCAAkC;KAC9E,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG;QACf,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAW,CAAC,CAAC;KAChG,CAAC;IACF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,aAAa,OAAO,EAAE;gBAC1B,KAAK,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,OAAO,EAAE;gBACzC,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,OAAO;aAChB,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,aAAa,OAAO,EAAE;gBAC1B,KAAK,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,OAAO,EAAE;gBACzC,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,yCAAyC;aAClD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO;QACL,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,8BAA8B;QAClE,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;KAChF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Board } from "./task.js";
2
+ import type { TaskState } from "./taskstate.js";
3
+ export type AttentionKind = "approval" | "clarification" | "blocked" | "gate-failed" | "decision";
4
+ export interface AttentionItem {
5
+ id: string;
6
+ kind: AttentionKind;
7
+ project: string;
8
+ task: string;
9
+ createdAt: string;
10
+ /** Operator actions allowed on this item. */
11
+ actions: string[];
12
+ /** The question, reason or findings behind the item, when known. Always rendered as text. */
13
+ detail?: string;
14
+ }
15
+ /**
16
+ * Items that need a human, derived from the board and the persisted task states (holds and
17
+ * approval comments). Approve is not offered while per-document comments exist (spec 8.3).
18
+ */
19
+ export declare function deriveAttention(project: string, board: Board, states?: ReadonlyMap<string, TaskState>): AttentionItem[];
20
+ //# sourceMappingURL=attention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attention.d.ts","sourceRoot":"","sources":["../../src/domain/attention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAc,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,CAAC;AAElG,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,GACtC,aAAa,EAAE,CA0BjB"}
@@ -0,0 +1,41 @@
1
+ const table = {
2
+ waiting_approval: { kind: "approval", actions: ["documents", "approve", "reject"] },
3
+ clarifying: { kind: "clarification", actions: ["answer"] },
4
+ blocked: { kind: "blocked", actions: ["retry", "delete", "accept"] },
5
+ rejected: { kind: "decision", actions: ["retry", "delete", "accept"] },
6
+ };
7
+ /**
8
+ * Items that need a human, derived from the board and the persisted task states (holds and
9
+ * approval comments). Approve is not offered while per-document comments exist (spec 8.3).
10
+ */
11
+ export function deriveAttention(project, board, states) {
12
+ const items = [];
13
+ for (const card of board.tasks) {
14
+ const entry = table[card.status];
15
+ if (!entry)
16
+ continue;
17
+ const state = states?.get(card.taskId);
18
+ let kind = entry.kind;
19
+ let actions = [...entry.actions];
20
+ let detail;
21
+ if (card.status === "blocked" && state?.hold?.kind === "gate-failed")
22
+ kind = "gate-failed";
23
+ if (card.status === "waiting_approval" && state && state.comments.length > 0) {
24
+ actions = ["documents", "comment", "reject"];
25
+ detail = `${state.comments.length} comment(s) must be resolved before approving`;
26
+ }
27
+ if (state?.hold && card.status !== "waiting_approval")
28
+ detail = state.hold.text;
29
+ items.push({
30
+ id: `${kind}:${project}:${card.name}`,
31
+ kind,
32
+ project,
33
+ task: card.name,
34
+ createdAt: card.updatedAt,
35
+ actions,
36
+ ...(detail !== undefined ? { detail } : {}),
37
+ });
38
+ }
39
+ return items;
40
+ }
41
+ //# sourceMappingURL=attention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attention.js","sourceRoot":"","sources":["../../src/domain/attention.ts"],"names":[],"mappings":"AAiBA,MAAM,KAAK,GAA4E;IACrF,gBAAgB,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE;IACnF,UAAU,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE;IAC1D,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE;IACpE,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE;CACvE,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,KAAY,EACZ,MAAuC;IAEvC,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,KAAK,GAAG,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,MAA0B,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,EAAE,IAAI,EAAE,IAAI,KAAK,aAAa;YAAE,IAAI,GAAG,aAAa,CAAC;QAC3F,IAAI,IAAI,CAAC,MAAM,KAAK,kBAAkB,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7E,OAAO,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,+CAA+C,CAAC;QACnF,CAAC;QACD,IAAI,KAAK,EAAE,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,kBAAkB;YAAE,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC;YACT,EAAE,EAAE,GAAG,IAAI,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE;YACrC,IAAI;YACJ,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO;YACP,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,37 @@
1
+ /** Child output envelopes (spec 4.4). Strict: invalid output is rejected, never repaired. */
2
+ export interface Evidence {
3
+ requirement: string;
4
+ proof: string;
5
+ }
6
+ export type Envelope = {
7
+ schemaVersion: 1;
8
+ kind: "handoff";
9
+ commit: string;
10
+ summary: string;
11
+ evidence: Evidence[];
12
+ } | {
13
+ schemaVersion: 1;
14
+ kind: "needs_clarification";
15
+ question: string;
16
+ } | {
17
+ schemaVersion: 1;
18
+ kind: "blocked";
19
+ reason: string;
20
+ } | {
21
+ schemaVersion: 1;
22
+ kind: "note";
23
+ message: string;
24
+ };
25
+ export type EnvelopeResult = {
26
+ ok: true;
27
+ envelope: Envelope;
28
+ } | {
29
+ ok: false;
30
+ reason: string;
31
+ };
32
+ export interface ParseOptions {
33
+ /** The child hit its turn limit: its text is partial and must not be trusted. */
34
+ turnsExceeded?: boolean;
35
+ }
36
+ export declare function parseEnvelope(output: string, options?: ParseOptions): EnvelopeResult;
37
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/domain/envelope.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,QAAQ,GAChB;IAAE,aAAa,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,EAAE,CAAA;CAAE,GAC5F;IAAE,aAAa,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,qBAAqB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,aAAa,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,aAAa,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA8BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,cAAc,CAwExF"}
@@ -0,0 +1,106 @@
1
+ import { validateCommit } from "./identifiers.js";
2
+ const MAX_BYTES = 64 * 1024;
3
+ const MAX_TEXT = 2000;
4
+ const MAX_NOTE = 80;
5
+ const MAX_EVIDENCE = 50;
6
+ const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
7
+ const fail = (reason) => ({ ok: false, reason });
8
+ function onlyKeys(value, allowed, label) {
9
+ for (const key of Object.keys(value)) {
10
+ if (!allowed.includes(key))
11
+ throw new Error(`Unknown key "${key}" in ${label}`);
12
+ }
13
+ }
14
+ function text(value, label, max) {
15
+ if (typeof value !== "string" || !value.trim())
16
+ throw new Error(`Invalid ${label}: expected non-empty text`);
17
+ if (value.length > max)
18
+ throw new Error(`Invalid ${label}: longer than ${max} characters`);
19
+ return value;
20
+ }
21
+ function unfence(raw) {
22
+ const fenced = /^```(?:json)?\n([\s\S]*?)\n```$/.exec(raw);
23
+ return fenced ? fenced[1].trim() : raw;
24
+ }
25
+ export function parseEnvelope(output, options = {}) {
26
+ if (options.turnsExceeded)
27
+ return fail("The run exceeded its turn limit; output is partial");
28
+ const raw = (output ?? "").trim();
29
+ if (!raw)
30
+ return fail("Empty output: expected one JSON envelope");
31
+ if (Buffer.byteLength(raw) > MAX_BYTES)
32
+ return fail("Output too large for an envelope");
33
+ let value;
34
+ try {
35
+ value = JSON.parse(unfence(raw));
36
+ }
37
+ catch {
38
+ return fail("Output is not a single valid JSON document");
39
+ }
40
+ if (!isRecord(value))
41
+ return fail("Envelope must be a JSON object");
42
+ try {
43
+ if (value.schemaVersion !== 1)
44
+ throw new Error("Unsupported envelope schemaVersion (expected 1)");
45
+ switch (value.kind) {
46
+ case "handoff": {
47
+ onlyKeys(value, ["schemaVersion", "kind", "commit", "summary", "evidence"], "handoff");
48
+ const commit = validateCommit(value.commit);
49
+ const summary = text(value.summary, "summary", MAX_TEXT);
50
+ if (!Array.isArray(value.evidence) || value.evidence.length === 0) {
51
+ throw new Error("Invalid evidence: provide at least one requirement/proof pair");
52
+ }
53
+ if (value.evidence.length > MAX_EVIDENCE)
54
+ throw new Error("Invalid evidence: too many entries");
55
+ const evidence = value.evidence.map((item) => {
56
+ if (!isRecord(item))
57
+ throw new Error("Invalid evidence entry: expected an object");
58
+ onlyKeys(item, ["requirement", "proof"], "evidence entry");
59
+ return {
60
+ requirement: text(item.requirement, "requirement", MAX_TEXT),
61
+ proof: text(item.proof, "proof", MAX_TEXT),
62
+ };
63
+ });
64
+ return {
65
+ ok: true,
66
+ envelope: { schemaVersion: 1, kind: "handoff", commit, summary, evidence },
67
+ };
68
+ }
69
+ case "needs_clarification": {
70
+ onlyKeys(value, ["schemaVersion", "kind", "question"], "needs_clarification");
71
+ return {
72
+ ok: true,
73
+ envelope: {
74
+ schemaVersion: 1,
75
+ kind: "needs_clarification",
76
+ question: text(value.question, "question", MAX_TEXT),
77
+ },
78
+ };
79
+ }
80
+ case "blocked": {
81
+ onlyKeys(value, ["schemaVersion", "kind", "reason"], "blocked");
82
+ return {
83
+ ok: true,
84
+ envelope: {
85
+ schemaVersion: 1,
86
+ kind: "blocked",
87
+ reason: text(value.reason, "reason", MAX_TEXT),
88
+ },
89
+ };
90
+ }
91
+ case "note": {
92
+ onlyKeys(value, ["schemaVersion", "kind", "message"], "note");
93
+ const message = text(value.message, "message", MAX_NOTE);
94
+ if (/[\r\n]/.test(message))
95
+ throw new Error("Invalid message: a note is a single line");
96
+ return { ok: true, envelope: { schemaVersion: 1, kind: "note", message } };
97
+ }
98
+ default:
99
+ throw new Error("Unknown envelope kind");
100
+ }
101
+ }
102
+ catch (error) {
103
+ return fail(error instanceof Error ? error.message : String(error));
104
+ }
105
+ }
106
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/domain/envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /** The phantom sender of a new task (spec 8.2). Never a real role. */
2
+ export declare const PHANTOM_SENDER = "(New Task)";
3
+ export type HandoffType = "git_handoff" | "note";
4
+ export interface Handoff {
5
+ id: string;
6
+ from: string;
7
+ to: string[];
8
+ priority: number;
9
+ type: HandoffType;
10
+ task: string;
11
+ taskId: string;
12
+ commit?: string;
13
+ taskBaseCommit?: string;
14
+ approved: boolean;
15
+ nonForwarding: boolean;
16
+ createdAt: string;
17
+ enqueuedAt?: string;
18
+ dequeuedAt?: string;
19
+ completedAt?: string;
20
+ body: string;
21
+ }
22
+ export type InboxBox = "new" | "in_process" | "completed";
23
+ export type HandoffLocation = {
24
+ kind: "outbox" | "sent" | "failed" | "audit_pending" | "pending_approval" | "join_pending";
25
+ role: string;
26
+ } | {
27
+ kind: "inbox";
28
+ role: string;
29
+ box: InboxBox;
30
+ };
31
+ export interface StoredHandoff {
32
+ handoff: Handoff;
33
+ location: HandoffLocation;
34
+ fileName: string;
35
+ }
36
+ /** Header block, one blank line, then the generated body (same shape as upstream, human-readable). */
37
+ export declare function serializeHandoff(handoff: Handoff): string;
38
+ /** Strict parse. Handoff files are never trusted without revalidation on load. */
39
+ export declare function parseHandoff(text: string): Handoff;
40
+ export interface HandoffNameParts {
41
+ priority: number;
42
+ createdAt: string;
43
+ seq: number;
44
+ from: string;
45
+ to: string[];
46
+ }
47
+ /** `<NN>_<UTC>_<seq>_from_<sender>_to_<r1_r2>.handoff` */
48
+ export declare function handoffFileName(parts: HandoffNameParts): string;
49
+ export interface ParsedHandoffName {
50
+ priority: number;
51
+ stamp: string;
52
+ seq: number;
53
+ from: string;
54
+ to: string[];
55
+ }
56
+ export declare function parseHandoffFileName(name: string): ParsedHandoffName | undefined;
57
+ //# sourceMappingURL=handoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../../src/domain/handoff.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,eAAO,MAAM,cAAc,eAAe,CAAC;AAG3C,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,YAAY,GAAG,WAAW,CAAC;AAC1D,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,GAAG,kBAAkB,GAAG,cAAc,CAAC;IAC3F,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAgDD,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAqBzD;AAsCD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA6ClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;CACd;AAID,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI/D;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;CACd;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAchF"}
@@ -0,0 +1,191 @@
1
+ import { validateCommit, validateRole, validateTaskId, validateTaskName } from "./identifiers.js";
2
+ /** The phantom sender of a new task (spec 8.2). Never a real role. */
3
+ export const PHANTOM_SENDER = "(New Task)";
4
+ const PHANTOM_TOKEN = "new-task";
5
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
6
+ const optionalStamps = [
7
+ ["enqueued_at", "enqueuedAt"],
8
+ ["dequeued_at", "dequeuedAt"],
9
+ ["completed_at", "completedAt"],
10
+ ];
11
+ const singleLine = (value, label) => {
12
+ if (/[\r\n\0]/.test(value))
13
+ throw new Error(`Invalid ${label}: line breaks are not allowed`);
14
+ return value;
15
+ };
16
+ const validSender = (from) => (from === PHANTOM_SENDER ? from : validateRole(from));
17
+ function isoStamp(value, label) {
18
+ if (!/^\d{4}-\d{2}-\d{2}T[\d:.]+Z$/.test(value) || Number.isNaN(Date.parse(value))) {
19
+ throw new Error(`Invalid ${label}: expected an ISO-8601 UTC timestamp`);
20
+ }
21
+ return value;
22
+ }
23
+ function checkHandoff(handoff) {
24
+ if (!uuidPattern.test(handoff.id))
25
+ throw new Error("Invalid id: expected a UUID");
26
+ validSender(handoff.from);
27
+ if (handoff.to.length === 0)
28
+ throw new Error("Invalid to: at least one recipient is required");
29
+ for (const role of handoff.to)
30
+ validateRole(role);
31
+ if (!Number.isInteger(handoff.priority) || handoff.priority < 0 || handoff.priority > 99) {
32
+ throw new Error("Invalid priority: expected an integer from 0 to 99");
33
+ }
34
+ if (handoff.type !== "git_handoff" && handoff.type !== "note") {
35
+ throw new Error("Invalid type: expected git_handoff or note");
36
+ }
37
+ validateTaskName(handoff.task);
38
+ validateTaskId(handoff.taskId);
39
+ if (handoff.type === "git_handoff" && handoff.commit === undefined) {
40
+ throw new Error("A git_handoff needs a commit");
41
+ }
42
+ if (handoff.commit !== undefined)
43
+ validateCommit(handoff.commit);
44
+ if (handoff.taskBaseCommit !== undefined)
45
+ validateCommit(handoff.taskBaseCommit);
46
+ isoStamp(handoff.createdAt, "created_at");
47
+ for (const [, field] of optionalStamps) {
48
+ const value = handoff[field];
49
+ if (value !== undefined)
50
+ isoStamp(value, field);
51
+ }
52
+ }
53
+ /** Header block, one blank line, then the generated body (same shape as upstream, human-readable). */
54
+ export function serializeHandoff(handoff) {
55
+ checkHandoff(handoff);
56
+ const lines = [
57
+ `id: ${handoff.id}`,
58
+ `from: ${singleLine(handoff.from, "from")}`,
59
+ `to: ${handoff.to.map((role) => singleLine(role, "to")).join(",")}`,
60
+ `priority: ${handoff.priority}`,
61
+ `type: ${handoff.type}`,
62
+ `task: ${singleLine(handoff.task, "task")}`,
63
+ `task_id: ${handoff.taskId}`,
64
+ ];
65
+ if (handoff.commit !== undefined)
66
+ lines.push(`commit: ${handoff.commit}`);
67
+ if (handoff.taskBaseCommit !== undefined)
68
+ lines.push(`task_base_commit: ${handoff.taskBaseCommit}`);
69
+ lines.push(`approved: ${handoff.approved}`, `non-forwarding: ${handoff.nonForwarding}`);
70
+ lines.push(`created_at: ${handoff.createdAt}`);
71
+ for (const [key, field] of optionalStamps) {
72
+ const value = handoff[field];
73
+ if (value !== undefined)
74
+ lines.push(`${key}: ${value}`);
75
+ }
76
+ return `${lines.join("\n")}\n\n${handoff.body}`;
77
+ }
78
+ const headerKeys = [
79
+ "id",
80
+ "from",
81
+ "to",
82
+ "priority",
83
+ "type",
84
+ "task",
85
+ "task_id",
86
+ "commit",
87
+ "task_base_commit",
88
+ "approved",
89
+ "non-forwarding",
90
+ "created_at",
91
+ "enqueued_at",
92
+ "dequeued_at",
93
+ "completed_at",
94
+ ];
95
+ const requiredKeys = [
96
+ "id",
97
+ "from",
98
+ "to",
99
+ "priority",
100
+ "type",
101
+ "task",
102
+ "task_id",
103
+ "approved",
104
+ "non-forwarding",
105
+ "created_at",
106
+ ];
107
+ function bool(value, label) {
108
+ if (value === "true")
109
+ return true;
110
+ if (value === "false")
111
+ return false;
112
+ throw new Error(`Invalid ${label}: expected true or false`);
113
+ }
114
+ /** Strict parse. Handoff files are never trusted without revalidation on load. */
115
+ export function parseHandoff(text) {
116
+ const normalized = text.replace(/\r\n/g, "\n");
117
+ const split = normalized.indexOf("\n\n");
118
+ if (split < 0)
119
+ throw new Error("Malformed handoff: missing the blank line after the header");
120
+ const header = new Map();
121
+ for (const line of normalized.slice(0, split).split("\n")) {
122
+ const match = /^([a-z_-]+): ?(.*)$/.exec(line);
123
+ if (!match)
124
+ throw new Error(`Malformed handoff header line: ${JSON.stringify(line.slice(0, 40))}`);
125
+ const key = match[1];
126
+ if (!headerKeys.includes(key))
127
+ throw new Error(`Unknown header "${key}"`);
128
+ if (header.has(key))
129
+ throw new Error(`Duplicate header "${key}"`);
130
+ header.set(key, match[2]);
131
+ }
132
+ for (const key of requiredKeys) {
133
+ if (!header.has(key))
134
+ throw new Error(`Missing header "${key}"`);
135
+ }
136
+ const get = (key) => header.get(key);
137
+ const priority = Number(get("priority"));
138
+ if (!/^\d{1,2}$/.test(get("priority")))
139
+ throw new Error("Invalid priority: expected an integer");
140
+ const type = get("type");
141
+ if (type !== "git_handoff" && type !== "note")
142
+ throw new Error("Invalid type in handoff header");
143
+ const handoff = {
144
+ id: get("id"),
145
+ from: get("from"),
146
+ to: get("to")
147
+ .split(",")
148
+ .map((role) => role.trim())
149
+ .filter(Boolean),
150
+ priority,
151
+ type,
152
+ task: get("task"),
153
+ taskId: get("task_id"),
154
+ approved: bool(get("approved"), "approved"),
155
+ nonForwarding: bool(get("non-forwarding"), "non-forwarding"),
156
+ createdAt: get("created_at"),
157
+ body: normalized.slice(split + 2),
158
+ };
159
+ if (header.has("commit"))
160
+ handoff.commit = get("commit");
161
+ if (header.has("task_base_commit"))
162
+ handoff.taskBaseCommit = get("task_base_commit");
163
+ for (const [key, field] of optionalStamps) {
164
+ if (header.has(key))
165
+ handoff[field] = get(key);
166
+ }
167
+ checkHandoff(handoff);
168
+ return handoff;
169
+ }
170
+ const compactStamp = (iso) => `${iso.replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z")}`;
171
+ /** `<NN>_<UTC>_<seq>_from_<sender>_to_<r1_r2>.handoff` */
172
+ export function handoffFileName(parts) {
173
+ const sender = parts.from === PHANTOM_SENDER ? PHANTOM_TOKEN : validateRole(parts.from);
174
+ for (const role of parts.to)
175
+ validateRole(role);
176
+ return `${String(parts.priority).padStart(2, "0")}_${compactStamp(isoStamp(parts.createdAt, "created_at"))}_${String(parts.seq).padStart(4, "0")}_from_${sender}_to_${parts.to.join("_")}.handoff`;
177
+ }
178
+ export function parseHandoffFileName(name) {
179
+ const match = /^(\d{2})_(\d{8}T\d{6}Z)_(\d{4,})_from_([a-z][a-z0-9-]{0,23})_to_([a-z][a-z0-9-]{0,23}(?:_[a-z][a-z0-9-]{0,23})*)\.handoff$/.exec(name);
180
+ if (!match)
181
+ return undefined;
182
+ const sender = match[4];
183
+ return {
184
+ priority: Number(match[1]),
185
+ stamp: match[2],
186
+ seq: Number(match[3]),
187
+ from: sender === PHANTOM_TOKEN ? PHANTOM_SENDER : sender,
188
+ to: match[5].split("_"),
189
+ };
190
+ }
191
+ //# sourceMappingURL=handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff.js","sourceRoot":"","sources":["../../src/domain/handoff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /** Identifier rules (spec 4.1). Every identifier is untrusted input and validated at each boundary. */
2
+ export declare const projectNamePattern: RegExp;
3
+ export declare const taskNamePattern: RegExp;
4
+ export declare const rolePattern: RegExp;
5
+ export declare const taskIdPattern: RegExp;
6
+ export declare const commitPattern: RegExp;
7
+ export declare function validateProjectName(name: string): string;
8
+ export declare function validateTaskName(name: string): string;
9
+ export declare function validateRole(role: string): string;
10
+ export declare function validateTaskId(taskId: string): string;
11
+ export declare function validateCommit(commit: string): string;
12
+ /** Derive a task slug from free-form text. May return an empty string when nothing is usable. */
13
+ export declare function slugify(text: string): string;
14
+ /** `YYYYMMDDTHHMMSSffffffZ-<slug>`; `sequence` (0-999) disambiguates ids minted in one millisecond. */
15
+ export declare function makeTaskId(now: Date, slug: string, sequence?: number): string;
16
+ /** A relative path made of plain segments: no absolute, drive, backslash, empty, `.` or `..` parts. */
17
+ export declare function assertRelativePath(path: string): string;
18
+ //# sourceMappingURL=identifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../../src/domain/identifiers.ts"],"names":[],"mappings":"AAAA,uGAAuG;AAEvG,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,eAAe,QAA8B,CAAC;AAC3D,eAAO,MAAM,WAAW,QAA2B,CAAC;AACpD,eAAO,MAAM,aAAa,QAA4C,CAAC;AACvE,eAAO,MAAM,aAAa,QAAmB,CAAC;AAE9C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,iGAAiG;AACjG,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C;AAED,uGAAuG;AACvG,wBAAgB,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CASxE;AAED,uGAAuG;AACvG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAavD"}