@oisincoveney/pipeline 3.24.2 → 3.24.3

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 (326) hide show
  1. package/README.md +2 -2
  2. package/defaults/pipeline.yaml +23 -5
  3. package/defaults/profiles.yaml +104 -21
  4. package/dist/argo-graph.js +88 -88
  5. package/dist/argo-submit.d.ts +109 -137
  6. package/dist/argo-submit.js +337 -360
  7. package/dist/argo-workflow.d.ts +156 -176
  8. package/dist/argo-workflow.js +96 -138
  9. package/dist/backlog.js +5 -4
  10. package/dist/bench/eval-report.js +21 -12
  11. package/dist/claude-settings-config.js +23 -31
  12. package/dist/claude-user-config.js +6 -16
  13. package/dist/cli/bootstrap-commands.js +3 -3
  14. package/dist/cli/doctor.d.ts +1 -1
  15. package/dist/cli/doctor.js +172 -169
  16. package/dist/cli/factory-commands.js +12 -11
  17. package/dist/cli/format.js +92 -86
  18. package/dist/cli/loop-commands.js +21 -21
  19. package/dist/cli/mcp-gateway-commands.js +12 -12
  20. package/dist/cli/plan-commands.js +85 -116
  21. package/dist/cli/program.d.ts +2 -2
  22. package/dist/cli/program.js +35 -43
  23. package/dist/cli/run-command.js +25 -25
  24. package/dist/cli/run-commands.js +25 -25
  25. package/dist/cli/run-resolver.d.ts +1 -1
  26. package/dist/cli/run-resolver.js +30 -32
  27. package/dist/cli/run-service.d.ts +2 -2
  28. package/dist/cli/run-service.js +226 -239
  29. package/dist/cli/submit-options.js +78 -82
  30. package/dist/cluster-doctor.js +147 -175
  31. package/dist/codex-config.js +24 -30
  32. package/dist/commands/bench-command.js +9 -7
  33. package/dist/commands/pipeline-command.js +9 -5
  34. package/dist/commands/runner-command-command.js +28 -18
  35. package/dist/commands/ticket/complete.js +29 -33
  36. package/dist/commands/ticket/create.js +82 -82
  37. package/dist/commands/ticket/graph-check.js +11 -11
  38. package/dist/commands/ticket/next.js +36 -36
  39. package/dist/commands/ticket/sequence.js +10 -12
  40. package/dist/commands/ticket/shared.d.ts +1 -1
  41. package/dist/commands/ticket/shared.js +63 -66
  42. package/dist/commands/ticket/start.js +58 -67
  43. package/dist/commands/ticket-command.js +2 -2
  44. package/dist/config/defaults.d.ts +139 -139
  45. package/dist/config/defaults.js +81 -80
  46. package/dist/config/lint.js +52 -64
  47. package/dist/config/load.d.ts +4 -4
  48. package/dist/config/load.js +62 -74
  49. package/dist/config/schema/catalog.js +1 -1
  50. package/dist/config/schema/mcp.js +46 -52
  51. package/dist/config/schemas.d.ts +375 -525
  52. package/dist/config/schemas.js +397 -397
  53. package/dist/config/validate.d.ts +1 -1
  54. package/dist/config/validate.js +236 -201
  55. package/dist/context/repo-map.js +102 -119
  56. package/dist/credentials/broker.d.ts +6 -8
  57. package/dist/credentials/broker.js +17 -16
  58. package/dist/credentials/codex-config.js +15 -19
  59. package/dist/credentials/file-targets.js +9 -15
  60. package/dist/credentials/local-codex-auth-sync.js +55 -58
  61. package/dist/credentials/opencode-config.js +28 -32
  62. package/dist/credentials/runner.js +13 -13
  63. package/dist/effect-sync-errors.js +28 -0
  64. package/dist/factory/create-experiment.js +38 -42
  65. package/dist/factory/exec.js +10 -12
  66. package/dist/factory/git-credentials.js +4 -4
  67. package/dist/factory/stamp-answers.js +10 -11
  68. package/dist/factory/template-update.js +65 -44
  69. package/dist/factory-lane.d.ts +26 -40
  70. package/dist/factory-lane.js +26 -25
  71. package/dist/gates.js +3 -3
  72. package/dist/git-remote-url.js +17 -19
  73. package/dist/hooks.d.ts +26 -25
  74. package/dist/hooks.js +20 -22
  75. package/dist/index.d.ts +1 -1
  76. package/dist/index.js +21 -35
  77. package/dist/install-commands/claude-code.js +96 -92
  78. package/dist/install-commands/host-selection.js +3 -6
  79. package/dist/install-commands/opencode.js +263 -259
  80. package/dist/install-commands/planner.js +159 -180
  81. package/dist/install-commands/result-format.js +1 -3
  82. package/dist/install-commands/shared.js +25 -42
  83. package/dist/install-commands/writer.js +22 -25
  84. package/dist/install-commands.js +8 -8
  85. package/dist/jscpd-output.js +29 -0
  86. package/dist/json-config-merge.js +14 -20
  87. package/dist/json-line-values.js +17 -6
  88. package/dist/loop/argo-poll.js +52 -71
  89. package/dist/loop/backlog-records.js +1 -3
  90. package/dist/loop/controller-deps.js +146 -159
  91. package/dist/loop/controller.js +162 -186
  92. package/dist/loop/gh-checks.js +62 -61
  93. package/dist/loop/gh-runner.js +22 -25
  94. package/dist/loop/loop-command.js +66 -58
  95. package/dist/loop/loop-controller-entrypoint.js +28 -30
  96. package/dist/loop/merge.js +36 -40
  97. package/dist/mcp/gateway-config.js +25 -29
  98. package/dist/mcp/gateway-doctor.js +104 -116
  99. package/dist/mcp/gateway-reconcile.js +17 -22
  100. package/dist/mcp/gateway-runtime.js +1 -3
  101. package/dist/mcp/host-config.js +37 -40
  102. package/dist/mcp/host-renderers.js +10 -18
  103. package/dist/mcp/repo-local-backends.js +50 -54
  104. package/dist/mcp/toolhive-vmcp.js +39 -39
  105. package/dist/model-resolver.js +29 -33
  106. package/dist/moka-global-config.d.ts +78 -54
  107. package/dist/moka-global-config.js +90 -92
  108. package/dist/moka-submit.d.ts +2163 -864
  109. package/dist/moka-submit.js +97 -99
  110. package/dist/opencode-project-config.js +34 -40
  111. package/dist/package-assets.js +1 -3
  112. package/dist/path-refs.js +2 -2
  113. package/dist/pipeline-init.js +25 -25
  114. package/dist/pipeline-runtime.d.ts +11 -11
  115. package/dist/pipeline-runtime.js +49 -50
  116. package/dist/planned-node.js +6 -3
  117. package/dist/planning/compile.d.ts +3 -3
  118. package/dist/planning/compile.js +140 -139
  119. package/dist/planning/dependency-refs.js +5 -5
  120. package/dist/planning/generate.d.ts +1293 -1293
  121. package/dist/planning/generate.js +305 -314
  122. package/dist/planning/graph.js +131 -149
  123. package/dist/remote/argo/model.d.ts +103 -121
  124. package/dist/remote/argo/model.js +205 -197
  125. package/dist/remote/argo/policy.js +39 -47
  126. package/dist/remote/argo/storage.js +81 -85
  127. package/dist/remote/submit/argo-submission.js +53 -58
  128. package/dist/remote/submit/compilation.js +46 -49
  129. package/dist/remote/submit/event-boundary.js +62 -76
  130. package/dist/remote/submit/hook-events.d.ts +5 -0
  131. package/dist/remote/submit/io.js +40 -43
  132. package/dist/remote/submit/options.d.ts +50 -0
  133. package/dist/remote/submit/options.js +27 -0
  134. package/dist/remote/submit/service.js +45 -49
  135. package/dist/run-control/command-context.js +6 -8
  136. package/dist/run-control/commands.js +10 -10
  137. package/dist/run-control/contracts.d.ts +40 -100
  138. package/dist/run-control/contracts.js +56 -69
  139. package/dist/run-control/detach.js +66 -72
  140. package/dist/run-control/file-errors.js +1 -3
  141. package/dist/run-control/logical-segment.js +3 -3
  142. package/dist/run-control/next-node.js +66 -78
  143. package/dist/run-control/postgres/postgres-run-control-store.js +195 -224
  144. package/dist/run-control/resume-command.js +33 -41
  145. package/dist/run-control/run-artifacts-command.js +83 -105
  146. package/dist/run-control/run-command-domain.js +7 -11
  147. package/dist/run-control/run-control-store.js +9 -9
  148. package/dist/run-control/run-query-command.js +73 -100
  149. package/dist/run-control/run-record.js +2 -2
  150. package/dist/run-control/run-state-lock.js +5 -5
  151. package/dist/run-control/runtime-event-projection.js +38 -50
  152. package/dist/run-control/runtime-reporter.js +84 -102
  153. package/dist/run-control/stop-command.js +42 -53
  154. package/dist/run-control/store-fs-effects.js +3 -1
  155. package/dist/run-control/store-manifest.js +38 -13
  156. package/dist/run-control/store-paths.js +3 -3
  157. package/dist/run-control/store.js +1 -1
  158. package/dist/run-control/submit-result.js +21 -21
  159. package/dist/run-control/supervisor.js +77 -68
  160. package/dist/run-control/workspace.js +2 -2
  161. package/dist/run-state/git-refs.js +257 -307
  162. package/dist/runner/opencode-excludes.js +4 -4
  163. package/dist/runner/subprocess-result.js +28 -37
  164. package/dist/runner/subprocess.js +41 -41
  165. package/dist/runner/timeouts.js +14 -13
  166. package/dist/runner-command/dynamic-command.js +46 -39
  167. package/dist/runner-command/finalize.js +174 -153
  168. package/dist/runner-command/lifecycle-context.js +112 -130
  169. package/dist/runner-command/lifecycle.js +77 -71
  170. package/dist/runner-command/pre-schedule.js +201 -222
  171. package/dist/runner-command/run.js +432 -447
  172. package/dist/runner-command/schedule-source-options.js +8 -10
  173. package/dist/runner-command/select-ready-wave.js +38 -29
  174. package/dist/runner-command/task-descriptor.js +11 -17
  175. package/dist/runner-command-contract.d.ts +147 -156
  176. package/dist/runner-command-contract.js +401 -410
  177. package/dist/runner-event-schema.d.ts +364 -504
  178. package/dist/runner-event-schema.js +187 -175
  179. package/dist/runner-event-sink.js +37 -43
  180. package/dist/runner-output.js +6 -6
  181. package/dist/runner.d.ts +56 -36
  182. package/dist/runner.js +175 -127
  183. package/dist/runtime/actor-ids.js +2 -2
  184. package/dist/runtime/agent-node/agent-node.js +99 -111
  185. package/dist/runtime/agent-node/handoff-finalization.js +23 -24
  186. package/dist/runtime/agent-node/model-selection.js +19 -22
  187. package/dist/runtime/agent-node/output-finalization.js +174 -205
  188. package/dist/runtime/agent-node/prompt-rendering.js +207 -251
  189. package/dist/runtime/agent-node/session-execution.js +34 -38
  190. package/dist/runtime/builtins/builtins.js +212 -284
  191. package/dist/runtime/changed-files/changed-files.js +26 -34
  192. package/dist/runtime/command-executor/command-executor.js +31 -23
  193. package/dist/runtime/config-error.js +1 -3
  194. package/dist/runtime/context/context.js +35 -37
  195. package/dist/runtime/contracts/index.d.ts +1 -1
  196. package/dist/runtime/detached-race.js +11 -14
  197. package/dist/runtime/drain-merge/drain-merge.js +137 -163
  198. package/dist/runtime/durable-store/acquisition.js +5 -3
  199. package/dist/runtime/durable-store/durable-store.d.ts +1 -1
  200. package/dist/runtime/durable-store/postgres/migrate-substrate.js +36 -33
  201. package/dist/runtime/durable-store/postgres/postgres-store.js +17 -18
  202. package/dist/runtime/events/events.js +255 -323
  203. package/dist/runtime/gates/adjudication/llm-judge.js +21 -24
  204. package/dist/runtime/gates/adjudication/structured-claim.js +15 -16
  205. package/dist/runtime/gates/adjudicator/adjudicator.js +27 -27
  206. package/dist/runtime/gates/adjudicator/index.js +2 -2
  207. package/dist/runtime/gates/contract.js +5 -9
  208. package/dist/runtime/gates/gates.js +9 -8
  209. package/dist/runtime/gates/kinds/acceptance/acceptance.js +66 -70
  210. package/dist/runtime/gates/kinds/acceptance/index.js +2 -2
  211. package/dist/runtime/gates/kinds/artifact/artifact.js +4 -4
  212. package/dist/runtime/gates/kinds/artifact/index.js +2 -2
  213. package/dist/runtime/gates/kinds/builtin/builtin.js +4 -4
  214. package/dist/runtime/gates/kinds/builtin/index.js +2 -2
  215. package/dist/runtime/gates/kinds/changed-files/changed-files.js +31 -35
  216. package/dist/runtime/gates/kinds/changed-files/index.js +2 -2
  217. package/dist/runtime/gates/kinds/command/command.js +3 -3
  218. package/dist/runtime/gates/kinds/command/index.js +2 -2
  219. package/dist/runtime/gates/kinds/json-schema/index.js +2 -2
  220. package/dist/runtime/gates/kinds/json-schema/json-schema.js +7 -6
  221. package/dist/runtime/gates/kinds/verdict/index.js +2 -2
  222. package/dist/runtime/gates/kinds/verdict/verdict.js +3 -3
  223. package/dist/runtime/gates/orchestrator.js +72 -86
  224. package/dist/runtime/gates/registry.js +1 -3
  225. package/dist/runtime/handoff.d.ts +1 -1
  226. package/dist/runtime/handoff.js +43 -46
  227. package/dist/runtime/hooks/command-hook.js +57 -71
  228. package/dist/runtime/hooks/context.js +21 -25
  229. package/dist/runtime/hooks/events.js +84 -100
  230. package/dist/runtime/hooks/execution.js +6 -12
  231. package/dist/runtime/hooks/hooks.js +34 -23
  232. package/dist/runtime/hooks/invocation.js +23 -29
  233. package/dist/runtime/hooks/module-hook.js +35 -35
  234. package/dist/runtime/hooks/policy.js +32 -29
  235. package/dist/runtime/hooks/results.js +56 -66
  236. package/dist/runtime/journal-acquisition.d.ts +1 -1
  237. package/dist/runtime/journal-acquisition.js +14 -14
  238. package/dist/runtime/json-validation/json-validation.js +50 -72
  239. package/dist/runtime/local-scheduler.js +46 -48
  240. package/dist/runtime/node-execution.js +375 -446
  241. package/dist/runtime/node-protocol/node-protocol.js +36 -79
  242. package/dist/runtime/node-state-store.d.ts +1 -1
  243. package/dist/runtime/node-state-store.js +13 -57
  244. package/dist/runtime/node-state-tracker.js +46 -54
  245. package/dist/runtime/open-pull-request/open-pull-request.js +143 -187
  246. package/dist/runtime/opencode-adapter.js +23 -24
  247. package/dist/runtime/opencode-agent-name.js +6 -6
  248. package/dist/runtime/opencode-runtime.js +92 -92
  249. package/dist/runtime/opencode-server.js +39 -53
  250. package/dist/runtime/opencode-session-executor.js +315 -334
  251. package/dist/runtime/parallel-node/parallel-node.js +122 -134
  252. package/dist/runtime/parallel-worktrees/parallel-worktrees.js +90 -73
  253. package/dist/runtime/protected-paths/protected-paths.js +70 -71
  254. package/dist/runtime/remediation/remediation.js +202 -231
  255. package/dist/runtime/retry.js +8 -10
  256. package/dist/runtime/run-journal.js +7 -7
  257. package/dist/runtime/runtime-results.js +54 -69
  258. package/dist/runtime/scheduled-dependencies.d.ts +1 -1
  259. package/dist/runtime/scheduled-dependencies.js +59 -74
  260. package/dist/runtime/scheduler.js +177 -202
  261. package/dist/runtime/services/agent-node-runtime-service.js +3 -3
  262. package/dist/runtime/services/backlog-service.js +8 -14
  263. package/dist/runtime/services/command-executor-service.js +1 -1
  264. package/dist/runtime/services/config-io-service.js +24 -21
  265. package/dist/runtime/services/drain-merge-git-service.js +1 -1
  266. package/dist/runtime/services/file-system-service.js +9 -7
  267. package/dist/runtime/services/git-porcelain-service.js +6 -6
  268. package/dist/runtime/services/kubernetes-argo-service.d.ts +10 -2
  269. package/dist/runtime/services/kubernetes-argo-service.js +90 -84
  270. package/dist/runtime/services/mcp-gateway-service.js +82 -81
  271. package/dist/runtime/services/open-pull-request-git-service.js +1 -3
  272. package/dist/runtime/services/opencode-runtime-server-service.js +10 -12
  273. package/dist/runtime/services/opencode-sdk-service.js +7 -7
  274. package/dist/runtime/services/repo-io-service.js +10 -12
  275. package/dist/runtime/services/run-journal-file-service.js +2 -2
  276. package/dist/runtime/services/runner-command-io-service.js +48 -52
  277. package/dist/runtime/services/runner-event-sink-http-service.js +31 -39
  278. package/dist/runtime/step/step-node.js +15 -15
  279. package/dist/runtime/workflow-execution.js +55 -57
  280. package/dist/runtime/workflow-lifecycle.js +81 -101
  281. package/dist/safe-json.js +16 -16
  282. package/dist/schedule/backlog-context.js +120 -116
  283. package/dist/schedule/baseline.js +249 -255
  284. package/dist/schedule/passes/coverage.js +63 -68
  285. package/dist/schedule/passes/drain-merge.js +34 -40
  286. package/dist/schedule/passes/ids.js +15 -17
  287. package/dist/schedule/passes/models.js +40 -37
  288. package/dist/schedule/passes/open-pull-request.js +12 -18
  289. package/dist/schedule/passes/references.js +18 -22
  290. package/dist/schedule/prompts.js +134 -141
  291. package/dist/schedule/scheduling-roles.js +8 -14
  292. package/dist/schema-boundary.d.ts +1 -0
  293. package/dist/schema-boundary.js +145 -0
  294. package/dist/serialized-write-queue.js +2 -2
  295. package/dist/standard-output-schemas.js +61 -59
  296. package/dist/strings.js +12 -12
  297. package/dist/task-ref.js +5 -5
  298. package/dist/tickets/apply-ticket-plan.js +51 -61
  299. package/dist/tickets/backlog-task-store.d.ts +13 -22
  300. package/dist/tickets/backlog-task-store.js +80 -94
  301. package/dist/tickets/completion/complete-ticket.js +53 -59
  302. package/dist/tickets/ticket-graph-dto.d.ts +24 -47
  303. package/dist/tickets/ticket-graph-dto.js +40 -40
  304. package/dist/tickets/ticket-graph.d.ts +7 -1
  305. package/dist/tickets/ticket-graph.js +36 -50
  306. package/dist/tickets/ticket-plan-command-args.js +6 -10
  307. package/dist/tickets/ticket-plan-render.js +10 -16
  308. package/dist/tickets/ticket-plan.d.ts +31 -40
  309. package/dist/tickets/ticket-plan.js +97 -77
  310. package/dist/tickets/ticket-selection.d.ts +1 -1
  311. package/dist/tickets/ticket-selection.js +37 -52
  312. package/dist/tickets/ticket-task-index.js +13 -17
  313. package/dist/tickets/validation-error-format.js +3 -10
  314. package/dist/token-estimator.js +12 -7
  315. package/dist/unknown-fields.js +21 -0
  316. package/dist/workflow-submit-contract.d.ts +9 -9
  317. package/dist/workflow-submit-contract.js +10 -9
  318. package/docs/adr-opencode-first-goal-loop-runtime.md +11 -11
  319. package/docs/config-architecture.md +13 -13
  320. package/docs/mcp-host-isolation.md +1 -0
  321. package/docs/moka-orchestrator-design.md +3 -3
  322. package/docs/operator-guide.md +1 -1
  323. package/docs/runtime-actor-model.md +18 -18
  324. package/docs/slash-command-adapter-contract.md +3 -3
  325. package/package.json +73 -76
  326. package/dist/config/schema/reference-validation.js +0 -83
@@ -1,13 +1,15 @@
1
- import { errorMessage, formatZodIssues } from "./validation-error-format.js";
1
+ import { formatSchemaIssueList, mutableArray, parseResultWithSchema, struct, trimmedRequiredString, withDefault } from "../schema-boundary.js";
2
+ import { errorMessage } from "./validation-error-format.js";
2
3
  import { RepoIoService } from "../runtime/services/repo-io-service.js";
3
4
  import { indexChildrenByParentId } from "./ticket-task-index.js";
4
- import { Data, Effect } from "effect";
5
- import { z } from "zod";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Schema from "effect/Schema";
6
7
  import { join } from "node:path";
8
+ import * as Data from "effect/Data";
7
9
  import matter from "gray-matter";
8
10
  //#region src/tickets/backlog-task-store.ts
9
- const LINE_RE = /\r?\n/;
10
- const ACCEPTANCE_ITEM_RE = /^\s*-\s*\[[ xX]\]\s*(?:#?[\w.-]+\s+)?(.+)$/;
11
+ const LINE_RE = /\r?\n/u;
12
+ const ACCEPTANCE_ITEM_RE = /^\s*-\s*\[[ xX]\]\s*(?:#?[\w.-]+\s+)?(.+)$/u;
11
13
  const DESCRIPTION_MARKERS = {
12
14
  end: "<!-- SECTION:DESCRIPTION:END -->",
13
15
  start: "<!-- SECTION:DESCRIPTION:BEGIN -->"
@@ -16,69 +18,68 @@ const ACCEPTANCE_MARKERS = {
16
18
  end: "<!-- AC:END -->",
17
19
  start: "<!-- AC:BEGIN -->"
18
20
  };
19
- const nonEmptyStringSchema = z.string().trim().min(1);
20
- const taskFrontmatterSchema = z.object({
21
- dependencies: z.array(nonEmptyStringSchema).default([]),
21
+ const nonEmptyStringSchema = trimmedRequiredString;
22
+ const taskFrontmatterSchema = struct({
23
+ dependencies: withDefault(mutableArray(nonEmptyStringSchema), []),
22
24
  id: nonEmptyStringSchema,
23
- modified_files: z.array(nonEmptyStringSchema).default([]),
24
- ordinal: z.number().finite().optional(),
25
- parent_task_id: nonEmptyStringSchema.optional(),
26
- priority: z.enum([
25
+ modified_files: withDefault(mutableArray(nonEmptyStringSchema), []),
26
+ ordinal: Schema.optional(Schema.Number),
27
+ parent_task_id: Schema.optional(nonEmptyStringSchema),
28
+ priority: Schema.optional(Schema.Literals([
27
29
  "high",
28
30
  "medium",
29
31
  "low"
30
- ]).optional(),
31
- references: z.array(nonEmptyStringSchema).default([]),
32
- status: z.enum([
32
+ ])),
33
+ references: withDefault(mutableArray(nonEmptyStringSchema), []),
34
+ status: Schema.Literals([
33
35
  "To Do",
34
36
  "In Progress",
35
37
  "Done"
36
38
  ]),
37
39
  title: nonEmptyStringSchema
38
- }).passthrough();
40
+ });
39
41
  var BacklogTaskStoreError = class extends Data.TaggedError("BacklogTaskStoreError") {};
40
- function loadBacklogTaskStoreEffect(worktreePath) {
41
- return Effect.gen(function* () {
42
- return yield* buildBacklogTaskStoreEffect(yield* readBacklogTasksEffect(worktreePath));
43
- });
44
- }
45
- function parseBacklogTaskMarkdownEffect(source, filePath) {
46
- return Effect.gen(function* () {
47
- const parsed = yield* Effect.try({
48
- catch: (error) => storeError(filePath, `Could not parse Backlog task frontmatter: ${errorMessage(error)}`),
49
- try: () => matter(source)
50
- });
51
- return taskRecordFromDocument(yield* decodeFrontmatterEffect(parsed.data, filePath), parsed.content, filePath);
52
- });
53
- }
54
- function readBacklogTasksEffect(worktreePath) {
55
- const tasksDir = join(worktreePath, "backlog", "tasks");
56
- return Effect.gen(function* () {
57
- const repoIo = yield* RepoIoService;
58
- if (!(yield* repoIo.exists(tasksDir).pipe(Effect.mapError((error) => ioStoreError(tasksDir, error))))) return [];
59
- const entries = yield* repoIo.readDir(tasksDir).pipe(Effect.mapError((error) => ioStoreError(tasksDir, error)));
60
- return yield* Effect.all(entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => {
61
- const filePath = join(tasksDir, entry.name);
62
- return repoIo.readText(filePath).pipe(Effect.mapError((error) => ioStoreError(filePath, error)), Effect.flatMap((source) => parseBacklogTaskMarkdownEffect(source, filePath)));
63
- }));
64
- });
65
- }
66
- function decodeFrontmatterEffect(frontmatter, filePath) {
67
- const decoded = taskFrontmatterSchema.safeParse(frontmatter);
68
- if (decoded.success) return Effect.succeed(decoded.data);
69
- return Effect.fail(storeError(filePath, `Invalid Backlog task frontmatter in ${filePath}: ${formatZodIssues(decoded.error.issues)}`));
70
- }
71
- function buildBacklogTaskStoreEffect(tasks) {
72
- return Effect.gen(function* () {
73
- const tasksById = yield* indexTasksByIdEffect(tasks);
74
- return {
75
- childrenByParentId: indexChildrenByParentId(tasks),
76
- tasks,
77
- tasksById
78
- };
42
+ const extractMarkedBlock = (content, markers) => {
43
+ const start = content.indexOf(markers.start);
44
+ if (start === -1) return "";
45
+ const bodyStart = start + markers.start.length;
46
+ const end = content.indexOf(markers.end, bodyStart);
47
+ if (end === -1) return "";
48
+ return content.slice(bodyStart, end).trim();
49
+ };
50
+ const extractDescription = (content) => extractMarkedBlock(content, DESCRIPTION_MARKERS);
51
+ const extractAcceptanceCriteria = (content) => extractMarkedBlock(content, ACCEPTANCE_MARKERS).split(LINE_RE).map((line) => ACCEPTANCE_ITEM_RE.exec(line)?.[1]?.trim()).filter((criterion) => criterion !== void 0 && criterion.length > 0);
52
+ const taskRecordFromDocument = (frontmatter, content, filePath) => ({
53
+ acceptanceCriteria: extractAcceptanceCriteria(content),
54
+ dependencies: frontmatter.dependencies,
55
+ description: extractDescription(content),
56
+ filePath,
57
+ id: frontmatter.id,
58
+ modifiedFiles: frontmatter.modified_files,
59
+ ordinal: frontmatter.ordinal,
60
+ parentTaskId: frontmatter.parent_task_id,
61
+ priority: frontmatter.priority,
62
+ references: frontmatter.references,
63
+ status: frontmatter.status,
64
+ title: frontmatter.title
65
+ });
66
+ const storeError = (path, message) => new BacklogTaskStoreError({
67
+ message,
68
+ path
69
+ });
70
+ const decodeFrontmatterEffect = (frontmatter, filePath) => {
71
+ const decoded = parseResultWithSchema(taskFrontmatterSchema, frontmatter, { onExcessProperty: "preserve" });
72
+ if (decoded.ok) return Effect.succeed(decoded.value);
73
+ return Effect.fail(storeError(filePath, `Invalid Backlog task frontmatter in ${filePath}: ${formatSchemaIssueList(decoded.issues)}`));
74
+ };
75
+ const parseBacklogTaskMarkdownEffect = (source, filePath) => Effect.gen(function* effectBody() {
76
+ const parsed = yield* Effect.try({
77
+ catch: (error) => storeError(filePath, `Could not parse Backlog task frontmatter: ${errorMessage(error)}`),
78
+ try: () => matter(source)
79
79
  });
80
- }
81
- function indexTasksByIdEffect(tasks) {
80
+ return taskRecordFromDocument(yield* decodeFrontmatterEffect(parsed.data, filePath), parsed.content, filePath);
81
+ });
82
+ const indexTasksByIdEffect = (tasks) => {
82
83
  const tasksById = /* @__PURE__ */ new Map();
83
84
  for (const task of tasks) {
84
85
  const existing = tasksById.get(task.id);
@@ -86,45 +87,30 @@ function indexTasksByIdEffect(tasks) {
86
87
  tasksById.set(task.id, task);
87
88
  }
88
89
  return Effect.succeed(tasksById);
89
- }
90
- function taskRecordFromDocument(frontmatter, content, filePath) {
90
+ };
91
+ const buildBacklogTaskStoreEffect = (tasks) => Effect.gen(function* effectBody() {
92
+ const tasksById = yield* indexTasksByIdEffect(tasks);
91
93
  return {
92
- acceptanceCriteria: extractAcceptanceCriteria(content),
93
- dependencies: frontmatter.dependencies,
94
- description: extractDescription(content),
95
- filePath,
96
- id: frontmatter.id,
97
- modifiedFiles: frontmatter.modified_files,
98
- ordinal: frontmatter.ordinal,
99
- parentTaskId: frontmatter.parent_task_id,
100
- priority: frontmatter.priority,
101
- references: frontmatter.references,
102
- status: frontmatter.status,
103
- title: frontmatter.title
94
+ childrenByParentId: indexChildrenByParentId(tasks),
95
+ tasks,
96
+ tasksById
104
97
  };
105
- }
106
- function extractDescription(content) {
107
- return extractMarkedBlock(content, DESCRIPTION_MARKERS);
108
- }
109
- function extractAcceptanceCriteria(content) {
110
- return extractMarkedBlock(content, ACCEPTANCE_MARKERS).split(LINE_RE).map((line) => ACCEPTANCE_ITEM_RE.exec(line)?.[1]?.trim()).filter((criterion) => Boolean(criterion));
111
- }
112
- function extractMarkedBlock(content, markers) {
113
- const start = content.indexOf(markers.start);
114
- if (start < 0) return "";
115
- const bodyStart = start + markers.start.length;
116
- const end = content.indexOf(markers.end, bodyStart);
117
- if (end < 0) return "";
118
- return content.slice(bodyStart, end).trim();
119
- }
120
- function ioStoreError(path, error) {
121
- return storeError(path, `Could not read Backlog task data: ${errorMessage(error)}`);
122
- }
123
- function storeError(path, message) {
124
- return new BacklogTaskStoreError({
125
- message,
126
- path
98
+ });
99
+ const ioStoreError = (path, error) => storeError(path, `Could not read Backlog task data: ${errorMessage(error)}`);
100
+ const readBacklogTasksEffect = (worktreePath) => {
101
+ const tasksDir = join(worktreePath, "backlog", "tasks");
102
+ return Effect.gen(function* effectBody() {
103
+ const repoIo = yield* RepoIoService;
104
+ if (!(yield* repoIo.exists(tasksDir).pipe(Effect.mapError((error) => ioStoreError(tasksDir, error))))) return [];
105
+ const entries = yield* repoIo.readDir(tasksDir).pipe(Effect.mapError((error) => ioStoreError(tasksDir, error)));
106
+ return yield* Effect.all(entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => {
107
+ const filePath = join(tasksDir, entry.name);
108
+ return repoIo.readText(filePath).pipe(Effect.mapError((error) => ioStoreError(filePath, error)), Effect.flatMap((source) => parseBacklogTaskMarkdownEffect(source, filePath)));
109
+ }));
127
110
  });
128
- }
111
+ };
112
+ const loadBacklogTaskStoreEffect = (worktreePath) => Effect.gen(function* effectBody() {
113
+ return yield* buildBacklogTaskStoreEffect(yield* readBacklogTasksEffect(worktreePath));
114
+ });
129
115
  //#endregion
130
116
  export { BacklogTaskStoreError, loadBacklogTaskStoreEffect };
@@ -24,61 +24,7 @@ const conservativeLayerAJudge = (input) => ({
24
24
  rationale: `Layer A judge does not honor residue criterion '${input.criterion.id}' without anchored deterministic evidence`,
25
25
  satisfied: false
26
26
  });
27
- /**
28
- * The keystone completion use-case (PIPE-90.11): load the ticket's acceptance
29
- * criteria, adjudicate the {@link CompletionClaim} through the layered gate, and
30
- * set the status to Done ONLY on a passing verdict. A refusal returns the
31
- * structured {@link UnmetCriterion}[] and performs no status write. Pure given
32
- * the injected store + judge.
33
- */
34
- function completeTicket(input) {
35
- return Effect.gen(function* () {
36
- const target = yield* input.store.loadTarget(input.ticketId);
37
- const verdict = yield* Effect.tryPromise({
38
- catch: (error) => new TicketCompletionError({ message: `Adjudication failed for ticket '${input.ticketId}': ${errorMessage(error)}` }),
39
- try: () => adjudicate({
40
- claim: input.claim,
41
- criteria: target.criteria,
42
- deterministicGates: input.deterministicGates,
43
- judge: input.judge
44
- })
45
- });
46
- if (!verdict.passed) return {
47
- status: "refused",
48
- ticketId: input.ticketId,
49
- unmet: verdict.unmet
50
- };
51
- yield* input.store.markDone(input.ticketId);
52
- return {
53
- status: "completed",
54
- ticketId: input.ticketId
55
- };
56
- });
57
- }
58
- /**
59
- * Builds the live {@link TicketCompletionStore} backed by the Backlog task store
60
- * (read) and the `backlog` CLI (status write). Captures the worktree path and
61
- * the resolved services so the returned store's methods are service-free,
62
- * keeping {@link completeTicket} free of an R requirement.
63
- */
64
- function backlogTicketCompletionStoreEffect(worktreePath) {
65
- return Effect.gen(function* () {
66
- const repoIo = yield* RepoIoService;
67
- const backlog = yield* BacklogService;
68
- return {
69
- loadTarget: (ticketId) => loadBacklogTaskStoreEffect(worktreePath).pipe(Effect.provideService(RepoIoService, repoIo), Effect.mapError((error) => new TicketCompletionError({ message: error.message })), Effect.flatMap((store) => resolveTargetEffect(store, ticketId))),
70
- markDone: (ticketId) => backlog.run([
71
- "task",
72
- "edit",
73
- ticketId,
74
- "--status",
75
- "Done",
76
- "--plain"
77
- ], worktreePath).pipe(Effect.asVoid, Effect.mapError((error) => new TicketCompletionError({ message: `Could not set ticket '${ticketId}' to Done: ${error.message}` })))
78
- };
79
- });
80
- }
81
- function resolveTargetEffect(store, ticketId) {
27
+ const resolveTargetEffect = (store, ticketId) => {
82
28
  const record = store.tasksById.get(ticketId);
83
29
  if (!record) return Effect.fail(new TicketCompletionError({ message: `Unknown Backlog ticket '${ticketId}'` }));
84
30
  return Effect.succeed({
@@ -88,9 +34,57 @@ function resolveTargetEffect(store, ticketId) {
88
34
  })),
89
35
  id: record.id
90
36
  });
91
- }
92
- function errorMessage(error) {
93
- return error instanceof Error ? error.message : String(error);
94
- }
37
+ };
38
+ /**
39
+ * Builds the live {@link TicketCompletionStore} backed by the Backlog task store
40
+ * (read) and the `backlog` CLI (status write). Captures the worktree path and
41
+ * the resolved services so the returned store's methods are service-free,
42
+ * keeping {@link completeTicket} free of an R requirement.
43
+ */
44
+ const backlogTicketCompletionStoreEffect = (worktreePath) => Effect.gen(function* effectBody() {
45
+ const repoIo = yield* RepoIoService;
46
+ const backlog = yield* BacklogService;
47
+ return {
48
+ loadTarget: (ticketId) => loadBacklogTaskStoreEffect(worktreePath).pipe(Effect.provideService(RepoIoService, repoIo), Effect.mapError((error) => new TicketCompletionError({ message: error.message })), Effect.flatMap((store) => resolveTargetEffect(store, ticketId))),
49
+ markDone: (ticketId) => backlog.run([
50
+ "task",
51
+ "edit",
52
+ ticketId,
53
+ "--status",
54
+ "Done",
55
+ "--plain"
56
+ ], worktreePath).pipe(Effect.asVoid, Effect.mapError((error) => new TicketCompletionError({ message: `Could not set ticket '${ticketId}' to Done: ${error.message}` })))
57
+ };
58
+ });
59
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
60
+ /**
61
+ * The keystone completion use-case (PIPE-90.11): load the ticket's acceptance
62
+ * criteria, adjudicate the {@link CompletionClaim} through the layered gate, and
63
+ * set the status to Done ONLY on a passing verdict. A refusal returns the
64
+ * structured {@link UnmetCriterion}[] and performs no status write. Pure given
65
+ * the injected store + judge.
66
+ */
67
+ const completeTicket = (input) => Effect.gen(function* effectBody() {
68
+ const target = yield* input.store.loadTarget(input.ticketId);
69
+ const verdict = yield* Effect.tryPromise({
70
+ catch: (error) => new TicketCompletionError({ message: `Adjudication failed for ticket '${input.ticketId}': ${errorMessage(error)}` }),
71
+ try: async () => await adjudicate({
72
+ claim: input.claim,
73
+ criteria: target.criteria,
74
+ deterministicGates: input.deterministicGates,
75
+ judge: input.judge
76
+ })
77
+ });
78
+ if (!verdict.passed) return {
79
+ status: "refused",
80
+ ticketId: input.ticketId,
81
+ unmet: verdict.unmet
82
+ };
83
+ yield* input.store.markDone(input.ticketId);
84
+ return {
85
+ status: "completed",
86
+ ticketId: input.ticketId
87
+ };
88
+ });
95
89
  //#endregion
96
90
  export { TicketCompletionError, backlogTicketCompletionStoreEffect, completeTicket, conservativeLayerAJudge };
@@ -1,60 +1,37 @@
1
- import { TicketGraph } from "./ticket-graph.js";
2
- import { Effect } from "effect";
3
- import { z } from "zod";
1
+ import { TicketGraph, TicketGraphError } from "./ticket-graph.js";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Schema from "effect/Schema";
4
4
 
5
5
  //#region src/tickets/ticket-graph-dto.d.ts
6
- /**
7
- * Node lifecycle in the loop controller DAG traversal.
8
- * One concept owns this variation — this enum is it.
9
- */
10
- declare const loopStateSchema: z.ZodEnum<{
11
- blocked: "blocked";
12
- merging: "merging";
13
- passed: "passed";
14
- queued: "queued";
15
- running: "running";
6
+ declare const LOOP_STATES: readonly ["queued", "running", "merging", "passed", "blocked"];
7
+ declare const loopState: Schema.Literals<readonly ["queued", "running", "merging", "passed", "blocked"]>;
8
+ type LoopState = typeof loopState.Type;
9
+ declare const ticketGraphDto: Schema.Struct<{
10
+ readonly batches: Schema.mutable<Schema.$Array<Schema.mutable<Schema.$Array<Schema.NonEmptyString>>>>;
11
+ readonly dangling: Schema.mutable<Schema.$Array<Schema.String>>;
12
+ readonly edges: Schema.mutable<Schema.$Array<Schema.Struct<{
13
+ readonly from: Schema.NonEmptyString;
14
+ readonly to: Schema.NonEmptyString;
15
+ }>>>;
16
+ readonly nodes: Schema.mutable<Schema.$Array<Schema.Struct<{
17
+ readonly id: Schema.NonEmptyString;
18
+ readonly loopState: Schema.Literals<readonly ["queued", "running", "merging", "passed", "blocked"]>;
19
+ readonly priority: Schema.optional<Schema.Literals<readonly ["high", "medium", "low"]>>;
20
+ readonly status: Schema.Literals<readonly ["To Do", "In Progress", "Done"]>;
21
+ readonly title: Schema.NonEmptyString;
22
+ }>>>;
16
23
  }>;
17
- type LoopState = z.infer<typeof loopStateSchema>;
18
- declare const ticketGraphDtoSchema: z.ZodObject<{
19
- batches: z.ZodArray<z.ZodArray<z.ZodString>>;
20
- dangling: z.ZodArray<z.ZodString>;
21
- edges: z.ZodArray<z.ZodObject<{
22
- from: z.ZodString;
23
- to: z.ZodString;
24
- }, z.core.$strip>>;
25
- nodes: z.ZodArray<z.ZodObject<{
26
- id: z.ZodString;
27
- loopState: z.ZodEnum<{
28
- blocked: "blocked";
29
- merging: "merging";
30
- passed: "passed";
31
- queued: "queued";
32
- running: "running";
33
- }>;
34
- priority: z.ZodOptional<z.ZodEnum<{
35
- high: "high";
36
- low: "low";
37
- medium: "medium";
38
- }>>;
39
- status: z.ZodEnum<{
40
- Done: "Done";
41
- "In Progress": "In Progress";
42
- "To Do": "To Do";
43
- }>;
44
- title: z.ZodString;
45
- }, z.core.$strip>>;
46
- }, z.core.$strip>;
47
- type TicketGraphDto = z.infer<typeof ticketGraphDtoSchema>;
24
+ type TicketGraphDto = typeof ticketGraphDto.Type;
48
25
  /**
49
26
  * Convert a `TicketGraph` to the cross-repo wire DTO.
50
27
  *
51
28
  * All nodes default to `loopState: "queued"` — the controller updates
52
- * individual nodes via `loop.node.transition` events as work proceeds.
29
+ * individual nodes via `loop.node.transition` events while work proceeds.
53
30
  *
54
31
  * Returns an Effect because `sequenceTicketBatchesEffect` is effectful (it can
55
32
  * fail on a graph that cannot be topologically sorted, which should never
56
33
  * happen for a well-formed backlog but must be handled explicitly).
57
34
  */
58
- declare function serializeTicketGraph(graph: TicketGraph): Effect.Effect<TicketGraphDto, never>;
35
+ declare const serializeTicketGraph: (graph: TicketGraph) => Effect.Effect<TicketGraphDto, TicketGraphError>;
59
36
  //#endregion
60
- export { LoopState, TicketGraphDto, loopStateSchema, serializeTicketGraph, ticketGraphDtoSchema };
37
+ export { LOOP_STATES, LoopState, TicketGraphDto, loopState as loopStateSchema, serializeTicketGraph, ticketGraphDto as ticketGraphDtoSchema };
@@ -1,73 +1,73 @@
1
1
  import { graphEdgeIds } from "../planning/graph.js";
2
+ import { mutableArray, requiredString, struct } from "../schema-boundary.js";
2
3
  import { sequenceTicketBatchesEffect } from "./ticket-graph.js";
3
- import { Effect } from "effect";
4
- import { z } from "zod";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Schema from "effect/Schema";
5
6
  //#region src/tickets/ticket-graph-dto.ts
6
7
  /**
7
8
  * Node lifecycle in the loop controller DAG traversal.
8
9
  * One concept owns this variation — this enum is it.
9
10
  */
10
- const loopStateSchema = z.enum([
11
+ const literalValues = (values) => values;
12
+ const LOOP_STATES = literalValues([
11
13
  "queued",
12
14
  "running",
13
15
  "merging",
14
16
  "passed",
15
17
  "blocked"
16
18
  ]);
17
- const ticketGraphDtoNodeSchema = z.object({
18
- id: z.string().min(1),
19
- loopState: loopStateSchema,
20
- priority: z.enum([
19
+ const loopState = Schema.Literals(LOOP_STATES);
20
+ const ticketGraphDtoNode = struct({
21
+ id: requiredString,
22
+ loopState,
23
+ priority: Schema.optional(Schema.Literals([
21
24
  "high",
22
25
  "medium",
23
26
  "low"
24
- ]).optional(),
25
- status: z.enum([
27
+ ])),
28
+ status: Schema.Literals([
26
29
  "To Do",
27
30
  "In Progress",
28
31
  "Done"
29
32
  ]),
30
- title: z.string().min(1)
33
+ title: requiredString
31
34
  });
32
- const ticketGraphDtoEdgeSchema = z.object({
33
- from: z.string().min(1),
34
- to: z.string().min(1)
35
+ const ticketGraphDtoEdge = struct({
36
+ from: requiredString,
37
+ to: requiredString
35
38
  });
36
- const ticketGraphDtoSchema = z.object({
37
- batches: z.array(z.array(z.string().min(1))),
38
- dangling: z.array(z.string()),
39
- edges: z.array(ticketGraphDtoEdgeSchema),
40
- nodes: z.array(ticketGraphDtoNodeSchema)
39
+ const ticketGraphDto = struct({
40
+ batches: mutableArray(mutableArray(requiredString)),
41
+ dangling: mutableArray(Schema.String),
42
+ edges: mutableArray(ticketGraphDtoEdge),
43
+ nodes: mutableArray(ticketGraphDtoNode)
41
44
  });
42
- const EMPTY_BATCHES = [];
43
45
  /**
44
46
  * Convert a `TicketGraph` to the cross-repo wire DTO.
45
47
  *
46
48
  * All nodes default to `loopState: "queued"` — the controller updates
47
- * individual nodes via `loop.node.transition` events as work proceeds.
49
+ * individual nodes via `loop.node.transition` events while work proceeds.
48
50
  *
49
51
  * Returns an Effect because `sequenceTicketBatchesEffect` is effectful (it can
50
52
  * fail on a graph that cannot be topologically sorted, which should never
51
53
  * happen for a well-formed backlog but must be handled explicitly).
52
54
  */
53
- function serializeTicketGraph(graph) {
54
- return Effect.gen(function* () {
55
- const batches = yield* sequenceTicketBatchesEffect(graph).pipe(Effect.catch(() => Effect.succeed(EMPTY_BATCHES)));
56
- const nodes = [...graph.tasksById.values()].map((task) => ({
57
- id: task.id,
58
- loopState: "queued",
59
- ...task.priority === void 0 ? {} : { priority: task.priority },
60
- status: task.status,
61
- title: task.title
62
- }));
63
- const edges = graphEdgeIds(graph.dependencyGraph);
64
- return {
65
- batches,
66
- dangling: [...graph.danglingDependencies],
67
- edges,
68
- nodes
69
- };
70
- });
71
- }
55
+ const serializeTicketGraph = (graph) => Effect.gen(function* effectBody() {
56
+ const batches = yield* sequenceTicketBatchesEffect(graph);
57
+ const nodes = [...graph.tasksById.values()].map((task) => ({
58
+ id: task.id,
59
+ loopState: "queued",
60
+ ...task.priority === void 0 ? {} : { priority: task.priority },
61
+ status: task.status,
62
+ title: task.title
63
+ }));
64
+ const edges = graphEdgeIds(graph.dependencyGraph);
65
+ return {
66
+ batches,
67
+ dangling: [...graph.danglingDependencies],
68
+ edges,
69
+ nodes
70
+ };
71
+ });
72
72
  //#endregion
73
- export { loopStateSchema, serializeTicketGraph, ticketGraphDtoSchema };
73
+ export { LOOP_STATES, loopState as loopStateSchema, serializeTicketGraph, ticketGraphDto as ticketGraphDtoSchema };
@@ -16,5 +16,11 @@ interface TicketGraph {
16
16
  readonly dependencyGraph: DependencyGraph<BacklogTaskRecord>;
17
17
  readonly tasksById: ReadonlyMap<string, BacklogTaskRecord>;
18
18
  }
19
+ declare const TicketGraphError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & {
20
+ readonly _tag: "TicketGraphError";
21
+ } & Readonly<A>;
22
+ declare class TicketGraphError extends TicketGraphError_base<{
23
+ readonly message: string;
24
+ }> {}
19
25
  //#endregion
20
- export { TicketGraph };
26
+ export { TicketGraph, TicketGraphError };