@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,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
|
+
}
|