@defrex/autobuild 0.8.0 → 0.10.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/README.md +1 -1
- package/SPEC.md +323 -26
- package/bin/ab-build-runner.ts +8 -2
- package/docs/architecture.md +166 -14
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +175 -28
- package/docs/hosted-dispatcher.md +69 -13
- package/docs/operator-api.md +2 -2
- package/docs/remote-store-protocol.md +204 -16
- package/docs/remote-ticket-protocol.md +1 -1
- package/docs/setup.md +12 -9
- package/docs/website.md +14 -0
- package/package.json +45 -23
- package/packages/core/src/cli/artifact.ts +130 -6
- package/packages/core/src/cli/binary.ts +9 -1
- package/packages/core/src/cli/build-control.ts +110 -13
- package/packages/core/src/cli/context.ts +46 -0
- package/packages/core/src/cli/dashboard/facts.ts +215 -0
- package/packages/core/src/cli/dashboard/frame-parse.ts +223 -0
- package/packages/core/src/cli/dashboard/model.ts +141 -153
- package/packages/core/src/cli/dashboard/poll.ts +76 -12
- package/packages/core/src/cli/dashboard/render.ts +34 -5
- package/packages/core/src/cli/dispatch-frontend.ts +54 -5
- package/packages/core/src/cli/dispatch-process.ts +1 -13
- package/packages/core/src/cli/dispatch.ts +392 -75
- package/packages/core/src/cli/harvest.ts +122 -8
- package/packages/core/src/cli/help.ts +62 -9
- package/packages/core/src/cli/init-readiness-remote.ts +110 -0
- package/packages/core/src/cli/init-readiness-shared.ts +72 -0
- package/packages/core/src/cli/init-validation.ts +266 -222
- package/packages/core/src/cli/installation.ts +50 -8
- package/packages/core/src/cli/main.ts +70 -11
- package/packages/core/src/cli/mcp.ts +23 -1
- package/packages/core/src/cli/plugin.ts +7 -4
- package/packages/core/src/cli/remote-poll.ts +277 -0
- package/packages/core/src/cli/repo-state.ts +22 -0
- package/packages/core/src/cli/repository-status.ts +39 -1
- package/packages/core/src/cli/self-update.ts +77 -2
- package/packages/core/src/cli/status.ts +150 -28
- package/packages/core/src/cli/terminals.ts +2 -1
- package/packages/core/src/cli/ticket-assets.ts +247 -0
- package/packages/core/src/cli/ticket.ts +453 -8
- package/packages/core/src/cli/wait.ts +157 -41
- package/packages/core/src/cli/watch.ts +138 -168
- package/packages/core/src/config/live.ts +23 -0
- package/packages/core/src/config/pipeline-source.ts +388 -0
- package/packages/core/src/config/schema.ts +251 -19
- package/packages/core/src/events/catalog.ts +1 -0
- package/packages/core/src/events/globs.ts +47 -0
- package/packages/core/src/events/payloads.ts +52 -2
- package/packages/core/src/events/repository.ts +52 -1
- package/packages/core/src/events/sessions.ts +69 -10
- package/packages/core/src/events/workspace-remote.ts +12 -0
- package/packages/core/src/integration/harness.ts +115 -2
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +1369 -0
- package/packages/core/src/kernel/auto-merge-default.ts +157 -0
- package/packages/core/src/kernel/auto-merge.ts +99 -22
- package/packages/core/src/kernel/dispatch-settings.ts +42 -17
- package/packages/core/src/kernel/dispatch-status.ts +23 -0
- package/packages/core/src/kernel/engine.ts +9 -438
- package/packages/core/src/kernel/generators/build-log.ts +259 -0
- package/packages/core/src/kernel/generators/repository-journals.ts +206 -0
- package/packages/core/src/kernel/generators/session-log.ts +72 -0
- package/packages/core/src/kernel/harvest.ts +62 -18
- package/packages/core/src/kernel/incremental-contract.ts +75 -0
- package/packages/core/src/kernel/incremental-fixtures.ts +211 -0
- package/packages/core/src/kernel/incremental.ts +53 -0
- package/packages/core/src/kernel/log-index.ts +620 -0
- package/packages/core/src/kernel/phases.ts +16 -2
- package/packages/core/src/kernel/pr-attachments.ts +164 -46
- package/packages/core/src/kernel/reducer-registry.ts +164 -0
- package/packages/core/src/kernel/reducer.ts +254 -73
- package/packages/core/src/operator/annotations.ts +6 -0
- package/packages/core/src/operator/control.ts +19 -5
- package/packages/core/src/operator/index.ts +71 -6
- package/packages/core/src/operator/query.ts +253 -24
- package/packages/core/src/operator/read-view.ts +98 -0
- package/packages/core/src/operator/registry.ts +55 -10
- package/packages/core/src/operator/requests.ts +9 -6
- package/packages/core/src/operator/sandbox-host.ts +140 -0
- package/packages/core/src/operator/sandbox.ts +247 -14
- package/packages/core/src/operator/ticket-source-backend.ts +47 -0
- package/packages/core/src/orchestrator/approvals.ts +115 -0
- package/packages/core/src/orchestrator/conversation.ts +167 -0
- package/packages/core/src/orchestrator/model.ts +86 -0
- package/packages/core/src/orchestrator/prompt.ts +61 -0
- package/packages/core/src/orchestrator/tools.ts +117 -0
- package/packages/core/src/orchestrator/turn-runner.ts +457 -0
- package/packages/core/src/plugin-sdk/index.ts +51 -1
- package/packages/core/src/plugins/load.ts +135 -7
- package/packages/core/src/plugins/manifest.ts +183 -10
- package/packages/core/src/plugins/registry.ts +73 -4
- package/packages/core/src/ports/forge/create.ts +1 -1
- package/packages/core/src/ports/forge/fake.ts +31 -0
- package/packages/core/src/ports/forge/github-transport.ts +13 -2
- package/packages/core/src/ports/forge/github.ts +13 -2
- package/packages/core/src/ports/forge/local-git.ts +31 -1
- package/packages/core/src/ports/runner/claude.ts +2 -2
- package/packages/core/src/ports/runner/codex.ts +6 -2
- package/packages/core/src/ports/runner/fake.ts +7 -1
- package/packages/core/src/ports/runner/pi.ts +8 -6
- package/packages/core/src/ports/runner/provider-error.ts +13 -3
- package/packages/core/src/ports/tickets/fake.ts +5 -1
- package/packages/core/src/ports/tickets/file.ts +169 -48
- package/packages/core/src/ports/tickets/{remote-index.ts → hosted-index.ts} +3 -2
- package/packages/core/src/ports/tickets/linear.ts +33 -1
- package/packages/core/src/ports/tickets/operations.ts +1 -1
- package/packages/core/src/ports/tickets/remote.ts +8 -1
- package/packages/core/src/ports/types.ts +49 -3
- package/packages/core/src/ports/workspace/builtin-capabilities.ts +114 -0
- package/packages/core/src/ports/workspace/create.ts +74 -39
- package/packages/core/src/ports/workspace/distribution-archive.ts +171 -12
- package/packages/core/src/ports/workspace/fake.ts +169 -4
- package/packages/core/src/ports/workspace/git-worktree.ts +246 -26
- package/packages/core/src/ports/workspace/github-origin.ts +36 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +7 -1
- package/packages/core/src/ports/workspace/provider-capabilities.ts +171 -0
- package/packages/core/src/ports/workspace/vercel-capabilities.ts +110 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +142 -114
- package/packages/core/src/processes/build-child.ts +17 -2
- package/packages/core/src/processes/build-execution-state.ts +68 -15
- package/packages/core/src/processes/build-runner.ts +238 -93
- package/packages/core/src/processes/dispatcher-selectors.ts +123 -0
- package/packages/core/src/processes/dispatcher.ts +501 -201
- package/packages/core/src/processes/execution-settlement.ts +72 -39
- package/packages/core/src/processes/harvest-child.ts +5 -0
- package/packages/core/src/processes/harvest-execution-state.ts +41 -29
- package/packages/core/src/processes/harvest-runner.ts +66 -19
- package/packages/core/src/processes/harvest.ts +196 -24
- package/packages/core/src/processes/infrastructure-failure-budget.ts +31 -18
- package/packages/core/src/processes/orchestrator-tick.ts +360 -0
- package/packages/core/src/processes/publication-state.ts +164 -52
- package/packages/core/src/processes/repo-view.ts +1436 -0
- package/packages/core/src/processes/sandbox-state.ts +36 -2
- package/packages/core/src/processes/tick-state.ts +33 -0
- package/packages/core/src/store/adapter.ts +41 -0
- package/packages/core/src/store/build-scope.ts +90 -0
- package/packages/core/src/store/contract.ts +1205 -0
- package/packages/core/src/store/digest.ts +102 -0
- package/packages/core/src/store/local/schema.ts +46 -2
- package/packages/core/src/store/local/store.ts +445 -4
- package/packages/core/src/store/memory.ts +246 -0
- package/packages/core/src/store/new-build.ts +17 -0
- package/packages/core/src/store/phase-session.ts +94 -0
- package/packages/core/src/store/remote/client.ts +310 -2
- package/packages/core/src/store/remote/index.ts +14 -2
- package/packages/core/src/store/remote/protocol.ts +115 -12
- package/packages/core/src/store/remote/version.ts +6 -2
- package/packages/core/src/store/repo-state-events.ts +205 -0
- package/packages/core/src/store/retention.ts +7 -0
- package/packages/core/src/store/session-handle.ts +94 -0
- package/packages/core/src/{sessions/reducer.ts → store/session-reducer.ts} +79 -19
- package/packages/core/src/store/snapshots.ts +88 -0
- package/packages/core/src/store/streams/session-writer.ts +42 -2
- package/packages/core/src/store/streams/wait.ts +4 -5
- package/packages/core/src/store/ticket-assets.ts +458 -0
- package/packages/core/src/store/types.ts +160 -1
- package/packages/core/src/testing/index.ts +59 -0
- package/skills/code-review/SKILL.md +5 -1
- package/skills/guide/SKILL.md +152 -40
- package/skills/guide/references/operator-api.md +2 -2
- package/skills/guide/references/plugin-authoring.md +17 -0
- package/skills/guide/references/remote-store-protocol.md +204 -16
- package/skills/guide/references/remote-ticket-protocol.md +1 -1
- package/skills/guide/references/setup.md +12 -9
- package/skills/implement/SKILL.md +3 -0
- package/skills/operate/SKILL.md +126 -0
- package/skills/plan/SKILL.md +4 -0
- package/skills/plan-review/SKILL.md +5 -1
- package/skills/spec/SKILL.md +46 -3
- package/skills/tickets/SKILL.md +57 -5
- package/packages/core/src/cli/dashboard/frame-image.ts +0 -373
- package/packages/core/src/cli/store-opening.contract.ts +0 -289
- package/packages/core/src/cli/testkit.ts +0 -194
- package/packages/core/src/markdown.ts +0 -67
- package/packages/core/src/operator/client.ts +0 -311
- package/packages/core/src/operator/server.ts +0 -616
- package/packages/core/src/operator/sessions.ts +0 -223
- package/packages/core/src/ports/runner/live-contract-fixture.ts +0 -176
- package/packages/core/src/ports/tickets/remote-server.ts +0 -185
- package/packages/core/src/store/remote/server.ts +0 -923
- package/packages/core/src/testing/store-failures.ts +0 -32
|
@@ -14,9 +14,16 @@
|
|
|
14
14
|
import { z } from 'zod'
|
|
15
15
|
import { prImageHostSchema } from '../ontology'
|
|
16
16
|
import { SANDBOX_FORBIDDEN_ENV } from '../ports/workspace/operator-sandbox'
|
|
17
|
+
import {
|
|
18
|
+
runtimeProvisioningMap,
|
|
19
|
+
runtimeProvisioningMissingMessage,
|
|
20
|
+
sandboxForbiddenEnvMessage,
|
|
21
|
+
} from '../ports/workspace/provider-capabilities'
|
|
17
22
|
import { defineEntry, openMap, ownEntries, parseEntry } from '../open-map'
|
|
18
23
|
import { forwardIssues } from '../zod-issues'
|
|
19
|
-
import {
|
|
24
|
+
import { compileEventGlobs } from '../events/globs'
|
|
25
|
+
import { REPOSITORY_ATTENTION_EVENTS } from '../events/repository'
|
|
26
|
+
import { effectiveRuntimeReferences } from './roles'
|
|
20
27
|
|
|
21
28
|
// ── Open maps ────────────────────────────────────────────────────────────────
|
|
22
29
|
|
|
@@ -242,6 +249,70 @@ export const workspaceSchema = z.strictObject({
|
|
|
242
249
|
})
|
|
243
250
|
export type WorkspaceConfig = z.infer<typeof workspaceSchema>
|
|
244
251
|
|
|
252
|
+
/**
|
|
253
|
+
* The parse-time subset of workspace-provider behavior a builtin declares
|
|
254
|
+
* (AUT-516). This table lives in `config/schema.ts` because the
|
|
255
|
+
* `[workspace.config]` validation runs inside `configSchema`'s
|
|
256
|
+
* `superRefine` — at parse time, long before plugins load, in processes that
|
|
257
|
+
* never touch the registry. The full capability objects (including the
|
|
258
|
+
* registry-seam declarations) are assembled in
|
|
259
|
+
* `ports/workspace/builtin-capabilities.ts`, which takes this subset from
|
|
260
|
+
* here — a parity now enforced by
|
|
261
|
+
* `ports/workspace/builtin-capabilities.test.ts` (AUT-573), so a builtin
|
|
262
|
+
* added to only one table fails tests rather than compiling silently. The
|
|
263
|
+
* construction-site refusal text differs by design from the
|
|
264
|
+
* parse-site one, so it is NOT copied from this table.
|
|
265
|
+
*/
|
|
266
|
+
export interface WorkspaceProviderConfigDeclaration {
|
|
267
|
+
/** Strict schema applied to `[workspace.config]`. A builtin declaration
|
|
268
|
+
* carries EXACTLY ONE of `configSchema` / `configRefusalMessage`: when the
|
|
269
|
+
* schema is absent, the declaration must carry `configRefusalMessage` for
|
|
270
|
+
* nonempty provider config to be refused (the neither-key side is pinned by
|
|
271
|
+
* the AUT-565 test in config.test.ts), and carrying BOTH is forbidden —
|
|
272
|
+
* `configSchema`'s superRefine checks `configRefusalMessage` first, so the
|
|
273
|
+
* refusal branch would win and the schema would silently never apply (the
|
|
274
|
+
* both-keys side is pinned by its sibling test). Providers absent from
|
|
275
|
+
* `BUILTIN_WORKSPACE_PROVIDER_CONFIG` are plugin-owned pass-throughs and
|
|
276
|
+
* are never refused here. */
|
|
277
|
+
configSchema?: z.ZodType
|
|
278
|
+
/** Parse-site refusal text for a provider that rejects `[workspace.config]`
|
|
279
|
+
* outright. This is the sole parse-site refusal key: a declaration without
|
|
280
|
+
* it never refuses `[workspace.config]` here, with or without a schema.
|
|
281
|
+
* Distinct from the capability's construction-site `configRefusal`. */
|
|
282
|
+
configRefusalMessage?: string
|
|
283
|
+
/** Referenced runtimes must have `[workspace.config.runtimeProvisioning]`
|
|
284
|
+
* entries (checked only once roles, verify, and finalize are all present). */
|
|
285
|
+
requireRuntimeProvisioning?: boolean
|
|
286
|
+
/** Names added to the operator-sandbox forbidden-forwarding set beyond
|
|
287
|
+
* `SANDBOX_FORBIDDEN_ENV`. */
|
|
288
|
+
sandboxForbiddenEnv?: readonly string[]
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Parse-time behavior of every builtin workspace provider, keyed by the
|
|
292
|
+
* provider name an operator writes in `[workspace].provider`. No provider
|
|
293
|
+
* name appears in any validation branch — every check reads this table. */
|
|
294
|
+
export const BUILTIN_WORKSPACE_PROVIDER_CONFIG: ReadonlyMap<
|
|
295
|
+
string,
|
|
296
|
+
WorkspaceProviderConfigDeclaration
|
|
297
|
+
> = new Map([
|
|
298
|
+
[
|
|
299
|
+
'git-worktree',
|
|
300
|
+
{
|
|
301
|
+
configRefusalMessage:
|
|
302
|
+
'[workspace.config] is not supported by the builtin "git-worktree" provider — remove it or select a plugin workspace provider',
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
[
|
|
306
|
+
'vercel-sandbox',
|
|
307
|
+
{
|
|
308
|
+
configSchema: vercelSandboxConfigSchema,
|
|
309
|
+
requireRuntimeProvisioning: true,
|
|
310
|
+
// Its credentials are already in SANDBOX_FORBIDDEN_ENV.
|
|
311
|
+
sandboxForbiddenEnv: [],
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
])
|
|
315
|
+
|
|
245
316
|
// ── [commands] ───────────────────────────────────────────────────────────────
|
|
246
317
|
//
|
|
247
318
|
// Open map of deterministic verbs the kernel may run. `setup`, `lint`,
|
|
@@ -485,6 +556,20 @@ export const ticketsSchema = z.strictObject({
|
|
|
485
556
|
(s) => s.trim().length > 0,
|
|
486
557
|
'[tickets].readyState must not be blank — name the one workflow state a ticket must sit in to be dispatched (e.g. "ready").',
|
|
487
558
|
),
|
|
559
|
+
/**
|
|
560
|
+
* Narrows readiness to tickets assigned to one Linear user — a third
|
|
561
|
+
* conjunctive gate beside readyState and readyLabels. `"me"` is the owner of
|
|
562
|
+
* the dispatcher's LINEAR_API_KEY; an email names a specific user (for a
|
|
563
|
+
* shared or bot key). Unassigned tickets are never ready while it is set.
|
|
564
|
+
* Linear only; absent leaves readiness unchanged.
|
|
565
|
+
*/
|
|
566
|
+
readyAssignee: z
|
|
567
|
+
.string()
|
|
568
|
+
.refine(
|
|
569
|
+
(s) => s === 'me' || (s === s.trim() && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s)),
|
|
570
|
+
'[tickets].readyAssignee must be "me" (the owner of the API key) or a user email address (e.g. "person@example.com")',
|
|
571
|
+
)
|
|
572
|
+
.optional(),
|
|
488
573
|
/** Backend team key (e.g. "ENG") — required for linear and hosted. */
|
|
489
574
|
teamKey: z.string().min(1).optional(),
|
|
490
575
|
/** Workflow state claim() moves an issue to (§12); Linear and hosted. */
|
|
@@ -534,9 +619,107 @@ export const orchestratorSandboxSchema = z.strictObject({
|
|
|
534
619
|
})
|
|
535
620
|
export type OrchestratorSandboxConfig = z.infer<typeof orchestratorSandboxSchema>
|
|
536
621
|
|
|
622
|
+
/** The turn runner lives inside the hosted operator route and the dispatcher
|
|
623
|
+
* tick, both of which run under a Vercel function duration limit the config's
|
|
624
|
+
* invocation budget can never exceed: a value above this is clamped (not
|
|
625
|
+
* rejected) at parse time so a mis-set budget degrades to the limit instead of
|
|
626
|
+
* failing every deploy. The operator route pins `maxDuration` to this same
|
|
627
|
+
* constant, so one number is cited by the config, the route, and the docs. */
|
|
628
|
+
export const ORCHESTRATOR_ROUTE_LIMIT_SECONDS = 300
|
|
629
|
+
|
|
630
|
+
/** Turn budget floor for the dispatcher tick's orchestrator step: when less
|
|
631
|
+
* than this many seconds of tick budget remain, the step skips the pass
|
|
632
|
+
* entirely (suspension state is durable, so skipping is always safe). */
|
|
633
|
+
export const ORCHESTRATOR_MIN_TURN_SECONDS = 30
|
|
634
|
+
|
|
635
|
+
/** The turn approval entries' shape: a registry tool name, optionally
|
|
636
|
+
* qualified by the discriminator field's accepted value (`tool:qualifier`).
|
|
637
|
+
* Validation is syntactic only — an entry naming an absent or unknown tool is
|
|
638
|
+
* inert by design (documented in docs/configuration.md), because the list
|
|
639
|
+
* is meant to stay stable while the registry grows — e.g. `sandbox.publish`
|
|
640
|
+
* was named here before its ticket landed, and stays inert in bindings
|
|
641
|
+
* where no sandbox backend is bound (the hosted default). */
|
|
642
|
+
const ORCHESTRATOR_APPROVAL_ENTRY = /^[a-z][a-z0-9_.]*(:[a-z0-9_-]+)?$/
|
|
643
|
+
|
|
644
|
+
/** The attention set a new session's wake settings inherit when the
|
|
645
|
+
* repository does not override `wake`: the build events an operator or agent
|
|
646
|
+
* must wake up for (the same set `ab watch` filters on by default) plus the
|
|
647
|
+
* repository-journal attention events (the same set `ab watch --repository`
|
|
648
|
+
* follows), spelled as literal globs. Both halves of the attention set can
|
|
649
|
+
* wake a session. */
|
|
650
|
+
export const defaultOrchestratorWakeGlobs: readonly string[] = [
|
|
651
|
+
'escalation.raised',
|
|
652
|
+
'phase.failed',
|
|
653
|
+
'infrastructure.failed',
|
|
654
|
+
'runner.setup-failed',
|
|
655
|
+
'dispatch.failed',
|
|
656
|
+
'publication.lost',
|
|
657
|
+
'finalize.completed',
|
|
658
|
+
'pr.conflicted',
|
|
659
|
+
'pr.merged',
|
|
660
|
+
'pr.closed',
|
|
661
|
+
'build.completed',
|
|
662
|
+
'build.aborted',
|
|
663
|
+
...REPOSITORY_ATTENTION_EVENTS,
|
|
664
|
+
]
|
|
665
|
+
|
|
666
|
+
/** The registry tools a turn asks its operator about before executing.
|
|
667
|
+
* The maintainer sets this to `[]` for autonomous operation. */
|
|
668
|
+
export const defaultOrchestratorApprovals: readonly string[] = [
|
|
669
|
+
'builds.control:abort',
|
|
670
|
+
'builds.control:discard',
|
|
671
|
+
'builds.answer:revise-spec',
|
|
672
|
+
'sandbox.publish',
|
|
673
|
+
'tickets.move:ready',
|
|
674
|
+
]
|
|
675
|
+
|
|
676
|
+
/** A session's effective wake globs: the configured `wake` when declared
|
|
677
|
+
* (an explicit `[]` means never wake), else the default attention set. Kept
|
|
678
|
+
* as a resolver rather than a schema default so `wake`'s absence (inherit)
|
|
679
|
+
* and emptiness (never wake) stay distinct. */
|
|
680
|
+
export function orchestratorWakeGlobs(config: OrchestratorConfig): string[] {
|
|
681
|
+
return [...(config.wake ?? defaultOrchestratorWakeGlobs)]
|
|
682
|
+
}
|
|
683
|
+
|
|
537
684
|
export const orchestratorSchema = z.strictObject({
|
|
538
685
|
enabled: z.boolean().default(false),
|
|
539
686
|
sandbox: orchestratorSandboxSchema.prefault({}),
|
|
687
|
+
/** Provider-qualified model string for the turn runner, in the same
|
|
688
|
+
* vocabulary as `[roles]` model strings (nonblank; a leading
|
|
689
|
+
* `vercel-ai-gateway/` prefix is stripped before the gateway provider).
|
|
690
|
+
* Resolved through the deployment's gateway credential. Required when
|
|
691
|
+
* `enabled = true` (cross-validated below); the runner never starts a turn
|
|
692
|
+
* on a repository whose config omits it. */
|
|
693
|
+
model: z.string().min(1).optional(),
|
|
694
|
+
/** Wall-clock budget for one turn invocation, in seconds. Clamped — not
|
|
695
|
+
* rejected — to ORCHESTRATOR_ROUTE_LIMIT_SECONDS, the hosted function
|
|
696
|
+
* duration the runner runs under. */
|
|
697
|
+
invocationBudgetSeconds: z
|
|
698
|
+
.number()
|
|
699
|
+
.int()
|
|
700
|
+
.positive()
|
|
701
|
+
.default(240)
|
|
702
|
+
.transform((value) => Math.min(value, ORCHESTRATOR_ROUTE_LIMIT_SECONDS)),
|
|
703
|
+
/** Registry tool names (optionally `tool:qualifier`) whose calls suspend a
|
|
704
|
+
* turn until the operator answers. Empty means none. Entries naming an
|
|
705
|
+
* absent or unknown tool are inert by design, which is what keeps the
|
|
706
|
+
* default list stable while the registry grows. */
|
|
707
|
+
approvals: z
|
|
708
|
+
.array(
|
|
709
|
+
z
|
|
710
|
+
.string()
|
|
711
|
+
.regex(
|
|
712
|
+
ORCHESTRATOR_APPROVAL_ENTRY,
|
|
713
|
+
'approval entries look like "tool" or "tool:qualifier"',
|
|
714
|
+
),
|
|
715
|
+
)
|
|
716
|
+
.default([...defaultOrchestratorApprovals]),
|
|
717
|
+
/** Wake-filter globs a NEW session inherits. Kept schema-optional (not
|
|
718
|
+
* `.default()`) so absent ("inherit the default attention set") and `[]`
|
|
719
|
+
* ("never wake") stay distinct. Each glob must match at least one build
|
|
720
|
+
* or repository-journal event type — validated below with the shared
|
|
721
|
+
* `ab watch` compiler. */
|
|
722
|
+
wake: z.array(z.string().min(1)).optional(),
|
|
540
723
|
})
|
|
541
724
|
export type OrchestratorConfig = z.infer<typeof orchestratorSchema>
|
|
542
725
|
|
|
@@ -614,33 +797,34 @@ export const configSchema = configRootSchema.superRefine((config, ctx) => {
|
|
|
614
797
|
})
|
|
615
798
|
})
|
|
616
799
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
'
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
800
|
+
const workspaceDeclaration = BUILTIN_WORKSPACE_PROVIDER_CONFIG.get(config.workspace.provider)
|
|
801
|
+
// The parse-site refusal fires only for a declaration carrying
|
|
802
|
+
// `configRefusalMessage`; providers absent from the table are plugin-owned
|
|
803
|
+
// pass-throughs and are never refused here.
|
|
804
|
+
if (workspaceDeclaration?.configRefusalMessage !== undefined) {
|
|
805
|
+
if (Object.keys(config.workspace.config).length > 0) {
|
|
806
|
+
ctx.addIssue({
|
|
807
|
+
code: 'custom',
|
|
808
|
+
path: ['workspace', 'config'],
|
|
809
|
+
message: workspaceDeclaration.configRefusalMessage,
|
|
810
|
+
})
|
|
811
|
+
}
|
|
812
|
+
} else if (workspaceDeclaration?.configSchema !== undefined) {
|
|
813
|
+
const parsed = workspaceDeclaration.configSchema.safeParse(config.workspace.config)
|
|
629
814
|
if (!parsed.success) {
|
|
630
815
|
forwardIssues(parsed.error.issues, ctx, ['workspace', 'config'])
|
|
631
816
|
} else if (
|
|
817
|
+
workspaceDeclaration.requireRuntimeProvisioning === true &&
|
|
632
818
|
config.roles !== undefined &&
|
|
633
819
|
config.verify !== undefined &&
|
|
634
820
|
config.finalize !== undefined
|
|
635
821
|
) {
|
|
636
822
|
for (const group of effectiveRuntimeReferences(config)) {
|
|
637
|
-
if (Object.hasOwn(parsed.data
|
|
823
|
+
if (Object.hasOwn(runtimeProvisioningMap(parsed.data), group.runtime)) continue
|
|
638
824
|
ctx.addIssue({
|
|
639
825
|
code: 'custom',
|
|
640
826
|
path: ['workspace', 'config', 'runtimeProvisioning', group.runtime],
|
|
641
|
-
message:
|
|
642
|
-
`runtime ${displayName(group.runtime)} is selected by ${group.references.join(', ')} but has no sandbox provisioning; add ` +
|
|
643
|
-
`[workspace.config.runtimeProvisioning.${tomlKey(group.runtime)}] with nonblank install and preflight commands`,
|
|
827
|
+
message: runtimeProvisioningMissingMessage(group),
|
|
644
828
|
})
|
|
645
829
|
}
|
|
646
830
|
}
|
|
@@ -753,18 +937,66 @@ export const configSchema = configRootSchema.superRefine((config, ctx) => {
|
|
|
753
937
|
}
|
|
754
938
|
}
|
|
755
939
|
}
|
|
940
|
+
if (tickets.source !== 'linear' && tickets.readyAssignee !== undefined) {
|
|
941
|
+
ctx.addIssue({
|
|
942
|
+
code: 'custom',
|
|
943
|
+
path: ['tickets', 'readyAssignee'],
|
|
944
|
+
message: `[tickets].readyAssignee applies only to source = "linear" — remove it or select that source (selected source: "${tickets.source}")`,
|
|
945
|
+
})
|
|
946
|
+
}
|
|
756
947
|
// Plugin sources receive the existing ticket lifecycle/configuration fields
|
|
757
948
|
// unchanged; adapter-specific validation belongs to their factory.
|
|
758
949
|
|
|
759
950
|
// The credential-free sandbox rule (AUT-340): a forwarded variable name may
|
|
760
951
|
// never name a store, forge, ticket-provider, model, or Vercel credential.
|
|
952
|
+
// A workspace provider can declare additional forbidden names beyond the
|
|
953
|
+
// shared set. This check is deliberately builtin-only: plugins are not
|
|
954
|
+
// loaded when config is parsed, so a plugin provider's declared names cannot
|
|
955
|
+
// be seen here (AUT-536). The deferral is accepted, not an oversight — the
|
|
956
|
+
// declared extras are enforced at the registry-aware seams instead,
|
|
957
|
+
// `createWorkspaceProvider` (construction) and `validateInitReadiness`
|
|
958
|
+
// (init validation), both of which run after plugin load; no site loads
|
|
959
|
+
// plugins earlier to widen parse-time coverage.
|
|
960
|
+
// Wake globs are validated with the same compiler `ab watch` uses, over the
|
|
961
|
+
// build AND repository-journal catalogs — the wake pass scans both — so a
|
|
962
|
+
// typo'd glob fails at parse time instead of silently matching nothing —
|
|
963
|
+
// regardless of `enabled`, since a later enablement would inherit the
|
|
964
|
+
// already-broken value.
|
|
965
|
+
if (config.orchestrator.wake !== undefined) {
|
|
966
|
+
try {
|
|
967
|
+
compileEventGlobs(config.orchestrator.wake, {
|
|
968
|
+
repository: true,
|
|
969
|
+
usage: '[orchestrator].wake',
|
|
970
|
+
})
|
|
971
|
+
} catch (error) {
|
|
972
|
+
ctx.addIssue({
|
|
973
|
+
code: 'custom',
|
|
974
|
+
path: ['orchestrator', 'wake'],
|
|
975
|
+
message: error instanceof Error ? error.message : String(error),
|
|
976
|
+
})
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
761
980
|
if (config.orchestrator.enabled) {
|
|
981
|
+
// The turn runner's model is not optional when the orchestrator is on:
|
|
982
|
+
// an enabled table without one would fail at first turn start, far from
|
|
983
|
+
// the edit that caused it.
|
|
984
|
+
if (config.orchestrator.model === undefined) {
|
|
985
|
+
ctx.addIssue({
|
|
986
|
+
code: 'custom',
|
|
987
|
+
path: ['orchestrator', 'model'],
|
|
988
|
+
message:
|
|
989
|
+
'[orchestrator].enabled = true requires [orchestrator].model — name the provider-qualified model string the turn runner uses (same vocabulary as [roles], resolved through the deployment gateway credential)',
|
|
990
|
+
})
|
|
991
|
+
}
|
|
992
|
+
const providerForbidden =
|
|
993
|
+
BUILTIN_WORKSPACE_PROVIDER_CONFIG.get(config.workspace.provider)?.sandboxForbiddenEnv ?? []
|
|
762
994
|
config.orchestrator.sandbox.environmentVariables.forEach((name, index) => {
|
|
763
|
-
if (SANDBOX_FORBIDDEN_ENV.includes(name)) {
|
|
995
|
+
if (SANDBOX_FORBIDDEN_ENV.includes(name) || providerForbidden.includes(name)) {
|
|
764
996
|
ctx.addIssue({
|
|
765
997
|
code: 'custom',
|
|
766
998
|
path: ['orchestrator', 'sandbox', 'environmentVariables', index],
|
|
767
|
-
message:
|
|
999
|
+
message: sandboxForbiddenEnvMessage(name),
|
|
768
1000
|
})
|
|
769
1001
|
}
|
|
770
1002
|
})
|
|
@@ -67,6 +67,7 @@ export const allowedActorKinds: Record<EventType, readonly ActorKind[]> = {
|
|
|
67
67
|
'build.discard-requested': ['human'],
|
|
68
68
|
'build.auto-merge-requested': ['human'],
|
|
69
69
|
'build.auto-merge-cancelled': ['human'],
|
|
70
|
+
'build.auto-merge-default-observed': ['dispatcher'],
|
|
70
71
|
'build.paused': ['kernel'],
|
|
71
72
|
'build.resumed': ['kernel'],
|
|
72
73
|
'build.aborted': ['kernel', 'dispatcher'],
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared event-glob compilation (§15.3, AUT-342): the anchored glob matcher
|
|
3
|
+
* `ab watch`, `ab wait`, and the `[orchestrator].wake` configuration use.
|
|
4
|
+
* `*` matches any run of characters, `?` exactly one, everything else
|
|
5
|
+
* verbatim (so the dots inside event type names are escaped). Every glob
|
|
6
|
+
* must match at least one event type in one of the given catalogs, else a
|
|
7
|
+
* validation error naming the glob is thrown.
|
|
8
|
+
*/
|
|
9
|
+
import { EVENT_TYPES } from './payloads'
|
|
10
|
+
import { REPOSITORY_EVENT_TYPES } from './repository'
|
|
11
|
+
|
|
12
|
+
export interface CompileEventGlobsOptions {
|
|
13
|
+
/** Also accept matches in the repository-journal catalog (the `--repository`
|
|
14
|
+
* behavior of `ab watch`/`ab wait`). */
|
|
15
|
+
repository?: boolean
|
|
16
|
+
usage: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function compileEventGlobs(
|
|
20
|
+
globs: readonly string[],
|
|
21
|
+
opts: CompileEventGlobsOptions,
|
|
22
|
+
): RegExp[] {
|
|
23
|
+
return globs.map((glob) => {
|
|
24
|
+
const source =
|
|
25
|
+
'^' +
|
|
26
|
+
[...glob]
|
|
27
|
+
.map((char) => {
|
|
28
|
+
if (char === '*') return '.*'
|
|
29
|
+
if (char === '?') return '.'
|
|
30
|
+
return char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
31
|
+
})
|
|
32
|
+
.join('') +
|
|
33
|
+
'$'
|
|
34
|
+
const regex = new RegExp(source)
|
|
35
|
+
const inBuildCatalog = EVENT_TYPES.some((type) => regex.test(type))
|
|
36
|
+
const inRepositoryCatalog =
|
|
37
|
+
opts.repository === true && REPOSITORY_EVENT_TYPES.some((type) => regex.test(type))
|
|
38
|
+
if (!inBuildCatalog && !inRepositoryCatalog) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`--event "${glob}" matches no known event type${
|
|
41
|
+
opts.repository ? ' in the build or repository catalogs' : ' in the build event catalog'
|
|
42
|
+
} — ${opts.usage}`,
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
return regex
|
|
46
|
+
})
|
|
47
|
+
}
|
|
@@ -36,6 +36,23 @@ import {
|
|
|
36
36
|
|
|
37
37
|
const empty = z.strictObject({})
|
|
38
38
|
const reasonOnly = z.strictObject({ reason: z.string().optional() })
|
|
39
|
+
/** Fan-out provenance shared by the auto-merge command facts: the repository
|
|
40
|
+
* seq of the `dispatcher.auto-merge-default-set` fact that caused the command,
|
|
41
|
+
* when one did. Per-build commands omit it. `{}` stays valid, so existing
|
|
42
|
+
* producers and logs replay unchanged. */
|
|
43
|
+
const autoMergeCommand = z.strictObject({
|
|
44
|
+
defaultSeq: z.number().int().positive().optional(),
|
|
45
|
+
})
|
|
46
|
+
/** A fan-out reconciliation marker: the repository seq of the newest
|
|
47
|
+
* `dispatcher.auto-merge-default-set` fact a build has observed when that fact
|
|
48
|
+
* already matched the build's requested state. It advances the build's
|
|
49
|
+
* provenance without appending a duplicate request/cancel command, so a later
|
|
50
|
+
* per-build toggle stands until a strictly newer default fact arrives
|
|
51
|
+
* (f_28b3fba1). Dispatcher-authored: it records a reconciliation, not a
|
|
52
|
+
* human command. */
|
|
53
|
+
const autoMergeDefaultObserved = z.strictObject({
|
|
54
|
+
defaultSeq: z.number().int().positive(),
|
|
55
|
+
})
|
|
39
56
|
const round = z.number().int().positive()
|
|
40
57
|
const attempt = z.number().int().positive()
|
|
41
58
|
const dispatchStage = z.enum(['create', 'workspace', 'spec', 'comment', 'launch'])
|
|
@@ -175,6 +192,20 @@ const finalizeStepCompletedPayloadSchema = z.discriminatedUnion('ok', [
|
|
|
175
192
|
}),
|
|
176
193
|
])
|
|
177
194
|
|
|
195
|
+
/** One ticket asset revision a build froze (SPEC §6.3): the identity and
|
|
196
|
+
* shape of what `listTicketAssets` returned at the pinning moment. */
|
|
197
|
+
export const pinnedAssetSchema = z.strictObject({
|
|
198
|
+
kind: z.string().min(1),
|
|
199
|
+
name: z.string().min(1),
|
|
200
|
+
revision: z.number().int().nonnegative(),
|
|
201
|
+
layout: z.enum(['file', 'tree']),
|
|
202
|
+
size: z.number().int().nonnegative(),
|
|
203
|
+
fileCount: z.number().int().nonnegative(),
|
|
204
|
+
dirCount: z.number().int().nonnegative(),
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
export type PinnedAsset = z.infer<typeof pinnedAssetSchema>
|
|
208
|
+
|
|
178
209
|
/** Shared by `plan-review.verdict` and `code-review.verdict` (symmetric by design). */
|
|
179
210
|
const reviewVerdictPayload = z.strictObject({
|
|
180
211
|
round,
|
|
@@ -194,12 +225,20 @@ export const eventPayloadSchemas = {
|
|
|
194
225
|
/** Claim-time auto-merge intent retained across dispatch recovery until the
|
|
195
226
|
* ordinary human-authored command fact can be materialized. */
|
|
196
227
|
autoMergeRequestedBy: z.string().min(1).optional(),
|
|
228
|
+
/** Claim-time auto-merge intent provenance: the repository seq of the
|
|
229
|
+
* `dispatcher.auto-merge-default-set` fact the claim sampled. Retained for
|
|
230
|
+
* the same recovery window as `autoMergeRequestedBy`; omitted when the
|
|
231
|
+
* claim sampled no default fact. */
|
|
232
|
+
autoMergeDefaultSeq: z.number().int().positive().optional(),
|
|
197
233
|
/** Frozen at claim time. Historical logs and disabled installs omit it. */
|
|
198
234
|
pr: z
|
|
199
235
|
.strictObject({
|
|
200
236
|
imageHost: prImageHostSchema.optional(),
|
|
201
237
|
})
|
|
202
238
|
.optional(),
|
|
239
|
+
/** Ticket assets frozen at claim time. Omitted when the ticket had none
|
|
240
|
+
* and in historical logs. */
|
|
241
|
+
assets: z.array(pinnedAssetSchema).optional(),
|
|
203
242
|
}),
|
|
204
243
|
'build.completed': z.strictObject({ outcome: buildOutcomeSchema }),
|
|
205
244
|
'runner.attached': z.strictObject({
|
|
@@ -223,6 +262,10 @@ export const eventPayloadSchemas = {
|
|
|
223
262
|
path: z.string().min(1).optional(),
|
|
224
263
|
/** Dispatcher-reachable path. Remote providers intentionally omit it. */
|
|
225
264
|
localPath: z.string().min(1).optional(),
|
|
265
|
+
/** True when the provider reported a remote publication capability at
|
|
266
|
+
* provision time. Absent ⇒ local (historical journals). AUT-505 removes
|
|
267
|
+
* the legacy fallback that reads absent as the provider name. */
|
|
268
|
+
remote: z.boolean().optional(),
|
|
226
269
|
branch: z.string().min(1),
|
|
227
270
|
base: workspaceBaseSchema,
|
|
228
271
|
}),
|
|
@@ -363,8 +406,9 @@ export const eventPayloadSchemas = {
|
|
|
363
406
|
'build.resume-requested': reasonOnly,
|
|
364
407
|
'build.abort-requested': reasonOnly,
|
|
365
408
|
'build.discard-requested': empty,
|
|
366
|
-
'build.auto-merge-requested':
|
|
367
|
-
'build.auto-merge-cancelled':
|
|
409
|
+
'build.auto-merge-requested': autoMergeCommand,
|
|
410
|
+
'build.auto-merge-cancelled': autoMergeCommand,
|
|
411
|
+
'build.auto-merge-default-observed': autoMergeDefaultObserved,
|
|
368
412
|
'build.paused': empty,
|
|
369
413
|
'build.resumed': empty,
|
|
370
414
|
'build.aborted': empty,
|
|
@@ -382,6 +426,9 @@ export const eventPayloadSchemas = {
|
|
|
382
426
|
artifact: artifactRefSchema,
|
|
383
427
|
/** seq of the `escalation.raised` event that forced the revision. */
|
|
384
428
|
escalation: z.number().int().positive(),
|
|
429
|
+
/** Present (possibly empty) when the revision re-pins the build to the
|
|
430
|
+
* ticket's then-current assets; absent leaves the pinned set unchanged. */
|
|
431
|
+
assets: z.array(pinnedAssetSchema).optional(),
|
|
385
432
|
}),
|
|
386
433
|
|
|
387
434
|
// ── Sessions (every agent run is bracketed by these) ──────────────────────
|
|
@@ -552,6 +599,9 @@ export const eventPayloadSchemas = {
|
|
|
552
599
|
resolution: escalationResolutionSchema,
|
|
553
600
|
/** Exact replacement spec authorized by a `revise-spec` answer. */
|
|
554
601
|
artifact: artifactRefSchema.optional(),
|
|
602
|
+
/** The ticket's assets sampled with a revise-from-ticket authorization, so a
|
|
603
|
+
* retry re-pins the set that was authorized. Absent otherwise. */
|
|
604
|
+
assets: z.array(pinnedAssetSchema).optional(),
|
|
555
605
|
/** Absolute per-loop/current-spec review budget selected by the operator. */
|
|
556
606
|
reviewRoundCeiling: z.number().int().positive().optional(),
|
|
557
607
|
}),
|
|
@@ -76,6 +76,10 @@ const tickCountersSchema = z.strictObject({
|
|
|
76
76
|
sandboxIdleStops: z.number().int().nonnegative().optional(),
|
|
77
77
|
/** Contained idle-settlement failures this tick; retried next tick. */
|
|
78
78
|
sandboxSettleFailures: z.number().int().nonnegative().optional(),
|
|
79
|
+
/** Contained sandbox-backend construction failures this tick; re-reported
|
|
80
|
+
* every tick until the config is repaired. Optional so historical tick
|
|
81
|
+
* facts replay. */
|
|
82
|
+
orchestratorSandboxFailures: z.number().int().nonnegative().optional(),
|
|
79
83
|
harvestStarted: z.number().int().nonnegative(),
|
|
80
84
|
harvestResumed: z.number().int().nonnegative(),
|
|
81
85
|
harvestCompleted: z.number().int().nonnegative(),
|
|
@@ -264,13 +268,20 @@ export const harvestEventPayloadSchemas = {
|
|
|
264
268
|
* `processes/sandbox-state.ts`, mirroring harvest execution settlement. */
|
|
265
269
|
export const orchestratorSandboxEventPayloadSchemas = {
|
|
266
270
|
/** First use of one operator's environment for this repository, or a
|
|
267
|
-
* post-reset reprovision. Carries the full environment identity
|
|
271
|
+
* post-reset reprovision. Carries the full environment identity, plus the
|
|
272
|
+
* base branch head the fresh provision selected — the publication
|
|
273
|
+
* precondition's reference point (a publish must be a descendant of it).
|
|
274
|
+
* Optional so historical journals replay unchanged. */
|
|
268
275
|
'orchestrator.sandbox.provisioned': z.strictObject({
|
|
269
276
|
operator: z.string().min(1),
|
|
270
277
|
environmentId: z.string().min(1),
|
|
271
278
|
provider: z.string().min(1),
|
|
272
279
|
sessionId: z.string().min(1).optional(),
|
|
273
280
|
workspacePath: z.string().min(1),
|
|
281
|
+
baseSha: z
|
|
282
|
+
.string()
|
|
283
|
+
.regex(/^[0-9a-f]{40,64}$/i)
|
|
284
|
+
.optional(),
|
|
274
285
|
}),
|
|
275
286
|
/** A stopped environment resumed for a later tool call. */
|
|
276
287
|
'orchestrator.sandbox.resumed': z.strictObject({
|
|
@@ -311,6 +322,34 @@ export const orchestratorSandboxEventPayloadSchemas = {
|
|
|
311
322
|
operator: z.string().min(1),
|
|
312
323
|
environmentId: z.string().min(1),
|
|
313
324
|
}),
|
|
325
|
+
/** One PR-only publication from an operator sandbox: the operator, the
|
|
326
|
+
* delegated orchestrator session when one triggered the tool, the
|
|
327
|
+
* deterministic branch the commit was pushed to, the published commit, and
|
|
328
|
+
* the opened-or-adopted PR. Written after both the push and the forge call
|
|
329
|
+
* succeed; the push-before-fact discipline makes a retry complete the
|
|
330
|
+
* journal rather than re-push. */
|
|
331
|
+
'orchestrator.sandbox.published': z.strictObject({
|
|
332
|
+
operator: z.string().min(1),
|
|
333
|
+
environmentId: z.string().min(1),
|
|
334
|
+
branch: z.string().min(1),
|
|
335
|
+
sha: z.string().regex(/^[0-9a-f]{40,64}$/i),
|
|
336
|
+
session: z.string().min(1).optional(),
|
|
337
|
+
pr: z.strictObject({
|
|
338
|
+
number: z.number().int().positive(),
|
|
339
|
+
url: z.string().min(1),
|
|
340
|
+
headSha: z.string().min(1),
|
|
341
|
+
}),
|
|
342
|
+
}),
|
|
343
|
+
/** A refused or failed publication attempt. `stage` names where it stopped
|
|
344
|
+
* (the precondition checks, the push, or the forge PR call); `message` is
|
|
345
|
+
* the provider's or forge's text, redacted of credential values by the
|
|
346
|
+
* service. */
|
|
347
|
+
'orchestrator.sandbox.publish-failed': z.strictObject({
|
|
348
|
+
operator: z.string().min(1),
|
|
349
|
+
environmentId: z.string().min(1),
|
|
350
|
+
stage: z.enum(['checks', 'push', 'pr']),
|
|
351
|
+
message: z.string().min(1),
|
|
352
|
+
}),
|
|
314
353
|
} as const
|
|
315
354
|
|
|
316
355
|
export const dispatcherStatusEventPayloadSchemas = {
|
|
@@ -421,6 +460,16 @@ export type RepositoryEventType = keyof typeof repositoryEventPayloadSchemas
|
|
|
421
460
|
export const REPOSITORY_EVENT_TYPES = Object.keys(
|
|
422
461
|
repositoryEventPayloadSchemas,
|
|
423
462
|
) as RepositoryEventType[]
|
|
463
|
+
|
|
464
|
+
/** The repository-journal attention set, followed only under `--repository`
|
|
465
|
+
* by `ab watch` and scanned by the orchestrator wake pass. */
|
|
466
|
+
export const REPOSITORY_ATTENTION_EVENTS = [
|
|
467
|
+
'harvest.escalated',
|
|
468
|
+
'harvest.failed',
|
|
469
|
+
'dispatcher.tick-failed',
|
|
470
|
+
'dispatcher.config-rejected',
|
|
471
|
+
'dispatcher.harvest-runner-failed',
|
|
472
|
+
] as const satisfies readonly RepositoryEventType[]
|
|
424
473
|
export type RepositoryEventPayload<T extends RepositoryEventType> = z.infer<
|
|
425
474
|
(typeof repositoryEventPayloadSchemas)[T]
|
|
426
475
|
>
|
|
@@ -487,6 +536,8 @@ const allowedActorKinds: Record<RepositoryEventType, readonly ActorKind[]> = {
|
|
|
487
536
|
// `harvest.execution.released` is dispatcher-authored.
|
|
488
537
|
'orchestrator.sandbox.released': ['human', 'dispatcher'],
|
|
489
538
|
'orchestrator.sandbox.reset': ['human'],
|
|
539
|
+
'orchestrator.sandbox.published': ['human'],
|
|
540
|
+
'orchestrator.sandbox.publish-failed': ['human'],
|
|
490
541
|
'dispatcher.run-started': ['dispatcher'],
|
|
491
542
|
'dispatcher.run-stopped': ['dispatcher'],
|
|
492
543
|
'dispatcher.tick-yielded': ['dispatcher'],
|