@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
package/docs/architecture.md
CHANGED
|
@@ -54,7 +54,15 @@ Git installation never requires a separately published core package.
|
|
|
54
54
|
`packages/core` owns the CLI, kernel, adapters, shared types, dashboard
|
|
55
55
|
projection, plugin SDK, and reusable contract suites. The optional
|
|
56
56
|
`packages/hosted-store-service` workspace composes the public remote-server
|
|
57
|
-
surface with `packages/postgres-store`;
|
|
57
|
+
surface with `packages/postgres-store`; the separate optional
|
|
58
|
+
`packages/hosted-dispatcher` workspace owns the cron-driven hosted dispatcher
|
|
59
|
+
(the `/api/dispatch` route driver, the deploy-time `pack-distribution` command,
|
|
60
|
+
and the packed-distribution trace helper) and peers on both the core and the
|
|
61
|
+
store service. The optional `packages/vercel-sandbox` workspace publishes the
|
|
62
|
+
`@defrex/autobuild-vercel-sandbox` provider plugin (AUT-517): while the
|
|
63
|
+
builtin hosts the implementation, its manifest re-registers the provider name
|
|
64
|
+
over the shared capability object and the loader skips it with a notice.
|
|
65
|
+
Neither optional package enters an ordinary CLI
|
|
58
66
|
installation's runtime dependency closure. `tools/` is repository-only
|
|
59
67
|
maintainer tooling. All workspace manifests share one version because the
|
|
60
68
|
remote-store protocol requires matching client and server versions.
|
|
@@ -68,16 +76,19 @@ remote-store protocol requires matching client and server versions.
|
|
|
68
76
|
| `packages/core/src/ontology.ts` | The shared nouns — findings, verdicts, phases, refs, the canonical verify outcome | §4 |
|
|
69
77
|
| `packages/core/src/events/` | Separate build, repository, and operator-session envelopes/catalogs, frozen payload schemas, actor validation, the `via` attribution marker | §15 |
|
|
70
78
|
| `packages/core/src/harvest/` | Structured occurrence, scan packet, proposal, and ledger schemas | §12 |
|
|
71
|
-
| `packages/core/src/
|
|
79
|
+
| `packages/core/src/store/session-reducer.ts` | The operator-session reducer: status, open turn, pending approval, wake settings and cursors, and the turn list, reduced purely from the session event log | §7.1.1 |
|
|
80
|
+
| `packages/core/src/orchestrator/` | The embedded orchestrator's turn runner: the canonical-skill system prompt, durable conversation reconstruction, registry entries as AI SDK tools with the session's repository injected, gateway model resolution with the typed failure vocabulary, the approval-list configuration, and the CAS-guarded start/resume loop with budget suspension and step-boundary checkpoints | §7.1.2 |
|
|
72
81
|
| `packages/core/src/store/` | BuildStore contract spanning builds, the repository journal, and operator sessions; interface-enforced build, operator-session, and local ambient-session scope wrappers; memory, SQLite/blob, and remote HTTP adapters | §7 |
|
|
73
82
|
| `packages/core/src/store/streams/` | The stream primitive's shared core (§7.6): record/chunk types and the `ai-ui-message-stream/v1` constants, SDK-backed close-time `UIMessage[]` assembly, and the uniform bounded-wait read loop | §7.6 |
|
|
74
|
-
| `packages/hosted-store-service/` | Environment-only hosted Fetch handler, lazy PostgreSQL/blob composition, offline token binary, tests, and deployment guide | §7.2, §18 |
|
|
75
|
-
| `
|
|
83
|
+
| `packages/hosted-store-service/` | Environment-only hosted Fetch handler, lazy PostgreSQL/blob composition, offline token binary, the Next.js operator web app (`app/`, `server.ts`, `next.config.ts`, `vercel.json`), tests, and deployment guide | §7.2, §18 |
|
|
84
|
+
| `packages/hosted-dispatcher/` | The optional cron-driven hosted dispatcher: endpoint driver, deploy-time `pack-distribution` binary, packed-distribution trace helper, tests, and operator guide | §7.2, §12, §18 |
|
|
85
|
+
| `packages/vercel-sandbox/` | Published `@defrex/autobuild-vercel-sandbox` provider plugin: transitional manifest over the shared capability object with a guarded factory (implementation stays builtin-hosted until AUT-505) | §3.2.1 |
|
|
86
|
+
| `packages/hosted-store-service/server.ts`, `packages/hosted-store-service/vercel.json` | One host-neutral Bun listener used locally and by Vercel's Bun preset | §7.2 |
|
|
76
87
|
| `packages/core/src/kernel/` | Phase table, build reducer, engine; pure harvest, dispatcher-settings, dispatcher-status, and PR-attachment selectors; converge, stall detection, verify gating | §5, §7.5, §10, §12, §14, §15.4–15.5 |
|
|
77
88
|
| `packages/core/src/ports/` | TicketSource / Workspace / Forge / AgentRunner / Telemetry interfaces, adapters, and fakes; registry-aware builtin/plugin construction; eager primary/alternate runtime routing and provider-failure classification under `ports/runner/` | §3.2, §9, §13 |
|
|
78
89
|
| `packages/core/src/plugins/` | Strict versioned plugin manifests, dual-root repository/package Bun loading, owner-aware adapter registration, contract/credential metadata, and runtime-factory materialization | §3.2.1, §9 |
|
|
79
90
|
| `packages/core/src/plugin-sdk/` | The sole supported `@defrex/autobuild/plugin-sdk` barrel: port/manifest types, contract suites, and reference fakes | §3.2.1 |
|
|
80
|
-
| `packages/core/src/processes/` | build-runner and standalone child composition, durable execution config/diagnostics, dispatcher (+ janitor duty and
|
|
91
|
+
| `packages/core/src/processes/` | build-runner and standalone child composition, durable execution config/diagnostics, dispatcher (+ janitor duty, harvest trigger, and the last-step orchestrator resume/reap/wake pass, origin-mode-gated), orchestrator tick step, harvest deterministic core + runner | §3.3, §7.1.2, §12, §15.7 |
|
|
81
92
|
| `packages/core/src/cli/` and `bin/` | The `ab` CLI — the only agent↔store channel — plus the shared presentation-only durable-progress projection, init/upgrade, the Store-only dispatch frontend, its private supervised kernel entry, and `bin/ab-build-runner.ts` (one child per build) | §8, §14, §16.3 |
|
|
82
93
|
| `packages/core/src/cli/dashboard/` | `ab dispatch`'s fixed live frame: pure projection, renderer, poll cache, and deterministic image renderer | §14 |
|
|
83
94
|
| `bin/agent/ab` | Private launcher placed first on agent-session `PATH`; delegates to the canonical `bin/ab.ts` | §8.1 |
|
|
@@ -92,10 +103,15 @@ remote-store protocol requires matching client and server versions.
|
|
|
92
103
|
## Key boundaries
|
|
93
104
|
|
|
94
105
|
**Hosted store service.** `@defrex/autobuild/remote-store` is the supported protocol
|
|
95
|
-
|
|
106
|
+
export for the remote store's client half, wire schemas, and token minting; the
|
|
107
|
+
protocol servers (the remote BuildStore HTTP server, the operator API server,
|
|
108
|
+
and the hosted ticket-source server) ship in
|
|
109
|
+
`@defrex/autobuild-hosted-store-service`, which builds against core's
|
|
110
|
+
`remote-store`, `operator`, and `hosted-tickets` subpaths only. The hosted
|
|
111
|
+
workspace validates its environment,
|
|
96
112
|
serves health without touching persistence, and lazily retains one
|
|
97
113
|
`openPostgresBuildStoreFromEnv` promise per warm process. Its Fetch handler has
|
|
98
|
-
no Vercel branch;
|
|
114
|
+
no Vercel branch; the package's `server.ts` is the sole `Bun.serve()` composition point.
|
|
99
115
|
The fixed 1 MiB decoded artifact policy is enforced before backing mutation.
|
|
100
116
|
The `ab-hosted-store` binary signs admin or build/session tokens offline from
|
|
101
117
|
`AB_STORE_SECRET`; the running service exposes no mint endpoint. Machine routes
|
|
@@ -110,11 +126,120 @@ and `packages/core/src/events/sessions.ts`
|
|
|
110
126
|
are the frozen catalogs; every write passes `validateEventWrite` /
|
|
111
127
|
`validateRepositoryEventWrite` / `validateSessionEventWrite`. `packages/core/src/kernel/reducer.ts` derives all build
|
|
112
128
|
status; `packages/core/src/kernel/harvest.ts` and `packages/core/src/kernel/dispatch-settings.ts` reduce
|
|
113
|
-
the repository journal independently of each other; `packages/core/src/
|
|
129
|
+
the repository journal independently of each other; `packages/core/src/store/session-reducer.ts` reduces an operator
|
|
114
130
|
session's status, open turn, pending approval, and wake cursors from its own
|
|
115
131
|
closed catalog. No decision anywhere
|
|
116
132
|
consults a snapshot in place of the append-only log.
|
|
117
133
|
|
|
134
|
+
**Incremental reducers.** Every pure projection of an event array is a fold plus
|
|
135
|
+
a final derivation, defined once with `defineReducer` in
|
|
136
|
+
`packages/core/src/kernel/incremental.ts`: `initial()` builds an empty
|
|
137
|
+
accumulator, `advance(acc, events)` folds in only the events newer than the
|
|
138
|
+
accumulator, and `finish(acc)` derives the public state. The whole-array
|
|
139
|
+
functions (`reduceBuild`, `reduceHarvest`, `reduceSession`, and the rest) are
|
|
140
|
+
wrappers over the same fold, so results cannot diverge, and a property test
|
|
141
|
+
checks every split point of fixture and generated logs. The accumulator is plain
|
|
142
|
+
JSON, so it survives a serialize-and-parse round trip. `advance` never mutates
|
|
143
|
+
its input: it clones, then folds, so a state derived earlier is never disturbed
|
|
144
|
+
by a later advance. Each reducer publishes a `version`; bump it whenever the
|
|
145
|
+
accumulator shape or the fold semantics change, because a cached accumulator is
|
|
146
|
+
only valid for the version that wrote it. `kernel/reducer-registry.ts` lists
|
|
147
|
+
every reducer with its version, and its snapshot test fails when an accumulator
|
|
148
|
+
shape changes without a bump. Most callers still read whole logs (the terminal
|
|
149
|
+
dashboard poll advances a carried build accumulator by per-build deltas; the
|
|
150
|
+
dispatcher view below restores from stored accumulators).
|
|
151
|
+
|
|
152
|
+
**Reducer snapshots.** The `BuildStore` port can keep, per scope (one build log
|
|
153
|
+
or the repository journal) and registered reducer, that reducer's accumulator as
|
|
154
|
+
of a cursor: `getReducerSnapshot(scope, reducer, version)` and
|
|
155
|
+
`putReducerSnapshot(scope, reducer, {version, cursor, state})`
|
|
156
|
+
(`packages/core/src/store/snapshots.ts`). The log stays the only authority. The
|
|
157
|
+
store enforces the invariants: the stored cursor never decreases (a write whose
|
|
158
|
+
cursor does not advance is a no-op, so concurrent writers cannot undo each
|
|
159
|
+
other), a row of another reducer version reads as absent and is replaced once a
|
|
160
|
+
writer advances past it, and a cursor beyond the log's tail is refused and never
|
|
161
|
+
returned. Every consumer treats a missing, mismatched, ahead-of-log, malformed
|
|
162
|
+
or unreadable snapshot as a miss and replays the log, with identical results;
|
|
163
|
+
deleting all snapshots changes no observable output (the integration harness
|
|
164
|
+
audits this for every scenario at cleanup). Memory, SQLite, Postgres and the
|
|
165
|
+
remote client implement it, and the hosted store service serves it on
|
|
166
|
+
`GET|PUT /builds|repos/{id}/snapshots/{reducer}`
|
|
167
|
+
(`docs/remote-store-protocol.md`). SQLite creates the `reducer_snapshots` table
|
|
168
|
+
at open. Postgres keeps it outside the versioned schema: `reducer_snapshots` is
|
|
169
|
+
created by the idempotent `SNAPSHOT_SCHEMA_DDL` in `migratePostgres`, with no
|
|
170
|
+
marker, version or checksum change, so a deployment at the current schema keeps
|
|
171
|
+
serving across the upgrade, a new client against a database that lacks the table
|
|
172
|
+
reads every snapshot as missing and replays (SQLSTATE `42P01` is never an
|
|
173
|
+
error), and an older client never asks. Run `bun run postgres:migrate` once to
|
|
174
|
+
gain the table; until then the dispatcher and every other reader replay in full.
|
|
175
|
+
|
|
176
|
+
**Dispatcher repository view.** A dispatcher process builds its view of the
|
|
177
|
+
repository once and refreshes it by deltas
|
|
178
|
+
(`packages/core/src/processes/repo-view.ts`). `RepoViewStore` decorates the
|
|
179
|
+
`BuildStore` port: the first `refresh()` reads the journal's high-water mark
|
|
180
|
+
(`getRepoHighWater`), the bounded journal subset, the build listing and digests,
|
|
181
|
+
and the full log of every build that still has work (the same predicate the tick
|
|
182
|
+
loops use); every later `refresh()` reads only events newer than its cursors —
|
|
183
|
+
one journal delta, one delta per held build, plus the listing and digest reads
|
|
184
|
+
that discover new builds. Every tick stage reads and writes through the view, so
|
|
185
|
+
an event a stage appends is folded in at once and later stages in the same tick
|
|
186
|
+
see it with no re-read; a foreign append interleaved with ours leaves a seq gap
|
|
187
|
+
that marks the log dirty, a lost compare-and-set does the same, and any delta
|
|
188
|
+
that is not contiguous from its cursor discards the log and re-reads it in full,
|
|
189
|
+
so no event is skipped. Every state transition of one source (a build, the
|
|
190
|
+
journal) runs on that source's single promise chain — snapshot and delta reads,
|
|
191
|
+
window opening and widening, the fold after an own append, a lost
|
|
192
|
+
compare-and-set's invalidation — and readers queue on it too, so overlapping
|
|
193
|
+
readers join the repair in flight instead of observing half-applied state.
|
|
194
|
+
Decisions that deliberately re-read at the last moment
|
|
195
|
+
(`refreshBuild`) take an explicit delta. Cursor-bearing readers such as the
|
|
196
|
+
orchestrator wake pass get per-source windows that hold only events above the
|
|
197
|
+
lowest session cursor. A `--once` invocation builds the view cold and reads each
|
|
198
|
+
log once; a long-running dispatcher pays that replay at its first tick and not
|
|
199
|
+
again.
|
|
200
|
+
|
|
201
|
+
The view restores from reducer snapshots on cold start. The journal comes from
|
|
202
|
+
the `journalView` reducer (the journal pruned on arrival: durable types plus the
|
|
203
|
+
tail from the latest `dispatcher.run-started`); each work build comes from one
|
|
204
|
+
`work` snapshot row — the `build`, `logIndex`, `openExecution`,
|
|
205
|
+
`openBuildWorkspace`, `publicationState`, `lastExecutionOutcome`, `buildDigest`,
|
|
206
|
+
`dashboardFacts` and `currentDeferralObservation` accumulators at a single
|
|
207
|
+
cursor. One row is one atomic write on every adapter, so a reader in another
|
|
208
|
+
process sees the old bundle or the new, never a mix; the bundle version is
|
|
209
|
+
derived from the component reducer versions, so a stale bundle misses and the
|
|
210
|
+
build is replayed once. The view
|
|
211
|
+
reads only the events newer than that cursor and holds, for a snapshot-backed
|
|
212
|
+
build, just that delta plus the reduced state of everything before it, so a tick
|
|
213
|
+
with no new events reads zero event rows (apart from the discovery listing and
|
|
214
|
+
digests) and one with M new events reads exactly M. Tick stages that run for every
|
|
215
|
+
build each tick take `buildFacts(slug)` (state, log index, open execution, open
|
|
216
|
+
workspace, publication view) instead of the event array, and read raw history
|
|
217
|
+
only on the branches that act on it. `persistSnapshots()` writes the advanced
|
|
218
|
+
state back at the end of each tick and on shutdown — best effort, throttled in a
|
|
219
|
+
resident dispatcher, always run by a cold single-tick process — and never writes
|
|
220
|
+
a cursor that was not read from the log.
|
|
221
|
+
|
|
222
|
+
**Operator reads.** The web dashboard, the repository and Harvest status
|
|
223
|
+
routes, the operator build listings, and the `ab watch`/`ab wait` discovery
|
|
224
|
+
passes derive from that same view instead of replaying the log
|
|
225
|
+
(`packages/core/src/operator/read-view.ts`). A dashboard row renders from the
|
|
226
|
+
`BuildState` plus the `dashboardFacts` reducer
|
|
227
|
+
(`packages/core/src/cli/dashboard/facts.ts`: queued-row dispatch text, session
|
|
228
|
+
brackets, phase intervals, pause time, and the per-round loop-output seqs) and
|
|
229
|
+
the auto-merge deferral ledger; `projectBuild(…, events)` is the same projection
|
|
230
|
+
over the whole-array reduction, so the snapshot and replay paths cannot differ.
|
|
231
|
+
Within one serving process, passes are single-flight per repository: requests
|
|
232
|
+
arriving while a pass runs join the one trailing pass, which restores from the
|
|
233
|
+
snapshot the previous pass persisted. A poll at idle therefore reads no event
|
|
234
|
+
rows beyond snapshot lookups and build discovery, and a poll after M new events
|
|
235
|
+
reads those M rows once, however many tabs or watchers the process serves.
|
|
236
|
+
Serving processes do not coordinate: with P of them, each may read the same M
|
|
237
|
+
rows once, so duplicate reads are bounded by P and never by history, tabs, or
|
|
238
|
+
watchers. A settled build listed under `--all` is restored from the snapshot the
|
|
239
|
+
dispatcher left at settlement, or read once and then persisted. The first
|
|
240
|
+
operator poll after upgrading replays each work build once, because the stored
|
|
241
|
+
bundle layout changed.
|
|
242
|
+
|
|
118
243
|
**Session Store authority.** `packages/core/src/cli/binary.ts` validates a complete build or
|
|
119
244
|
Harvest ambient tuple before opening its phase Store through
|
|
120
245
|
`packages/core/src/cli/store-opening.ts`. `packages/core/src/cli/env.ts` also classifies optional ambient
|
|
@@ -503,9 +628,16 @@ It follows one dispatch run's repository facts incrementally through
|
|
|
503
628
|
`packages/core/src/kernel/dispatch-status.ts`, retaining only low-volume settings/Harvest
|
|
504
629
|
facts for their replay reducers, validates/caches its effective-config artifact,
|
|
505
630
|
and polls build streams independently while elapsed paints continue from cached
|
|
506
|
-
intervals. The same polling path
|
|
507
|
-
|
|
508
|
-
|
|
631
|
+
intervals. The same polling path samples the header's current count from build
|
|
632
|
+
digests plus the repository journal — one journal read plus one repo-scoped
|
|
633
|
+
digest read, plus a journal-record probe — paired with the effective config's
|
|
634
|
+
`policy.harvestThreshold`. A journal record that does not yet exist reads as an
|
|
635
|
+
empty journal: the sample, the operator query, the dispatcher's
|
|
636
|
+
repository-wide journal reads (controls, settlement stages, and ready
|
|
637
|
+
scans), and the operator sandbox service's journal reads all go through
|
|
638
|
+
the same shared missing-record journal read (the AUT-524 helper in
|
|
639
|
+
`packages/core/src/processes/harvest.ts`), which writes nothing (no
|
|
640
|
+
`ensureRepo` from a display path). That sample
|
|
509
641
|
is process-local presentation state: a failed refresh preserves the last
|
|
510
642
|
successful value and adds a local diagnostic, while an initial failure delays
|
|
511
643
|
the complete frame rather than inventing zero. No repository fact transports
|
|
@@ -579,8 +711,8 @@ bun typecheck # tsc --noEmit
|
|
|
579
711
|
For dashboard presentation work, run the repository-only hot CLI:
|
|
580
712
|
|
|
581
713
|
```sh
|
|
582
|
-
bun run dev -- dispatch
|
|
583
|
-
# Generic form: bun run dev -- <ab arguments>
|
|
714
|
+
bun run dev:cli -- dispatch
|
|
715
|
+
# Generic form: bun run dev:cli -- <ab arguments>
|
|
584
716
|
```
|
|
585
717
|
|
|
586
718
|
Bun keeps the original CLI promise and its `DispatchLoop` alive while hot
|
|
@@ -596,7 +728,7 @@ installed `ab` binary remains the non-watching production entry.
|
|
|
596
728
|
The seams are the contract. Five reusable contract families run the same
|
|
597
729
|
behavioral assertions against every implementation:
|
|
598
730
|
|
|
599
|
-
- `packages/core/src/store/contract.ts` — `BuildStore` and `BlobStore`, including the streams block (§7.6) every adapter runs;
|
|
731
|
+
- `packages/core/src/store/contract.ts` — `BuildStore` and `BlobStore`, including the streams block (§7.6) and the ticket-asset block (§7.1.3) every adapter runs;
|
|
600
732
|
- `packages/core/src/ports/tickets/contract.ts` — `TicketSource`;
|
|
601
733
|
- `packages/core/src/ports/workspace/contract.ts` — `WorkspaceProvider`;
|
|
602
734
|
- `packages/core/src/ports/forge/contract.ts` — `Forge`, including idempotent PR close and
|
|
@@ -618,6 +750,19 @@ A normal `bun test` runs the memory/fake/local registrations, including a fake
|
|
|
618
750
|
selected through the plugin ticket-source registry, the real filesystem and
|
|
619
751
|
local-git adapters, the injected Claude and Codex CLI subprocess contracts,
|
|
620
752
|
and the injected Pi CLI/RPC contract.
|
|
753
|
+
Ticket assets (SPEC §7.1.3) are files attached to a ticket and kept in the
|
|
754
|
+
BuildStore, never in the ticket source. `packages/core/src/store/ticket-assets.ts`
|
|
755
|
+
holds the types, the limits, and the one validator every adapter calls;
|
|
756
|
+
`packages/core/src/cli/ticket-assets.ts` holds the filesystem walk, the download
|
|
757
|
+
writer, and the note builder; `ab ticket attach`, `asset get`, and `asset rm`
|
|
758
|
+
live in `packages/core/src/cli/ticket.ts`. The only thing a ticket source sees is
|
|
759
|
+
a note through `comment()`, so no source or plugin protocol changed.
|
|
760
|
+
A claim freezes the ticket's current assets into the build (SPEC §6.3):
|
|
761
|
+
`build.created.assets` (and, when a spec is revised from the ticket,
|
|
762
|
+
`spec.revised.assets`) record the pinned revisions, `state.pinnedAssets` in the
|
|
763
|
+
reducer projects them, `getPinnedTicketAsset` reads them back by pinned revision,
|
|
764
|
+
and `ab context` writes them under `.ab/assets/`.
|
|
765
|
+
|
|
621
766
|
Both `ab dispatch` and sessionless `ab ticket` load the repository's plugins
|
|
622
767
|
before selecting their TicketSource; dispatch passes that one adapter instance
|
|
623
768
|
through readiness, dependency, harvest, and completion paths. The Linear,
|
|
@@ -626,6 +771,13 @@ but reported as skipped: live provider access requires credentials/resources
|
|
|
626
771
|
and an explicit opt-in. When
|
|
627
772
|
adding an adapter, start from its contract suite, not only the interface.
|
|
628
773
|
|
|
774
|
+
The two remote clients get client-only unit coverage in core itself:
|
|
775
|
+
`RemoteBuildStore` (`store/remote/client.test.ts`, which also pins scoped-token
|
|
776
|
+
minting/verification) and `HostedTicketSource` (`ports/tickets/remote.test.ts`)
|
|
777
|
+
run against an injected `fetchFn` with responses built from the protocol's wire
|
|
778
|
+
schemas. The full over-the-wire seam suites (client → HTTP → server) run in
|
|
779
|
+
`packages/hosted-store-service` in the same `bun test`.
|
|
780
|
+
|
|
629
781
|
To run the Linear contract manually against a destructive scratch target:
|
|
630
782
|
|
|
631
783
|
```sh
|
|
Binary file
|
package/docs/configuration.md
CHANGED
|
@@ -23,7 +23,11 @@ environment. They are published to npm as separate packages alongside the
|
|
|
23
23
|
PostgreSQL, or blob-provider dependencies; a project that embeds the adapter
|
|
24
24
|
or the service adds the package it needs (`bun add
|
|
25
25
|
@defrex/autobuild-postgres-store` or `bun add
|
|
26
|
-
@defrex/autobuild-hosted-store-service`). The
|
|
26
|
+
@defrex/autobuild-hosted-store-service`). The cron-driven hosted dispatcher is
|
|
27
|
+
separately published as `@defrex/autobuild-hosted-dispatcher`; a deployment
|
|
28
|
+
opts into building by installing that package, and its `AB_DISPATCHER_*`
|
|
29
|
+
variables are documented in the [hosted-dispatcher runbook](hosted-dispatcher.md),
|
|
30
|
+
not here. The deployable web application still
|
|
27
31
|
runs from a release checkout: choose the compatible tag shown in
|
|
28
32
|
[GitHub Releases](https://github.com/defrex/autobuild/releases), clone that exact
|
|
29
33
|
revision into a dedicated checkout, and run the idempotent migration there:
|
|
@@ -37,7 +41,9 @@ DATABASE_URL=postgres://… bun run postgres:migrate
|
|
|
37
41
|
|
|
38
42
|
Replace `v0.6.0` with the selected release tag. Schema diagnostics' root
|
|
39
43
|
`postgres:migrate` script is relative to that pinned checkout. A hosted build
|
|
40
|
-
can run the same step itself: `bun run deploy:build`
|
|
44
|
+
can run the same step itself: `bun run deploy:build` — run from the hosted
|
|
45
|
+
store service package directory (`packages/hosted-store-service`), the Next.js
|
|
46
|
+
project directory — migrates against the
|
|
41
47
|
deployment's database URL, logs the host it prepared, and then builds the web
|
|
42
48
|
application, failing the build when no database URL is configured or the
|
|
43
49
|
schema is incompatible. The database
|
|
@@ -146,6 +152,33 @@ snapshot. Raising capacity can fill the new slots immediately; lowering it
|
|
|
146
152
|
prevents new claims without terminating builds already above the limit. `ab
|
|
147
153
|
dispatch --once` performs one pass and does not watch or reload.
|
|
148
154
|
|
|
155
|
+
The sections `autobuild.toml` delivers to a build fall into three ownership
|
|
156
|
+
classes. **Build-owned** sections define the pipeline a build runs — `[verify]`
|
|
157
|
+
with `[verify.<step>]`, `[finalize]` with `[finalize.<step>]`, `[commands]`,
|
|
158
|
+
and `[workspace]` (provider and config, including `provisioning` and
|
|
159
|
+
`runtimeProvisioning`). They are pinned to the build's own branch: the
|
|
160
|
+
dispatcher reads them at the build's recorded base commit, or at its branch
|
|
161
|
+
head once the build has published commits, and records the exact commit in the
|
|
162
|
+
effective-config artifact metadata. In checkout mode the worktree read is the
|
|
163
|
+
exception: when it cannot be attributed to the branch-head commit — because
|
|
164
|
+
the worktree file has uncommitted edits, because the clean/dirty comparison
|
|
165
|
+
could not be performed (non-git workspace or a transient git error), or because
|
|
166
|
+
the branch head never carried the file — the deposit records the source
|
|
167
|
+
`worktree-dirty` with no commit. The build still runs the worktree bytes, but
|
|
168
|
+
provenance declines to attribute them to any commit. **Deployment-owned** sections — `[roles]`
|
|
169
|
+
(runtime, model, args, alternates, per-role `sessionBudgetSeconds`) and
|
|
170
|
+
`[policy]` — keep flowing live. **Dispatcher-owned** sections (`baseBranch`,
|
|
171
|
+
`capacity`, `forge`, `plugins`, `pr`, `tickets`, `orchestrator`) configure the
|
|
172
|
+
dispatcher itself and are never interpreted by the build runner.
|
|
173
|
+
|
|
174
|
+
Consequently, an accepted base-branch reload re-deposits only the
|
|
175
|
+
deployment-owned sections to active builds; it does not retarget an in-flight
|
|
176
|
+
build's verify universe, finalize steps, commands, or provisioning. A build
|
|
177
|
+
that publishes a pipeline change to its own branch picks it up at its next
|
|
178
|
+
launch, so config changes still flow through the pipeline for the build that
|
|
179
|
+
carries them. In-flight environments are never re-provisioned: a fresh build
|
|
180
|
+
from the new base is the path for a changed provisioning definition.
|
|
181
|
+
|
|
149
182
|
Every accepted revision is deposited verbatim as a `dispatcher-config`
|
|
150
183
|
repository artifact and referenced by a `dispatcher.config-reloaded` repository
|
|
151
184
|
event. The dashboard reports the reload and reprojects values such as capacity;
|
|
@@ -164,11 +197,12 @@ These fields hot-reload:
|
|
|
164
197
|
| `baseBranch` | Next dispatch or base fallback decision; existing `build.created` facts remain immutable. |
|
|
165
198
|
| `capacity` | Next dispatcher tick. |
|
|
166
199
|
| `[pr]` | Next build creation. |
|
|
167
|
-
| `[commands]` | Next setup, verify check, or finalize check selected. |
|
|
168
|
-
| `[verify]` and `[finalize]` | Next engine step selection. An approved plan's stored verify selection remains authoritative. |
|
|
169
|
-
| `[
|
|
170
|
-
| `[
|
|
171
|
-
| `
|
|
200
|
+
| `[commands]` | Next setup, verify check, or finalize check selected for a build whose OWN branch carries it. A base-branch reload does not retarget an in-flight build. |
|
|
201
|
+
| `[verify]` and `[finalize]` | Next engine step selection for a build whose OWN branch carries it. An approved plan's stored verify selection remains authoritative, and a base-branch reload does not retarget an in-flight build. |
|
|
202
|
+
| `[workspace]` provider and config (including `provisioning`) | Next launch of a build whose OWN branch carries it. A base-branch reload never re-provisions an in-flight build. |
|
|
203
|
+
| `[roles]` | Next agent invocation for that role. Runtime, model, args, alternates, and the session budget are captured together and delivered live. |
|
|
204
|
+
| `[policy]` | Next policy, convergence, or agent-session boundary, delivered live. A running session keeps its captured budget. |
|
|
205
|
+
| `tickets.readyLabels`, `tickets.readyState`, `tickets.readyAssignee` | Next ready-ticket scan. |
|
|
172
206
|
| `tickets.triageState`, `tickets.proposalState` | Next dispatcher handback or harvest filing boundary. |
|
|
173
207
|
|
|
174
208
|
Adapter selectors and factory inputs are constructed once and require a
|
|
@@ -239,6 +273,40 @@ earlier configured plugins are reserved, and declaration order never permits
|
|
|
239
273
|
shadowing. A collision between distinct plugin declarations continues to name
|
|
240
274
|
the conflicting adapter and both owners.
|
|
241
275
|
|
|
276
|
+
Two transitional downgrade paths announce themselves instead of failing
|
|
277
|
+
startup (AUT-517). First, while the builtin hosts the `vercel-sandbox`
|
|
278
|
+
implementation, a configured plugin that re-registers that workspace-provider
|
|
279
|
+
name — `@defrex/autobuild-vercel-sandbox` is exactly such a plugin — is
|
|
280
|
+
skipped with a one-line notice and the builtin keeps serving the provider:
|
|
281
|
+
|
|
282
|
+
```text
|
|
283
|
+
plugin "autobuild-vercel-sandbox" declares only builtin workspace-provider registration(s) "vercel-sandbox"; skipping it — the builtin keeps serving the provider
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The skip applies only when the manifest declares at least one registration,
|
|
287
|
+
only workspace-provider registrations, and every declared name collides with
|
|
288
|
+
a builtin registration; any other collision still throws. The rule keys on
|
|
289
|
+
builtin ownership, so it retires itself when the builtin is removed and a
|
|
290
|
+
second plugin registering the name collides and fails startup as always.
|
|
291
|
+
`ab plugin list` and `ab plugin doctor` show skipped modules as `SKIP` lines
|
|
292
|
+
and exit 0. Second, guest processes (build and harvest children,
|
|
293
|
+
`ab init`'s guest readiness probe, and scoped phase CLI commands) tolerate a
|
|
294
|
+
configured bare package specifier they cannot resolve — guests never
|
|
295
|
+
construct workspace providers, so a provider plugin they need not load is
|
|
296
|
+
skipped with a notice instead of failing the process:
|
|
297
|
+
|
|
298
|
+
```text
|
|
299
|
+
plugin module "@defrex/autobuild-vercel-sandbox" could not be resolved from repository "…" or installation "…"; guests never construct workspace providers, so the provider plugin is skipped here
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Repository-path specifiers and every post-resolution failure (evaluation,
|
|
303
|
+
manifest, collision) stay fail-closed everywhere, including guests. The guest
|
|
304
|
+
readiness probe surfaces a resolution-stage skip in its `configuration and
|
|
305
|
+
plugins` check detail: the detail names the missing module (the skip notice
|
|
306
|
+
quoted above) and the remediation — add the package to the repository's
|
|
307
|
+
dependencies so it resolves from the repository install during
|
|
308
|
+
`commands.setup`.
|
|
309
|
+
|
|
242
310
|
Plugin authors import the stable surface from `@defrex/autobuild/plugin-sdk`, normally
|
|
243
311
|
with `import type`, and can develop against Autobuild as a dev/peer dependency
|
|
244
312
|
without adding a runtime Autobuild dependency to the plugin. That entry point
|
|
@@ -255,6 +323,24 @@ require `^1.3.0`. API 1.4 adds the optional `Ticket.creationKey` projection: a
|
|
|
255
323
|
ticket-source plugin should return its stable external create/adoption key from
|
|
256
324
|
create, get, and ready listings so dispatch can correlate Autobuild's durable
|
|
257
325
|
in-flight creations. Legacy tickets may omit it and remain dispatchable.
|
|
326
|
+
API 1.6 adds workspace-provider capability declarations: a
|
|
327
|
+
`workspaceProviders` registration may carry a `capabilities` object declaring
|
|
328
|
+
its `[workspace.config]` schema (or an outright refusal), required runtime
|
|
329
|
+
provisioning, supported forges, required environment groups (with per-site
|
|
330
|
+
messages), variables that must exist in the launcher process environment,
|
|
331
|
+
additional operator-sandbox forbidden variable names, HTTPS/token store
|
|
332
|
+
requirements, an origin validator, and a remote readiness validation for
|
|
333
|
+
`ab init --validate`. The host enforces every declared capability at its seam
|
|
334
|
+
without naming the provider; required environment belongs on
|
|
335
|
+
`capabilities.requiredEnv` (a descriptor-level `requiredEnv` is rejected at
|
|
336
|
+
manifest parse), and each `capabilities.requiredEnv` group must declare at
|
|
337
|
+
least one of `dispatchMessage` / `validationMessage` — a group with neither is
|
|
338
|
+
rejected at manifest parse, because the host would silently skip it at both
|
|
339
|
+
enforcement sites. API 1.7 extends the workspace-provider factory context with
|
|
340
|
+
the host-derived seams: `storeRef` and `storeToken` (guaranteed present under
|
|
341
|
+
`capabilities.storeRequirements`), `runtimeReferences`, and the checkout-less
|
|
342
|
+
`origin` and `remoteBranchHead` readers, all optional and ignored by factories
|
|
343
|
+
that do not read them. A plugin reading the new seams should require `^1.7.0`.
|
|
258
344
|
|
|
259
345
|
```ts
|
|
260
346
|
import type { AutobuildPluginManifest } from '@defrex/autobuild/plugin-sdk'
|
|
@@ -438,7 +524,7 @@ gitPasswordEnv = "AB_GIT_READ_TOKEN"
|
|
|
438
524
|
|
|
439
525
|
[workspace.config.runtimeProvisioning.pi]
|
|
440
526
|
install = "npm install --global --ignore-scripts @earendil-works/pi-coding-agent@0.84.4"
|
|
441
|
-
preflight = "test \"$(pi --version)\" = \"0.84.4\""
|
|
527
|
+
preflight = "test \"$(pi --version)\" = \"0.84.4\" && pi update --models"
|
|
442
528
|
```
|
|
443
529
|
|
|
444
530
|
| Vercel field | Default | Constraints |
|
|
@@ -518,13 +604,14 @@ checkout-only hooks such as Husky are not provisioning dependencies. A Bun
|
|
|
518
604
|
install or verification failure identifies the configured image and expected
|
|
519
605
|
universal-image capabilities, deletes the partial environment, and leaves no
|
|
520
606
|
readiness marker. A retry also deletes any named sandbox without that marker
|
|
521
|
-
instead of adopting partial setup. The installed distribution's version
|
|
522
|
-
recorded in the guest's
|
|
523
|
-
reused sandbox whose marker
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
delivery path and the hosted
|
|
607
|
+
instead of adopting partial setup. The installed distribution's identity — package version and remote-store
|
|
608
|
+
protocol version — is recorded in the guest's
|
|
609
|
+
`/opt/autobuild/.distribution-version` marker; a reused sandbox whose marker
|
|
610
|
+
no longer matches the identity the current system would deliver is refreshed
|
|
611
|
+
by reinstalling the archive before reuse, so an upgraded dispatcher retrofits
|
|
612
|
+
its persistent guests (see "Guest distribution version selection and delivery"
|
|
613
|
+
in docs/setup.md for the release-and-upgrade delivery path and the hosted
|
|
614
|
+
store's version-lockstep gate). Immediately
|
|
528
615
|
before every build-child launch, the adapter verifies the
|
|
529
616
|
provisioned absolute Bun executable and every referenced runtime again. A
|
|
530
617
|
failure names the runtime, selecting role/alternate, stage, and provisioning
|
|
@@ -884,7 +971,7 @@ session starts, with a copyable table and all registered runtime names.
|
|
|
884
971
|
| `args` | inherited; otherwise `[]` | optional array of nonempty strings; `[]` allowed | Ordered extra CLI argv tokens applied to every phase and tool-free one-shot invocation. A supplied list replaces, rather than unions with, the inherited list. |
|
|
885
972
|
| `extensions` | no effect | deprecated optional array of nonempty strings | Compatibility-only field. Dispatch warns for each declaring role; migrate explicit runtime options to `args`. |
|
|
886
973
|
| `sessionBudgetSeconds` | inherited; otherwise `[policy].sessionBudgetSeconds` | optional positive integer | Wall-clock budget for each build phase session routed through this logical role. One budget covers the primary and every alternate target. |
|
|
887
|
-
| `alternates` | inherited; otherwise `[]` | optional ordered array of strict `{ runtime?, model?, args?, extensions? }` entries; `[]` allowed | Failure-triggered execution targets. A role's list replaces the inherited list wholesale; each entry overlays that role's effective primary axes. `extensions` is accepted there only as the same deprecated no-op. |
|
|
974
|
+
| `alternates` | inherited; otherwise `[]` | optional ordered array of strict `{ runtime?, model?, args?, extensions? }` entries; `[]` allowed | Failure-triggered execution targets (provider failures and a runtime whose CLI is not installed). A role's list replaces the inherited list wholesale; each entry overlays that role's effective primary axes. `extensions` is accepted there only as the same deprecated no-op. |
|
|
888
975
|
|
|
889
976
|
<!-- config-fragment:roles -->
|
|
890
977
|
```toml
|
|
@@ -984,7 +1071,7 @@ useful; mixed models are not a configuration inconsistency.
|
|
|
984
1071
|
Each session attempt starts with its role's primary. Overload, rate limits, 5xx,
|
|
985
1072
|
timeout, transport, unknown provider failures, and quota/usage/billing
|
|
986
1073
|
exhaustion try alternates in declaration order inside that same phase attempt.
|
|
987
|
-
Authentication, permission, and local runtime-configuration failures do not.
|
|
1074
|
+
Authentication, permission, and local runtime-configuration failures do not, with one exception: a runtime whose CLI executable is not installed is skipped like any other eligible failure, so the phase runs on the first declared target whose CLI is present. An unsupported installed version is still a stopping configuration failure. If no target's CLI is installed, the chain ends in one non-retried `configuration` failure carrying the last target's missing-CLI message. The remote provisioning preflight stays fail-closed: a missing runtime there is a provisioning defect.
|
|
988
1075
|
Each target gets a separate session and transcript; a continuation that moves
|
|
989
1076
|
to another target starts fresh from durable context and cannot inherit the
|
|
990
1077
|
failed provider's conversation. Selection is not sticky: the next phase, next
|
|
@@ -1080,6 +1167,7 @@ state. Within a present table, `source` and `readyState` are required.
|
|
|
1080
1167
|
| `source` | — | required nonblank builtin or plugin registration name | Select the ticket adapter after configured plugins load. |
|
|
1081
1168
|
| `readyLabels` | source-aware | optional array of nonempty strings; `[]` allowed | Require every listed label in addition to the state gate. |
|
|
1082
1169
|
| `readyState` | — | required nonblank string | The one workflow state eligible for dispatch. |
|
|
1170
|
+
| `readyAssignee` | — | optional; `"me"` or an email address; Linear only (rejected for file, hosted, and plugin sources) | Require the ticket to be assigned to one Linear user, in addition to the state and label gates. |
|
|
1083
1171
|
| `teamKey` | — | required for Linear and hosted; forbidden for file; optional for plugins | Backend team key such as `"ENG"`, or an existing plugin config field. |
|
|
1084
1172
|
| `claimedState` | `"In Progress"` for direct Linear; hosted backend default otherwise | optional nonempty string; forbidden for file; allowed for plugins | Workflow state entered when a ticket is claimed. |
|
|
1085
1173
|
| `createState` | provider default | optional nonempty string | Default state for newly created tickets when `ab ticket create` omits `--state`. |
|
|
@@ -1087,13 +1175,33 @@ state. Within a present table, `source` and `readyState` are required.
|
|
|
1087
1175
|
| `proposalState` | the resolved `triageState` | optional nonempty string | State harvest files its synthesized proposals into. Setting it to `readyState` waives the human grooming gate, not dependency eligibility. |
|
|
1088
1176
|
| `dir` | file: selected local state root's `tickets/`; plugin: omitted | optional nonempty path; forbidden for Linear; allowed for plugins | Root containing file-source state directories, or an existing plugin config field. Relative file paths resolve from the repository. |
|
|
1089
1177
|
|
|
1178
|
+
`readyAssignee = "me"` resolves to the Linear user who owns `LINEAR_API_KEY`,
|
|
1179
|
+
so one committed value makes each person's local dispatcher take only the work
|
|
1180
|
+
assigned to them; an email names a specific user, for a shared or bot key. The
|
|
1181
|
+
gate is conjunctive with `readyState` and `readyLabels`, and a ticket with no
|
|
1182
|
+
assignee is never ready while it is set. It applies to every ready surface
|
|
1183
|
+
(dispatch, the queued count, and `ab ticket list` without filters); blockers
|
|
1184
|
+
still prevent claim. It is empty or blank, or neither `"me"` nor an email,
|
|
1185
|
+
fails validation. `ab init --validate` prints the user the gate resolves to and
|
|
1186
|
+
fails when an email matches no workspace user. The field reloads without a
|
|
1187
|
+
restart.
|
|
1188
|
+
|
|
1090
1189
|
When `readyLabels` is absent, direct Linear uses `["autobuild"]`; hosted, file,
|
|
1091
1190
|
and plugin sources use `[]`, meaning no host-imposed label gate. An explicit value always
|
|
1092
1191
|
wins. A nonempty list is
|
|
1093
1192
|
conjunctive: every configured label must be present. `readyState` remains
|
|
1094
1193
|
mandatory regardless of labels. Linear compares state and label names exactly
|
|
1095
|
-
and case-sensitively. The file source
|
|
1096
|
-
|
|
1194
|
+
and case-sensitively. The file source matches state names case-insensitively against the directories
|
|
1195
|
+
under the tracker root: the four lifecycle directories (`triage/`, `ready/`,
|
|
1196
|
+
`doing/`, `done/`, always present) plus any directory the repository adds, such
|
|
1197
|
+
as `icebox/`. A name matching no directory is an error listing those that
|
|
1198
|
+
exist, so creating a state stays an explicit act: `ab ticket state create <name>
|
|
1199
|
+
[--about <text>]` makes one, and `ab ticket states` lists every state with its
|
|
1200
|
+
roles, ticket count, and purpose. A purpose is stored as a one-line `.about` file
|
|
1201
|
+
inside the state directory, so it travels with a git-tracked `dir` and is never
|
|
1202
|
+
read as a ticket. `createState`, `readyState`,
|
|
1203
|
+
`triageState`, and `proposalState` may each name any existing directory;
|
|
1204
|
+
`ab ticket list` with no filter still lists only `readyState`.
|
|
1097
1205
|
|
|
1098
1206
|
Source-specific validation is strict:
|
|
1099
1207
|
|
|
@@ -1203,14 +1311,19 @@ Set `COMPANY_TICKET_TOKEN` in the environment, never in this table.
|
|
|
1203
1311
|
|
|
1204
1312
|
## `[orchestrator]`
|
|
1205
1313
|
|
|
1206
|
-
Gates the operator-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
table is closed to unknown keys and
|
|
1314
|
+
Gates the operator-agent feature (AUT-340, AUT-342): the per-operator sandbox
|
|
1315
|
+
environments the `sandbox.*` registry tools drive, and — when the embedded
|
|
1316
|
+
orchestrator is enabled — the turn runner that answers operator sessions and
|
|
1317
|
+
wakes on build events. The table is closed to unknown keys and
|
|
1318
|
+
restart-classified as a whole.
|
|
1210
1319
|
|
|
1211
1320
|
| Field | Default | Constraints | Purpose |
|
|
1212
1321
|
|---|---:|---|---|
|
|
1213
|
-
| `enabled` | `false` | boolean | Master gate. With the table absent or `enabled = false`, the sandbox tools are absent from every binding
|
|
1322
|
+
| `enabled` | `false` | boolean | Master gate. With the table absent or `enabled = false`, the sandbox tools are absent from every binding, no environment is ever provisioned, and no dispatcher tick or message post ever runs orchestrator code — a local dispatcher is inert even when enabled, because the turn runner exists only in the hosted deployment. |
|
|
1323
|
+
| `model` | — | nonblank string, required when `enabled = true` | Provider-qualified model string for the turn runner, in the same vocabulary as `[roles]` model strings (a leading `vercel-ai-gateway/` prefix is accepted and stripped). Resolved through the deployment's gateway credential. |
|
|
1324
|
+
| `invocationBudgetSeconds` | `240` | positive integer, clamped to 300 | Wall-clock budget for one turn invocation. A larger value is clamped — not rejected — to the 300-second hosted function limit the runner executes under (`ORCHESTRATOR_ROUTE_LIMIT_SECONDS`, the same number the operator route pins as `maxDuration`). |
|
|
1325
|
+
| `approvals` | see below | array of `tool` or `tool:qualifier` strings | Registry tool names whose calls suspend a turn until the operator answers. Default: `["builds.control:abort", "builds.control:discard", "builds.answer:revise-spec", "sandbox.publish", "tickets.move:ready"]`. Empty (`[]`) means none — autonomous operation. An entry naming an absent or unknown tool is **inert by design**, never a config error: the list stays stable while the registry grows, and a tool's removal cannot break parsing. A `tool:qualifier` entry applies to the named discriminator value only (for example `tickets.move:ready` approves moving a ticket to `ready`); for a tool without a discriminator, only the bare form matches. |
|
|
1326
|
+
| `wake` | inherits the default | array of event-type globs | The attention events a **new** session wakes for. Kept schema-optional so absent (inherit the default attention set — the build events `ab watch` filters on by default plus the repository-journal events `ab watch --repository` follows) and `[]` (never wake; message-only) stay distinct. Each glob must match at least one build or repository-journal event type, validated with the same compiler `ab watch` uses. Existing sessions never retroactively inherit; an explicit `PUT .../wake` always wins. |
|
|
1214
1327
|
| `sandbox` | — | strict subtable; absence reads as the defaults | Sandbox behavior knobs. |
|
|
1215
1328
|
|
|
1216
1329
|
`[orchestrator.sandbox]` fields:
|
|
@@ -1225,7 +1338,16 @@ forge, ticket-provider, or model credential, and naming any of `AB_STORE`,
|
|
|
1225
1338
|
`AB_TOKEN`, `GITHUB_TOKEN`, `GH_TOKEN`, `LINEAR_API_KEY`, `VERCEL_OIDC_TOKEN`,
|
|
1226
1339
|
`VERCEL_TOKEN`, `VERCEL_TEAM_ID`, `VERCEL_PROJECT_ID`, or `AI_GATEWAY_API_KEY`
|
|
1227
1340
|
in `environmentVariables` is a config error. The typed operator tools remain
|
|
1228
|
-
the only route to build state from inside the sandbox.
|
|
1341
|
+
the only route to build state from inside the sandbox. A workspace-provider
|
|
1342
|
+
plugin can declare additional forbidden names via its registration's
|
|
1343
|
+
`capabilities.sandboxForbiddenEnv`; those are enforced at the post-load seams
|
|
1344
|
+
(provider construction and `ab init --validate`) rather than at config parse,
|
|
1345
|
+
because plugins are not loaded when configuration is parsed — a deliberate
|
|
1346
|
+
deferral, not an oversight. Concretely, the shared names above are rejected
|
|
1347
|
+
when the config is parsed, plugin-declared extras are rejected when the
|
|
1348
|
+
provider is constructed (dispatch and `ab mcp`), and `ab init --validate`
|
|
1349
|
+
re-checks both sets after plugins load, before any infrastructure is
|
|
1350
|
+
provisioned. Tool bounds:
|
|
1229
1351
|
`sandbox.exec` waits at most 300 seconds; stdout and stderr are each truncated
|
|
1230
1352
|
at 65,536 bytes with a `[truncated by autobuild: output exceeded 65536 bytes]`
|
|
1231
1353
|
marker; `sandbox.reset` and archiving an operator's last session are
|
|
@@ -1236,6 +1358,10 @@ one environment per operator and repository.
|
|
|
1236
1358
|
```toml
|
|
1237
1359
|
[orchestrator]
|
|
1238
1360
|
enabled = true
|
|
1361
|
+
model = "anthropic/claude-sonnet-4"
|
|
1362
|
+
invocationBudgetSeconds = 240
|
|
1363
|
+
approvals = ["builds.control:abort", "sandbox.publish", "tickets.move:ready"]
|
|
1364
|
+
# wake omitted: new sessions inherit the default attention set
|
|
1239
1365
|
|
|
1240
1366
|
[orchestrator.sandbox]
|
|
1241
1367
|
idleMinutes = 30
|
|
@@ -1245,6 +1371,25 @@ environmentVariables = ["MY_TOOL_CONFIG"]
|
|
|
1245
1371
|
Set `MY_TOOL_CONFIG` in the host environment of the `ab mcp` process; a missing
|
|
1246
1372
|
value fails the tool call that needs it, named as a typed `environment` error.
|
|
1247
1373
|
|
|
1374
|
+
In the hosted deployment the `sandbox.*` tools are served to orchestrator
|
|
1375
|
+
turns from two bindings, both composing the backend from the repository's
|
|
1376
|
+
deposited effective config: the store service's message-turn runner (and its
|
|
1377
|
+
same-invocation approval resumes), and the origin-mode dispatcher tick's wake
|
|
1378
|
+
and resume runner, which reuses the dispatcher's wired workspace provider and
|
|
1379
|
+
forge. Both use the builtin workspace provider and the builtin `github` forge
|
|
1380
|
+
from that config — a plugin-provider or forgeless config degrades that
|
|
1381
|
+
binding's sandbox tools only, with a reported diagnostic. The store-service
|
|
1382
|
+
path authenticates the Sandbox SDK with the request's `x-vercel-oidc-token`
|
|
1383
|
+
header (or an explicit `VERCEL_OIDC_TOKEN`, which takes precedence, or the
|
|
1384
|
+
durable `VERCEL_TOKEN`/`VERCEL_TEAM_ID`/`VERCEL_PROJECT_ID` triple, which
|
|
1385
|
+
needs no header); the tick path inherits the dispatcher's already-forwarded
|
|
1386
|
+
token. Every serving path derives the same deterministic environment name per
|
|
1387
|
+
(repository origin, operator), so an operator's environment is one
|
|
1388
|
+
environment per repository no matter which binding drove it, and the guest
|
|
1389
|
+
still receives no credential. Serving stays gated exactly as locally: the
|
|
1390
|
+
effective `[orchestrator].enabled`, the registry's closed-table filtering,
|
|
1391
|
+
and the `approvals` suspension list in the turn loop.
|
|
1392
|
+
|
|
1248
1393
|
## Complete example
|
|
1249
1394
|
|
|
1250
1395
|
The following deliberately exercises every fixed table and both verify/finalize
|
|
@@ -1427,8 +1572,10 @@ pre-attempt index and reports the original Git failure; merged worktree files
|
|
|
1427
1572
|
remain in place and the merge's exit status is unchanged. Upgrade never pushes
|
|
1428
1573
|
or rewrites history.
|
|
1429
1574
|
|
|
1430
|
-
Autobuild now installs
|
|
1431
|
-
are model-invocable.
|
|
1575
|
+
Autobuild now installs 12 skills; only `ab-spec`, `ab-tickets`, and `ab-guide`
|
|
1576
|
+
are model-invocable. (`ab-operate` — the embedded orchestrator's operating
|
|
1577
|
+
manual — also carries `disable-model-invocation: true`; it is read by the
|
|
1578
|
+
hosted turn runner, never invoked by a model in a build session.) The setup reference is an ordinary support file in the
|
|
1432
1579
|
editable/pristine `ab-guide` tree and participates in the same three-way
|
|
1433
1580
|
upgrade merge as every other vendored file.
|
|
1434
1581
|
|
|
@@ -1459,7 +1606,7 @@ poll. Editing TOML cannot change them.
|
|
|
1459
1606
|
|---|---:|---|---|
|
|
1460
1607
|
| Ticket intake | on | `ab dispatch --intake` / `--no-intake`; `i` on the dashboard's global row, and `p` (pause all) / `r` (resume all) on that row or their sessionless equivalents `ab pause --all` / `ab resume --all`, which turn intake off and on as part of quiescing the repository | When off, skip only new ticket list/claim/dispatch work. Janitor work, lease recovery, in-flight builds, and harvesting continue. Turning intake off does not hold builds the repository has already accepted — that is the repository pause below. |
|
|
1461
1608
|
| Repository pause | off | `p` (pause all) / `r` (resume all) on the dashboard's global row, or `ab pause --all` / `ab resume --all` | While on, the dashboard controls line shows `repository PAUSED`, and each held queued row shows `(held)` while retaining its literal `QUEUED` status. No dispatcher tick attaches a runner to such a build — recovery, startup resume, and the lease sweep all skip them. Running builds are parked by the per-build pauses the same command writes; the janitor still settles aborts and discards. |
|
|
1462
|
-
|
|
|
1609
|
+
| Repository auto-merge default | off | `ab dispatch --auto-merge` / `--no-auto-merge`; `m` on the global row | Seeds durable auto-merge intent on builds claimed while the setting is enabled, and — on each change — also applies as a bulk action to every current non-terminal build: turning it on requests consent for each build (turning it off withdraws it), attributed to the operator who toggled it exactly as if they had pressed `m` on every row. A per-build toggle overrides its build's state until the global row is toggled again. |
|
|
1463
1610
|
| Harvest gate | on | `h` on the dashboard's global row | Pauses or resumes repository observation harvesting. The header shows the kernel-acknowledged gate, not merely a pending keypress. |
|
|
1464
1611
|
|
|
1465
1612
|
The opposite flag forms for each dispatch setting are mutually exclusive.
|