cool-workflow 0.1.98 → 0.2.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 (306) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +2 -0
  139. package/docs/cli-mcp-parity.7.md +280 -219
  140. package/docs/contract-migration-tooling.7.md +2 -0
  141. package/docs/control-plane-scheduling.7.md +2 -0
  142. package/docs/durable-state-and-locking.7.md +2 -0
  143. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  144. package/docs/execution-backends.7.md +2 -0
  145. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  146. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  147. package/docs/multi-agent-operator-ux.7.md +2 -0
  148. package/docs/node-snapshot-diff-replay.7.md +2 -0
  149. package/docs/observability-cost-accounting.7.md +2 -0
  150. package/docs/project-index.md +132 -71
  151. package/docs/real-execution-backends.7.md +2 -0
  152. package/docs/release-and-migration.7.md +2 -0
  153. package/docs/release-tooling.7.md +2 -0
  154. package/docs/run-registry-control-plane.7.md +2 -0
  155. package/docs/run-retention-reclamation.7.md +23 -0
  156. package/docs/state-explosion-management.7.md +2 -0
  157. package/docs/team-collaboration.7.md +2 -0
  158. package/docs/web-desktop-workbench.7.md +2 -0
  159. package/manifest/plugin.manifest.json +1 -1
  160. package/manifest/source-context-profiles.json +9 -13
  161. package/package.json +1 -1
  162. package/scripts/agents/cw-attest-wrap.js +2 -2
  163. package/scripts/bump-version.js +4 -3
  164. package/scripts/canonical-apps.js +4 -4
  165. package/scripts/dogfood-architecture-review.js +2 -3
  166. package/scripts/dogfood-release.js +3 -3
  167. package/scripts/gen-parity-doc.js +15 -2
  168. package/scripts/golden-path.js +4 -4
  169. package/scripts/onramp-check.js +1 -1
  170. package/scripts/parity-check.js +38 -21
  171. package/scripts/release-flow.js +2 -2
  172. package/scripts/sync-project-index.js +51 -27
  173. package/scripts/validate-run-state-schema.js +2 -2
  174. package/scripts/version-sync-check.js +30 -30
  175. package/dist/candidate-scoring.js +0 -729
  176. package/dist/capability-core.js +0 -1189
  177. package/dist/capability-registry.js +0 -885
  178. package/dist/cli/command-surface.js +0 -494
  179. package/dist/cli/format.js +0 -56
  180. package/dist/cli/handlers/audit.js +0 -82
  181. package/dist/cli/handlers/blackboard.js +0 -81
  182. package/dist/cli/handlers/candidate.js +0 -40
  183. package/dist/cli/handlers/clones.js +0 -34
  184. package/dist/cli/handlers/collaboration.js +0 -61
  185. package/dist/cli/handlers/eval.js +0 -40
  186. package/dist/cli/handlers/ledger.js +0 -169
  187. package/dist/cli/handlers/maintenance.js +0 -107
  188. package/dist/cli/handlers/multi-agent.js +0 -165
  189. package/dist/cli/handlers/node.js +0 -41
  190. package/dist/cli/handlers/operational.js +0 -155
  191. package/dist/cli/handlers/operator.js +0 -146
  192. package/dist/cli/handlers/registry.js +0 -68
  193. package/dist/cli/handlers/run.js +0 -153
  194. package/dist/cli/handlers/scheduling.js +0 -132
  195. package/dist/cli/handlers/workbench.js +0 -41
  196. package/dist/cli/handlers/worker.js +0 -45
  197. package/dist/cli/run-summary.js +0 -45
  198. package/dist/clones.js +0 -162
  199. package/dist/collaboration.js +0 -726
  200. package/dist/commit.js +0 -592
  201. package/dist/compare.js +0 -18
  202. package/dist/coordinator/classify.js +0 -45
  203. package/dist/coordinator/paths.js +0 -42
  204. package/dist/coordinator/util.js +0 -126
  205. package/dist/coordinator.js +0 -990
  206. package/dist/daemon.js +0 -44
  207. package/dist/dispatch.js +0 -250
  208. package/dist/drive.js +0 -864
  209. package/dist/error-feedback.js +0 -419
  210. package/dist/evidence-grounding.js +0 -184
  211. package/dist/execution-backend/agent.js +0 -354
  212. package/dist/execution-backend/probes.js +0 -112
  213. package/dist/execution-backend/util.js +0 -47
  214. package/dist/execution-backend.js +0 -961
  215. package/dist/gates.js +0 -48
  216. package/dist/harness.js +0 -61
  217. package/dist/ledger.js +0 -313
  218. package/dist/loop-expansion.js +0 -60
  219. package/dist/mcp/tool-call.js +0 -470
  220. package/dist/mcp/tool-definitions.js +0 -1066
  221. package/dist/mcp-surface.js +0 -30
  222. package/dist/multi-agent/graph.js +0 -84
  223. package/dist/multi-agent/helpers.js +0 -141
  224. package/dist/multi-agent/ids.js +0 -20
  225. package/dist/multi-agent/paths.js +0 -22
  226. package/dist/multi-agent-eval/normalize.js +0 -51
  227. package/dist/multi-agent-eval.js +0 -678
  228. package/dist/multi-agent-host.js +0 -777
  229. package/dist/multi-agent.js +0 -984
  230. package/dist/node-projection.js +0 -59
  231. package/dist/node-snapshot.js +0 -260
  232. package/dist/operator-ux.js +0 -631
  233. package/dist/orchestrator/app-operations.js +0 -211
  234. package/dist/orchestrator/audit-operations.js +0 -182
  235. package/dist/orchestrator/candidate-operations.js +0 -117
  236. package/dist/orchestrator/cli-options.js +0 -294
  237. package/dist/orchestrator/collaboration-operations.js +0 -86
  238. package/dist/orchestrator/feedback-operations.js +0 -81
  239. package/dist/orchestrator/host-operations.js +0 -78
  240. package/dist/orchestrator/lifecycle-operations.js +0 -650
  241. package/dist/orchestrator/migration-operations.js +0 -44
  242. package/dist/orchestrator/multi-agent-operations.js +0 -362
  243. package/dist/orchestrator/topology-operations.js +0 -84
  244. package/dist/orchestrator.js +0 -925
  245. package/dist/pipeline-runner.js +0 -285
  246. package/dist/reclamation/hash.js +0 -72
  247. package/dist/reclamation.js +0 -812
  248. package/dist/reporter.js +0 -67
  249. package/dist/run-export.js +0 -815
  250. package/dist/run-registry/derive.js +0 -175
  251. package/dist/run-registry/format.js +0 -124
  252. package/dist/run-registry/gc.js +0 -251
  253. package/dist/run-registry/policy.js +0 -16
  254. package/dist/run-registry/queue.js +0 -115
  255. package/dist/run-registry.js +0 -850
  256. package/dist/run-state-schema.js +0 -68
  257. package/dist/scheduling.js +0 -184
  258. package/dist/state-explosion.js +0 -1014
  259. package/dist/state.js +0 -367
  260. package/dist/telemetry-ledger.js +0 -196
  261. package/dist/topology.js +0 -565
  262. package/dist/triggers.js +0 -184
  263. package/dist/trust-audit.js +0 -644
  264. package/dist/types/blackboard.js +0 -2
  265. package/dist/types/candidate.js +0 -2
  266. package/dist/types/collaboration.js +0 -2
  267. package/dist/types/core.js +0 -2
  268. package/dist/types/drive.js +0 -10
  269. package/dist/types/error-feedback.js +0 -2
  270. package/dist/types/evidence-reasoning.js +0 -2
  271. package/dist/types/execution-backend.js +0 -2
  272. package/dist/types/multi-agent.js +0 -2
  273. package/dist/types/observability.js +0 -2
  274. package/dist/types/pipeline.js +0 -2
  275. package/dist/types/reclamation.js +0 -8
  276. package/dist/types/report-bundle.js +0 -6
  277. package/dist/types/result.js +0 -2
  278. package/dist/types/run-registry.js +0 -2
  279. package/dist/types/run.js +0 -2
  280. package/dist/types/sandbox.js +0 -2
  281. package/dist/types/schedule.js +0 -2
  282. package/dist/types/state-node.js +0 -2
  283. package/dist/types/topology.js +0 -2
  284. package/dist/types/trust.js +0 -2
  285. package/dist/types/workbench.js +0 -2
  286. package/dist/types/worker.js +0 -2
  287. package/dist/types/workflow-app.js +0 -2
  288. package/dist/types.js +0 -44
  289. package/dist/util/fingerprint.js +0 -19
  290. package/dist/util/fingerprint.test.js +0 -27
  291. package/dist/verifier.js +0 -78
  292. package/dist/version.js +0 -8
  293. package/dist/workbench-host.js +0 -192
  294. package/dist/workbench.js +0 -192
  295. package/dist/worker-accept/acceptance.js +0 -114
  296. package/dist/worker-accept/blackboard-fanout.js +0 -80
  297. package/dist/worker-accept/blackboard-linkage.js +0 -19
  298. package/dist/worker-accept/context.js +0 -2
  299. package/dist/worker-accept/telemetry-ledger.js +0 -126
  300. package/dist/worker-accept/validation.js +0 -77
  301. package/dist/worker-accept/verifier-completion.js +0 -73
  302. package/dist/worker-isolation/helpers.js +0 -51
  303. package/dist/worker-isolation/paths.js +0 -46
  304. package/dist/worker-isolation.js +0 -656
  305. package/dist/workflow-api.js +0 -131
  306. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -1,175 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.LIFECYCLE_STATES = void 0;
7
- exports.compareRecords = compareRecords;
8
- exports.compareHistory = compareHistory;
9
- exports.compareQueue = compareQueue;
10
- exports.matchesQuery = matchesQuery;
11
- exports.distinctBackends = distinctBackends;
12
- exports.digestInputs = digestInputs;
13
- exports.countRecords = countRecords;
14
- exports.optionalLower = optionalLower;
15
- exports.clampInt = clampInt;
16
- exports.queueId = queueId;
17
- exports.isRunLifecycleState = isRunLifecycleState;
18
- exports.loadReclaimedFromDir = loadReclaimedFromDir;
19
- // Pure, stateless helpers for the run registry — comparison, query matching,
20
- // input digesting, counting, and small utilities. Carved out of run-registry.ts
21
- // (FreeBSD-audit R2) so the stateful RunRegistry class no longer bundles the pure
22
- // derivation layer. Nothing here touches `this`; everything is a pure function of
23
- // its arguments (queueId is the lone exception — a process-local counter, kept as
24
- // it was; making ID minting deterministic is a separate tracked item).
25
- const node_fs_1 = __importDefault(require("node:fs"));
26
- const node_path_1 = __importDefault(require("node:path"));
27
- const compare_1 = require("../compare");
28
- exports.LIFECYCLE_STATES = [
29
- "queued",
30
- "running",
31
- "blocked",
32
- "completed",
33
- "failed",
34
- "archived",
35
- "reclaimed"
36
- ];
37
- function compareRecords(a, b) {
38
- if (a.createdAt !== b.createdAt)
39
- return a.createdAt < b.createdAt ? -1 : 1;
40
- return (0, compare_1.compareBytes)(a.runId, b.runId);
41
- }
42
- function compareHistory(a, b) {
43
- // Newest first.
44
- if (a.createdAt !== b.createdAt)
45
- return a.createdAt < b.createdAt ? 1 : -1;
46
- return (0, compare_1.compareBytes)(a.runId, b.runId);
47
- }
48
- function compareQueue(a, b) {
49
- if (a.priority !== b.priority)
50
- return a.priority - b.priority;
51
- if (a.enqueuedAt !== b.enqueuedAt)
52
- return a.enqueuedAt < b.enqueuedAt ? -1 : 1;
53
- return (0, compare_1.compareBytes)(a.id, b.id);
54
- }
55
- function matchesQuery(record, query) {
56
- if (query.app && !(record.appId || record.workflowId || "").toLowerCase().includes(query.app))
57
- return false;
58
- if (query.status && record.lifecycle !== query.status && record.derivedLifecycle !== query.status)
59
- return false;
60
- if (query.repo && node_path_1.default.resolve(record.repo) !== query.repo)
61
- return false;
62
- if (query.since && record.createdAt < query.since)
63
- return false;
64
- if (query.until && record.createdAt > query.until)
65
- return false;
66
- if (query.text) {
67
- const haystack = [
68
- record.runId,
69
- record.appId,
70
- record.workflowId,
71
- record.title,
72
- record.repo,
73
- record.lifecycle,
74
- record.loopStage,
75
- record.inputsDigest
76
- ]
77
- .filter(Boolean)
78
- .join(" ")
79
- .toLowerCase();
80
- if (!haystack.includes(query.text))
81
- return false;
82
- }
83
- return true;
84
- }
85
- /** Bounded, deterministic stringification of run inputs for free-text search.
86
- * Descriptive intent keys (question, prompt, ...) come first so they survive
87
- * truncation; the rest follow alphabetically. Deterministic and compact. */
88
- const DIGEST_PRIORITY_KEYS = ["question", "prompt", "task", "summary", "title", "objective", "focus", "topic"];
89
- /** Distinct execution backends used by a run's dispatches/tasks, recomputed from
90
- * source state. Sorted; empty for pre-v0.1.29 / default-only runs that never
91
- * recorded a backend. The registry stays backend-agnostic — this is metadata. */
92
- function distinctBackends(run) {
93
- const backends = new Set();
94
- for (const dispatch of run.dispatches || []) {
95
- if (dispatch.backendId)
96
- backends.add(dispatch.backendId);
97
- }
98
- for (const task of run.tasks || []) {
99
- if (task.backendId)
100
- backends.add(task.backendId);
101
- }
102
- return [...backends].sort();
103
- }
104
- function digestInputs(inputs) {
105
- if (!inputs || typeof inputs !== "object")
106
- return undefined;
107
- const keys = Object.keys(inputs);
108
- const ordered = [
109
- ...DIGEST_PRIORITY_KEYS.filter((k) => keys.includes(k)),
110
- ...keys.filter((k) => !DIGEST_PRIORITY_KEYS.includes(k)).sort()
111
- ];
112
- const parts = [];
113
- for (const key of ordered) {
114
- const value = inputs[key];
115
- if (value === undefined || value === null)
116
- continue;
117
- const rendered = Array.isArray(value) ? value.join(",") : typeof value === "object" ? JSON.stringify(value) : String(value);
118
- parts.push(`${key}=${rendered}`);
119
- }
120
- const joined = parts.join(" ").replace(/\s+/g, " ").trim();
121
- return joined.length > 360 ? `${joined.slice(0, 357)}...` : joined;
122
- }
123
- function countRecords(records) {
124
- const counts = {
125
- total: records.length,
126
- queued: 0,
127
- running: 0,
128
- blocked: 0,
129
- completed: 0,
130
- failed: 0,
131
- archived: 0,
132
- reclaimed: 0
133
- };
134
- for (const record of records) {
135
- counts[record.lifecycle] = (counts[record.lifecycle] || 0) + 1;
136
- }
137
- return counts;
138
- }
139
- function optionalLower(value) {
140
- if (value === undefined || value === null || value === "")
141
- return undefined;
142
- return String(value).toLowerCase();
143
- }
144
- function clampInt(value, fallback, min) {
145
- const n = Number(value);
146
- if (!Number.isFinite(n))
147
- return fallback;
148
- return Math.max(min, Math.floor(n));
149
- }
150
- let queueCounter = 0;
151
- function queueId() {
152
- queueCounter += 1;
153
- const stamp = new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
154
- return `q-${stamp}-${String(queueCounter).padStart(3, "0")}`;
155
- }
156
- function isRunLifecycleState(value) {
157
- return typeof value === "string" && exports.LIFECYCLE_STATES.includes(value);
158
- }
159
- /** Read a run dir's `reclaimed.json` overlay (v0.1.39). Fail-closed to an empty
160
- * chain on absence/corruption — a malformed overlay must never brick the run. */
161
- function loadReclaimedFromDir(runDir) {
162
- const file = node_path_1.default.join(runDir, "reclaimed.json");
163
- if (!node_fs_1.default.existsSync(file))
164
- return { schemaVersion: 1, runId: "", tombstones: [] };
165
- try {
166
- const parsed = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
167
- if (!parsed || typeof parsed !== "object" || parsed.schemaVersion !== 1 || !Array.isArray(parsed.tombstones)) {
168
- return { schemaVersion: 1, runId: "", tombstones: [] };
169
- }
170
- return { schemaVersion: 1, runId: String(parsed.runId || ""), tombstones: parsed.tombstones };
171
- }
172
- catch {
173
- return { schemaVersion: 1, runId: "", tombstones: [] };
174
- }
175
- }
@@ -1,124 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatRegistryReport = formatRegistryReport;
4
- exports.formatRunSearch = formatRunSearch;
5
- exports.formatRunShow = formatRunShow;
6
- exports.formatGcPlan = formatGcPlan;
7
- exports.formatGcRun = formatGcRun;
8
- exports.formatGcVerify = formatGcVerify;
9
- exports.formatResume = formatResume;
10
- exports.formatHistory = formatHistory;
11
- exports.formatQueueList = formatQueueList;
12
- function countsLine(counts) {
13
- return `total=${counts.total} queued=${counts.queued} running=${counts.running} blocked=${counts.blocked} completed=${counts.completed} failed=${counts.failed} archived=${counts.archived} reclaimed=${counts.reclaimed}`;
14
- }
15
- function recordLine(record) {
16
- const flags = [record.archived ? "archived" : "", record.provenance?.rerunOf ? `rerunOf=${record.provenance.rerunOf}` : ""].filter(Boolean).join(" ");
17
- return ` [${record.lifecycle}] ${record.runId} (${record.appId || record.workflowId}) ${record.loopStage}${flags ? ` {${flags}}` : ""}`;
18
- }
19
- function formatRegistryReport(report) {
20
- const lines = [];
21
- lines.push(`Run Registry (${report.scope}): ${report.root}`);
22
- lines.push(`Freshness: ${report.freshness.status}${report.freshness.staleRuns.length ? ` (stale: ${report.freshness.staleRuns.join(", ")})` : ""}${report.freshness.missingRuns.length ? ` (missing: ${report.freshness.missingRuns.join(", ")})` : ""}`);
23
- lines.push(`Repos: ${report.index.repos.length}`);
24
- lines.push(countsLine(report.counts));
25
- if (report.freshness.status !== "valid")
26
- lines.push(`Next Action: ${report.nextAction}`);
27
- return lines.join("\n");
28
- }
29
- function formatRunSearch(result) {
30
- const lines = [];
31
- lines.push(`Run Search (${result.scope}): ${result.total} match(es), showing ${result.records.length} [offset ${result.offset}] freshness=${result.freshness}`);
32
- for (const record of result.records)
33
- lines.push(recordLine(record));
34
- if (!result.records.length)
35
- lines.push(" (no matching runs)");
36
- return lines.join("\n");
37
- }
38
- function formatRunShow(result) {
39
- if (!result.found) {
40
- return `Run ${result.runId}: MISSING (source state.json absent — fail closed). Next: ${result.nextAction}`;
41
- }
42
- const r = result.record;
43
- const lines = [
44
- `Run ${r.runId} [${r.lifecycle}] (derived: ${r.derivedLifecycle})`,
45
- ` app=${r.appId || r.workflowId} loopStage=${r.loopStage} repo=${r.repo}`,
46
- ` tasks: total=${r.tasks.total} pending=${r.tasks.pending} running=${r.tasks.running} failed=${r.tasks.failed} completed=${r.tasks.completed}`,
47
- ` commits=${r.commitCount} (verifier-gated=${r.verifierGatedCommitCount}) openFeedback=${r.openFeedbackCount}`
48
- ];
49
- if (r.provenance?.rerunOf)
50
- lines.push(` provenance: rerunOf=${r.provenance.rerunOf} gen=${r.provenance.generation} origin=${r.provenance.originRunId}`);
51
- if (r.tier && r.tier !== "live") {
52
- lines.push(` tier=${r.tier} capability=${r.capability} reason=${r.capabilityReason}${r.reclaimedBytes ? ` bytesFreed=${r.reclaimedBytes}` : ""}${r.tombstoneHash ? ` tombstone=${r.tombstoneHash.slice(0, 19)}` : ""}`);
53
- }
54
- return lines.join("\n");
55
- }
56
- function formatGcPlan(result) {
57
- const lines = [
58
- `GC Plan (${result.scope}): ${result.eligibleCount}/${result.total} eligible, ${result.bytesToFree} byte(s) would be freed [DRY-RUN, frees nothing]`,
59
- ` policy: reclaimAfterArchiveDays=${result.policy.reclaimAfterArchiveDays} keepScratch=${result.policy.keepScratch} keepSnapshots=${result.policy.keepSnapshots}`
60
- ];
61
- for (const entry of result.entries) {
62
- if (entry.eligible) {
63
- const kinds = Object.entries(entry.byKind).map(([k, v]) => `${k}=${v}`).join(" ");
64
- lines.push(` [eligible] ${entry.runId} -> ${entry.capability} (${entry.capabilityReason}) ${entry.bytesToFree}B {${kinds}}`);
65
- }
66
- else {
67
- lines.push(` [skip:${entry.reason}] ${entry.runId} (tier=${entry.tier})`);
68
- }
69
- }
70
- if (!result.entries.length)
71
- lines.push(" (no runs in scope)");
72
- return lines.join("\n");
73
- }
74
- function formatGcRun(result) {
75
- const lines = [`GC Run (${result.scope}): reclaimed ${result.reclaimed.length} run(s), freed ${result.totalBytesFreed} byte(s)`];
76
- for (const r of result.reclaimed)
77
- lines.push(` [reclaimed] ${r.runId} -> ${r.capability} (${r.capabilityReason}) ${r.bytesFreed}B tombstone=${r.tombstoneHash.slice(0, 19)}`);
78
- for (const r of result.refused)
79
- lines.push(` [refused:${r.code}] ${r.runId}`);
80
- if (!result.reclaimed.length && !result.refused.length)
81
- lines.push(" (nothing eligible)");
82
- return lines.join("\n");
83
- }
84
- function formatGcVerify(result) {
85
- const lines = [
86
- `GC Verify ${result.runId}: reclaimed=${result.reclaimed} verified=${result.verified} tier=${result.tier} capability=${result.capability}${result.tombstoneHash ? ` tombstone=${result.tombstoneHash.slice(0, 19)}` : ""}`
87
- ];
88
- for (const check of result.checks)
89
- lines.push(` ${check.pass ? "PASS" : "FAIL"} ${check.name}${check.code ? ` [${check.code}]` : ""}${check.detail ? ` (${check.detail})` : ""}`);
90
- return lines.join("\n");
91
- }
92
- function formatResume(result) {
93
- const lines = [
94
- `Resume ${result.runId} [${result.lifecycle}] loopStage=${result.loopStage} (resolved from ${result.resolvedFrom}, ${result.freshness})`,
95
- ` resumable=${result.resumable} nextTasks=${result.nextTasks.length}`
96
- ];
97
- for (const action of result.nextActions)
98
- lines.push(` -> ${action.command}\n ${action.reason}`);
99
- // Only when --drive/--once continued the run; the default read-only resume text is unchanged.
100
- if (result.drive) {
101
- const d = result.drive;
102
- lines.push(` drive: ${d.status} (${d.completedWorkers}/${d.plannedWorkers} workers${d.commitId ? `, committed ${d.commitId}` : ""})`);
103
- }
104
- return lines.join("\n");
105
- }
106
- function formatHistory(result) {
107
- const lines = [];
108
- lines.push(`Run History (${result.scope}): ${result.total} run(s) across ${result.repos.length} repo(s), freshness=${result.freshness}`);
109
- for (const entry of result.entries) {
110
- lines.push(` ${entry.createdAt} [${entry.lifecycle}] ${entry.runId} (${entry.appId || entry.workflowId})${entry.provenance?.rerunOf ? ` rerunOf=${entry.provenance.rerunOf}` : ""}`);
111
- }
112
- if (!result.entries.length)
113
- lines.push(" (no runs)");
114
- return lines.join("\n");
115
- }
116
- function formatQueueList(result) {
117
- const lines = [`Run Queue: ${result.total} entry(ies) [priority asc]`];
118
- for (const entry of result.entries) {
119
- lines.push(` #${entry.priority} ${entry.id} [${entry.status}] ${entry.appId || entry.workflowId || entry.runId || "?"} repo=${entry.repo}${entry.note ? ` note=${entry.note}` : ""}`);
120
- }
121
- if (!result.entries.length)
122
- lines.push(" (queue empty)");
123
- return lines.join("\n");
124
- }
@@ -1,251 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.reclamationPolicy = reclamationPolicy;
4
- exports.reclaimEligibility = reclaimEligibility;
5
- exports.gcPlan = gcPlan;
6
- exports.gcRun = gcRun;
7
- exports.gcVerify = gcVerify;
8
- const reclamation_1 = require("../reclamation");
9
- const trust_audit_1 = require("../trust-audit");
10
- const policy_1 = require("./policy");
11
- /** Resolve the effective reclamation policy (defaults reclaim NOTHING). */
12
- function reclamationPolicy(overrides = {}) {
13
- return { ...policy_1.DEFAULT_RUN_REGISTRY_POLICY, ...overrides };
14
- }
15
- /** Fail-closed eligibility: terminal AND archived AND no open feedback AND past
16
- * retention. Returns the matching refusal code, or null when eligible. Reads
17
- * the live-source-derived record; order yields distinct, stable codes. */
18
- function reclaimEligibility(record, policy, nowMs) {
19
- if (record.tier === "reclaimed")
20
- return "already-reclaimed";
21
- const terminalStates = policy.reclaimStates && policy.reclaimStates.length ? policy.reclaimStates : ["completed", "failed"];
22
- if (record.derivedLifecycle !== "completed" && record.derivedLifecycle !== "failed")
23
- return "non-terminal";
24
- if (!terminalStates.includes(record.derivedLifecycle))
25
- return "non-terminal";
26
- if (record.openFeedbackCount > 0)
27
- return "open-feedback";
28
- if (!record.archived)
29
- return "not-archived";
30
- const days = policy.reclaimAfterArchiveDays ?? 0;
31
- if (days > 0) {
32
- const archivedAtMs = record.archivedAt ? Date.parse(record.archivedAt) : NaN;
33
- if (!Number.isFinite(archivedAtMs))
34
- return "within-retention";
35
- if (archivedAtMs > nowMs - days * 24 * 60 * 60 * 1000)
36
- return "within-retention";
37
- }
38
- return null;
39
- }
40
- /** Resolve a single run to a one-element record list via locate() (repo-first),
41
- * avoiding a full-registry scan for single-run gc plan/run. */
42
- function recordsForRunId(host, runId, scope) {
43
- const located = host.locate(runId, scope);
44
- return located ? [located.record] : [];
45
- }
46
- /** Dry-run: compute eligible runs, per-kind bytes that WOULD be freed, and the
47
- * capability downgrade. Frees NOTHING. */
48
- function gcPlan(host, options = {}) {
49
- const scope = options.scope || "home";
50
- const policy = reclamationPolicy(options.policy);
51
- const nowIso = options.now || new Date().toISOString();
52
- const nowMs = Date.parse(nowIso);
53
- // Fast, deterministic single-run path: resolve just that run via locate()
54
- // (repo-first) so a home-scope plan never re-scans the whole registry.
55
- const records = options.runId ? recordsForRunId(host, options.runId, scope) : host.buildIndex(scope).records;
56
- const entries = [];
57
- let bytesToFree = 0;
58
- let eligibleCount = 0;
59
- for (const record of records) {
60
- const refusal = reclaimEligibility(record, policy, nowMs);
61
- let plan;
62
- try {
63
- const run = host.loadRun(record.repo, record.runId);
64
- plan = (0, reclamation_1.planReclamation)(run, { keepScratch: policy.keepScratch, keepSnapshots: policy.keepSnapshots });
65
- }
66
- catch {
67
- entries.push({
68
- runId: record.runId,
69
- repo: record.repo,
70
- eligible: false,
71
- reason: "unreadable",
72
- tier: record.tier || "live",
73
- capability: record.capability || "re-runnable",
74
- capabilityReason: record.capabilityReason || "live-full",
75
- bytesToFree: 0,
76
- byKind: {},
77
- freeable: []
78
- });
79
- continue;
80
- }
81
- const eligible = refusal === null;
82
- const entry = {
83
- runId: record.runId,
84
- repo: record.repo,
85
- eligible,
86
- reason: eligible ? "eligible" : refusal,
87
- tier: record.tier || "live",
88
- capability: plan.capability,
89
- capabilityReason: plan.capabilityReason,
90
- bytesToFree: eligible ? plan.bytesToFree : 0,
91
- byKind: eligible ? plan.byKind : {},
92
- freeable: eligible ? plan.freeable.map((f) => ({ path: f.path, kind: f.kind, bytes: f.bytes })) : []
93
- };
94
- entries.push(entry);
95
- if (eligible) {
96
- eligibleCount += 1;
97
- bytesToFree += plan.bytesToFree;
98
- }
99
- }
100
- return {
101
- schemaVersion: 1,
102
- scope,
103
- generatedAt: nowIso,
104
- policy: {
105
- reclaimAfterArchiveDays: policy.reclaimAfterArchiveDays ?? 0,
106
- keepSnapshots: Boolean(policy.keepSnapshots),
107
- keepScratch: Boolean(policy.keepScratch),
108
- reclaimStates: policy.reclaimStates && policy.reclaimStates.length ? policy.reclaimStates : ["completed", "failed"]
109
- },
110
- total: entries.length,
111
- eligibleCount,
112
- bytesToFree,
113
- entries,
114
- nextAction: eligibleCount ? "node scripts/cw.js gc run" : "node scripts/cw.js run search"
115
- };
116
- }
117
- /** Execute the write-ahead reclamation transaction for eligible runs. Bounded
118
- * (`maxReclaimRuns` / `maxReclaimBytes`), fail-closed on any incomplete
119
- * skeleton. Produces a tombstone and frees the bulk. */
120
- function gcRun(host, options = {}) {
121
- const scope = options.scope || "home";
122
- const policy = reclamationPolicy(options.policy);
123
- const nowIso = options.now || new Date().toISOString();
124
- const nowMs = Date.parse(nowIso);
125
- const records = options.runId ? recordsForRunId(host, options.runId, scope) : host.buildIndex(scope).records;
126
- const maxRuns = options.limit ?? (policy.maxReclaimRuns || 0);
127
- const maxBytes = policy.maxReclaimBytes || 0;
128
- const reclaimed = [];
129
- const refused = [];
130
- let totalBytesFreed = 0;
131
- for (const record of records) {
132
- const refusal = reclaimEligibility(record, policy, nowMs);
133
- if (refusal) {
134
- refused.push({ runId: record.runId, code: refusal });
135
- continue;
136
- }
137
- if (maxRuns > 0 && reclaimed.length >= maxRuns)
138
- break;
139
- let run;
140
- try {
141
- run = host.loadRun(record.repo, record.runId);
142
- }
143
- catch {
144
- refused.push({ runId: record.runId, code: "unreadable" });
145
- continue;
146
- }
147
- try {
148
- const result = (0, reclamation_1.runReclamation)(run, {
149
- now: nowIso,
150
- actor: options.actor,
151
- policy: { reclaimAfterArchiveDays: policy.reclaimAfterArchiveDays, keepScratch: policy.keepScratch, keepSnapshots: policy.keepSnapshots },
152
- reclaimPolicy: { keepScratch: policy.keepScratch, keepSnapshots: policy.keepSnapshots }
153
- });
154
- // No post-free saveCheckpoint: runReclamation now DURABLY persists the
155
- // result-node re-point inside the transaction (before any byte is freed),
156
- // so state.json can never reference a freed path even on a crash here.
157
- reclaimed.push({
158
- runId: record.runId,
159
- bytesFreed: result.bytesFreed,
160
- tombstoneHash: result.tombstone.tombstoneHash,
161
- capability: result.tombstone.capability,
162
- capabilityReason: result.tombstone.capabilityReason
163
- });
164
- // Independent reclamation WITNESS in the tamper-evident trust-audit chain:
165
- // proves this run WAS reclaimed even if reclaimed.json is later deleted — so
166
- // `gc verify` can tell proof-deletion apart from never-reclaimed.
167
- (0, trust_audit_1.recordTrustAuditEvent)(run, {
168
- kind: "run.reclaimed",
169
- decision: "recorded",
170
- source: "cw-validated",
171
- metadata: { tombstoneHash: result.tombstone.tombstoneHash, bytesFreed: result.bytesFreed, capability: result.tombstone.capability }
172
- });
173
- totalBytesFreed += result.bytesFreed;
174
- if (maxBytes > 0 && totalBytesFreed >= maxBytes)
175
- break;
176
- }
177
- catch (error) {
178
- if (error instanceof reclamation_1.ReclamationError)
179
- refused.push({ runId: record.runId, code: error.code });
180
- else
181
- throw error;
182
- }
183
- }
184
- return {
185
- schemaVersion: 1,
186
- scope,
187
- generatedAt: nowIso,
188
- dryRun: false,
189
- reclaimed,
190
- refused,
191
- totalBytesFreed,
192
- nextAction: reclaimed.length ? "node scripts/cw.js gc verify <run-id>" : "node scripts/cw.js gc plan"
193
- };
194
- }
195
- /** Re-prove a reclaimed run: skeleton schema-complete, tombstone chain
196
- * recomputed-and-untampered, each reconstructable artifact re-derived from its
197
- * RETAINED inputs to its expectDigest, and eligible-when-reclaimed. */
198
- function gcVerify(host, runId, options = {}) {
199
- const scope = options.scope || "home";
200
- const located = host.locate(runId, scope);
201
- if (!located) {
202
- return {
203
- schemaVersion: 1,
204
- runId,
205
- reclaimed: false,
206
- verified: false,
207
- tier: "live",
208
- capability: "re-runnable",
209
- chainLength: 0,
210
- checks: [{ name: "located", pass: false, code: "not-reclaimed", detail: "run source not found" }],
211
- nextAction: "node scripts/cw.js registry refresh" + (scope === "home" ? " --scope home" : "")
212
- };
213
- }
214
- const run = host.loadRun(located.record.repo, runId);
215
- const result = (0, reclamation_1.verifyReclamation)(run);
216
- const checks = result.checks.map((c) => ({ name: c.name, pass: c.pass, code: c.code, detail: c.detail }));
217
- // Eligible-when-reclaimed: each tombstone must have sealed a terminal verdict.
218
- let eligibleWhenReclaimed = result.reclaimed;
219
- for (const tombstone of result.tombstones) {
220
- const terminal = tombstone.skeleton.finalVerdict?.terminal === true;
221
- if (!terminal) {
222
- eligibleWhenReclaimed = false;
223
- checks.push({ name: `eligible-when-reclaimed:${tombstone.tombstoneId}`, pass: false, code: "ineligible-when-reclaimed", detail: "non-terminal verdict sealed" });
224
- }
225
- }
226
- const last = result.tombstones[result.tombstones.length - 1];
227
- // Independent witness: a trust-audit "run.reclaimed" event proves this run was
228
- // reclaimed even if reclaimed.json was deleted. A present witness + missing proof
229
- // = the proof was deleted/tampered (NOT "never reclaimed") — fail closed so
230
- // `gc verify <run> && deploy` cannot pass on a wiped reclamation record.
231
- const witnessed = (0, trust_audit_1.listTrustAuditEvents)(run).some((event) => event.kind === "run.reclaimed");
232
- const proofDeleted = witnessed && !result.reclaimed;
233
- if (proofDeleted) {
234
- checks.push({ name: "reclaim-witness", pass: false, code: "reclaim-proof-deleted", detail: "trust-audit attests reclamation but reclaimed.json is missing/empty" });
235
- }
236
- const reclaimed = result.reclaimed || proofDeleted;
237
- const verified = result.verified && eligibleWhenReclaimed && !proofDeleted;
238
- return {
239
- schemaVersion: 1,
240
- runId,
241
- reclaimed,
242
- verified,
243
- tier: located.record.tier || (reclaimed ? "reclaimed" : "live"),
244
- capability: located.record.capability || "re-runnable",
245
- capabilityReason: located.record.capabilityReason,
246
- tombstoneHash: last?.tombstoneHash,
247
- chainLength: result.tombstones.length,
248
- checks,
249
- nextAction: verified ? "node scripts/cw.js run show " + runId : "node scripts/cw.js gc plan"
250
- };
251
- }
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_RUN_REGISTRY_POLICY = exports.RUN_REGISTRY_SCHEMA_VERSION = void 0;
4
- exports.RUN_REGISTRY_SCHEMA_VERSION = 1;
5
- exports.DEFAULT_RUN_REGISTRY_POLICY = {
6
- schemaVersion: 1,
7
- archiveOlderThanDays: 0,
8
- archiveStates: ["completed", "failed"],
9
- defaultQueuePriority: 100,
10
- reclaimAfterArchiveDays: 0,
11
- reclaimStates: ["completed", "failed"],
12
- keepSnapshots: false,
13
- keepScratch: false,
14
- maxReclaimRuns: 0,
15
- maxReclaimBytes: 0
16
- };
@@ -1,115 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.queueFilePath = queueFilePath;
7
- exports.loadQueue = loadQueue;
8
- exports.saveQueue = saveQueue;
9
- exports.queueAdd = queueAdd;
10
- exports.queueList = queueList;
11
- exports.queueShow = queueShow;
12
- exports.queueDrain = queueDrain;
13
- // Durable run-queue operations for the run registry (FreeBSD-audit R2 deep).
14
- // Carved out of run-registry.ts so the RunRegistry class no longer bundles the
15
- // stateful queue cluster; the class keeps the public methods as thin delegators.
16
- //
17
- // BEHAVIOR-PRESERVING — pure code movement, zero logic change. Each function
18
- // takes a `QueueHost` (the registry, narrowed to exactly the file-access +
19
- // repo-registration helpers the queue needs) so it stays a function of its
20
- // inputs, matching the existing router pattern (orchestrator/*-operations.ts,
21
- // run-registry/derive.ts + format.ts).
22
- //
23
- // The queue file lives beside the other home-registry plain files (EXPLICIT,
24
- // INSPECTABLE STATE): readable, diffable, no hidden database. Cross-process
25
- // read-modify-write is locked (v0.1.40, P1-D) so a concurrent add/drain can
26
- // never drop or double-drain an entry.
27
- const node_path_1 = __importDefault(require("node:path"));
28
- const node_fs_1 = __importDefault(require("node:fs"));
29
- const state_1 = require("../state");
30
- const derive_1 = require("./derive");
31
- function queueFilePath(host) {
32
- return node_path_1.default.join(host.homeRegistryDir(), "queue.json");
33
- }
34
- function loadQueue(host) {
35
- const file = queueFilePath(host);
36
- // Absent => empty queue. A present-but-corrupt queue must FAIL CLOSED rather
37
- // than read as empty: silently draining to [] would lose every queued run and
38
- // let scheduling/lease ops proceed as if the store were clean. readJson throws
39
- // `Invalid JSON in <file>` on a present, unparseable store; let it propagate.
40
- if (!node_fs_1.default.existsSync(file))
41
- return [];
42
- const parsed = (0, state_1.readJson)(file);
43
- return Array.isArray(parsed.entries) ? parsed.entries : [];
44
- }
45
- function saveQueue(host, entries) {
46
- (0, state_1.writeJson)(queueFilePath(host), { schemaVersion: 1, entries }, { durable: true });
47
- }
48
- function queueAdd(host, options = {}) {
49
- const repo = options.repo ? node_path_1.default.resolve(options.repo) : host.repoRoot;
50
- // Cross-process read-modify-write on the home queue: lock so a concurrently
51
- // added task can never vanish (v0.1.40, P1-D).
52
- return (0, state_1.withFileLock)(queueFilePath(host), () => {
53
- const entries = loadQueue(host);
54
- const entry = {
55
- schemaVersion: 1,
56
- id: options.id || (0, derive_1.queueId)(),
57
- runId: options.runId,
58
- appId: options.appId,
59
- workflowId: options.workflowId,
60
- repo,
61
- priority: Number.isFinite(options.priority) ? Number(options.priority) : host.defaultQueuePriority,
62
- enqueuedAt: new Date().toISOString(),
63
- status: "pending",
64
- inputs: options.inputs,
65
- note: options.note
66
- };
67
- entries.push(entry);
68
- host.registerRepo(repo);
69
- saveQueue(host, entries);
70
- return entry;
71
- });
72
- }
73
- function queueList(host, options = {}) {
74
- let entries = loadQueue(host);
75
- if (options.status)
76
- entries = entries.filter((e) => e.status === options.status);
77
- if (options.repo) {
78
- const repo = node_path_1.default.resolve(options.repo);
79
- entries = entries.filter((e) => node_path_1.default.resolve(e.repo) === repo);
80
- }
81
- entries = [...entries].sort(derive_1.compareQueue);
82
- return { schemaVersion: 1, total: entries.length, entries };
83
- }
84
- function queueShow(host, id) {
85
- const entry = loadQueue(host).find((e) => e.id === id);
86
- if (!entry)
87
- throw new Error(`Queue entry not found: ${id}`);
88
- return entry;
89
- }
90
- /** Drain the next N ready/pending entries in policy order, marking them drained.
91
- * CW records readiness/order; the HOST still executes the workers. */
92
- function queueDrain(host, options = {}) {
93
- const limit = (0, derive_1.clampInt)(options.limit, 1, 1);
94
- const repoFilter = options.repo ? node_path_1.default.resolve(options.repo) : undefined;
95
- // Lock the drain RMW so two hosts can never double-drain the same entry
96
- // (v0.1.40, P1-D — the scheduling kernel's concurrency ceiling now holds
97
- // across processes, not just within one).
98
- return (0, state_1.withFileLock)(queueFilePath(host), () => {
99
- const entries = loadQueue(host);
100
- const drainable = entries
101
- .filter((e) => e.status === "pending" || e.status === "ready")
102
- .filter((e) => !repoFilter || node_path_1.default.resolve(e.repo) === repoFilter)
103
- .sort(derive_1.compareQueue);
104
- const drained = [];
105
- const drainedAt = new Date().toISOString();
106
- for (const entry of drainable.slice(0, limit)) {
107
- entry.status = "drained";
108
- entry.drainedAt = drainedAt;
109
- drained.push(entry);
110
- }
111
- saveQueue(host, entries);
112
- const remaining = entries.filter((e) => e.status === "pending" || e.status === "ready").length;
113
- return { schemaVersion: 1, drained, remaining };
114
- });
115
- }