@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.
- package/LICENSE +201 -0
- package/README.md +192 -0
- package/SPEC.md +2269 -0
- package/bin/ab-build-runner.ts +47 -0
- package/bin/ab-dev.ts +27 -0
- package/bin/ab-dispatch-kernel.ts +92 -0
- package/bin/ab-harvest-runner.ts +29 -0
- package/bin/ab-init-probe.ts +19 -0
- package/bin/ab-process-group-reaper.ts +32 -0
- package/bin/ab.ts +6 -0
- package/bin/agent/ab +3 -0
- package/docs/answering-blocked-builds.md +89 -0
- package/docs/architecture.md +726 -0
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +1623 -0
- package/docs/hosted-dispatcher-cutover-validation.md +178 -0
- package/docs/hosted-dispatcher.md +204 -0
- package/docs/mcp.md +119 -0
- package/docs/operator-api.md +186 -0
- package/docs/remote-store-protocol.md +972 -0
- package/docs/remote-ticket-protocol.md +79 -0
- package/docs/setup.md +289 -0
- package/docs/spec-standard.md +63 -0
- package/docs/ticket-dependencies.md +59 -0
- package/docs/vercel-sandbox-rollout-probe.md +7 -0
- package/package.json +135 -0
- package/packages/core/src/cli/args.ts +74 -0
- package/packages/core/src/cli/artifact.ts +182 -0
- package/packages/core/src/cli/binary.ts +198 -0
- package/packages/core/src/cli/build-control.ts +789 -0
- package/packages/core/src/cli/build-progress.ts +55 -0
- package/packages/core/src/cli/bulk-control.ts +385 -0
- package/packages/core/src/cli/context.ts +470 -0
- package/packages/core/src/cli/dashboard/actions.ts +59 -0
- package/packages/core/src/cli/dashboard/cells.ts +92 -0
- package/packages/core/src/cli/dashboard/composer.ts +230 -0
- package/packages/core/src/cli/dashboard/detail.ts +100 -0
- package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
- package/packages/core/src/cli/dashboard/live.ts +152 -0
- package/packages/core/src/cli/dashboard/model.ts +1173 -0
- package/packages/core/src/cli/dashboard/poll.ts +196 -0
- package/packages/core/src/cli/dashboard/render.ts +1568 -0
- package/packages/core/src/cli/dashboard/selection.ts +66 -0
- package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
- package/packages/core/src/cli/dashboard/session-view.ts +506 -0
- package/packages/core/src/cli/dashboard/transcript.ts +108 -0
- package/packages/core/src/cli/dev.ts +46 -0
- package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
- package/packages/core/src/cli/dispatch-process.ts +259 -0
- package/packages/core/src/cli/dispatch.ts +3692 -0
- package/packages/core/src/cli/dotenv.ts +48 -0
- package/packages/core/src/cli/env.ts +206 -0
- package/packages/core/src/cli/harvest.ts +698 -0
- package/packages/core/src/cli/help.ts +713 -0
- package/packages/core/src/cli/init-agent.ts +106 -0
- package/packages/core/src/cli/init-validation.ts +583 -0
- package/packages/core/src/cli/init.ts +756 -0
- package/packages/core/src/cli/installation.ts +333 -0
- package/packages/core/src/cli/keyboard.ts +261 -0
- package/packages/core/src/cli/main.ts +1399 -0
- package/packages/core/src/cli/mcp.ts +279 -0
- package/packages/core/src/cli/models.ts +65 -0
- package/packages/core/src/cli/observe.ts +55 -0
- package/packages/core/src/cli/plugin.ts +227 -0
- package/packages/core/src/cli/pr-attachments.ts +138 -0
- package/packages/core/src/cli/pr-summary.ts +124 -0
- package/packages/core/src/cli/publication-settlement.ts +188 -0
- package/packages/core/src/cli/repo-state.ts +213 -0
- package/packages/core/src/cli/repository-status.ts +80 -0
- package/packages/core/src/cli/self-update.ts +443 -0
- package/packages/core/src/cli/status.ts +895 -0
- package/packages/core/src/cli/store-opening.contract.ts +289 -0
- package/packages/core/src/cli/store-opening.ts +167 -0
- package/packages/core/src/cli/store-ref.ts +33 -0
- package/packages/core/src/cli/terminal-restore.ts +174 -0
- package/packages/core/src/cli/terminal.ts +361 -0
- package/packages/core/src/cli/terminals.ts +995 -0
- package/packages/core/src/cli/testkit.ts +194 -0
- package/packages/core/src/cli/ticket.ts +484 -0
- package/packages/core/src/cli/upgrade-agent.ts +180 -0
- package/packages/core/src/cli/upgrade-commit.ts +468 -0
- package/packages/core/src/cli/upgrade-notice.ts +91 -0
- package/packages/core/src/cli/upgrade-progress.ts +110 -0
- package/packages/core/src/cli/upgrade.ts +888 -0
- package/packages/core/src/cli/wait.ts +587 -0
- package/packages/core/src/cli/watch.ts +932 -0
- package/packages/core/src/config/live.ts +255 -0
- package/packages/core/src/config/load.ts +98 -0
- package/packages/core/src/config/roles.ts +438 -0
- package/packages/core/src/config/schema.ts +774 -0
- package/packages/core/src/distribution.ts +24 -0
- package/packages/core/src/events/catalog.ts +201 -0
- package/packages/core/src/events/envelope.ts +59 -0
- package/packages/core/src/events/payloads.ts +618 -0
- package/packages/core/src/events/repository.ts +559 -0
- package/packages/core/src/events/sessions.ts +180 -0
- package/packages/core/src/harvest/schema.ts +147 -0
- package/packages/core/src/ids.ts +45 -0
- package/packages/core/src/integration/harness.ts +681 -0
- package/packages/core/src/kernel/auto-merge.ts +259 -0
- package/packages/core/src/kernel/converge.ts +117 -0
- package/packages/core/src/kernel/dispatch-settings.ts +60 -0
- package/packages/core/src/kernel/dispatch-status.ts +141 -0
- package/packages/core/src/kernel/engine.ts +1047 -0
- package/packages/core/src/kernel/harvest.ts +823 -0
- package/packages/core/src/kernel/origin.ts +69 -0
- package/packages/core/src/kernel/phases.ts +196 -0
- package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
- package/packages/core/src/kernel/pr-attachments.ts +82 -0
- package/packages/core/src/kernel/reducer.ts +752 -0
- package/packages/core/src/kernel/stall.ts +155 -0
- package/packages/core/src/kernel/verify-applicability.ts +52 -0
- package/packages/core/src/markdown.ts +67 -0
- package/packages/core/src/ontology.ts +288 -0
- package/packages/core/src/open-map.ts +203 -0
- package/packages/core/src/operator/annotations.ts +187 -0
- package/packages/core/src/operator/client.ts +311 -0
- package/packages/core/src/operator/control.ts +117 -0
- package/packages/core/src/operator/index.ts +62 -0
- package/packages/core/src/operator/presentation.ts +22 -0
- package/packages/core/src/operator/protocol.ts +98 -0
- package/packages/core/src/operator/query.ts +201 -0
- package/packages/core/src/operator/registry.ts +1043 -0
- package/packages/core/src/operator/requests.ts +138 -0
- package/packages/core/src/operator/sandbox.ts +458 -0
- package/packages/core/src/operator/server.ts +616 -0
- package/packages/core/src/operator/sessions.ts +223 -0
- package/packages/core/src/operator/tickets.ts +177 -0
- package/packages/core/src/plugin-sdk/index.ts +174 -0
- package/packages/core/src/plugins/contract-entry.ts +74 -0
- package/packages/core/src/plugins/contract-env.ts +3 -0
- package/packages/core/src/plugins/load.ts +259 -0
- package/packages/core/src/plugins/manifest.ts +236 -0
- package/packages/core/src/plugins/registry.ts +274 -0
- package/packages/core/src/plugins/runtimes.ts +75 -0
- package/packages/core/src/ports/forge/contract.ts +258 -0
- package/packages/core/src/ports/forge/create.ts +74 -0
- package/packages/core/src/ports/forge/fake.ts +488 -0
- package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
- package/packages/core/src/ports/forge/github-transport.ts +352 -0
- package/packages/core/src/ports/forge/github.ts +1162 -0
- package/packages/core/src/ports/forge/local-git.ts +568 -0
- package/packages/core/src/ports/runner/claude.ts +873 -0
- package/packages/core/src/ports/runner/cli-stream.ts +90 -0
- package/packages/core/src/ports/runner/codex.ts +873 -0
- package/packages/core/src/ports/runner/contract.ts +492 -0
- package/packages/core/src/ports/runner/fake.ts +213 -0
- package/packages/core/src/ports/runner/json-record.ts +2 -0
- package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
- package/packages/core/src/ports/runner/one-shot.ts +29 -0
- package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
- package/packages/core/src/ports/runner/pi-bridge.js +6 -0
- package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
- package/packages/core/src/ports/runner/pi.ts +617 -0
- package/packages/core/src/ports/runner/production.ts +74 -0
- package/packages/core/src/ports/runner/provider-error.ts +121 -0
- package/packages/core/src/ports/runner/routing.ts +234 -0
- package/packages/core/src/ports/runner/runtime.ts +214 -0
- package/packages/core/src/ports/runner/session-env.ts +34 -0
- package/packages/core/src/ports/runner/stream-parts.ts +103 -0
- package/packages/core/src/ports/tickets/contract.ts +570 -0
- package/packages/core/src/ports/tickets/create.ts +135 -0
- package/packages/core/src/ports/tickets/fake.ts +215 -0
- package/packages/core/src/ports/tickets/file.ts +559 -0
- package/packages/core/src/ports/tickets/linear.ts +811 -0
- package/packages/core/src/ports/tickets/operations.ts +161 -0
- package/packages/core/src/ports/tickets/remote-index.ts +13 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
- package/packages/core/src/ports/tickets/remote-server.ts +185 -0
- package/packages/core/src/ports/tickets/remote.ts +130 -0
- package/packages/core/src/ports/tickets/update.ts +51 -0
- package/packages/core/src/ports/types.ts +528 -0
- package/packages/core/src/ports/workspace/build-execution.ts +123 -0
- package/packages/core/src/ports/workspace/contract.ts +136 -0
- package/packages/core/src/ports/workspace/create.ts +132 -0
- package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
- package/packages/core/src/ports/workspace/fake.ts +432 -0
- package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
- package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
- package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
- package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
- package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
- package/packages/core/src/processes/build-child-exit.ts +78 -0
- package/packages/core/src/processes/build-child.ts +119 -0
- package/packages/core/src/processes/build-execution-state.ts +95 -0
- package/packages/core/src/processes/build-parent-watch.ts +35 -0
- package/packages/core/src/processes/build-runner.ts +2614 -0
- package/packages/core/src/processes/dispatcher.ts +2825 -0
- package/packages/core/src/processes/execution-settlement.ts +171 -0
- package/packages/core/src/processes/harvest-child.ts +115 -0
- package/packages/core/src/processes/harvest-execution-state.ts +121 -0
- package/packages/core/src/processes/harvest-runner.ts +1656 -0
- package/packages/core/src/processes/harvest.ts +595 -0
- package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
- package/packages/core/src/processes/phase-failure-budget.ts +52 -0
- package/packages/core/src/processes/process-group-reaper.ts +35 -0
- package/packages/core/src/processes/process-group.ts +65 -0
- package/packages/core/src/processes/publication-loss.ts +62 -0
- package/packages/core/src/processes/publication-state.ts +93 -0
- package/packages/core/src/processes/sandbox-state.ts +101 -0
- package/packages/core/src/registry.ts +24 -0
- package/packages/core/src/sessions/reducer.ts +185 -0
- package/packages/core/src/skills.ts +7 -0
- package/packages/core/src/spec-standard.ts +47 -0
- package/packages/core/src/store/adapter.ts +80 -0
- package/packages/core/src/store/build-scope.ts +272 -0
- package/packages/core/src/store/contract.ts +2581 -0
- package/packages/core/src/store/local/blobs.ts +38 -0
- package/packages/core/src/store/local/schema.ts +180 -0
- package/packages/core/src/store/local/store.ts +1713 -0
- package/packages/core/src/store/memory.ts +1138 -0
- package/packages/core/src/store/phase-session.ts +425 -0
- package/packages/core/src/store/remote/client.ts +780 -0
- package/packages/core/src/store/remote/index.ts +12 -0
- package/packages/core/src/store/remote/protocol.ts +348 -0
- package/packages/core/src/store/remote/server.ts +923 -0
- package/packages/core/src/store/remote/token.ts +137 -0
- package/packages/core/src/store/remote/version.ts +10 -0
- package/packages/core/src/store/retention.ts +91 -0
- package/packages/core/src/store/session-handle.ts +265 -0
- package/packages/core/src/store/streams/assemble.ts +79 -0
- package/packages/core/src/store/streams/lock.ts +42 -0
- package/packages/core/src/store/streams/session-writer.ts +306 -0
- package/packages/core/src/store/streams/types.ts +171 -0
- package/packages/core/src/store/streams/wait.ts +127 -0
- package/packages/core/src/store/subscribe.ts +105 -0
- package/packages/core/src/store/types.ts +466 -0
- package/packages/core/src/testing/fixed.ts +44 -0
- package/packages/core/src/testing/store-failures.ts +32 -0
- package/packages/core/src/zod-issues.ts +91 -0
- package/skills/code-review/SKILL.md +100 -0
- package/skills/finalize/SKILL.md +44 -0
- package/skills/guide/SKILL.md +1781 -0
- package/skills/guide/references/operator-api.md +164 -0
- package/skills/guide/references/plugin-authoring.md +354 -0
- package/skills/guide/references/remote-store-protocol.md +972 -0
- package/skills/guide/references/remote-ticket-protocol.md +79 -0
- package/skills/guide/references/setup.md +289 -0
- package/skills/guide/references/spec-standard.md +63 -0
- package/skills/guide/references/ticket-dependencies.md +59 -0
- package/skills/harvest/SKILL.md +66 -0
- package/skills/harvest-review/SKILL.md +63 -0
- package/skills/implement/SKILL.md +82 -0
- package/skills/plan/SKILL.md +123 -0
- package/skills/plan-review/SKILL.md +121 -0
- package/skills/reconcile/SKILL.md +65 -0
- package/skills/spec/SKILL.md +132 -0
- package/skills/tickets/SKILL.md +139 -0
- 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
|
+
}
|