@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,180 @@
1
+ /**
2
+ * Operator-session event catalog (SPEC §7.1.1). Operator sessions are a third
3
+ * BuildStore resource kind alongside builds and the repository journal: an
4
+ * orchestrator conversation is durable state, but it is neither a build nor a
5
+ * repository fact. The catalog is closed and validated separately from the
6
+ * build and repository catalogs, so no build reducer can interpret session
7
+ * events — the same separation the repository journal has.
8
+ *
9
+ * Sessions are hosted-only: local installs interact through the CLI and
10
+ * dashboard and never create one. Nothing in this module encodes this
11
+ * repository's specifics.
12
+ */
13
+ import { z } from 'zod'
14
+ import { EventValidationError } from './catalog'
15
+ import {
16
+ actorSchema,
17
+ VIA_ACTOR_RULE,
18
+ viaOnNonHumanActor,
19
+ type Actor,
20
+ type ActorKind,
21
+ } from './envelope'
22
+
23
+ const positiveInt = z.number().int().positive()
24
+
25
+ const empty = z.strictObject({})
26
+
27
+ /** The trigger that started a turn: a human message or a wake event. */
28
+ const turnTriggerSchema = z.discriminatedUnion('kind', [
29
+ z.strictObject({ kind: z.literal('message'), messageSeq: positiveInt }),
30
+ z.strictObject({
31
+ kind: z.literal('wake'),
32
+ build: z.string().min(1),
33
+ seq: positiveInt,
34
+ type: z.string().min(1),
35
+ }),
36
+ ])
37
+
38
+ /** Matches the transcript usage shape (harvest.session.ended, §15.3). */
39
+ const usageSchema = z.strictObject({
40
+ inputTokens: z.number().int().nonnegative(),
41
+ outputTokens: z.number().int().nonnegative(),
42
+ turns: z.number().int().nonnegative().optional(),
43
+ })
44
+
45
+ export const sessionEventPayloadSchemas = {
46
+ /** The fact of creation; the session record carries the same title. */
47
+ 'session.created': z.strictObject({ title: z.string().min(1).optional() }),
48
+ /** A human operator's message. */
49
+ 'message.posted': z.strictObject({ text: z.string().min(1) }),
50
+ /** Which attention events may start a turn. Possibly empty: empty means
51
+ * no wake sources (message-only). */
52
+ 'session.wake-set': z.strictObject({
53
+ globs: z.array(z.string().min(1)).max(100),
54
+ }),
55
+ /** The (later) turn runner's start of one turn on its stream. */
56
+ 'turn.started': z.strictObject({
57
+ turn: z.string().min(1),
58
+ stream: z.string().min(1),
59
+ trigger: turnTriggerSchema,
60
+ }),
61
+ /** A turn was suspended for budget or for an approval. */
62
+ 'turn.suspended': z.strictObject({
63
+ turn: z.string().min(1),
64
+ cause: z.enum(['budget', 'approval']),
65
+ }),
66
+ /** The runner's acknowledgement that a suspended turn continues. */
67
+ 'turn.resumed': z.strictObject({ turn: z.string().min(1) }),
68
+ /** The turn asks its human to approve one tool call. */
69
+ 'approval.requested': z.strictObject({
70
+ turn: z.string().min(1),
71
+ toolCallId: z.string().min(1),
72
+ toolName: z.string().min(1),
73
+ input: z.record(z.string(), z.unknown()),
74
+ }),
75
+ /** The human's decision on one requested approval. */
76
+ 'approval.answered': z.strictObject({
77
+ turn: z.string().min(1),
78
+ toolCallId: z.string().min(1),
79
+ decision: z.enum(['approve', 'deny']),
80
+ }),
81
+ /** One turn finished. */
82
+ 'turn.completed': z.strictObject({ turn: z.string().min(1), usage: usageSchema }),
83
+ /** One turn failed. */
84
+ 'turn.failed': z.strictObject({ turn: z.string().min(1), error: z.string().min(1) }),
85
+ /** Terminal. The session is read-only afterwards. */
86
+ 'session.archived': empty,
87
+ } as const
88
+
89
+ export type SessionEventType = keyof typeof sessionEventPayloadSchemas
90
+ export const SESSION_EVENT_TYPES = Object.keys(sessionEventPayloadSchemas) as SessionEventType[]
91
+ export type SessionEventPayload<T extends SessionEventType> = z.infer<
92
+ (typeof sessionEventPayloadSchemas)[T]
93
+ >
94
+
95
+ export interface SessionEventEnvelope<T extends SessionEventType = SessionEventType> {
96
+ session: string
97
+ seq: number
98
+ ts: string
99
+ actor: Actor
100
+ type: T
101
+ payload: SessionEventPayload<T>
102
+ }
103
+
104
+ export type SessionEvent = {
105
+ [T in SessionEventType]: SessionEventEnvelope<T>
106
+ }[SessionEventType]
107
+
108
+ export interface SessionEventWrite<T extends SessionEventType = SessionEventType> {
109
+ actor: Actor
110
+ type: T
111
+ payload: SessionEventPayload<T>
112
+ }
113
+
114
+ /** Which actor kinds may emit each event type — the session catalog's Actor
115
+ * column, enforced at validation exactly as for builds (§15.3). Humans create,
116
+ * message, configure wakes, answer approvals, and archive; the agent (the
117
+ * embedded orchestrator's turn runner) owns the turn lifecycle. */
118
+ const allowedActorKinds: Record<SessionEventType, readonly ActorKind[]> = {
119
+ 'session.created': ['human'],
120
+ 'message.posted': ['human'],
121
+ 'session.wake-set': ['human'],
122
+ 'approval.answered': ['human'],
123
+ 'session.archived': ['human'],
124
+ 'turn.started': ['agent'],
125
+ 'turn.suspended': ['agent'],
126
+ 'turn.resumed': ['agent'],
127
+ 'approval.requested': ['agent'],
128
+ 'turn.completed': ['agent'],
129
+ 'turn.failed': ['agent'],
130
+ }
131
+
132
+ export function isSessionEventType(value: string): value is SessionEventType {
133
+ return Object.hasOwn(sessionEventPayloadSchemas, value)
134
+ }
135
+
136
+ /**
137
+ * The session catalog's single validation gate. Every store adapter MUST pass
138
+ * session writes through here before appending, with the same exact strictness
139
+ * as the build and repository gates (D6).
140
+ */
141
+ export function validateSessionEventWrite(input: {
142
+ actor: unknown
143
+ type: string
144
+ payload: unknown
145
+ }): SessionEventWrite {
146
+ if (!isSessionEventType(input.type)) {
147
+ throw new EventValidationError(
148
+ `unknown session event type "${input.type}" — known types: ${SESSION_EVENT_TYPES.join(', ')}`,
149
+ )
150
+ }
151
+ if (viaOnNonHumanActor(input.actor)) {
152
+ throw new EventValidationError(VIA_ACTOR_RULE)
153
+ }
154
+ const actorResult = actorSchema.safeParse(input.actor)
155
+ if (!actorResult.success) {
156
+ throw new EventValidationError(
157
+ `invalid actor for "${input.type}": ${actorResult.error.message}`,
158
+ actorResult.error.issues,
159
+ )
160
+ }
161
+ const actor = actorResult.data
162
+ const allowed = allowedActorKinds[input.type]
163
+ if (!allowed.includes(actor.kind)) {
164
+ throw new EventValidationError(
165
+ `actor kind "${actor.kind}" may not emit "${input.type}" (allowed: ${allowed.join(', ')})`,
166
+ )
167
+ }
168
+ const result = sessionEventPayloadSchemas[input.type].safeParse(input.payload)
169
+ if (!result.success) {
170
+ throw new EventValidationError(
171
+ `invalid payload for "${input.type}": ${result.error.message}`,
172
+ result.error.issues,
173
+ )
174
+ }
175
+ return {
176
+ actor,
177
+ type: input.type,
178
+ payload: result.data,
179
+ } as SessionEventWrite
180
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Frozen structured values shared by the harvest kernel, CLI, skills, and
3
+ * repository journal. Harvest operates on observation occurrences, not on an
4
+ * observation id alone: event sequence numbers are only monotonic within one
5
+ * build, so `{build, seq}` is the canonical identity.
6
+ */
7
+ import { z } from 'zod'
8
+ import { observationKindSchema, ticketRefSchema } from '../ontology'
9
+
10
+ export const occurrenceKeySchema = z.strictObject({
11
+ build: z.string().min(1),
12
+ seq: z.number().int().positive(),
13
+ })
14
+ export type OccurrenceKey = z.infer<typeof occurrenceKeySchema>
15
+
16
+ export function occurrenceKey(key: OccurrenceKey): string {
17
+ return `${key.build}:${key.seq}`
18
+ }
19
+
20
+ export const harvestObservationSchema = z.strictObject({
21
+ occurrence: occurrenceKeySchema,
22
+ id: z.string().min(1),
23
+ kind: observationKindSchema,
24
+ summary: z.string().min(1),
25
+ files: z.array(z.string()).optional(),
26
+ refs: z.array(z.string()).optional(),
27
+ ts: z.string().min(1),
28
+ ticket: ticketRefSchema.optional(),
29
+ })
30
+ export type HarvestObservation = z.infer<typeof harvestObservationSchema>
31
+
32
+ /** The durable reason a Harvest snapshot crossed repository pressure. */
33
+ export const harvestTriggerSchema = z.enum(['count', 'drift', 'both'])
34
+ export type HarvestTrigger = z.infer<typeof harvestTriggerSchema>
35
+
36
+ /** Source-aware lifecycle visibility for distinct tickets that originated the
37
+ * observations in a scan. Foreign-source refs remain visible but are never
38
+ * sent to the repository's configured TicketSource. */
39
+ export const harvestOriginatingTicketSchema = z.strictObject({
40
+ ticket: ticketRefSchema,
41
+ sourceMatches: z.boolean(),
42
+ exists: z.boolean(),
43
+ resolved: z.boolean(),
44
+ })
45
+ export type HarvestOriginatingTicket = z.infer<typeof harvestOriginatingTicketSchema>
46
+
47
+ /** Filing records both meanings even when one id is simultaneously declared
48
+ * by the agent and automatically derived from an originating ticket. */
49
+ export const harvestBlockerProvenanceSchema = z.strictObject({
50
+ declared: z.array(z.string().min(1)),
51
+ derived: z.array(z.string().min(1)),
52
+ })
53
+ export type HarvestBlockerProvenance = z.infer<typeof harvestBlockerProvenanceSchema>
54
+
55
+ /** Prior proposals are semantic-dedup context for the synthesizer. The source
56
+ * owns lifecycle resolution; harvest only reports the returned facts. */
57
+ export const harvestLedgerTicketSchema = z.strictObject({
58
+ proposalKey: z.string().min(1),
59
+ ticket: ticketRefSchema,
60
+ exists: z.boolean(),
61
+ resolved: z.boolean(),
62
+ title: z.string().optional(),
63
+ body: z.string().optional(),
64
+ })
65
+ export type HarvestLedgerTicket = z.infer<typeof harvestLedgerTicketSchema>
66
+
67
+ export const harvestScanPacketSchema = z.strictObject({
68
+ repo: z.string().min(1),
69
+ run: z.string().min(1),
70
+ observations: z.array(harvestObservationSchema).min(1),
71
+ originatingTickets: z.array(harvestOriginatingTicketSchema).default([]),
72
+ ledger: z.array(harvestLedgerTicketSchema).default([]),
73
+ })
74
+ export type HarvestScanPacket = z.infer<typeof harvestScanPacketSchema>
75
+
76
+ const proposalMembers = z.array(occurrenceKeySchema).min(1)
77
+
78
+ export const harvestCreateProposalSchema = z.strictObject({
79
+ action: z.literal('create'),
80
+ title: z.string().min(1),
81
+ /** The prose for the rendered `What and why` section. */
82
+ whatWhy: z.string().min(1),
83
+ acceptanceCriteria: z.array(z.string().min(1)).min(1),
84
+ /** Explicit even when there is little to exclude. */
85
+ outOfScope: z.array(z.string().min(1)).min(1),
86
+ /** Source-local ticket ids that must complete before this work can start. */
87
+ blockedBy: z
88
+ .array(
89
+ z
90
+ .string()
91
+ .min(1)
92
+ .refine((id) => id.trim().length > 0, 'blocker id must be nonblank'),
93
+ )
94
+ .optional(),
95
+ observations: proposalMembers,
96
+ })
97
+
98
+ export const harvestJoinProposalSchema = z.strictObject({
99
+ action: z.literal('join'),
100
+ ticket: ticketRefSchema,
101
+ observations: proposalMembers,
102
+ reason: z.string().min(1),
103
+ })
104
+
105
+ export const harvestSuppressProposalSchema = z.strictObject({
106
+ action: z.literal('suppress'),
107
+ observations: proposalMembers,
108
+ reason: z.string().min(1),
109
+ })
110
+
111
+ export const harvestProposalSchema = z.discriminatedUnion('action', [
112
+ harvestCreateProposalSchema,
113
+ harvestJoinProposalSchema,
114
+ harvestSuppressProposalSchema,
115
+ ])
116
+ export type HarvestProposal = z.infer<typeof harvestProposalSchema>
117
+
118
+ export const harvestProposalSetSchema = z.strictObject({
119
+ proposals: z.array(harvestProposalSchema).min(1),
120
+ })
121
+ export type HarvestProposalSet = z.infer<typeof harvestProposalSetSchema>
122
+
123
+ export const harvestDispositionSchema = z.strictObject({
124
+ occurrence: occurrenceKeySchema,
125
+ action: z.enum(['filed', 'joined', 'suppressed']),
126
+ proposalKey: z.string().min(1).optional(),
127
+ ticket: ticketRefSchema.optional(),
128
+ reason: z.string().min(1).optional(),
129
+ })
130
+ export type HarvestDisposition = z.infer<typeof harvestDispositionSchema>
131
+
132
+ /** Stable, prose-free summary of proposal work still pending when automatic
133
+ * recovery is exhausted. The released occurrence list remains authoritative;
134
+ * these descriptors let status name reviewed proposal clusters without
135
+ * loading an artifact. */
136
+ export const harvestPendingProposalSchema = z.strictObject({
137
+ proposalKey: z.string().min(1),
138
+ action: z.enum(['create', 'join', 'suppress']),
139
+ observations: z.array(occurrenceKeySchema).min(1),
140
+ })
141
+ export type HarvestPendingProposal = z.infer<typeof harvestPendingProposalSchema>
142
+
143
+ export const harvestStepSchema = z.enum(['scan', 'synthesize', 'review', 'file'])
144
+ export type HarvestStep = z.infer<typeof harvestStepSchema>
145
+
146
+ export const harvestStatusSchema = z.enum(['running', 'completed', 'escalated', 'failed'])
147
+ export type HarvestStatus = z.infer<typeof harvestStatusSchema>
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Id stamping. Finding, observation, and escalation ids are kernel-assigned
3
+ * at deposit (§15.4) — agents never self-assign them. Injectable so tests
4
+ * are deterministic.
5
+ */
6
+ export type IdSource = (prefix: string) => string
7
+
8
+ /** UUID v4 allocation is separate from short kernel ids: callers that need
9
+ * crash-stable UUIDs must reserve the generated value in durable state. */
10
+ export type UuidSource = () => string
11
+
12
+ /** Platform-backed UUID v4 allocation; no hand-rolled UUID bit shaping. */
13
+ export function randomUuids(): UuidSource {
14
+ return () => crypto.randomUUID()
15
+ }
16
+
17
+ /** e.g. `f_3a91c07b` — 4 random bytes, hex. */
18
+ export function randomIds(): IdSource {
19
+ return (prefix) => {
20
+ const bytes = new Uint8Array(4)
21
+ crypto.getRandomValues(bytes)
22
+ const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')
23
+ return `${prefix}_${hex}`
24
+ }
25
+ }
26
+
27
+ /** Deterministic ids for tests: `f_1`, `f_2`, … (counter per prefix). */
28
+ export function sequentialIds(): IdSource {
29
+ const counters = new Map<string, number>()
30
+ return (prefix) => {
31
+ const next = (counters.get(prefix) ?? 0) + 1
32
+ counters.set(prefix, next)
33
+ return `${prefix}_${next}`
34
+ }
35
+ }
36
+
37
+ /** Deterministic UUID v4 values for integration harnesses. The version and
38
+ * variant nibbles remain valid while the final 48 bits carry the sequence. */
39
+ export function sequentialUuids(): UuidSource {
40
+ let next = 0
41
+ return () => {
42
+ next += 1
43
+ return `00000000-0000-4000-8000-${next.toString(16).padStart(12, '0')}`
44
+ }
45
+ }