@defrex/autobuild 0.8.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 (251) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/SPEC.md +2269 -0
  4. package/bin/ab-build-runner.ts +47 -0
  5. package/bin/ab-dev.ts +27 -0
  6. package/bin/ab-dispatch-kernel.ts +92 -0
  7. package/bin/ab-harvest-runner.ts +29 -0
  8. package/bin/ab-init-probe.ts +19 -0
  9. package/bin/ab-process-group-reaper.ts +32 -0
  10. package/bin/ab.ts +6 -0
  11. package/bin/agent/ab +3 -0
  12. package/docs/answering-blocked-builds.md +89 -0
  13. package/docs/architecture.md +726 -0
  14. package/docs/assets/headline-wide.png +0 -0
  15. package/docs/configuration.md +1623 -0
  16. package/docs/hosted-dispatcher-cutover-validation.md +178 -0
  17. package/docs/hosted-dispatcher.md +204 -0
  18. package/docs/mcp.md +119 -0
  19. package/docs/operator-api.md +186 -0
  20. package/docs/remote-store-protocol.md +972 -0
  21. package/docs/remote-ticket-protocol.md +79 -0
  22. package/docs/setup.md +289 -0
  23. package/docs/spec-standard.md +63 -0
  24. package/docs/ticket-dependencies.md +59 -0
  25. package/docs/vercel-sandbox-rollout-probe.md +7 -0
  26. package/package.json +135 -0
  27. package/packages/core/src/cli/args.ts +74 -0
  28. package/packages/core/src/cli/artifact.ts +182 -0
  29. package/packages/core/src/cli/binary.ts +198 -0
  30. package/packages/core/src/cli/build-control.ts +789 -0
  31. package/packages/core/src/cli/build-progress.ts +55 -0
  32. package/packages/core/src/cli/bulk-control.ts +385 -0
  33. package/packages/core/src/cli/context.ts +470 -0
  34. package/packages/core/src/cli/dashboard/actions.ts +59 -0
  35. package/packages/core/src/cli/dashboard/cells.ts +92 -0
  36. package/packages/core/src/cli/dashboard/composer.ts +230 -0
  37. package/packages/core/src/cli/dashboard/detail.ts +100 -0
  38. package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
  39. package/packages/core/src/cli/dashboard/live.ts +152 -0
  40. package/packages/core/src/cli/dashboard/model.ts +1173 -0
  41. package/packages/core/src/cli/dashboard/poll.ts +196 -0
  42. package/packages/core/src/cli/dashboard/render.ts +1568 -0
  43. package/packages/core/src/cli/dashboard/selection.ts +66 -0
  44. package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
  45. package/packages/core/src/cli/dashboard/session-view.ts +506 -0
  46. package/packages/core/src/cli/dashboard/transcript.ts +108 -0
  47. package/packages/core/src/cli/dev.ts +46 -0
  48. package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
  49. package/packages/core/src/cli/dispatch-process.ts +259 -0
  50. package/packages/core/src/cli/dispatch.ts +3692 -0
  51. package/packages/core/src/cli/dotenv.ts +48 -0
  52. package/packages/core/src/cli/env.ts +206 -0
  53. package/packages/core/src/cli/harvest.ts +698 -0
  54. package/packages/core/src/cli/help.ts +713 -0
  55. package/packages/core/src/cli/init-agent.ts +106 -0
  56. package/packages/core/src/cli/init-validation.ts +583 -0
  57. package/packages/core/src/cli/init.ts +756 -0
  58. package/packages/core/src/cli/installation.ts +333 -0
  59. package/packages/core/src/cli/keyboard.ts +261 -0
  60. package/packages/core/src/cli/main.ts +1399 -0
  61. package/packages/core/src/cli/mcp.ts +279 -0
  62. package/packages/core/src/cli/models.ts +65 -0
  63. package/packages/core/src/cli/observe.ts +55 -0
  64. package/packages/core/src/cli/plugin.ts +227 -0
  65. package/packages/core/src/cli/pr-attachments.ts +138 -0
  66. package/packages/core/src/cli/pr-summary.ts +124 -0
  67. package/packages/core/src/cli/publication-settlement.ts +188 -0
  68. package/packages/core/src/cli/repo-state.ts +213 -0
  69. package/packages/core/src/cli/repository-status.ts +80 -0
  70. package/packages/core/src/cli/self-update.ts +443 -0
  71. package/packages/core/src/cli/status.ts +895 -0
  72. package/packages/core/src/cli/store-opening.contract.ts +289 -0
  73. package/packages/core/src/cli/store-opening.ts +167 -0
  74. package/packages/core/src/cli/store-ref.ts +33 -0
  75. package/packages/core/src/cli/terminal-restore.ts +174 -0
  76. package/packages/core/src/cli/terminal.ts +361 -0
  77. package/packages/core/src/cli/terminals.ts +995 -0
  78. package/packages/core/src/cli/testkit.ts +194 -0
  79. package/packages/core/src/cli/ticket.ts +484 -0
  80. package/packages/core/src/cli/upgrade-agent.ts +180 -0
  81. package/packages/core/src/cli/upgrade-commit.ts +468 -0
  82. package/packages/core/src/cli/upgrade-notice.ts +91 -0
  83. package/packages/core/src/cli/upgrade-progress.ts +110 -0
  84. package/packages/core/src/cli/upgrade.ts +888 -0
  85. package/packages/core/src/cli/wait.ts +587 -0
  86. package/packages/core/src/cli/watch.ts +932 -0
  87. package/packages/core/src/config/live.ts +255 -0
  88. package/packages/core/src/config/load.ts +98 -0
  89. package/packages/core/src/config/roles.ts +438 -0
  90. package/packages/core/src/config/schema.ts +774 -0
  91. package/packages/core/src/distribution.ts +24 -0
  92. package/packages/core/src/events/catalog.ts +201 -0
  93. package/packages/core/src/events/envelope.ts +59 -0
  94. package/packages/core/src/events/payloads.ts +618 -0
  95. package/packages/core/src/events/repository.ts +559 -0
  96. package/packages/core/src/events/sessions.ts +180 -0
  97. package/packages/core/src/harvest/schema.ts +147 -0
  98. package/packages/core/src/ids.ts +45 -0
  99. package/packages/core/src/integration/harness.ts +681 -0
  100. package/packages/core/src/kernel/auto-merge.ts +259 -0
  101. package/packages/core/src/kernel/converge.ts +117 -0
  102. package/packages/core/src/kernel/dispatch-settings.ts +60 -0
  103. package/packages/core/src/kernel/dispatch-status.ts +141 -0
  104. package/packages/core/src/kernel/engine.ts +1047 -0
  105. package/packages/core/src/kernel/harvest.ts +823 -0
  106. package/packages/core/src/kernel/origin.ts +69 -0
  107. package/packages/core/src/kernel/phases.ts +196 -0
  108. package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
  109. package/packages/core/src/kernel/pr-attachments.ts +82 -0
  110. package/packages/core/src/kernel/reducer.ts +752 -0
  111. package/packages/core/src/kernel/stall.ts +155 -0
  112. package/packages/core/src/kernel/verify-applicability.ts +52 -0
  113. package/packages/core/src/markdown.ts +67 -0
  114. package/packages/core/src/ontology.ts +288 -0
  115. package/packages/core/src/open-map.ts +203 -0
  116. package/packages/core/src/operator/annotations.ts +187 -0
  117. package/packages/core/src/operator/client.ts +311 -0
  118. package/packages/core/src/operator/control.ts +117 -0
  119. package/packages/core/src/operator/index.ts +62 -0
  120. package/packages/core/src/operator/presentation.ts +22 -0
  121. package/packages/core/src/operator/protocol.ts +98 -0
  122. package/packages/core/src/operator/query.ts +201 -0
  123. package/packages/core/src/operator/registry.ts +1043 -0
  124. package/packages/core/src/operator/requests.ts +138 -0
  125. package/packages/core/src/operator/sandbox.ts +458 -0
  126. package/packages/core/src/operator/server.ts +616 -0
  127. package/packages/core/src/operator/sessions.ts +223 -0
  128. package/packages/core/src/operator/tickets.ts +177 -0
  129. package/packages/core/src/plugin-sdk/index.ts +174 -0
  130. package/packages/core/src/plugins/contract-entry.ts +74 -0
  131. package/packages/core/src/plugins/contract-env.ts +3 -0
  132. package/packages/core/src/plugins/load.ts +259 -0
  133. package/packages/core/src/plugins/manifest.ts +236 -0
  134. package/packages/core/src/plugins/registry.ts +274 -0
  135. package/packages/core/src/plugins/runtimes.ts +75 -0
  136. package/packages/core/src/ports/forge/contract.ts +258 -0
  137. package/packages/core/src/ports/forge/create.ts +74 -0
  138. package/packages/core/src/ports/forge/fake.ts +488 -0
  139. package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
  140. package/packages/core/src/ports/forge/github-transport.ts +352 -0
  141. package/packages/core/src/ports/forge/github.ts +1162 -0
  142. package/packages/core/src/ports/forge/local-git.ts +568 -0
  143. package/packages/core/src/ports/runner/claude.ts +873 -0
  144. package/packages/core/src/ports/runner/cli-stream.ts +90 -0
  145. package/packages/core/src/ports/runner/codex.ts +873 -0
  146. package/packages/core/src/ports/runner/contract.ts +492 -0
  147. package/packages/core/src/ports/runner/fake.ts +213 -0
  148. package/packages/core/src/ports/runner/json-record.ts +2 -0
  149. package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
  150. package/packages/core/src/ports/runner/one-shot.ts +29 -0
  151. package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
  152. package/packages/core/src/ports/runner/pi-bridge.js +6 -0
  153. package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
  154. package/packages/core/src/ports/runner/pi.ts +617 -0
  155. package/packages/core/src/ports/runner/production.ts +74 -0
  156. package/packages/core/src/ports/runner/provider-error.ts +121 -0
  157. package/packages/core/src/ports/runner/routing.ts +234 -0
  158. package/packages/core/src/ports/runner/runtime.ts +214 -0
  159. package/packages/core/src/ports/runner/session-env.ts +34 -0
  160. package/packages/core/src/ports/runner/stream-parts.ts +103 -0
  161. package/packages/core/src/ports/tickets/contract.ts +570 -0
  162. package/packages/core/src/ports/tickets/create.ts +135 -0
  163. package/packages/core/src/ports/tickets/fake.ts +215 -0
  164. package/packages/core/src/ports/tickets/file.ts +559 -0
  165. package/packages/core/src/ports/tickets/linear.ts +811 -0
  166. package/packages/core/src/ports/tickets/operations.ts +161 -0
  167. package/packages/core/src/ports/tickets/remote-index.ts +13 -0
  168. package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
  169. package/packages/core/src/ports/tickets/remote-server.ts +185 -0
  170. package/packages/core/src/ports/tickets/remote.ts +130 -0
  171. package/packages/core/src/ports/tickets/update.ts +51 -0
  172. package/packages/core/src/ports/types.ts +528 -0
  173. package/packages/core/src/ports/workspace/build-execution.ts +123 -0
  174. package/packages/core/src/ports/workspace/contract.ts +136 -0
  175. package/packages/core/src/ports/workspace/create.ts +132 -0
  176. package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
  177. package/packages/core/src/ports/workspace/fake.ts +432 -0
  178. package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
  179. package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
  180. package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
  181. package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
  182. package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
  183. package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
  184. package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
  185. package/packages/core/src/processes/build-child-exit.ts +78 -0
  186. package/packages/core/src/processes/build-child.ts +119 -0
  187. package/packages/core/src/processes/build-execution-state.ts +95 -0
  188. package/packages/core/src/processes/build-parent-watch.ts +35 -0
  189. package/packages/core/src/processes/build-runner.ts +2614 -0
  190. package/packages/core/src/processes/dispatcher.ts +2825 -0
  191. package/packages/core/src/processes/execution-settlement.ts +171 -0
  192. package/packages/core/src/processes/harvest-child.ts +115 -0
  193. package/packages/core/src/processes/harvest-execution-state.ts +121 -0
  194. package/packages/core/src/processes/harvest-runner.ts +1656 -0
  195. package/packages/core/src/processes/harvest.ts +595 -0
  196. package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
  197. package/packages/core/src/processes/phase-failure-budget.ts +52 -0
  198. package/packages/core/src/processes/process-group-reaper.ts +35 -0
  199. package/packages/core/src/processes/process-group.ts +65 -0
  200. package/packages/core/src/processes/publication-loss.ts +62 -0
  201. package/packages/core/src/processes/publication-state.ts +93 -0
  202. package/packages/core/src/processes/sandbox-state.ts +101 -0
  203. package/packages/core/src/registry.ts +24 -0
  204. package/packages/core/src/sessions/reducer.ts +185 -0
  205. package/packages/core/src/skills.ts +7 -0
  206. package/packages/core/src/spec-standard.ts +47 -0
  207. package/packages/core/src/store/adapter.ts +80 -0
  208. package/packages/core/src/store/build-scope.ts +272 -0
  209. package/packages/core/src/store/contract.ts +2581 -0
  210. package/packages/core/src/store/local/blobs.ts +38 -0
  211. package/packages/core/src/store/local/schema.ts +180 -0
  212. package/packages/core/src/store/local/store.ts +1713 -0
  213. package/packages/core/src/store/memory.ts +1138 -0
  214. package/packages/core/src/store/phase-session.ts +425 -0
  215. package/packages/core/src/store/remote/client.ts +780 -0
  216. package/packages/core/src/store/remote/index.ts +12 -0
  217. package/packages/core/src/store/remote/protocol.ts +348 -0
  218. package/packages/core/src/store/remote/server.ts +923 -0
  219. package/packages/core/src/store/remote/token.ts +137 -0
  220. package/packages/core/src/store/remote/version.ts +10 -0
  221. package/packages/core/src/store/retention.ts +91 -0
  222. package/packages/core/src/store/session-handle.ts +265 -0
  223. package/packages/core/src/store/streams/assemble.ts +79 -0
  224. package/packages/core/src/store/streams/lock.ts +42 -0
  225. package/packages/core/src/store/streams/session-writer.ts +306 -0
  226. package/packages/core/src/store/streams/types.ts +171 -0
  227. package/packages/core/src/store/streams/wait.ts +127 -0
  228. package/packages/core/src/store/subscribe.ts +105 -0
  229. package/packages/core/src/store/types.ts +466 -0
  230. package/packages/core/src/testing/fixed.ts +44 -0
  231. package/packages/core/src/testing/store-failures.ts +32 -0
  232. package/packages/core/src/zod-issues.ts +91 -0
  233. package/skills/code-review/SKILL.md +100 -0
  234. package/skills/finalize/SKILL.md +44 -0
  235. package/skills/guide/SKILL.md +1781 -0
  236. package/skills/guide/references/operator-api.md +164 -0
  237. package/skills/guide/references/plugin-authoring.md +354 -0
  238. package/skills/guide/references/remote-store-protocol.md +972 -0
  239. package/skills/guide/references/remote-ticket-protocol.md +79 -0
  240. package/skills/guide/references/setup.md +289 -0
  241. package/skills/guide/references/spec-standard.md +63 -0
  242. package/skills/guide/references/ticket-dependencies.md +59 -0
  243. package/skills/harvest/SKILL.md +66 -0
  244. package/skills/harvest-review/SKILL.md +63 -0
  245. package/skills/implement/SKILL.md +82 -0
  246. package/skills/plan/SKILL.md +123 -0
  247. package/skills/plan-review/SKILL.md +121 -0
  248. package/skills/reconcile/SKILL.md +65 -0
  249. package/skills/spec/SKILL.md +132 -0
  250. package/skills/tickets/SKILL.md +139 -0
  251. package/templates/autobuild.toml +36 -0
@@ -0,0 +1,559 @@
1
+ /**
2
+ * Repository-scoped event catalog. These events deliberately do not enter the
3
+ * build event union: repository controls and harvest runs are not synthetic
4
+ * builds or new phases in the fixed build grammar.
5
+ */
6
+ import { z } from 'zod'
7
+ import {
8
+ artifactRefSchema,
9
+ findingSchema,
10
+ reviewVerdictKindSchema,
11
+ ticketRefSchema,
12
+ } from '../ontology'
13
+ import {
14
+ harvestBlockerProvenanceSchema,
15
+ harvestDispositionSchema,
16
+ harvestPendingProposalSchema,
17
+ harvestStepSchema,
18
+ harvestTriggerSchema,
19
+ occurrenceKeySchema,
20
+ } from '../harvest/schema'
21
+ import {
22
+ actorSchema,
23
+ VIA_ACTOR_RULE,
24
+ viaOnNonHumanActor,
25
+ type Actor,
26
+ type ActorKind,
27
+ } from './envelope'
28
+ import { EventValidationError } from './catalog'
29
+ import { providerAttemptsSchema, providerSubstitutionSchema } from './payloads'
30
+
31
+ const round = z.number().int().positive()
32
+ const attempt = z.number().int().positive()
33
+ /** Provider-native identity of the environment one harvest run executed in. */
34
+ const harvestEnvironmentSchema = z.strictObject({
35
+ provider: z.string().min(1),
36
+ environmentId: z.string().min(1),
37
+ sessionId: z.string().min(1).optional(),
38
+ })
39
+ const empty = z.strictObject({})
40
+ const setting = z.strictObject({ enabled: z.boolean() })
41
+ const dispatchRun = z.string().min(1)
42
+ /** A durable repository setting written by a dispatcher's startup launch
43
+ * flags. `run` optionally names the dispatcher invocation that performed the
44
+ * write (the operator intent stays `human`); optional so historical journals
45
+ * replay unchanged. */
46
+ const dispatcherSetting = z.strictObject({ enabled: z.boolean(), run: dispatchRun.optional() })
47
+ const boundedDiagnostics = z.array(z.string().min(1)).max(1_000)
48
+ const tickCountersSchema = z.strictObject({
49
+ merged: z.number().int().nonnegative(),
50
+ closed: z.number().int().nonnegative(),
51
+ conflicted: z.number().int().nonnegative(),
52
+ abandoned: z.number().int().nonnegative(),
53
+ discarded: z.number().int().nonnegative(),
54
+ janitorFailed: z.number().int().nonnegative(),
55
+ recovered: z.number().int().nonnegative(),
56
+ dispatchFailed: z.number().int().nonnegative(),
57
+ resumed: z.number().int().nonnegative(),
58
+ swept: z.number().int().nonnegative(),
59
+ dispatched: z.number().int().nonnegative(),
60
+ authored: z.number().int().nonnegative(),
61
+ bounced: z.number().int().nonnegative(),
62
+ claimRaces: z.number().int().nonnegative(),
63
+ invalidTickets: z.number().int().nonnegative(),
64
+ /** Added in plugin/API-era 1.4; optional so historical tick facts replay. */
65
+ creationWithheld: z.number().int().nonnegative().optional(),
66
+ dependencyBlocked: z.number().int().nonnegative(),
67
+ /** Foreign executions settled from durable facts plus provider liveness.
68
+ * Added with durable supervision; optional so historical tick facts replay. */
69
+ settled: z.number().int().nonnegative().optional(),
70
+ /** Builds whose remote workspace provisioning was (re-)kicked as background
71
+ * work this tick. Added with durable supervision; optional so historical
72
+ * tick facts replay. */
73
+ provisioning: z.number().int().nonnegative().optional(),
74
+ /** Operator sandboxes idly stopped this tick. Added with the operator
75
+ * sandbox; optional so historical tick facts replay. */
76
+ sandboxIdleStops: z.number().int().nonnegative().optional(),
77
+ /** Contained idle-settlement failures this tick; retried next tick. */
78
+ sandboxSettleFailures: z.number().int().nonnegative().optional(),
79
+ harvestStarted: z.number().int().nonnegative(),
80
+ harvestResumed: z.number().int().nonnegative(),
81
+ harvestCompleted: z.number().int().nonnegative(),
82
+ harvestEscalated: z.number().int().nonnegative(),
83
+ harvestFailed: z.number().int().nonnegative(),
84
+ })
85
+ const restartRequiredConfigPathSchema = z.enum([
86
+ 'forge',
87
+ 'plugins',
88
+ 'workspace.provider',
89
+ 'workspace.config',
90
+ 'tickets.source',
91
+ 'tickets.teamKey',
92
+ 'tickets.claimedState',
93
+ 'tickets.createState',
94
+ 'tickets.dir',
95
+ 'orchestrator',
96
+ ])
97
+ const restartRequiredConfigPathsSchema = z
98
+ .array(restartRequiredConfigPathSchema)
99
+ .superRefine((paths, ctx) => {
100
+ const seen = new Set<string>()
101
+ paths.forEach((path, index) => {
102
+ if (seen.has(path)) {
103
+ ctx.addIssue({ code: 'custom', path: [index], message: `duplicate config path ${path}` })
104
+ }
105
+ seen.add(path)
106
+ })
107
+ })
108
+
109
+ export const harvestEventPayloadSchemas = {
110
+ // Repository-wide operator control. Requests are human commands; paused /
111
+ // resumed are kernel acknowledgements made only at workflow boundaries.
112
+ 'harvest.pause-requested': empty,
113
+ 'harvest.resume-requested': empty,
114
+ 'harvest.paused': empty,
115
+ 'harvest.resumed': empty,
116
+ /** Durable selection of one outer automatic recovery. The acknowledgement
117
+ * is the same harvest.resumed fact used by a human request. */
118
+ 'harvest.recovery-requested': z.strictObject({
119
+ run: z.string().min(1),
120
+ attempt,
121
+ limit: z.number().int().positive(),
122
+ }),
123
+ /** Atomic give-up boundary: commit the safe partial ledger, release only
124
+ * pending observations, and raise a durable human-attention barrier. */
125
+ 'harvest.recovery-exhausted': z.strictObject({
126
+ run: z.string().min(1),
127
+ step: harvestStepSchema,
128
+ round: round.optional(),
129
+ error: z.string().min(1),
130
+ attempts: z.number().int().positive(),
131
+ limit: z.number().int().positive(),
132
+ releasedObservations: z.array(occurrenceKeySchema),
133
+ committedDispositions: z.array(harvestDispositionSchema),
134
+ pendingProposals: z.array(harvestPendingProposalSchema),
135
+ }),
136
+ 'harvest.started': z.strictObject({
137
+ run: z.string().min(1),
138
+ observations: z.array(occurrenceKeySchema).min(1),
139
+ scan: artifactRefSchema,
140
+ /** Optional only so historical repository journals replay without migration. */
141
+ trigger: harvestTriggerSchema.optional(),
142
+ /** Where this run's sessions executed. Present on hosted harvest runs;
143
+ * absent on locally run ones. Optional only so historical journals replay
144
+ * without migration. */
145
+ environment: harvestEnvironmentSchema.optional(),
146
+ }),
147
+ /** Dispatcher-authored durable identity of one hosted harvest execution:
148
+ * the disposable environment the provider provisioned and the detached
149
+ * command supervising the guest runner. Written as soon as the launch
150
+ * succeeds so a later process can re-observe the execution without
151
+ * process memory. */
152
+ 'harvest.execution.started': z.strictObject({
153
+ execution: z.string().min(1),
154
+ provider: z.string().min(1),
155
+ environmentId: z.string().min(1),
156
+ sessionId: z.string().min(1).optional(),
157
+ commandId: z.string().min(1).optional(),
158
+ }),
159
+ /** Dispatcher-authored close of one hosted harvest execution. Absence of a
160
+ * matching released fact keeps the execution open for durable settlement;
161
+ * the snapshot purge outcome proves absence from the provider. */
162
+ 'harvest.execution.released': z.strictObject({
163
+ execution: z.string().min(1),
164
+ environmentId: z.string().min(1),
165
+ snapshots: z.strictObject({
166
+ outcome: z.enum(['confirmed', 'unknown']),
167
+ deleted: z.number().int().nonnegative().optional(),
168
+ error: z.string().min(1).optional(),
169
+ }),
170
+ }),
171
+ 'harvest.step.started': z.strictObject({
172
+ run: z.string().min(1),
173
+ step: harvestStepSchema,
174
+ round: round.optional(),
175
+ }),
176
+ 'harvest.step.completed': z.strictObject({
177
+ run: z.string().min(1),
178
+ step: harvestStepSchema,
179
+ outcome: z.enum(['completed', 'approve', 'revise', 'escalate', 'failed']),
180
+ round: round.optional(),
181
+ artifact: artifactRefSchema.optional(),
182
+ detail: z.string().optional(),
183
+ }),
184
+ 'harvest.session.started': z.strictObject({
185
+ run: z.string().min(1),
186
+ session: z.string().min(1),
187
+ role: z.enum(['harvest', 'harvest-review']),
188
+ runner: z.string().min(1),
189
+ model: z.string().optional(),
190
+ /** Optional only so historical repository journals replay without migration. */
191
+ args: z.array(z.string()).optional(),
192
+ step: z.enum(['synthesize', 'review']),
193
+ round,
194
+ /** Stream id this session's live view writes to (SPEC §9, session
195
+ * streams). Optional only so historical journals replay unchanged. */
196
+ stream: z.string().min(1).optional(),
197
+ substitution: providerSubstitutionSchema.optional(),
198
+ }),
199
+ 'harvest.session.ended': z.strictObject({
200
+ run: z.string().min(1),
201
+ session: z.string().min(1),
202
+ transcript: artifactRefSchema,
203
+ usage: z.strictObject({
204
+ inputTokens: z.number().int().nonnegative(),
205
+ outputTokens: z.number().int().nonnegative(),
206
+ turns: z.number().int().nonnegative(),
207
+ }),
208
+ }),
209
+ 'harvest.proposals.submitted': z.strictObject({
210
+ run: z.string().min(1),
211
+ round,
212
+ artifact: artifactRefSchema,
213
+ }),
214
+ 'harvest.review.verdict': z.strictObject({
215
+ run: z.string().min(1),
216
+ round,
217
+ verdict: reviewVerdictKindSchema,
218
+ findings: z.array(findingSchema),
219
+ artifact: artifactRefSchema,
220
+ reason: z.string().optional(),
221
+ }),
222
+ 'harvest.proposal.id-reserved': z.strictObject({
223
+ run: z.string().min(1),
224
+ proposalKey: z.string().min(1),
225
+ id: z.uuidv4(),
226
+ }),
227
+ 'harvest.proposal.filed': z.strictObject({
228
+ run: z.string().min(1),
229
+ proposalKey: z.string().min(1),
230
+ ticket: ticketRefSchema,
231
+ blockers: harvestBlockerProvenanceSchema.optional(),
232
+ }),
233
+ /** The authoritative committed ledger facts for a successful run. */
234
+ 'harvest.completed': z.strictObject({
235
+ run: z.string().min(1),
236
+ dispositions: z.array(harvestDispositionSchema).min(1),
237
+ report: artifactRefSchema,
238
+ }),
239
+ /** Terminal and deliberately consumes the claimed snapshot, preventing an
240
+ * agent/stall/policy escalation from becoming a watch-tick hot loop. */
241
+ 'harvest.escalated': z.strictObject({
242
+ run: z.string().min(1),
243
+ source: z.enum(['agent', 'stall', 'policy']),
244
+ reason: z.string().min(1),
245
+ round: round.optional(),
246
+ observations: z.array(occurrenceKeySchema).min(1),
247
+ }),
248
+ /** Infrastructure failure. A non-retrying failure stops the run at a
249
+ * durable boundary for bounded automatic or explicit recovery. */
250
+ 'harvest.failed': z.strictObject({
251
+ run: z.string().min(1),
252
+ step: harvestStepSchema,
253
+ round: round.optional(),
254
+ attempt,
255
+ error: z.string().min(1),
256
+ willRetry: z.boolean(),
257
+ providerAttempts: providerAttemptsSchema.optional(),
258
+ }),
259
+ } as const
260
+
261
+ /** Operator sandbox lifecycle facts (AUT-340). The provision/resume/activity
262
+ * facts bracket one environment's usable window and rate-limited activity;
263
+ * stopped/released/reset close it. Derived state lives in the pure reducer
264
+ * `processes/sandbox-state.ts`, mirroring harvest execution settlement. */
265
+ export const orchestratorSandboxEventPayloadSchemas = {
266
+ /** First use of one operator's environment for this repository, or a
267
+ * post-reset reprovision. Carries the full environment identity. */
268
+ 'orchestrator.sandbox.provisioned': z.strictObject({
269
+ operator: z.string().min(1),
270
+ environmentId: z.string().min(1),
271
+ provider: z.string().min(1),
272
+ sessionId: z.string().min(1).optional(),
273
+ workspacePath: z.string().min(1),
274
+ }),
275
+ /** A stopped environment resumed for a later tool call. */
276
+ 'orchestrator.sandbox.resumed': z.strictObject({
277
+ operator: z.string().min(1),
278
+ environmentId: z.string().min(1),
279
+ provider: z.string().min(1),
280
+ sessionId: z.string().min(1).optional(),
281
+ }),
282
+ /** Rate-limited activity evidence (at most one per 60 s per environment),
283
+ * the last-evidence input to the dispatcher's idle settlement. */
284
+ 'orchestrator.sandbox.activity': z.strictObject({
285
+ operator: z.string().min(1),
286
+ environmentId: z.string().min(1),
287
+ }),
288
+ /** Dispatcher-authored idle stop: the session ended, the snapshot kept. */
289
+ 'orchestrator.sandbox.stopped': z.strictObject({
290
+ operator: z.string().min(1),
291
+ environmentId: z.string().min(1),
292
+ reason: z.literal('idle'),
293
+ }),
294
+ /** Full teardown + snapshot purge. Operator-driven (reset, session archive)
295
+ * or dispatcher-authored (closing an orphan's trail whose environment was
296
+ * already absent at idle settlement, or closing the trail of an idle
297
+ * environment whose journaled provider no longer matches the wired provider
298
+ * — both unconfirmed, with the reason recorded in `snapshots.error`). */
299
+ 'orchestrator.sandbox.released': z.strictObject({
300
+ operator: z.string().min(1),
301
+ environmentId: z.string().min(1),
302
+ snapshots: z.strictObject({
303
+ outcome: z.enum(['confirmed', 'unknown']),
304
+ deleted: z.number().int().nonnegative().optional(),
305
+ error: z.string().min(1).optional(),
306
+ }),
307
+ }),
308
+ /** Destructive reset marker. The following released/provisioned facts
309
+ * carry the evidence. */
310
+ 'orchestrator.sandbox.reset': z.strictObject({
311
+ operator: z.string().min(1),
312
+ environmentId: z.string().min(1),
313
+ }),
314
+ } as const
315
+
316
+ export const dispatcherStatusEventPayloadSchemas = {
317
+ /** A terminal frontend follows exactly one run. The referenced artifact is
318
+ * the composed Config the kernel is actually using, never an on-disk guess. */
319
+ 'dispatcher.run-started': z.strictObject({
320
+ run: dispatchRun,
321
+ pid: z.number().int().positive(),
322
+ effectiveConfig: artifactRefSchema,
323
+ roleWarnings: boundedDiagnostics,
324
+ }),
325
+ 'dispatcher.run-stopped': z.strictObject({
326
+ run: dispatchRun,
327
+ outcome: z.enum(['normal', 'abnormal', 'forced']),
328
+ exitCode: z.number().int().nullable().optional(),
329
+ signal: z.string().min(1).optional(),
330
+ error: z.string().min(1).optional(),
331
+ /** Optional clean-stop reason. Supersession is a normal exit (exit 0)
332
+ * with this reason — never a new outcome value — so historical consumers
333
+ * keep reading a clean stop. */
334
+ reason: z.enum(['superseded']).optional(),
335
+ }),
336
+ /** Durable record that this invocation found the repository held by another
337
+ * supervisor and performed no claims, launches, or publications. */
338
+ 'dispatcher.tick-yielded': z.strictObject({
339
+ run: dispatchRun.optional(),
340
+ holder: z.string().min(1),
341
+ }),
342
+ 'dispatcher.config-rejected': z.strictObject({ run: dispatchRun, error: z.string().min(1) }),
343
+ 'dispatcher.config-publication-failed': z.strictObject({
344
+ run: dispatchRun,
345
+ error: z.string().min(1),
346
+ }),
347
+ /** Brackets the one unsafe-to-force dispatcher turn. A supervisor may kill
348
+ * an unresponsive child only when no started turn remains open. */
349
+ 'dispatcher.tick-started': z.strictObject({ run: dispatchRun }),
350
+ 'dispatcher.tick-completed': z.strictObject({
351
+ run: dispatchRun,
352
+ queued: z.number().int().nonnegative(),
353
+ /** Replay-only compatibility for journals written while the isolated
354
+ * dispatcher child transported a display sample in its tick fact. */
355
+ observations: z.number().int().nonnegative().optional(),
356
+ counters: tickCountersSchema,
357
+ janitorDiagnostics: boundedDiagnostics,
358
+ ticketDiagnostics: boundedDiagnostics,
359
+ creationDiagnostics: boundedDiagnostics.optional(),
360
+ dependencyDiagnostics: boundedDiagnostics,
361
+ }),
362
+ 'dispatcher.tick-failed': z.strictObject({ run: dispatchRun, error: z.string().min(1) }),
363
+ 'dispatcher.runner-settled': z.strictObject({
364
+ run: dispatchRun,
365
+ slug: z.string().min(1),
366
+ outcome: z.enum(['parked', 'lease-held', 'launch-failed', 'failed']),
367
+ status: z.string().min(1).optional(),
368
+ error: z.string().min(1).optional(),
369
+ }),
370
+ 'dispatcher.harvest-runner-failed': z.strictObject({
371
+ run: dispatchRun,
372
+ error: z.string().min(1),
373
+ }),
374
+ /** Newer published release discovered by the private kernel. Keeping this
375
+ * run-correlated makes the release courtesy available to Store-only UIs. */
376
+ 'dispatcher.upgrade-available': z.strictObject({
377
+ run: dispatchRun,
378
+ version: z.string().min(1),
379
+ }),
380
+ /** Durable acknowledgement text for controls whose exact bulk result is not
381
+ * otherwise one event. It is presentation-neutral operator evidence. */
382
+ 'dispatcher.operator-reported': z.strictObject({
383
+ run: dispatchRun,
384
+ level: z.enum(['info', 'warning']),
385
+ message: z.string().min(1),
386
+ }),
387
+ } as const
388
+
389
+ export const dispatcherSettingEventPayloadSchemas = {
390
+ /** Accepted main-checkout config revision. The exact TOML is deposited in
391
+ * the repository artifact stream atomically with this fact. Historical
392
+ * events omit run/effectiveConfig and remain replayable. */
393
+ 'dispatcher.config-reloaded': z.strictObject({
394
+ artifact: artifactRefSchema,
395
+ restartRequired: restartRequiredConfigPathsSchema,
396
+ effectiveChanged: z.boolean(),
397
+ run: dispatchRun.optional(),
398
+ effectiveConfig: artifactRefSchema.optional(),
399
+ roleWarnings: boundedDiagnostics.optional(),
400
+ }),
401
+ /** Current repository-wide intake gate sampled by every dispatcher tick. */
402
+ 'dispatcher.intake-set': dispatcherSetting,
403
+ /** Repository-wide quiescence flag: pause-all sets it, resume-all clears it,
404
+ * and while it is set no dispatcher tick attaches a runner to a queued build.
405
+ * Deliberately independent of intake — intake governs new ticket intake, and
406
+ * an operator who only turns intake off is declining new work, not disowning
407
+ * work the repository has already accepted. */
408
+ 'dispatcher.pause-set': setting,
409
+ /** Claim-time auto-merge default sampled by every dispatcher tick. */
410
+ 'dispatcher.auto-merge-default-set': dispatcherSetting,
411
+ } as const
412
+
413
+ export const repositoryEventPayloadSchemas = {
414
+ ...harvestEventPayloadSchemas,
415
+ ...orchestratorSandboxEventPayloadSchemas,
416
+ ...dispatcherStatusEventPayloadSchemas,
417
+ ...dispatcherSettingEventPayloadSchemas,
418
+ } as const
419
+
420
+ export type RepositoryEventType = keyof typeof repositoryEventPayloadSchemas
421
+ export const REPOSITORY_EVENT_TYPES = Object.keys(
422
+ repositoryEventPayloadSchemas,
423
+ ) as RepositoryEventType[]
424
+ export type RepositoryEventPayload<T extends RepositoryEventType> = z.infer<
425
+ (typeof repositoryEventPayloadSchemas)[T]
426
+ >
427
+
428
+ export interface RepositoryEventEnvelope<T extends RepositoryEventType = RepositoryEventType> {
429
+ repo: string
430
+ seq: number
431
+ ts: string
432
+ actor: Actor
433
+ type: T
434
+ payload: RepositoryEventPayload<T>
435
+ }
436
+
437
+ export type RepositoryEvent = {
438
+ [T in RepositoryEventType]: RepositoryEventEnvelope<T>
439
+ }[RepositoryEventType]
440
+
441
+ export interface RepositoryEventWrite<T extends RepositoryEventType = RepositoryEventType> {
442
+ actor: Actor
443
+ type: T
444
+ payload: RepositoryEventPayload<T>
445
+ }
446
+
447
+ export type HarvestEventType = keyof typeof harvestEventPayloadSchemas
448
+ export type HarvestEventPayload<T extends HarvestEventType> = RepositoryEventPayload<T>
449
+ export type HarvestEventEnvelope<T extends HarvestEventType = HarvestEventType> =
450
+ RepositoryEventEnvelope<T>
451
+ export type HarvestEvent = {
452
+ [T in HarvestEventType]: RepositoryEventEnvelope<T>
453
+ }[HarvestEventType]
454
+ export type HarvestEventWrite<T extends HarvestEventType = HarvestEventType> =
455
+ RepositoryEventWrite<T>
456
+
457
+ export const HARVEST_EVENT_TYPES = Object.keys(harvestEventPayloadSchemas) as HarvestEventType[]
458
+
459
+ const allowedActorKinds: Record<RepositoryEventType, readonly ActorKind[]> = {
460
+ 'harvest.pause-requested': ['human'],
461
+ 'harvest.resume-requested': ['human'],
462
+ 'harvest.paused': ['kernel'],
463
+ 'harvest.resumed': ['kernel'],
464
+ 'harvest.recovery-requested': ['kernel'],
465
+ 'harvest.recovery-exhausted': ['kernel'],
466
+ 'harvest.started': ['dispatcher', 'kernel'],
467
+ 'harvest.execution.started': ['dispatcher'],
468
+ 'harvest.execution.released': ['dispatcher'],
469
+ 'harvest.step.started': ['kernel'],
470
+ 'harvest.step.completed': ['kernel'],
471
+ 'harvest.session.started': ['kernel'],
472
+ 'harvest.session.ended': ['kernel'],
473
+ 'harvest.proposals.submitted': ['agent'],
474
+ 'harvest.review.verdict': ['agent'],
475
+ 'harvest.proposal.id-reserved': ['kernel'],
476
+ 'harvest.proposal.filed': ['kernel'],
477
+ 'harvest.completed': ['kernel'],
478
+ 'harvest.escalated': ['kernel', 'agent'],
479
+ 'harvest.failed': ['kernel'],
480
+ 'orchestrator.sandbox.provisioned': ['human'],
481
+ 'orchestrator.sandbox.resumed': ['human'],
482
+ 'orchestrator.sandbox.activity': ['human'],
483
+ 'orchestrator.sandbox.stopped': ['dispatcher'],
484
+ // Operator-driven releases (reset, session archive) are human-authored;
485
+ // the idle settlement's absent path closes an orphan's trail with a
486
+ // dispatcher-authored released fact, exactly as
487
+ // `harvest.execution.released` is dispatcher-authored.
488
+ 'orchestrator.sandbox.released': ['human', 'dispatcher'],
489
+ 'orchestrator.sandbox.reset': ['human'],
490
+ 'dispatcher.run-started': ['dispatcher'],
491
+ 'dispatcher.run-stopped': ['dispatcher'],
492
+ 'dispatcher.tick-yielded': ['dispatcher'],
493
+ 'dispatcher.config-rejected': ['dispatcher'],
494
+ 'dispatcher.config-publication-failed': ['dispatcher'],
495
+ 'dispatcher.tick-started': ['dispatcher'],
496
+ 'dispatcher.tick-completed': ['dispatcher'],
497
+ 'dispatcher.tick-failed': ['dispatcher'],
498
+ 'dispatcher.runner-settled': ['dispatcher'],
499
+ 'dispatcher.harvest-runner-failed': ['dispatcher'],
500
+ 'dispatcher.upgrade-available': ['dispatcher'],
501
+ 'dispatcher.operator-reported': ['human'],
502
+ 'dispatcher.config-reloaded': ['dispatcher'],
503
+ 'dispatcher.intake-set': ['human'],
504
+ 'dispatcher.pause-set': ['human'],
505
+ 'dispatcher.auto-merge-default-set': ['human'],
506
+ }
507
+
508
+ export function isHarvestEventType(value: string): value is HarvestEventType {
509
+ return Object.hasOwn(harvestEventPayloadSchemas, value)
510
+ }
511
+
512
+ export function isHarvestEvent(event: RepositoryEvent): event is HarvestEvent {
513
+ return isHarvestEventType(event.type)
514
+ }
515
+
516
+ export function isRepositoryEventType(value: string): value is RepositoryEventType {
517
+ return Object.hasOwn(repositoryEventPayloadSchemas, value)
518
+ }
519
+
520
+ export function validateRepositoryEventWrite(input: {
521
+ actor: unknown
522
+ type: string
523
+ payload: unknown
524
+ }): RepositoryEventWrite {
525
+ if (!isRepositoryEventType(input.type)) {
526
+ throw new EventValidationError(
527
+ `unknown repository event type "${input.type}" — known types: ${REPOSITORY_EVENT_TYPES.join(', ')}`,
528
+ )
529
+ }
530
+ if (viaOnNonHumanActor(input.actor)) {
531
+ throw new EventValidationError(VIA_ACTOR_RULE)
532
+ }
533
+ const actorResult = actorSchema.safeParse(input.actor)
534
+ if (!actorResult.success) {
535
+ throw new EventValidationError(
536
+ `invalid actor for "${input.type}": ${actorResult.error.message}`,
537
+ actorResult.error.issues,
538
+ )
539
+ }
540
+ const actor = actorResult.data
541
+ const allowed = allowedActorKinds[input.type]
542
+ if (!allowed.includes(actor.kind)) {
543
+ throw new EventValidationError(
544
+ `actor kind "${actor.kind}" may not emit "${input.type}" (allowed: ${allowed.join(', ')})`,
545
+ )
546
+ }
547
+ const result = repositoryEventPayloadSchemas[input.type].safeParse(input.payload)
548
+ if (!result.success) {
549
+ throw new EventValidationError(
550
+ `invalid payload for "${input.type}": ${result.error.message}`,
551
+ result.error.issues,
552
+ )
553
+ }
554
+ return {
555
+ actor,
556
+ type: input.type,
557
+ payload: result.data,
558
+ } as RepositoryEventWrite
559
+ }