@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,24 @@
1
+ import { dirname, resolve } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ /**
5
+ * Directory of this module. Bun exposes `import.meta.dir`; bundlers that
6
+ * embed the module (Next.js/Turbopack for the hosted service) provide only
7
+ * `import.meta.url`, so fall back to it rather than resolving from undefined
8
+ * at module evaluation time.
9
+ */
10
+ function moduleDir(): string {
11
+ const dir = (import.meta as { dir?: unknown }).dir
12
+ if (typeof dir === 'string' && dir !== '') return dir
13
+ return dirname(fileURLToPath(import.meta.url))
14
+ }
15
+
16
+ /** Root of the installable `autobuild` compatibility distribution. */
17
+ export function distributionRoot(): string {
18
+ return resolve(moduleDir(), '..', '..', '..')
19
+ }
20
+
21
+ /** Resolve a path owned by the root distribution. */
22
+ export function distributionPath(...segments: string[]): string {
23
+ return resolve(distributionRoot(), ...segments)
24
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The event catalog: envelope + payload schemas + per-type actor rules, and
3
+ * the single validation entry point every store adapter calls at append.
4
+ * This is where the ontology is *enforced*, not just described (SPEC §8).
5
+ */
6
+ import {
7
+ actorSchema,
8
+ VIA_ACTOR_RULE,
9
+ viaOnNonHumanActor,
10
+ type Actor,
11
+ type ActorKind,
12
+ } from './envelope'
13
+ import { eventPayloadSchemas, isEventType, type EventPayload, type EventType } from './payloads'
14
+
15
+ /**
16
+ * A stored event: envelope (SPEC §15.1) with a known type. `seq` is per-build
17
+ * and monotonic, `ts` is ISO-8601; both are store-assigned on append.
18
+ */
19
+ export interface EventEnvelope<T extends EventType = EventType> {
20
+ build: string
21
+ seq: number
22
+ ts: string
23
+ actor: Actor
24
+ type: T
25
+ payload: EventPayload<T>
26
+ }
27
+
28
+ /** The discriminated union over every event type — what reducers consume. */
29
+ export type AbEvent = { [T in EventType]: EventEnvelope<T> }[EventType]
30
+
31
+ /** What a producer submits; the store assigns `seq`/`ts` (§15.1). */
32
+ export interface EventWrite<T extends EventType = EventType> {
33
+ actor: Actor
34
+ type: T
35
+ payload: EventPayload<T>
36
+ }
37
+
38
+ /**
39
+ * Which actor kinds may emit each event type — the Actor column of the
40
+ * catalog tables in SPEC §15.3.
41
+ */
42
+ export const allowedActorKinds: Record<EventType, readonly ActorKind[]> = {
43
+ 'build.created': ['dispatcher', 'human'],
44
+ 'build.completed': ['dispatcher'],
45
+ 'runner.attached': ['kernel'],
46
+ 'runner.setup-failed': ['kernel'],
47
+ 'workspace.provisioned': ['dispatcher', 'kernel'],
48
+ 'workspace.provision-started': ['dispatcher'],
49
+ 'workspace.released': ['dispatcher', 'kernel'],
50
+ 'execution.started': ['dispatcher'],
51
+ 'execution.ended': ['dispatcher'],
52
+ 'infrastructure.failed': ['dispatcher'],
53
+ 'infrastructure.cleanup-attempted': ['dispatcher'],
54
+ 'publication.requested': ['agent', 'kernel'],
55
+ // Only the dispatcher releases workspaces and settles publications (§15.7);
56
+ // the kernel never destroys one, so it never records a loss.
57
+ 'publication.lost': ['dispatcher'],
58
+ 'abort.remote-branch-deleted': ['dispatcher'],
59
+ 'abort.local-branch-deleted': ['dispatcher'],
60
+ 'abort.ticket-returned': ['dispatcher'],
61
+ 'dispatch.failed': ['dispatcher'],
62
+ 'dispatch.comment-posted': ['dispatcher'],
63
+
64
+ 'build.pause-requested': ['human'],
65
+ 'build.resume-requested': ['human'],
66
+ 'build.abort-requested': ['human'],
67
+ 'build.discard-requested': ['human'],
68
+ 'build.auto-merge-requested': ['human'],
69
+ 'build.auto-merge-cancelled': ['human'],
70
+ 'build.paused': ['kernel'],
71
+ 'build.resumed': ['kernel'],
72
+ 'build.aborted': ['kernel', 'dispatcher'],
73
+
74
+ 'spec.imported': ['dispatcher'],
75
+ 'spec.authored': ['agent'],
76
+ 'spec.revised': ['kernel'],
77
+
78
+ 'session.started': ['kernel'],
79
+ 'session.ended': ['kernel'],
80
+
81
+ 'plan.started': ['kernel'],
82
+ 'plan.completed': ['agent'],
83
+ 'plan-review.started': ['kernel'],
84
+ 'plan-review.verdict': ['agent'],
85
+ 'implement.started': ['kernel'],
86
+ 'implement.completed': ['agent', 'dispatcher'],
87
+ 'code-review.started': ['kernel'],
88
+ 'code-review.verdict': ['agent'],
89
+
90
+ 'verify.started': ['kernel'],
91
+ 'verify.completed': ['kernel', 'agent'],
92
+ 'finalize.started': ['kernel'],
93
+ 'finalize.completed': ['kernel'],
94
+ 'finalize.step-completed': ['kernel', 'agent'],
95
+ 'pr-attachment.designated': ['agent'],
96
+ 'pr-attachment.hosted': ['kernel'],
97
+ 'pr-attachment.reclaimed': ['dispatcher'],
98
+ 'pr-attachment.reclaim-failed': ['dispatcher'],
99
+
100
+ 'pr.auto-merge-enabled': ['kernel', 'dispatcher'],
101
+ 'pr.auto-merge-disabled': ['kernel', 'dispatcher'],
102
+ 'pr.merged': ['dispatcher'],
103
+ 'pr.closed': ['dispatcher'],
104
+ 'pr.conflicted': ['dispatcher'],
105
+ 'reconcile.progress-checked': ['kernel'],
106
+ 'reconcile.started': ['kernel'],
107
+ 'reconcile.completed': ['agent', 'dispatcher'],
108
+
109
+ 'observation.recorded': ['kernel', 'agent'],
110
+ 'escalation.raised': ['agent', 'kernel', 'dispatcher'],
111
+ // Answering an escalation is always a deliberate human act. Historical
112
+ // dispatcher-authored retries remain reducible because replay does not
113
+ // revalidate already-persisted envelopes.
114
+ 'escalation.answered': ['human'],
115
+ 'phase.failed': ['kernel'],
116
+ }
117
+
118
+ /**
119
+ * Validation failures are agent feedback (D6): the message carries what was
120
+ * wrong and what would be accepted, so the in-session correction loop is
121
+ * immediate and cheap.
122
+ */
123
+ export class EventValidationError extends Error {
124
+ constructor(
125
+ message: string,
126
+ readonly issues?: unknown,
127
+ ) {
128
+ super(message)
129
+ this.name = 'EventValidationError'
130
+ }
131
+ }
132
+
133
+ /**
134
+ * The single validation gate. Every store adapter MUST pass writes through
135
+ * here before appending — the contract tests enforce that they do.
136
+ */
137
+ export function validateEventWrite(input: {
138
+ actor: unknown
139
+ type: string
140
+ payload: unknown
141
+ }): EventWrite {
142
+ if (!isEventType(input.type)) {
143
+ throw new EventValidationError(
144
+ `unknown event type "${input.type}" — known types: ${Object.keys(eventPayloadSchemas).join(', ')}`,
145
+ )
146
+ }
147
+
148
+ if (viaOnNonHumanActor(input.actor)) {
149
+ throw new EventValidationError(VIA_ACTOR_RULE)
150
+ }
151
+ const actorResult = actorSchema.safeParse(input.actor)
152
+ if (!actorResult.success) {
153
+ throw new EventValidationError(
154
+ `invalid actor for "${input.type}": ${actorResult.error.message}`,
155
+ actorResult.error.issues,
156
+ )
157
+ }
158
+ const actor = actorResult.data
159
+
160
+ const allowed = allowedActorKinds[input.type]
161
+ if (!allowed.includes(actor.kind)) {
162
+ throw new EventValidationError(
163
+ `actor kind "${actor.kind}" may not emit "${input.type}" (allowed: ${allowed.join(', ')})`,
164
+ )
165
+ }
166
+
167
+ const schema = eventPayloadSchemas[input.type]
168
+ const payloadResult = schema.safeParse(input.payload)
169
+ if (!payloadResult.success) {
170
+ throw new EventValidationError(
171
+ `invalid payload for "${input.type}": ${payloadResult.error.message}`,
172
+ payloadResult.error.issues,
173
+ )
174
+ }
175
+ if (input.type === 'escalation.raised') {
176
+ const escalation = payloadResult.data as EventPayload<'escalation.raised'>
177
+ if (
178
+ actor.kind === 'dispatcher' &&
179
+ (escalation.source !== 'policy' || escalation.policyCause !== 'infrastructure-failure-limit')
180
+ ) {
181
+ throw new EventValidationError(
182
+ 'invalid dispatcher escalation: only policy/infrastructure-failure-limit may be raised outside the kernel',
183
+ )
184
+ }
185
+ if (escalation.source === 'policy' && escalation.policyCause === undefined) {
186
+ throw new EventValidationError(
187
+ 'invalid payload for "escalation.raised": source "policy" requires a recognized policyCause',
188
+ )
189
+ }
190
+ if (escalation.source !== 'policy' && escalation.policyCause !== undefined) {
191
+ throw new EventValidationError(
192
+ `invalid payload for "escalation.raised": policyCause is only allowed when source is "policy", not ${JSON.stringify(escalation.source)}`,
193
+ )
194
+ }
195
+ }
196
+ return {
197
+ actor,
198
+ type: input.type,
199
+ payload: payloadResult.data,
200
+ } as EventWrite
201
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The event envelope (SPEC §15.1). Every event in a build's append-only log
3
+ * shares this shape; the store assigns `seq` and `ts` so producers can't fake
4
+ * ordering.
5
+ */
6
+ import { z } from 'zod'
7
+
8
+ /** Delegated-write attribution (SPEC §15.1): when an agent acts for a person,
9
+ * the event still names the person as the human actor and may name the
10
+ * delegate, so an audit trail can distinguish "the operator did this" from
11
+ * "the operator's orchestrator session did this" or "an MCP client did this"
12
+ * forever. Only human actors may carry `via`, and only on build and
13
+ * repository events; session events are the delegate's own log. */
14
+ export const viaSchema = z.discriminatedUnion('kind', [
15
+ z.strictObject({ kind: z.literal('session'), id: z.string().min(1) }),
16
+ z.strictObject({ kind: z.literal('mcp'), client: z.string().min(1) }),
17
+ ])
18
+ export type Via = z.infer<typeof viaSchema>
19
+
20
+ export const actorSchema = z.discriminatedUnion('kind', [
21
+ z.strictObject({ kind: z.literal('kernel') }),
22
+ z.strictObject({
23
+ kind: z.literal('agent'),
24
+ role: z.string().min(1),
25
+ session: z.string().min(1),
26
+ }),
27
+ z.strictObject({
28
+ kind: z.literal('human'),
29
+ user: z.string().min(1),
30
+ via: viaSchema.optional(),
31
+ }),
32
+ z.strictObject({ kind: z.literal('dispatcher') }),
33
+ z.strictObject({ kind: z.literal('ingester'), source: z.string().min(1) }),
34
+ ])
35
+ export type Actor = z.infer<typeof actorSchema>
36
+ export type ActorKind = Actor['kind']
37
+
38
+ export const KERNEL: Actor = { kind: 'kernel' }
39
+ export const DISPATCHER: Actor = { kind: 'dispatcher' }
40
+
41
+ export function agentActor(role: string, session: string): Actor {
42
+ return { kind: 'agent', role, session }
43
+ }
44
+
45
+ export function humanActor(user: string, via?: Via): Actor {
46
+ return via === undefined ? { kind: 'human', user } : { kind: 'human', user, via }
47
+ }
48
+
49
+ /** The validation rule every catalog's gate names when a `via` rides on a
50
+ * non-human actor (the strict variants reject the key themselves, but their
51
+ * parse error would not say why it is forbidden). */
52
+ export const VIA_ACTOR_RULE = 'only human actors may carry via'
53
+
54
+ /** Raw-input check used before actor parsing: `via` on any non-human actor. */
55
+ export function viaOnNonHumanActor(actor: unknown): boolean {
56
+ if (typeof actor !== 'object' || actor === null) return false
57
+ const candidate = actor as { kind?: unknown; via?: unknown }
58
+ return candidate.kind !== 'human' && candidate.via !== undefined
59
+ }