@defrex/autobuild 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +192 -0
- package/SPEC.md +2269 -0
- package/bin/ab-build-runner.ts +47 -0
- package/bin/ab-dev.ts +27 -0
- package/bin/ab-dispatch-kernel.ts +92 -0
- package/bin/ab-harvest-runner.ts +29 -0
- package/bin/ab-init-probe.ts +19 -0
- package/bin/ab-process-group-reaper.ts +32 -0
- package/bin/ab.ts +6 -0
- package/bin/agent/ab +3 -0
- package/docs/answering-blocked-builds.md +89 -0
- package/docs/architecture.md +726 -0
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +1623 -0
- package/docs/hosted-dispatcher-cutover-validation.md +178 -0
- package/docs/hosted-dispatcher.md +204 -0
- package/docs/mcp.md +119 -0
- package/docs/operator-api.md +186 -0
- package/docs/remote-store-protocol.md +972 -0
- package/docs/remote-ticket-protocol.md +79 -0
- package/docs/setup.md +289 -0
- package/docs/spec-standard.md +63 -0
- package/docs/ticket-dependencies.md +59 -0
- package/docs/vercel-sandbox-rollout-probe.md +7 -0
- package/package.json +135 -0
- package/packages/core/src/cli/args.ts +74 -0
- package/packages/core/src/cli/artifact.ts +182 -0
- package/packages/core/src/cli/binary.ts +198 -0
- package/packages/core/src/cli/build-control.ts +789 -0
- package/packages/core/src/cli/build-progress.ts +55 -0
- package/packages/core/src/cli/bulk-control.ts +385 -0
- package/packages/core/src/cli/context.ts +470 -0
- package/packages/core/src/cli/dashboard/actions.ts +59 -0
- package/packages/core/src/cli/dashboard/cells.ts +92 -0
- package/packages/core/src/cli/dashboard/composer.ts +230 -0
- package/packages/core/src/cli/dashboard/detail.ts +100 -0
- package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
- package/packages/core/src/cli/dashboard/live.ts +152 -0
- package/packages/core/src/cli/dashboard/model.ts +1173 -0
- package/packages/core/src/cli/dashboard/poll.ts +196 -0
- package/packages/core/src/cli/dashboard/render.ts +1568 -0
- package/packages/core/src/cli/dashboard/selection.ts +66 -0
- package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
- package/packages/core/src/cli/dashboard/session-view.ts +506 -0
- package/packages/core/src/cli/dashboard/transcript.ts +108 -0
- package/packages/core/src/cli/dev.ts +46 -0
- package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
- package/packages/core/src/cli/dispatch-process.ts +259 -0
- package/packages/core/src/cli/dispatch.ts +3692 -0
- package/packages/core/src/cli/dotenv.ts +48 -0
- package/packages/core/src/cli/env.ts +206 -0
- package/packages/core/src/cli/harvest.ts +698 -0
- package/packages/core/src/cli/help.ts +713 -0
- package/packages/core/src/cli/init-agent.ts +106 -0
- package/packages/core/src/cli/init-validation.ts +583 -0
- package/packages/core/src/cli/init.ts +756 -0
- package/packages/core/src/cli/installation.ts +333 -0
- package/packages/core/src/cli/keyboard.ts +261 -0
- package/packages/core/src/cli/main.ts +1399 -0
- package/packages/core/src/cli/mcp.ts +279 -0
- package/packages/core/src/cli/models.ts +65 -0
- package/packages/core/src/cli/observe.ts +55 -0
- package/packages/core/src/cli/plugin.ts +227 -0
- package/packages/core/src/cli/pr-attachments.ts +138 -0
- package/packages/core/src/cli/pr-summary.ts +124 -0
- package/packages/core/src/cli/publication-settlement.ts +188 -0
- package/packages/core/src/cli/repo-state.ts +213 -0
- package/packages/core/src/cli/repository-status.ts +80 -0
- package/packages/core/src/cli/self-update.ts +443 -0
- package/packages/core/src/cli/status.ts +895 -0
- package/packages/core/src/cli/store-opening.contract.ts +289 -0
- package/packages/core/src/cli/store-opening.ts +167 -0
- package/packages/core/src/cli/store-ref.ts +33 -0
- package/packages/core/src/cli/terminal-restore.ts +174 -0
- package/packages/core/src/cli/terminal.ts +361 -0
- package/packages/core/src/cli/terminals.ts +995 -0
- package/packages/core/src/cli/testkit.ts +194 -0
- package/packages/core/src/cli/ticket.ts +484 -0
- package/packages/core/src/cli/upgrade-agent.ts +180 -0
- package/packages/core/src/cli/upgrade-commit.ts +468 -0
- package/packages/core/src/cli/upgrade-notice.ts +91 -0
- package/packages/core/src/cli/upgrade-progress.ts +110 -0
- package/packages/core/src/cli/upgrade.ts +888 -0
- package/packages/core/src/cli/wait.ts +587 -0
- package/packages/core/src/cli/watch.ts +932 -0
- package/packages/core/src/config/live.ts +255 -0
- package/packages/core/src/config/load.ts +98 -0
- package/packages/core/src/config/roles.ts +438 -0
- package/packages/core/src/config/schema.ts +774 -0
- package/packages/core/src/distribution.ts +24 -0
- package/packages/core/src/events/catalog.ts +201 -0
- package/packages/core/src/events/envelope.ts +59 -0
- package/packages/core/src/events/payloads.ts +618 -0
- package/packages/core/src/events/repository.ts +559 -0
- package/packages/core/src/events/sessions.ts +180 -0
- package/packages/core/src/harvest/schema.ts +147 -0
- package/packages/core/src/ids.ts +45 -0
- package/packages/core/src/integration/harness.ts +681 -0
- package/packages/core/src/kernel/auto-merge.ts +259 -0
- package/packages/core/src/kernel/converge.ts +117 -0
- package/packages/core/src/kernel/dispatch-settings.ts +60 -0
- package/packages/core/src/kernel/dispatch-status.ts +141 -0
- package/packages/core/src/kernel/engine.ts +1047 -0
- package/packages/core/src/kernel/harvest.ts +823 -0
- package/packages/core/src/kernel/origin.ts +69 -0
- package/packages/core/src/kernel/phases.ts +196 -0
- package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
- package/packages/core/src/kernel/pr-attachments.ts +82 -0
- package/packages/core/src/kernel/reducer.ts +752 -0
- package/packages/core/src/kernel/stall.ts +155 -0
- package/packages/core/src/kernel/verify-applicability.ts +52 -0
- package/packages/core/src/markdown.ts +67 -0
- package/packages/core/src/ontology.ts +288 -0
- package/packages/core/src/open-map.ts +203 -0
- package/packages/core/src/operator/annotations.ts +187 -0
- package/packages/core/src/operator/client.ts +311 -0
- package/packages/core/src/operator/control.ts +117 -0
- package/packages/core/src/operator/index.ts +62 -0
- package/packages/core/src/operator/presentation.ts +22 -0
- package/packages/core/src/operator/protocol.ts +98 -0
- package/packages/core/src/operator/query.ts +201 -0
- package/packages/core/src/operator/registry.ts +1043 -0
- package/packages/core/src/operator/requests.ts +138 -0
- package/packages/core/src/operator/sandbox.ts +458 -0
- package/packages/core/src/operator/server.ts +616 -0
- package/packages/core/src/operator/sessions.ts +223 -0
- package/packages/core/src/operator/tickets.ts +177 -0
- package/packages/core/src/plugin-sdk/index.ts +174 -0
- package/packages/core/src/plugins/contract-entry.ts +74 -0
- package/packages/core/src/plugins/contract-env.ts +3 -0
- package/packages/core/src/plugins/load.ts +259 -0
- package/packages/core/src/plugins/manifest.ts +236 -0
- package/packages/core/src/plugins/registry.ts +274 -0
- package/packages/core/src/plugins/runtimes.ts +75 -0
- package/packages/core/src/ports/forge/contract.ts +258 -0
- package/packages/core/src/ports/forge/create.ts +74 -0
- package/packages/core/src/ports/forge/fake.ts +488 -0
- package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
- package/packages/core/src/ports/forge/github-transport.ts +352 -0
- package/packages/core/src/ports/forge/github.ts +1162 -0
- package/packages/core/src/ports/forge/local-git.ts +568 -0
- package/packages/core/src/ports/runner/claude.ts +873 -0
- package/packages/core/src/ports/runner/cli-stream.ts +90 -0
- package/packages/core/src/ports/runner/codex.ts +873 -0
- package/packages/core/src/ports/runner/contract.ts +492 -0
- package/packages/core/src/ports/runner/fake.ts +213 -0
- package/packages/core/src/ports/runner/json-record.ts +2 -0
- package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
- package/packages/core/src/ports/runner/one-shot.ts +29 -0
- package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
- package/packages/core/src/ports/runner/pi-bridge.js +6 -0
- package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
- package/packages/core/src/ports/runner/pi.ts +617 -0
- package/packages/core/src/ports/runner/production.ts +74 -0
- package/packages/core/src/ports/runner/provider-error.ts +121 -0
- package/packages/core/src/ports/runner/routing.ts +234 -0
- package/packages/core/src/ports/runner/runtime.ts +214 -0
- package/packages/core/src/ports/runner/session-env.ts +34 -0
- package/packages/core/src/ports/runner/stream-parts.ts +103 -0
- package/packages/core/src/ports/tickets/contract.ts +570 -0
- package/packages/core/src/ports/tickets/create.ts +135 -0
- package/packages/core/src/ports/tickets/fake.ts +215 -0
- package/packages/core/src/ports/tickets/file.ts +559 -0
- package/packages/core/src/ports/tickets/linear.ts +811 -0
- package/packages/core/src/ports/tickets/operations.ts +161 -0
- package/packages/core/src/ports/tickets/remote-index.ts +13 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
- package/packages/core/src/ports/tickets/remote-server.ts +185 -0
- package/packages/core/src/ports/tickets/remote.ts +130 -0
- package/packages/core/src/ports/tickets/update.ts +51 -0
- package/packages/core/src/ports/types.ts +528 -0
- package/packages/core/src/ports/workspace/build-execution.ts +123 -0
- package/packages/core/src/ports/workspace/contract.ts +136 -0
- package/packages/core/src/ports/workspace/create.ts +132 -0
- package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
- package/packages/core/src/ports/workspace/fake.ts +432 -0
- package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
- package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
- package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
- package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
- package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
- package/packages/core/src/processes/build-child-exit.ts +78 -0
- package/packages/core/src/processes/build-child.ts +119 -0
- package/packages/core/src/processes/build-execution-state.ts +95 -0
- package/packages/core/src/processes/build-parent-watch.ts +35 -0
- package/packages/core/src/processes/build-runner.ts +2614 -0
- package/packages/core/src/processes/dispatcher.ts +2825 -0
- package/packages/core/src/processes/execution-settlement.ts +171 -0
- package/packages/core/src/processes/harvest-child.ts +115 -0
- package/packages/core/src/processes/harvest-execution-state.ts +121 -0
- package/packages/core/src/processes/harvest-runner.ts +1656 -0
- package/packages/core/src/processes/harvest.ts +595 -0
- package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
- package/packages/core/src/processes/phase-failure-budget.ts +52 -0
- package/packages/core/src/processes/process-group-reaper.ts +35 -0
- package/packages/core/src/processes/process-group.ts +65 -0
- package/packages/core/src/processes/publication-loss.ts +62 -0
- package/packages/core/src/processes/publication-state.ts +93 -0
- package/packages/core/src/processes/sandbox-state.ts +101 -0
- package/packages/core/src/registry.ts +24 -0
- package/packages/core/src/sessions/reducer.ts +185 -0
- package/packages/core/src/skills.ts +7 -0
- package/packages/core/src/spec-standard.ts +47 -0
- package/packages/core/src/store/adapter.ts +80 -0
- package/packages/core/src/store/build-scope.ts +272 -0
- package/packages/core/src/store/contract.ts +2581 -0
- package/packages/core/src/store/local/blobs.ts +38 -0
- package/packages/core/src/store/local/schema.ts +180 -0
- package/packages/core/src/store/local/store.ts +1713 -0
- package/packages/core/src/store/memory.ts +1138 -0
- package/packages/core/src/store/phase-session.ts +425 -0
- package/packages/core/src/store/remote/client.ts +780 -0
- package/packages/core/src/store/remote/index.ts +12 -0
- package/packages/core/src/store/remote/protocol.ts +348 -0
- package/packages/core/src/store/remote/server.ts +923 -0
- package/packages/core/src/store/remote/token.ts +137 -0
- package/packages/core/src/store/remote/version.ts +10 -0
- package/packages/core/src/store/retention.ts +91 -0
- package/packages/core/src/store/session-handle.ts +265 -0
- package/packages/core/src/store/streams/assemble.ts +79 -0
- package/packages/core/src/store/streams/lock.ts +42 -0
- package/packages/core/src/store/streams/session-writer.ts +306 -0
- package/packages/core/src/store/streams/types.ts +171 -0
- package/packages/core/src/store/streams/wait.ts +127 -0
- package/packages/core/src/store/subscribe.ts +105 -0
- package/packages/core/src/store/types.ts +466 -0
- package/packages/core/src/testing/fixed.ts +44 -0
- package/packages/core/src/testing/store-failures.ts +32 -0
- package/packages/core/src/zod-issues.ts +91 -0
- package/skills/code-review/SKILL.md +100 -0
- package/skills/finalize/SKILL.md +44 -0
- package/skills/guide/SKILL.md +1781 -0
- package/skills/guide/references/operator-api.md +164 -0
- package/skills/guide/references/plugin-authoring.md +354 -0
- package/skills/guide/references/remote-store-protocol.md +972 -0
- package/skills/guide/references/remote-ticket-protocol.md +79 -0
- package/skills/guide/references/setup.md +289 -0
- package/skills/guide/references/spec-standard.md +63 -0
- package/skills/guide/references/ticket-dependencies.md +59 -0
- package/skills/harvest/SKILL.md +66 -0
- package/skills/harvest-review/SKILL.md +63 -0
- package/skills/implement/SKILL.md +82 -0
- package/skills/plan/SKILL.md +123 -0
- package/skills/plan-review/SKILL.md +121 -0
- package/skills/reconcile/SKILL.md +65 -0
- package/skills/spec/SKILL.md +132 -0
- package/skills/tickets/SKILL.md +139 -0
- package/templates/autobuild.toml +36 -0
|
@@ -0,0 +1,1623 @@
|
|
|
1
|
+
# Configuration reference
|
|
2
|
+
|
|
3
|
+
Autobuild reads one declarative `autobuild.toml` from the repository root.
|
|
4
|
+
Commands in the file are shell strings; the file itself is never evaluated as
|
|
5
|
+
code. A running dispatcher owns an accepted snapshot from the main checkout,
|
|
6
|
+
while scoped phase commands read the build worktree's branch-owned file.
|
|
7
|
+
Configuration changes therefore move through review like any other change and,
|
|
8
|
+
once merged, can be adopted by the running dispatcher.
|
|
9
|
+
|
|
10
|
+
This document covers the complete accepted TOML surface. Follow the
|
|
11
|
+
[installation and repository setup procedure](setup.md) for the supported local
|
|
12
|
+
versus Vercel decision flow, credential checklist, reproducible provisioning,
|
|
13
|
+
and sessionless `ab init --validate` readiness check. Examples labelled as
|
|
14
|
+
fragments are intended to be added to an existing file; the [complete
|
|
15
|
+
example](#complete-example) is valid on its own.
|
|
16
|
+
|
|
17
|
+
## Hosted service and PostgreSQL BuildStore environment
|
|
18
|
+
|
|
19
|
+
The optional `@defrex/autobuild-hosted-store-service` and its
|
|
20
|
+
`@defrex/autobuild-postgres-store` adapter are configured entirely through the
|
|
21
|
+
environment. They are published to npm as separate packages alongside the
|
|
22
|
+
`@defrex/autobuild` CLI, so a CLI install never acquires the service,
|
|
23
|
+
PostgreSQL, or blob-provider dependencies; a project that embeds the adapter
|
|
24
|
+
or the service adds the package it needs (`bun add
|
|
25
|
+
@defrex/autobuild-postgres-store` or `bun add
|
|
26
|
+
@defrex/autobuild-hosted-store-service`). The deployable web application still
|
|
27
|
+
runs from a release checkout: choose the compatible tag shown in
|
|
28
|
+
[GitHub Releases](https://github.com/defrex/autobuild/releases), clone that exact
|
|
29
|
+
revision into a dedicated checkout, and run the idempotent migration there:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
git clone --depth 1 --branch v0.6.0 --single-branch https://github.com/defrex/autobuild.git autobuild-v0.6.0
|
|
33
|
+
cd autobuild-v0.6.0
|
|
34
|
+
bun install --frozen-lockfile
|
|
35
|
+
DATABASE_URL=postgres://… bun run postgres:migrate
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Replace `v0.6.0` with the selected release tag. Schema diagnostics' root
|
|
39
|
+
`postgres:migrate` script is relative to that pinned checkout. A hosted build
|
|
40
|
+
can run the same step itself: `bun run deploy:build` migrates against the
|
|
41
|
+
deployment's database URL, logs the host it prepared, and then builds the web
|
|
42
|
+
application, failing the build when no database URL is configured or the
|
|
43
|
+
schema is incompatible. The database
|
|
44
|
+
identity needs permission to create tables during migration and to select,
|
|
45
|
+
insert, and update those tables at runtime. The complete run, deployment, and
|
|
46
|
+
token procedure is in the [hosted service
|
|
47
|
+
guide](../packages/hosted-store-service/README.md); operator clients use the
|
|
48
|
+
separate signed-user token described by the [operator API](operator-api.md).
|
|
49
|
+
|
|
50
|
+
| Variable | Required when | Values / purpose |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `AB_STORE_SECRET` | Hosted service | Nonblank HMAC signing secret. Service-side only; never give it to clients. |
|
|
53
|
+
| `AB_HOST` | Optional, ordinary Bun host | Bind hostname; defaults to `0.0.0.0`. Vercel supplies its own listener. |
|
|
54
|
+
| `PORT` | Optional, ordinary Bun host | Integer 1–65535; defaults to `3000`. Vercel supplies its own listener. |
|
|
55
|
+
| `AB_STORE` | Remote client | Hosted service base URL used by the kernel, phase commands, and dashboard. |
|
|
56
|
+
| `AB_TOKEN` | Authenticated remote client | Offline-minted bearer token; this is not the signing secret. |
|
|
57
|
+
| `BETTER_AUTH_SECRET` | Web dashboard | Independent server-only session secret with at least 32 high-entropy characters. |
|
|
58
|
+
| `BETTER_AUTH_URL` | Web dashboard | Exact HTTP(S) origin, without credentials or a path; HTTPS is mandatory in production. |
|
|
59
|
+
| `GITHUB_CLIENT_ID` | GitHub sign-in | OAuth application client id. |
|
|
60
|
+
| `GITHUB_CLIENT_SECRET` | GitHub sign-in | Server-only OAuth application secret. |
|
|
61
|
+
| `AB_WEB_AUTH_PROVIDERS` | Web dashboard | Comma-separated provider set; currently exactly `github`. |
|
|
62
|
+
| `AB_WEB_ALLOWED_EMAILS` | Web dashboard | Nonempty comma-separated operator email allowlist; matching is case-insensitive. |
|
|
63
|
+
| `AB_WEB_REPOSITORIES` | Web dashboard | Nonempty comma-separated repository allowlist exposed to signed-in operators. Entries are repository identities — normalized `https://` origins (ssh-like spellings such as `git@github.com:owner/repo.git` are accepted and normalized); they must match the Store's identity for the repository. |
|
|
64
|
+
|
|
65
|
+
GitHub's callback is `{BETTER_AUTH_URL}/api/auth/callback/github`; the app needs
|
|
66
|
+
the `user:email` scope (or GitHub App read-only email permission). Sessions are
|
|
67
|
+
database-backed and represented in the browser only by an HTTP-only cookie.
|
|
68
|
+
Allowlist removal takes effect on the next web API request. Never expose these
|
|
69
|
+
secrets, `AB_STORE_SECRET`, database/blob credentials, machine tokens, or OAuth
|
|
70
|
+
account tokens through a `NEXT_PUBLIC_` variable.
|
|
71
|
+
|
|
72
|
+
The following variables configure the service-side durable adapter (or a direct
|
|
73
|
+
use of `@defrex/autobuild-postgres-store`).
|
|
74
|
+
|
|
75
|
+
| Variable | Required when | Values / purpose |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `DATABASE_URL` | Always, unless `AB_POSTGRES_URL` is set | Nonblank PostgreSQL connection URL under its conventional name, as Vercel's Neon and Postgres storage integrations inject it. |
|
|
78
|
+
| `AB_POSTGRES_URL` | Optional override | Explicit Autobuild-specific connection URL; a nonblank value wins over `DATABASE_URL`. No other ambient variable is consulted. |
|
|
79
|
+
| `AB_BLOB_BACKEND` | Always | `s3` or `vercel`. |
|
|
80
|
+
| `AB_BLOB_PREFIX` | Optional | Object pathname prefix; redundant `/` characters are normalized. |
|
|
81
|
+
| `AB_S3_BUCKET` | S3 | Bucket name. |
|
|
82
|
+
| `AB_S3_REGION` | S3 | S3 signing region. |
|
|
83
|
+
| `AB_S3_ENDPOINT` | Optional, S3 | Endpoint URL for an S3-compatible service. |
|
|
84
|
+
| `AB_S3_ACCESS_KEY_ID` | S3 | Explicit access key; ambient AWS credential lookup is not used. |
|
|
85
|
+
| `AB_S3_SECRET_ACCESS_KEY` | S3 | Explicit secret key. |
|
|
86
|
+
| `AB_S3_SESSION_TOKEN` | Optional, S3 | Explicit temporary-credential session token. |
|
|
87
|
+
| `AB_S3_FORCE_PATH_STYLE` | Optional, S3 | Exact boolean `true` or `false`. |
|
|
88
|
+
| `AB_VERCEL_BLOB_ACCESS` | Vercel | `public` or `private`. |
|
|
89
|
+
| `BLOB_READ_WRITE_TOKEN` | Vercel auth option | Blob read-write token. |
|
|
90
|
+
| `VERCEL_OIDC_TOKEN` | Vercel OIDC option | Must be paired with `BLOB_STORE_ID` when no read-write token is supplied. |
|
|
91
|
+
| `BLOB_STORE_ID` | Vercel OIDC option | Must be paired with `VERCEL_OIDC_TOKEN`. |
|
|
92
|
+
|
|
93
|
+
S3 identities need `GetObject` and `PutObject` on the bucket/prefix. The adapter
|
|
94
|
+
maps only a provider 404 to absence and propagates authorization/service errors.
|
|
95
|
+
Vercel object names are deterministic and overwritten idempotently.
|
|
96
|
+
|
|
97
|
+
## Strict parsing and validation
|
|
98
|
+
|
|
99
|
+
Parsing is strict. Unknown top-level keys or tables, unknown fields in a known
|
|
100
|
+
table, fields from the wrong step variant, malformed values, and dangling
|
|
101
|
+
command references are errors. The open maps are `[commands]`, `[roles]`,
|
|
102
|
+
`[workspace.config]`, `[workspace.config.runtimeProvisioning]`,
|
|
103
|
+
`[verify.<step>]`, and `[finalize.<step>]`. Autobuild
|
|
104
|
+
validates repository-defined command, role, and step entries.
|
|
105
|
+
`[workspace.config]` is plugin-owned and passed through unchanged for plugin
|
|
106
|
+
providers; the builtin `git-worktree` provider requires it to be empty, while
|
|
107
|
+
`vercel-sandbox` interprets it as a closed, typed table.
|
|
108
|
+
Every other known table is closed to unknown keys.
|
|
109
|
+
|
|
110
|
+
There are three validation layers:
|
|
111
|
+
|
|
112
|
+
1. TOML syntax and schema/cross-field validation happen while
|
|
113
|
+
`autobuild.toml` is loaded. Errors start with the file path and either
|
|
114
|
+
`TOML syntax error` or `invalid config`; schema errors then identify paths
|
|
115
|
+
such as `verify.e2e.skill` or `tickets.teamKey`.
|
|
116
|
+
2. Configured plugin modules are resolved from the repository, evaluated,
|
|
117
|
+
manifest-validated, checked for plugin-API compatibility, and registered
|
|
118
|
+
before production adapters, stores, claims, or runners are started.
|
|
119
|
+
3. Runtime/model compatibility is checked eagerly after the configured
|
|
120
|
+
adapters are wired. Its separate `invalid runtime/model configuration`
|
|
121
|
+
error reports every incompatible declared role before a build launches.
|
|
122
|
+
|
|
123
|
+
Run `ab dispatch --once` from the repository to exercise production loading
|
|
124
|
+
and adapter validation. It is a real dispatcher pass, not a read-only linter,
|
|
125
|
+
so run it only when one intake/janitor tick is acceptable.
|
|
126
|
+
|
|
127
|
+
TOML does not return to the root after entering a table. Put root scalars before
|
|
128
|
+
the first table header; otherwise a value such as `capacity` becomes an unknown
|
|
129
|
+
field in whichever table precedes it.
|
|
130
|
+
|
|
131
|
+
## Reloading a running dispatcher
|
|
132
|
+
|
|
133
|
+
A long-running `ab dispatch` watches the main checkout's `autobuild.toml` and
|
|
134
|
+
checks it before every dispatch tick. A valid save is adopted within that tick.
|
|
135
|
+
In origin mode (`ab dispatch --repository <origin>`, no checkout) the same
|
|
136
|
+
cadence reads `autobuild.toml` from the forge at the current `baseBranch`
|
|
137
|
+
instead: a push to the base branch is honored by a later tick without
|
|
138
|
+
restarting anything. Hot fields apply immediately; restart-classified fields
|
|
139
|
+
keep the startup-built adapters and file the existing restart-required notice
|
|
140
|
+
(a long-lived origin-mode watch picks them up on its next process restart;
|
|
141
|
+
the hosted cron entry point re-reads them on every invocation).
|
|
142
|
+
Each dispatch, build, check, or agent action captures the accepted configuration
|
|
143
|
+
snapshot at its start boundary: work already running is not interrupted, while
|
|
144
|
+
the next setup or pipeline step of the same in-flight build uses the new
|
|
145
|
+
snapshot. Raising capacity can fill the new slots immediately; lowering it
|
|
146
|
+
prevents new claims without terminating builds already above the limit. `ab
|
|
147
|
+
dispatch --once` performs one pass and does not watch or reload.
|
|
148
|
+
|
|
149
|
+
Every accepted revision is deposited verbatim as a `dispatcher-config`
|
|
150
|
+
repository artifact and referenced by a `dispatcher.config-reloaded` repository
|
|
151
|
+
event. The dashboard reports the reload and reprojects values such as capacity;
|
|
152
|
+
plain watch mode writes the reload notice to stdout. A missing or unreadable
|
|
153
|
+
file, invalid syntax or schema, or invalid runtime routing is a rejected
|
|
154
|
+
candidate: it cannot replace or partially modify the last valid snapshot. The
|
|
155
|
+
dispatcher keeps that snapshot active, reports an actionable notice in the
|
|
156
|
+
dashboard notice row or on plain-mode stderr, and suppresses repeats while the
|
|
157
|
+
same failure remains. Restoring `autobuild.toml` with valid configuration clears
|
|
158
|
+
the rejection and ordinary live reload resumes without a dispatcher restart.
|
|
159
|
+
|
|
160
|
+
These fields hot-reload:
|
|
161
|
+
|
|
162
|
+
| Field or table | Boundary where a new value applies |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `baseBranch` | Next dispatch or base fallback decision; existing `build.created` facts remain immutable. |
|
|
165
|
+
| `capacity` | Next dispatcher tick. |
|
|
166
|
+
| `[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
|
+
| `[roles]` | Next agent invocation for that role. Runtime, model, args, alternates, and the session budget are captured together. |
|
|
170
|
+
| `[policy]` | Next policy, convergence, or agent-session boundary. A running session keeps its captured budget. |
|
|
171
|
+
| `tickets.readyLabels`, `tickets.readyState` | Next ready-ticket scan. |
|
|
172
|
+
| `tickets.triageState`, `tickets.proposalState` | Next dispatcher handback or harvest filing boundary. |
|
|
173
|
+
|
|
174
|
+
Adapter selectors and factory inputs are constructed once and require a
|
|
175
|
+
restart. A valid save may still apply its hot fields, but these changed paths
|
|
176
|
+
remain pinned to their startup values and are reported explicitly:
|
|
177
|
+
|
|
178
|
+
| Restart-required field | Reason |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `plugins` | Plugin modules and registration catalogs load before wiring. |
|
|
181
|
+
| `forge` | Selects the constructed Forge adapter. |
|
|
182
|
+
| `workspace.provider`, `workspace.config` | Select and configure the constructed workspace provider. |
|
|
183
|
+
| `tickets.source`, `tickets.teamKey`, `tickets.claimedState`, `tickets.createState`, `tickets.dir` | Select or configure the constructed TicketSource. |
|
|
184
|
+
| `orchestrator` | Tool availability and the dispatcher's sandbox idle settlement are startup-constructed. |
|
|
185
|
+
|
|
186
|
+
The dispatch process reads the main checkout because it owns repository intake.
|
|
187
|
+
Scoped phase commands still read the build worktree's `autobuild.toml`; this is
|
|
188
|
+
the existing build-context boundary and is not unified by hot reload.
|
|
189
|
+
|
|
190
|
+
## Root scalars
|
|
191
|
+
|
|
192
|
+
All root scalars are optional and receive defaults. They must appear before
|
|
193
|
+
any table header.
|
|
194
|
+
|
|
195
|
+
| Field | Default | Constraints | Purpose |
|
|
196
|
+
|---|---:|---|---|
|
|
197
|
+
| `baseBranch` | `"main"` | nonempty string | Branch used to cut builds, target PRs, and merge during reconciliation. |
|
|
198
|
+
| `capacity` | `1` | positive integer | Maximum concurrent nonterminal builds for this repository. Paused and blocked builds still occupy capacity. |
|
|
199
|
+
| `forge` | `"github"` | nonblank string | Forge adapter name. Shipped values are `github` and `local-git`; configured plugins may register other names. |
|
|
200
|
+
| `plugins` | `[]` | array of unique nonblank module specifiers | Trusted Bun plugin modules loaded by dispatch, `ab ticket`, and scoped phase CLI processes. |
|
|
201
|
+
|
|
202
|
+
### Plugin modules
|
|
203
|
+
|
|
204
|
+
<!-- config-fragment:plugins -->
|
|
205
|
+
```toml
|
|
206
|
+
plugins = ["./plugins/company.ts", "@acme/autobuild-plugin"]
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Repository-path specifiers (relative, absolute, and `file:`) resolve from the
|
|
210
|
+
root whose config is being read. In a scoped phase process that root is the
|
|
211
|
+
immutable build worktree. Bare npm package specifiers resolve first from the
|
|
212
|
+
consuming repository's main checkout and therefore use its installed
|
|
213
|
+
dependencies; a package the repository lacks then resolves from Autobuild's own
|
|
214
|
+
installation, so an extension installed next to the CLI (`bun add -g
|
|
215
|
+
@defrex/autobuild-<extension>`) loads without being added to the repository. A
|
|
216
|
+
repository copy always wins over an installed one. This package lookup remains
|
|
217
|
+
stable when a relocated local store places a linked worktree outside the
|
|
218
|
+
checkout. Dispatch and sessionless commands use the main checkout for both
|
|
219
|
+
repository roots. `ab plugin list` reports which root satisfied each package
|
|
220
|
+
(`from=repository` or `from=installation`). Missing packages fail loading;
|
|
221
|
+
Autobuild does not install them.
|
|
222
|
+
|
|
223
|
+
Every configured specifier string must be unique. An exact repeat fails schema
|
|
224
|
+
validation before any plugin resolves or evaluates; the diagnostic identifies
|
|
225
|
+
the repeated value and both list positions and tells the operator to remove or
|
|
226
|
+
deduplicate the entry. Distinct specifier strings remain separate declarations.
|
|
227
|
+
Each module must default-export a strict manifest with a plugin name, a semver
|
|
228
|
+
range in `apiVersion`, and optional `ticketSources`, `agentRuntimes`,
|
|
229
|
+
`workspaceProviders`, and `forges` factory maps. One manifest may contribute to
|
|
230
|
+
several ports.
|
|
231
|
+
|
|
232
|
+
Plugin modules execute in-process during `ab dispatch`, sessionless `ab ticket`
|
|
233
|
+
commands, and configured scoped phase CLI composition. They have the same trust
|
|
234
|
+
as repository-supplied commands;
|
|
235
|
+
there is no sandbox. A missing module, module
|
|
236
|
+
that throws, malformed manifest, incompatible API range, or adapter-name
|
|
237
|
+
collision fails startup before a ticket claim. Builtin names and names from
|
|
238
|
+
earlier configured plugins are reserved, and declaration order never permits
|
|
239
|
+
shadowing. A collision between distinct plugin declarations continues to name
|
|
240
|
+
the conflicting adapter and both owners.
|
|
241
|
+
|
|
242
|
+
Plugin authors import the stable surface from `@defrex/autobuild/plugin-sdk`, normally
|
|
243
|
+
with `import type`, and can develop against Autobuild as a dev/peer dependency
|
|
244
|
+
without adding a runtime Autobuild dependency to the plugin. That entry point
|
|
245
|
+
exports the manifest/factory types, port types, fake adapters, and reusable
|
|
246
|
+
TicketSource, AgentRunner, WorkspaceProvider, Forge, BuildStore, and BlobStore
|
|
247
|
+
contract suites. Adapter values may use the backward-compatible bare factory or
|
|
248
|
+
carry a contract fixture descriptor. Ticket sources may also declare
|
|
249
|
+
`requiredEnv`; the host checks every declared variable for a nonempty value
|
|
250
|
+
before invoking the adapter factory. Plugins using Forge abort-cleanup and
|
|
251
|
+
AgentRunner turn-cancellation capabilities introduced in API 1.2 should require
|
|
252
|
+
`^1.2.0`. The structured failure `cause` and provider-exhaustion contract
|
|
253
|
+
scenario were added in API 1.3; a runtime plugin that relies on them should
|
|
254
|
+
require `^1.3.0`. API 1.4 adds the optional `Ticket.creationKey` projection: a
|
|
255
|
+
ticket-source plugin should return its stable external create/adoption key from
|
|
256
|
+
create, get, and ready listings so dispatch can correlate Autobuild's durable
|
|
257
|
+
in-flight creations. Legacy tickets may omit it and remain dispatchable.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import type { AutobuildPluginManifest } from '@defrex/autobuild/plugin-sdk'
|
|
261
|
+
|
|
262
|
+
export default {
|
|
263
|
+
name: 'acme-integrations',
|
|
264
|
+
apiVersion: '^1.2.0',
|
|
265
|
+
ticketSources: {
|
|
266
|
+
jira: {
|
|
267
|
+
requiredEnv: ['JIRA_TOKEN'],
|
|
268
|
+
factory: ({ config, env, repoRoot }) => createJira(config, env, repoRoot),
|
|
269
|
+
contract: {
|
|
270
|
+
// Return the TicketSourceContractFactory consumed by the shared suite.
|
|
271
|
+
factory: ({ env }) => makeJiraContractFactory(env),
|
|
272
|
+
live: true,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
} satisfies AutobuildPluginManifest
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Use the sessionless author/operator loop from the repository checkout:
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
ab plugin list
|
|
283
|
+
ab plugin doctor
|
|
284
|
+
ab plugin test ticket-source jira
|
|
285
|
+
AB_RUN_LIVE_PORT_CONTRACTS=1 ab plugin test ticket-source jira
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The four test port tokens are `ticket-source`, `agent-runtime`,
|
|
289
|
+
`workspace-provider`, and `forge`. `list` includes builtins and successful
|
|
290
|
+
plugin registrations with module, resolved path/kind, owner, API status, and
|
|
291
|
+
contract availability. `doctor` attempts every configured module in declaration
|
|
292
|
+
order, reports each resolution/evaluation/manifest/registration result, and
|
|
293
|
+
exits nonzero if any fail. Dispatch intentionally differs: it stops on the
|
|
294
|
+
first plugin failure before opening stores or claiming work. `test` invokes one
|
|
295
|
+
unchanged shared suite under `bun test`; Bun's per-test output and exit status
|
|
296
|
+
are authoritative. A missing `contract.factory` is an actionable error. A
|
|
297
|
+
`live = true` descriptor cannot launch or create its harness unless
|
|
298
|
+
`AB_RUN_LIVE_PORT_CONTRACTS=1` is explicitly set. Plugin authors should run the
|
|
299
|
+
corresponding contract suite against every adapter, including
|
|
300
|
+
`describeAgentRunnerContract` for each runtime.
|
|
301
|
+
|
|
302
|
+
Ticket-source, forge, workspace, and agent-runtime selection are open. Set
|
|
303
|
+
`[tickets].source`, the root `forge` scalar, `[workspace].provider`, or any
|
|
304
|
+
`[roles.*].runtime` to a registered name; omission selects the builtin defaults
|
|
305
|
+
where applicable. A selected plugin forge factory receives an empty
|
|
306
|
+
adapter-specific `config` object, while a workspace factory receives
|
|
307
|
+
`[workspace.config]`; both receive the process environment and absolute
|
|
308
|
+
repository root and are invoked lazily after the complete plugin catalog loads.
|
|
309
|
+
Unknown names fail with the available names for that port, and factory failures
|
|
310
|
+
are contextualized with the adapter and plugin names. Scoped build-session CLI
|
|
311
|
+
processes repeat forge config/plugin loading from the build worktree, so phase
|
|
312
|
+
terminal plumbing uses the same configured forge. The returned forge is not
|
|
313
|
+
wrapped: an absent `prAttachments` capability intentionally selects text-only
|
|
314
|
+
attachment summaries, while a present capability serves upload and terminal
|
|
315
|
+
reclamation.
|
|
316
|
+
|
|
317
|
+
Plugin runtimes receive empty adapter config, the process environment, and the
|
|
318
|
+
absolute repository root. They participate in the same eager exact-name,
|
|
319
|
+
model-family, and default-model role validation as builtins, and their
|
|
320
|
+
registration key is used for session event and transcript attribution. A
|
|
321
|
+
runtime's optional one-shot capability serves slug naming and lazy upgrade
|
|
322
|
+
conflict resolution; when absent, each caller retains its existing safe
|
|
323
|
+
fallback.
|
|
324
|
+
|
|
325
|
+
## `[pr]`
|
|
326
|
+
|
|
327
|
+
Optional. Omitting the table leaves attachment hosting off; attached artifacts
|
|
328
|
+
still receive exact BuildStore download commands in the PR summary.
|
|
329
|
+
|
|
330
|
+
| Field | Default | Constraints | Purpose |
|
|
331
|
+
|---|---:|---|---|
|
|
332
|
+
| `imageHost` | omitted (off) | optional `[pr.imageHost]` table | Copy attached images to a temporary public review location so they can render inline. |
|
|
333
|
+
|
|
334
|
+
### `[pr.imageHost]`
|
|
335
|
+
|
|
336
|
+
This optional nested table selects the one shipped image-host adapter. If the
|
|
337
|
+
table exists, all three fields are required.
|
|
338
|
+
|
|
339
|
+
| Field | Default | Constraints | Purpose |
|
|
340
|
+
|---|---:|---|---|
|
|
341
|
+
| `provider` | — | required literal `"github-release"` | Select the GitHub release adapter. |
|
|
342
|
+
| `repository` | — | required exact nonblank `owner/repo` pair | Public repository containing the existing review release. |
|
|
343
|
+
| `releaseId` | — | required positive integer | Numeric id of an existing published, mutable release. |
|
|
344
|
+
|
|
345
|
+
<!-- config-fragment:image-host -->
|
|
346
|
+
```toml
|
|
347
|
+
[pr.imageHost]
|
|
348
|
+
provider = "github-release"
|
|
349
|
+
repository = "acme/public-review-assets"
|
|
350
|
+
releaseId = 123456
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Agents opt in one artifact at a time with
|
|
354
|
+
`ab artifact put <kind> <file> --attach`. Every designation remains available
|
|
355
|
+
through a pinned `ab artifact download <build> <kind>@<rev> --output <file>`
|
|
356
|
+
command. With an image host configured, only normalized `image/*` attachments
|
|
357
|
+
are copied and rendered inline; non-images remain text-download-only.
|
|
358
|
+
|
|
359
|
+
The repository must be public because GitHub renders release assets without
|
|
360
|
+
authentication. A private source repository can name a separate public asset
|
|
361
|
+
repository. Autobuild creates no repository, release, or tag. The release must
|
|
362
|
+
already be published and mutable, and the `gh` identity running Autobuild needs
|
|
363
|
+
Contents write permission there. Obtain a release's numeric id with, for
|
|
364
|
+
example:
|
|
365
|
+
|
|
366
|
+
```sh
|
|
367
|
+
gh api repos/acme/public-review-assets/releases/tags/review-window --jq .id
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Configuring this table is an explicit temporary-public-disclosure opt-in.
|
|
371
|
+
Hosted copies are deleted after `build.completed`; failed deletions remain
|
|
372
|
+
durable and retry on later dispatcher ticks. Upload, target-validation, and
|
|
373
|
+
timeout failures create follow-up observations but preserve every text download
|
|
374
|
+
command and do not fail verification or finalize. BuildStore artifacts remain
|
|
375
|
+
the authoritative copies under the store's own retention policy — dispatcher
|
|
376
|
+
run/config artifact kinds keep their latest 200 revisions
|
|
377
|
+
(`AB_ARTIFACT_RETENTION_MAX_REVISIONS` overrides the bound); other kinds are
|
|
378
|
+
never pruned.
|
|
379
|
+
|
|
380
|
+
## `[workspace]`
|
|
381
|
+
|
|
382
|
+
Optional. Omission preserves the shipped git-worktree behavior. The surrounding
|
|
383
|
+
table is strict; only the explicit nested `config` table is plugin-owned.
|
|
384
|
+
|
|
385
|
+
| Field | Default | Constraints | Purpose |
|
|
386
|
+
|---|---:|---|---|
|
|
387
|
+
| `provider` | `"git-worktree"` | nonblank builtin or plugin-registered name | Select the provider used for provisioning, recovery, PR epilogue working-directory calls, and release. |
|
|
388
|
+
| `config` | `{}` | nested open table; must be empty for `git-worktree` | Adapter-specific declarative configuration passed unchanged to the selected plugin factory. |
|
|
389
|
+
|
|
390
|
+
<!-- config-fragment:workspace -->
|
|
391
|
+
```toml
|
|
392
|
+
[workspace]
|
|
393
|
+
provider = "company-container"
|
|
394
|
+
|
|
395
|
+
[workspace.config]
|
|
396
|
+
image = "ghcr.io/acme/build:bun"
|
|
397
|
+
writableCache = true
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The builtin `git-worktree` provider needs no table and accepts no adapter
|
|
401
|
+
configuration. A plugin factory receives exactly `[workspace.config]`, the
|
|
402
|
+
process environment, and the absolute repository root. Every declared key
|
|
403
|
+
reaches the factory verbatim. Factory invocation is lazy; unknown names fail
|
|
404
|
+
before claims and list every available builtin and plugin provider.
|
|
405
|
+
|
|
406
|
+
### Vercel Sandbox
|
|
407
|
+
|
|
408
|
+
Remote execution is explicit and requires the hosted HTTPS BuildStore plus its
|
|
409
|
+
scoped token, an HTTPS `github.com/owner/repository` origin, `forge = "github"`,
|
|
410
|
+
and Vercel authentication. Use either `VERCEL_OIDC_TOKEN`, or all of
|
|
411
|
+
`VERCEL_TOKEN`, `VERCEL_TEAM_ID`, and `VERCEL_PROJECT_ID`. The dispatcher also
|
|
412
|
+
requires a push-capable `GITHUB_TOKEN` or `GH_TOKEN`; this is validated before
|
|
413
|
+
ready tickets are listed or claimed. Keyring-only `gh auth login` is not enough
|
|
414
|
+
because publication injects the credential through Vercel's network transform.
|
|
415
|
+
|
|
416
|
+
<!-- config-fragment:workspace-vercel -->
|
|
417
|
+
```toml
|
|
418
|
+
[workspace]
|
|
419
|
+
provider = "vercel-sandbox"
|
|
420
|
+
|
|
421
|
+
[workspace.config]
|
|
422
|
+
image = "vercel/sandbox/universal:latest"
|
|
423
|
+
vcpus = 4
|
|
424
|
+
timeoutSeconds = 2700
|
|
425
|
+
operationTimeoutMs = 30000
|
|
426
|
+
snapshotExpirationSeconds = 86400
|
|
427
|
+
region = "iad1"
|
|
428
|
+
failoverRegions = ["sfo1"]
|
|
429
|
+
environmentVariables = ["AI_GATEWAY_API_KEY"]
|
|
430
|
+
provisioning = [
|
|
431
|
+
{ name = "system-install", command = """apt-get update
|
|
432
|
+
DEBIAN_FRONTEND=noninteractive apt-get install -y chromium""" },
|
|
433
|
+
{ name = "browser-smoke", command = "CHROMIUM_BIN=/usr/bin/chromium ./scripts/browser-smoke.sh" },
|
|
434
|
+
]
|
|
435
|
+
# Private repositories only:
|
|
436
|
+
gitUsernameEnv = "AB_GIT_READ_USER"
|
|
437
|
+
gitPasswordEnv = "AB_GIT_READ_TOKEN"
|
|
438
|
+
|
|
439
|
+
[workspace.config.runtimeProvisioning.pi]
|
|
440
|
+
install = "npm install --global --ignore-scripts @earendil-works/pi-coding-agent@0.84.4"
|
|
441
|
+
preflight = "test \"$(pi --version)\" = \"0.84.4\""
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
| Vercel field | Default | Constraints |
|
|
445
|
+
|---|---:|---|
|
|
446
|
+
| `image` | `vercel/sandbox/universal:latest` | `vercel/sandbox/universal`, a tag such as `:latest`, or a `@sha256:<64 hex digits>` digest |
|
|
447
|
+
| `vcpus` | `4` | integer 1–32 (account limits may be lower) |
|
|
448
|
+
| `timeoutSeconds` | — | required, integer 60–86400; VM/session lifetime and abrupt-orphan bound; Hobby currently permits at most 2700 |
|
|
449
|
+
| `operationTimeoutMs` | `30000` | integer 1000–300000; deadline for each provider acknowledgement, not session lifetime |
|
|
450
|
+
| `snapshotExpirationSeconds` | — | optional integer 300–2592000; snapshots created for this environment expire after this duration instead of the provider default (30 days); recommended 86400 (24h) |
|
|
451
|
+
| `region` | Vercel default | nonempty region |
|
|
452
|
+
| `failoverRegions` | `[]` | unique and different from `region` |
|
|
453
|
+
| `environmentVariables` | `[]` | unique API-credential variable names copied into runtime and agent/check commands |
|
|
454
|
+
| `provisioning` | `[]` | ordered strict inline tables `{ name, command }`; names and commands are nonblank and names are unique |
|
|
455
|
+
| `runtimeProvisioning` | `{}` | open map keyed by every effective role/alternate runtime; each entry requires `install` and `preflight` |
|
|
456
|
+
| `gitUsernameEnv` | — | optional variable name for the dedicated read-only clone username; requires `gitPasswordEnv` |
|
|
457
|
+
| `gitPasswordEnv` | — | optional variable name for the dedicated read-only clone password; requires `gitUsernameEnv` |
|
|
458
|
+
|
|
459
|
+
`snapshotExpirationSeconds` is a billing safety net: snapshot cleanup normally
|
|
460
|
+
purges on release, but a crash that loses the workspace before cleanup can
|
|
461
|
+
strand snapshots that hold billed storage until Vercel's 30-day default expiry.
|
|
462
|
+
A configured bound ages those stranded snapshots out instead. Omitting the key
|
|
463
|
+
keeps today's behavior (the provider default), and `0` ("no expiration") is
|
|
464
|
+
rejected because never-expiring snapshots would contradict the setting's
|
|
465
|
+
purpose.
|
|
466
|
+
|
|
467
|
+
Each `[workspace.config.runtimeProvisioning.<runtime>]` entry is strict:
|
|
468
|
+
|
|
469
|
+
| Runtime provisioning field | Constraints |
|
|
470
|
+
|---|---|
|
|
471
|
+
| `install` | required nonblank shell command; use an immutable package version, digest, or commit |
|
|
472
|
+
| `preflight` | required nonblank shell command; verify the expected executable and exact version |
|
|
473
|
+
|
|
474
|
+
Runtime names are not a built-in enum. Plugin registrations work with the same
|
|
475
|
+
map, for example `[workspace.config.runtimeProvisioning.opencode]` with a pinned
|
|
476
|
+
plugin-owned install command and its executable/version preflight.
|
|
477
|
+
|
|
478
|
+
The built-in adapter supports only Vercel's
|
|
479
|
+
`vercel/sandbox/universal` managed image, selected by its bare/default name, a
|
|
480
|
+
tag, or a digest. Other managed images and arbitrary VCR images are rejected at
|
|
481
|
+
configuration load because their package tools, libc/CPU, and filesystem
|
|
482
|
+
contract have not been validated. The universal image must retain working
|
|
483
|
+
Node/npm, `sh`, and the ordinary writable filesystem layout. Autobuild does not
|
|
484
|
+
rely on image-preinstalled Bun: on each fresh sandbox it uses npm to install the
|
|
485
|
+
pinned `bun@1.4.0` package under `/opt/autobuild-runtime`, verifies that exact
|
|
486
|
+
executable with `--version`, and uses it for Autobuild and Bun-lockfile
|
|
487
|
+
dependency installation. Package-registry network access is therefore required
|
|
488
|
+
during provisioning. The child launch prepends the adapter-owned Bun directory
|
|
489
|
+
to `PATH`, so setup, checks, agents, and their descendants resolve the same Bun.
|
|
490
|
+
|
|
491
|
+
`provisioning` is repository-owned declarative data; Autobuild never evaluates
|
|
492
|
+
configuration as code. After pinned Bun is verified, each command runs serially
|
|
493
|
+
as `sh -c <command>` with provider `sudo: true` and
|
|
494
|
+
`/vercel/sandbox/workspace` as its working directory. Distribution installation,
|
|
495
|
+
lockfile dependency bootstrap, `[commands].setup`, and every agent session occur
|
|
496
|
+
after these system steps. Put system packages here, not in repeated setup.
|
|
497
|
+
Commands that need a browser should set the repository-controlled location,
|
|
498
|
+
for example `CHROMIUM_BIN=/usr/bin/chromium bun test`; the example's checked-in
|
|
499
|
+
`scripts/browser-smoke.sh` must start a local dev server, launch
|
|
500
|
+
`"$CHROMIUM_BIN" --headless` against it in the same guest, verify the rendered
|
|
501
|
+
page, and exit nonzero on failure.
|
|
502
|
+
|
|
503
|
+
The private-repository token must grant repository contents read and no
|
|
504
|
+
contents write. It must be distinct from Forge and Vercel credentials and may
|
|
505
|
+
not also appear in `environmentVariables`. Autobuild gives its firewall broker
|
|
506
|
+
only exact upload-pack GET/POST matchers, scrubs origin credentials, credential
|
|
507
|
+
helpers, and extra headers after clone, and gives normal sessions no Forge
|
|
508
|
+
credential. After Bun verification, every declared system step runs before the
|
|
509
|
+
Autobuild distribution and dependency bootstrap. Then runtime provisioning runs
|
|
510
|
+
each referenced runtime's `install` and `preflight` commands in sorted
|
|
511
|
+
runtime-name order from the checkout. Only `environmentVariables` values enter
|
|
512
|
+
runtime commands. The same sequence runs in fresh readiness sandboxes and every
|
|
513
|
+
replacement sandbox. Provisioning writes its readiness marker only after all
|
|
514
|
+
scrubbing, Bun verification, system steps, distribution installation,
|
|
515
|
+
dependency bootstrap, and runtime preflights complete; distribution
|
|
516
|
+
packing/install disables package lifecycle scripts so
|
|
517
|
+
checkout-only hooks such as Husky are not provisioning dependencies. A Bun
|
|
518
|
+
install or verification failure identifies the configured image and expected
|
|
519
|
+
universal-image capabilities, deletes the partial environment, and leaves no
|
|
520
|
+
readiness marker. A retry also deletes any named sandbox without that marker
|
|
521
|
+
instead of adopting partial setup. The installed distribution's version is
|
|
522
|
+
recorded in the guest's `/opt/autobuild/.distribution-version` marker; a
|
|
523
|
+
reused sandbox whose marker no longer matches the version the current system
|
|
524
|
+
would deliver is refreshed by reinstalling the archive before reuse, so an
|
|
525
|
+
upgraded dispatcher retrofits its persistent guests (see "Guest distribution
|
|
526
|
+
version selection and delivery" in docs/setup.md for the release-and-upgrade
|
|
527
|
+
delivery path and the hosted store's version-lockstep gate). Immediately
|
|
528
|
+
before every build-child launch, the adapter verifies the
|
|
529
|
+
provisioned absolute Bun executable and every referenced runtime again. A
|
|
530
|
+
failure names the runtime, selecting role/alternate, stage, and provisioning
|
|
531
|
+
field, launches no child, and instructs the operator to fix/reprovision. This
|
|
532
|
+
fails closed for stale marked snapshots. Runtime installation is deliberately
|
|
533
|
+
separate from `[commands].setup`, which starts inside the runner and cannot make
|
|
534
|
+
the runtime available before the first agent session. Branch publication is
|
|
535
|
+
requested durably, then performed by the
|
|
536
|
+
local supervisor only after the remote command exits, the VM is stopped, and
|
|
537
|
+
the execution lease is released. The supervisor uses a narrow credential
|
|
538
|
+
transform for a fixed non-force push and verifies the remote head before
|
|
539
|
+
recording completion; PR API work stays local. If that acknowledgement is lost
|
|
540
|
+
after the push lands, the next launch observes the exact remote branch head and
|
|
541
|
+
records the missing completion before allowing a replacement phase to run. If
|
|
542
|
+
the commit is absent, release of the old workspace abandons its request and the
|
|
543
|
+
replacement reruns the phase. Before every guest runner launch, the provider
|
|
544
|
+
reasserts the normal receive-pack-free policy, so a failed policy
|
|
545
|
+
restore cannot expose publication authority to later setup or plugin code.
|
|
546
|
+
Ordinary completion deletes the sandbox.
|
|
547
|
+
|
|
548
|
+
A Vercel VM is disposable, not durable build state. Every execution records its
|
|
549
|
+
Autobuild instance, deterministic sandbox name, and Vercel session id. After a
|
|
550
|
+
lease expires, recovery first stops/deletes the exact old name and confirms its
|
|
551
|
+
absence; only then does it provision the next generation. An interrupted create
|
|
552
|
+
is retried with the same deterministic generation/name, so an unknown response
|
|
553
|
+
cannot fan out duplicate VMs. Recovery uses the published build branch when it
|
|
554
|
+
exists and otherwise the original recorded branch-cut SHA, never a newly moved
|
|
555
|
+
base. Setup reruns and open sessions are reclaimed by the normal durable
|
|
556
|
+
protocol. Uncommitted files and `.ab` scratch may be lost; Store events/artifacts
|
|
557
|
+
and published Git checkpoints remain authoritative.
|
|
558
|
+
|
|
559
|
+
Paused and policy-blocked builds release their sandbox after the execution
|
|
560
|
+
lease expires. Resume creates a replacement and reruns setup. Graceful shutdown
|
|
561
|
+
bounds stop/delete calls by `operationTimeoutMs`; an unknown acknowledgement
|
|
562
|
+
keeps ownership fenced until lease expiry and is retried visibly. After abrupt
|
|
563
|
+
supervisor loss, Vercel's configured `timeoutSeconds` bounds orphan compute, and
|
|
564
|
+
the next dispatcher waits for lease expiry before exact-name cleanup. Provider
|
|
565
|
+
quota/vCPU/duration messages are retained verbatim in infrastructure events and
|
|
566
|
+
status output. A failed system step records its name, exact shell command, exit
|
|
567
|
+
status, labeled stdout and stderr, and `ab init --validate` remediation. It never
|
|
568
|
+
writes the marker or launches setup/an agent; the partial sandbox is deleted.
|
|
569
|
+
The existing `infrastructure.failed` stream exposes the diagnostic in `ab
|
|
570
|
+
status` and both dashboards, consumes `maxInfrastructureAttempts`, and raises
|
|
571
|
+
one setup-targeted escalation when exhausted. If cleanup also fails, both the
|
|
572
|
+
step output and cleanup uncertainty remain durable instead of hiding the root
|
|
573
|
+
cause. A marked sandbox does not repeat provisioning; every replacement/new
|
|
574
|
+
generation does.
|
|
575
|
+
|
|
576
|
+
The representative actual-provider check is opt-in and must point at an
|
|
577
|
+
independent consuming repository whose `autobuild.toml` selects the universal
|
|
578
|
+
image above, contains one ready ticket, and declares the `system-install` and
|
|
579
|
+
`browser-smoke` provisioning steps shown above. The checked-in browser smoke
|
|
580
|
+
must use `CHROMIUM_BIN`, serve a page, render it with headless Chromium in the
|
|
581
|
+
same guest, and fail if any stage fails. With hosted Store, Vercel, GitHub,
|
|
582
|
+
and configured agent-runtime credentials available, run:
|
|
583
|
+
|
|
584
|
+
```sh
|
|
585
|
+
AB_RUN_VERCEL_SANDBOX_LIVE=1 \
|
|
586
|
+
AB_VERCEL_SANDBOX_LIVE_REPO=/absolute/path/to/consumer \
|
|
587
|
+
AB_STORE=https://store.example AB_TOKEN=… \
|
|
588
|
+
bun test packages/core/src/integration/vercel-sandbox.live.test.ts
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
The suite first runs `ab init` readiness validation through the production
|
|
592
|
+
adapter: it creates an unnamed fresh sandbox, reports both named provisioning
|
|
593
|
+
steps, accepts the structured guest probe marker, and verifies deletion by
|
|
594
|
+
querying that exact sandbox identity. A separate
|
|
595
|
+
case runs a complete child-driven build to publish and finalize, then requests
|
|
596
|
+
cleanup and verifies sandbox deletion. It does not demonstrate support for other
|
|
597
|
+
Vercel images or package managers.
|
|
598
|
+
|
|
599
|
+
Vercel workspaces return an absolute guest `path` plus an opaque sandbox-name
|
|
600
|
+
`ref`; they omit dispatcher-local path evidence. Branch config, relative/package
|
|
601
|
+
plugins, installed skills, setup, runtimes, checks, and phase CLI commands all
|
|
602
|
+
resolve in the guest checkout. Only names in `environmentVariables` plus scoped `AB_STORE`/`AB_TOKEN` enter
|
|
603
|
+
commands—dispatcher environment variables are never copied wholesale. Pi in a
|
|
604
|
+
sandbox should use an API-key provider such as `vercel-ai-gateway/...` with
|
|
605
|
+
`AI_GATEWAY_API_KEY`; never copy dispatcher `~/.pi` state, OAuth refresh tokens,
|
|
606
|
+
or attempt an interactive login. The same API-key-only rule applies to Codex,
|
|
607
|
+
Claude Code, and plugin runtimes.
|
|
608
|
+
|
|
609
|
+
The opt-in live lifecycle suite is
|
|
610
|
+
`packages/core/src/integration/vercel-sandbox.live.test.ts`. Set
|
|
611
|
+
`AB_RUN_VERCEL_SANDBOX_LIVE=1`, `AB_VERCEL_SANDBOX_LIVE_REPO` to an independent
|
|
612
|
+
checkout with a ready file ticket and this provider configuration, plus the
|
|
613
|
+
hosted Store, Vercel, runtime, and GitHub credentials above. Its readiness case
|
|
614
|
+
creates and probes a fresh sandbox, parses the guest marker, and confirms
|
|
615
|
+
deletion by exact identity. Separately, its interruption case deletes the first
|
|
616
|
+
build sandbox while its durable execution identity is live, waits through the
|
|
617
|
+
lease fence, and asserts a distinct replacement sandbox/session identity,
|
|
618
|
+
successful continuation, and final provider cleanup. The suite is credentialed,
|
|
619
|
+
long-running, and mutates the configured test repository/Store; it is skipped
|
|
620
|
+
unless the opt-in flag is set.
|
|
621
|
+
|
|
622
|
+
## `[commands]`
|
|
623
|
+
|
|
624
|
+
An optional open map of repository-defined names to shell strings. Its default
|
|
625
|
+
is `{}`. `setup`, `lint`, `type-check`, and `test` are conventions, not fixed
|
|
626
|
+
schema fields.
|
|
627
|
+
|
|
628
|
+
| Field | Default | Constraints | Purpose |
|
|
629
|
+
|---|---:|---|---|
|
|
630
|
+
| `<name>` | — | nonempty key and nonempty shell string | Name a deterministic verb for setup or a check step. |
|
|
631
|
+
|
|
632
|
+
<!-- config-fragment:commands -->
|
|
633
|
+
```toml
|
|
634
|
+
[commands]
|
|
635
|
+
setup = "bun install"
|
|
636
|
+
typecheck = "bun tsc --noEmit"
|
|
637
|
+
test = "bun test"
|
|
638
|
+
publish = "bun run publish"
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
`setup` is special by convention: the kernel runs it after workspace provision
|
|
642
|
+
and after sandbox rehydration. A failure is recorded durably with its command,
|
|
643
|
+
exit status, attempt, and output. Retries are bounded by
|
|
644
|
+
`policy.maxSetupAttempts`; exhaustion blocks on a human-answerable setup
|
|
645
|
+
escalation without creating a phase or verification result. A later successful
|
|
646
|
+
attachment clears the current error projection. Other names run only when
|
|
647
|
+
referenced by a configured check. A check's `command` value is the key in this
|
|
648
|
+
map, not an inline shell command. The kernel passes the mapped string to a shell
|
|
649
|
+
as written.
|
|
650
|
+
|
|
651
|
+
## `[verify]` and `[verify.<step>]`
|
|
652
|
+
|
|
653
|
+
`[verify].steps` defines the configured universe and execution order. It is
|
|
654
|
+
optional and defaults to no verify phases. Every listed name needs one matching
|
|
655
|
+
subtable, and every named subtable must be listed; a defined-but-unlisted step
|
|
656
|
+
never silently disappears.
|
|
657
|
+
|
|
658
|
+
There are two strict step variants:
|
|
659
|
+
|
|
660
|
+
- `kind = "check"` runs a configured command directly and decides pass/fail
|
|
661
|
+
from its exit status. It starts no agent session.
|
|
662
|
+
- `kind = "agent"` runs the exact configured skill, which terminates with an
|
|
663
|
+
`ab verdict` of `pass`, `fail`, or `skip`.
|
|
664
|
+
|
|
665
|
+
| Field | Default | Constraints | Purpose |
|
|
666
|
+
|---|---:|---|---|
|
|
667
|
+
| `steps` | `[]` | array of nonempty step names | Canonical execution order. |
|
|
668
|
+
| `kind` | — | required `"check"` or `"agent"` | Select the strict step variant. |
|
|
669
|
+
| `command` | — | required for check; nonempty key in `[commands]` | Deterministic shell verb to run. Forbidden for agent steps. |
|
|
670
|
+
| `skill` | — | required for agent; nonempty string | Exact installed verifier skill. Forbidden for check steps. |
|
|
671
|
+
| `paths` | omitted (unconditional) | optional nonempty array of positive repository-relative globs | Apply only when at least one changed path matches. Available to both variants. |
|
|
672
|
+
| `always` | omitted (`false`) | optional boolean | `true` makes the step mandatory in plan selection and bypasses path gating. Available to both variants. |
|
|
673
|
+
|
|
674
|
+
<!-- config-fragment:verify -->
|
|
675
|
+
```toml
|
|
676
|
+
[commands]
|
|
677
|
+
typecheck = "bun tsc --noEmit"
|
|
678
|
+
|
|
679
|
+
[verify]
|
|
680
|
+
steps = ["types", "e2e"]
|
|
681
|
+
|
|
682
|
+
[verify.types]
|
|
683
|
+
kind = "check"
|
|
684
|
+
command = "typecheck"
|
|
685
|
+
always = true
|
|
686
|
+
|
|
687
|
+
[verify.e2e]
|
|
688
|
+
kind = "agent"
|
|
689
|
+
skill = "verify-app-e2e" # repository-authored skill
|
|
690
|
+
paths = ["web/**", "src/routes/**"]
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
Autobuild does not ship a generic agent verifier. `verify-app-e2e` above is a
|
|
694
|
+
skill authored by the repository for its own running application.
|
|
695
|
+
|
|
696
|
+
Cross-field validation rejects:
|
|
697
|
+
|
|
698
|
+
- a listed step without its `[verify.<step>]` table;
|
|
699
|
+
- a named table not present in `steps`;
|
|
700
|
+
- a check whose command is absent from `[commands]`;
|
|
701
|
+
- fields from the other kind, unknown fields, and malformed selectors.
|
|
702
|
+
|
|
703
|
+
`always = true` does not make malformed `paths` acceptable: all supplied
|
|
704
|
+
selectors are validated even though the mandatory step will not use them for
|
|
705
|
+
applicability.
|
|
706
|
+
|
|
707
|
+
### Role routing for an agent verify step
|
|
708
|
+
|
|
709
|
+
An agent verify step selects `[roles.<step>]` by its **logical step name** —
|
|
710
|
+
the name in `[verify].steps`, not the skill it runs. This is the same rule an
|
|
711
|
+
agent finalize post-step follows.
|
|
712
|
+
|
|
713
|
+
<!-- config-fragment:verify-role -->
|
|
714
|
+
```toml
|
|
715
|
+
[verify]
|
|
716
|
+
steps = ["e2e"]
|
|
717
|
+
|
|
718
|
+
[verify.e2e]
|
|
719
|
+
kind = "agent"
|
|
720
|
+
skill = "verify-app-e2e" # repository-authored skill
|
|
721
|
+
|
|
722
|
+
[roles.e2e] # the STEP name — not "verify-app-e2e"
|
|
723
|
+
runtime = "pi"
|
|
724
|
+
model = "openai-codex/gpt-5.6-sol"
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
The step's configured skill name remains a deprecated alias for existing
|
|
728
|
+
configurations and will be removed in a future release. It is consulted only
|
|
729
|
+
when `[roles.<step>]` is undeclared, so the step name always wins when both are
|
|
730
|
+
present, and `ab dispatch` reports the alias with the step name to rename it to.
|
|
731
|
+
|
|
732
|
+
### Plan-selected steps
|
|
733
|
+
|
|
734
|
+
A plan may open with strict TOML front matter naming the complete set of
|
|
735
|
+
optional verification warranted by that plan:
|
|
736
|
+
|
|
737
|
+
<!-- plan-front-matter -->
|
|
738
|
+
```toml
|
|
739
|
+
+++
|
|
740
|
+
verifySteps = ["types", "e2e"]
|
|
741
|
+
+++
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
No opening metadata selects every configured step, preserving older plans. An
|
|
745
|
+
explicit `verifySteps = []` selects none and is valid only when no configured
|
|
746
|
+
step has `always = true`. Names must be known, nonempty, unpadded, and unique.
|
|
747
|
+
Unknown names, duplicate names, malformed metadata, or omission of a mandatory
|
|
748
|
+
step make the planner's `ab done` fail before `plan.completed` is recorded.
|
|
749
|
+
|
|
750
|
+
The list is a set: its written order never reorders execution. The selected
|
|
751
|
+
steps are stored in `[verify].steps` order from the exact plan completion later
|
|
752
|
+
approved by plan review. A spec restart replaces the selection with the newly
|
|
753
|
+
approved plan; reconciliation reuses it.
|
|
754
|
+
|
|
755
|
+
For each step the kernel evaluates approved-plan selection first, then path
|
|
756
|
+
applicability. Exclusion by either mechanism records a queryable `skipped`
|
|
757
|
+
outcome. A selection exclusion or path miss starts no check or agent.
|
|
758
|
+
An `always = true` step cannot be excluded by the plan and runs regardless of
|
|
759
|
+
its `paths` value.
|
|
760
|
+
|
|
761
|
+
### Path applicability
|
|
762
|
+
|
|
763
|
+
Selectors are case-sensitive over Git's `/`-separated repository-relative
|
|
764
|
+
paths. Supported syntax is literal characters, `*`, `?`, and `**` only as a
|
|
765
|
+
complete path segment. The following are rejected: absolute paths, `.` or `..`
|
|
766
|
+
traversal segments, empty segments, NUL bytes, negation,
|
|
767
|
+
backslashes/escapes, character classes, brace expansion, extglobs, and `**`
|
|
768
|
+
embedded in another segment. Selectors and changed paths use any-match
|
|
769
|
+
semantics.
|
|
770
|
+
|
|
771
|
+
Immediately before a selected conditional step, the runner performs a
|
|
772
|
+
NUL-delimited, no-rename Git name diff from the initial branch-cut SHA to the
|
|
773
|
+
current `HEAD`. Adds, modifications, deletions, and both sides of a rename can
|
|
774
|
+
therefore make a step apply. After a completed reconcile, the refreshed base
|
|
775
|
+
becomes the diff base: upstream-only paths merged from the base are excluded,
|
|
776
|
+
while build-owned changes and conflict resolutions remain visible. A Git or
|
|
777
|
+
base-resolution failure is an infrastructure failure, never a permissive skip.
|
|
778
|
+
|
|
779
|
+
### Pass, fail, and skip
|
|
780
|
+
|
|
781
|
+
A passing check or agent advances to the next step. An agent failure requires
|
|
782
|
+
`ab verdict fail --report <file>`; the exact report is routed back to implement,
|
|
783
|
+
and the failure consumes the verify retry budget in
|
|
784
|
+
`policy.maxVerifyAttempts`. Check failures similarly retain command output as
|
|
785
|
+
the implementation feedback.
|
|
786
|
+
|
|
787
|
+
An agent may instead use `ab verdict skip --reason <text>` when the configured
|
|
788
|
+
judgment genuinely does not apply. The trimmed reason must be nonblank, and no
|
|
789
|
+
failure report is required. Plan and path exclusions are kernel-authored skips
|
|
790
|
+
with deterministic reasons. Every skip satisfies that one step and advances,
|
|
791
|
+
but remains distinct from a pass, does not mask another step's failure, and
|
|
792
|
+
does not consume the failure retry budget.
|
|
793
|
+
|
|
794
|
+
## `[finalize]` and `[finalize.<step>]`
|
|
795
|
+
|
|
796
|
+
`[finalize].steps` is an optional ordered list of post-PR actions and defaults
|
|
797
|
+
to `[]`. These run after the finalize agent writes the PR description and the
|
|
798
|
+
kernel opens the PR. Like verify, every listed name requires one matching table
|
|
799
|
+
and every table must be listed.
|
|
800
|
+
|
|
801
|
+
Finalize has a smaller strict union. Verify-only `paths` and `always` fields
|
|
802
|
+
are errors.
|
|
803
|
+
|
|
804
|
+
| Field | Default | Constraints | Purpose |
|
|
805
|
+
|---|---:|---|---|
|
|
806
|
+
| `steps` | `[]` | array of nonempty step names | Ordered post-PR actions. |
|
|
807
|
+
| `kind` | — | required `"check"` or `"agent"` | Select deterministic command or agent action. |
|
|
808
|
+
| `command` | — | required for check; nonempty key in `[commands]` | Run the mapped command with no agent session. |
|
|
809
|
+
| `skill` | — | required for agent; nonempty string | Exact installed skill; no prefix is inferred. |
|
|
810
|
+
|
|
811
|
+
<!-- config-fragment:finalize -->
|
|
812
|
+
```toml
|
|
813
|
+
[commands]
|
|
814
|
+
publish = "bun run publish"
|
|
815
|
+
|
|
816
|
+
[finalize]
|
|
817
|
+
steps = ["publish", "release-notes"]
|
|
818
|
+
|
|
819
|
+
[finalize.publish]
|
|
820
|
+
kind = "check"
|
|
821
|
+
command = "publish"
|
|
822
|
+
|
|
823
|
+
[finalize.release-notes]
|
|
824
|
+
kind = "agent"
|
|
825
|
+
skill = "ab-release-notes"
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
The logical step name selects `[roles.<step>]` for an agent post-step — the
|
|
829
|
+
same rule an agent verify step follows, so one convention covers both:
|
|
830
|
+
|
|
831
|
+
<!-- config-fragment:finalize-role -->
|
|
832
|
+
```toml
|
|
833
|
+
[verify]
|
|
834
|
+
steps = ["e2e"]
|
|
835
|
+
|
|
836
|
+
[verify.e2e]
|
|
837
|
+
kind = "agent"
|
|
838
|
+
skill = "verify-app-e2e" # repository-authored skill
|
|
839
|
+
|
|
840
|
+
[finalize]
|
|
841
|
+
steps = ["release-notes"]
|
|
842
|
+
|
|
843
|
+
[finalize.release-notes]
|
|
844
|
+
kind = "agent"
|
|
845
|
+
skill = "ab-release-notes"
|
|
846
|
+
|
|
847
|
+
[roles.e2e] # verify STEP name — not "verify-app-e2e"
|
|
848
|
+
runtime = "pi"
|
|
849
|
+
|
|
850
|
+
[roles.release-notes] # finalize step name
|
|
851
|
+
runtime = "pi"
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
For an agent *verify* step, the step's configured skill name remains a
|
|
855
|
+
deprecated alias for existing configurations and will be removed in a future
|
|
856
|
+
release. Finalize has never had such an alias.
|
|
857
|
+
|
|
858
|
+
Both
|
|
859
|
+
kinds are failure-tolerant: nonzero commands, launch/execution errors, and
|
|
860
|
+
structured agent failures record `ok = false` plus a follow-up observation,
|
|
861
|
+
then the sequence continues. A post-step cannot turn an otherwise green build
|
|
862
|
+
red.
|
|
863
|
+
|
|
864
|
+
A content-producing step selects and commits only its intended files locally
|
|
865
|
+
and must leave a clean worktree. The runner proves the last published head is
|
|
866
|
+
an ancestor, then performs a regular non-force push through the Forge port to
|
|
867
|
+
extend the open PR branch. Agents never push. An unchanged `HEAD` creates and
|
|
868
|
+
pushes no commit. Dirty output, rewritten history, Git errors, and publication
|
|
869
|
+
failures become failure-tolerant follow-up observations.
|
|
870
|
+
|
|
871
|
+
## `[roles]`
|
|
872
|
+
|
|
873
|
+
An open map from a nonempty role name to three primary agent axes, one
|
|
874
|
+
wall-clock session budget, and an ordered alternate list. All five live fields
|
|
875
|
+
inherit independently. The reserved
|
|
876
|
+
`default` entry is required to name a runtime, is the raw base for every other
|
|
877
|
+
role, and is never dispatched itself. Missing it fails eagerly before any
|
|
878
|
+
session starts, with a copyable table and all registered runtime names.
|
|
879
|
+
|
|
880
|
+
| Field | Default | Constraints | Purpose |
|
|
881
|
+
|---|---:|---|---|
|
|
882
|
+
| `runtime` | inherited from required `[roles.default].runtime` | required on `default`; optional nonempty registered runtime name on children | Select an agent adapter. |
|
|
883
|
+
| `model` | inherited; otherwise selected runtime's own default | optional nonempty model id compatible with the resolved runtime | Select the exact model. Codex uses unqualified `gpt-*`; Pi ids are provider-qualified. |
|
|
884
|
+
| `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
|
+
| `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
|
+
| `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. |
|
|
888
|
+
|
|
889
|
+
<!-- config-fragment:roles -->
|
|
890
|
+
```toml
|
|
891
|
+
[roles.default]
|
|
892
|
+
runtime = "claude"
|
|
893
|
+
args = []
|
|
894
|
+
alternates = [
|
|
895
|
+
{ runtime = "pi", model = "openai-codex/gpt-5.6-sol", args = ["--extension", "./tools/pi.js"] },
|
|
896
|
+
]
|
|
897
|
+
|
|
898
|
+
[roles.plan]
|
|
899
|
+
args = ["--effort", "high"]
|
|
900
|
+
alternates = [] # replace the inherited list for this role
|
|
901
|
+
|
|
902
|
+
[roles.code-review]
|
|
903
|
+
runtime = "pi"
|
|
904
|
+
model = "kimi-coding/k3"
|
|
905
|
+
args = ["--extension", "./tools/web-access.js"]
|
|
906
|
+
sessionBudgetSeconds = 7200
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
Inheritance is mechanical per field. For example, changing only a child
|
|
910
|
+
runtime does not discard a model inherited from `default`; the resulting exact
|
|
911
|
+
pair must be compatible. Autobuild never searches for a runtime that happens
|
|
912
|
+
to serve a configured model and never substitutes a different model to repair
|
|
913
|
+
an invalid pair. The only implicit fill is when neither the role nor `default`
|
|
914
|
+
names a model, in which case the selected runtime uses its own default. Every
|
|
915
|
+
alternate overlays that concrete role's effective primary runtime, model, and
|
|
916
|
+
args, then undergoes the same exact-pair validation. Each configured string is
|
|
917
|
+
one argv token; Autobuild does not shell-expand it. The narrow rejected set is
|
|
918
|
+
derived from the model and machine-readable protocol options each adapter emits:
|
|
919
|
+
Claude rejects `-p`/`--print`, `--output-format`, and `--model`; Codex rejects
|
|
920
|
+
`--json` and `--model`/`-m`; Pi rejects `--mode` and `--model`. Claude and Codex
|
|
921
|
+
also reject a standalone `--` because Autobuild owns and appends that structural
|
|
922
|
+
separator immediately before their positional prompt. Those option spellings,
|
|
923
|
+
`--option=value` forms, compact documented short aliases, and the exact affected
|
|
924
|
+
prompt separator join the eager aggregated startup error with a
|
|
925
|
+
role/runtime-specific diagnostic. Every other option passes through without
|
|
926
|
+
Autobuild validation, including sandbox, approval, session, tool, skill,
|
|
927
|
+
thinking, extension, lifecycle, unknown options, and arguments that merely
|
|
928
|
+
contain two hyphens, even when they supplement another occurrence emitted by the
|
|
929
|
+
adapter. Unknown fields,
|
|
930
|
+
runtimes, and incompatible models in any indexed entry join the aggregated
|
|
931
|
+
eager startup error; they never first surface during an outage.
|
|
932
|
+
|
|
933
|
+
Three runtimes ship: `claude`, `codex`, and `pi`; `ab plugin list` projects all
|
|
934
|
+
three as builtin agent runtimes, and trusted plugins may register additional
|
|
935
|
+
names. The `pi` runtime requires a locally installed Pi CLI version 0.84.3 or
|
|
936
|
+
newer and uses that installation's login and model catalog through headless RPC;
|
|
937
|
+
Autobuild does not install Pi. `ab init` reports a missing or older executable
|
|
938
|
+
as unusable, and `pi` must be logged in for every configured model. `ab models`
|
|
939
|
+
lists this local catalog, while `ab models --available` limits it to models with
|
|
940
|
+
configured credentials. Builtin and plugin runtimes use the same exact-pair
|
|
941
|
+
validation and event attribution. With no configured model, the selected runtime uses its declared
|
|
942
|
+
default when present: Claude and Codex delegate to their local CLI's selected
|
|
943
|
+
default, while Pi declares `kimi-coding/k3`. Codex accepts unqualified `gpt-*`
|
|
944
|
+
ids. Pi ids remain provider-qualified—including the independent
|
|
945
|
+
`openai-codex/*` route—and `ab models [query]` looks them up. Pi always passes
|
|
946
|
+
`--no-extensions` to disable ambient package discovery and loads the Autobuild
|
|
947
|
+
bridge explicitly. Repeatable `--extension <path>` tokens in `args` supplement
|
|
948
|
+
that setup and expose every tool registered by that explicitly loaded extension;
|
|
949
|
+
without them, no operator-installed extension loads. A plugin may declare
|
|
950
|
+
optional tool-free one-shot completion for `slug` and `upgrade`; absence keeps
|
|
951
|
+
each caller's existing fail-safe behavior. Role `args` apply to those one-shots
|
|
952
|
+
too. Effective arguments are recorded on session-start facts.
|
|
953
|
+
|
|
954
|
+
The injected Codex protocol/contract suite is deterministic and credential-free:
|
|
955
|
+
`bun test packages/core/src/ports/runner/codex.test.ts`. An authenticated local smoke run is
|
|
956
|
+
opt-in:
|
|
957
|
+
`AB_RUN_LIVE_PORT_CONTRACTS=1 AB_CODEX_CONTRACT_MODEL=gpt-… bun test packages/core/src/ports/runner/codex.live.test.ts`.
|
|
958
|
+
|
|
959
|
+
Core agent phases route by phase name (`plan`, `plan-review`, `implement`,
|
|
960
|
+
`code-review`, `finalize`, and `reconcile`). Agent verify steps and agent
|
|
961
|
+
finalize post-steps both route by their logical step name. The verify step's
|
|
962
|
+
configured skill name remains a deprecated alias for existing configurations
|
|
963
|
+
and will be removed in a future release. Repository judgments use `harvest` and
|
|
964
|
+
`harvest-review`; `slug` and `upgrade` configure tool-free one-shot judgments.
|
|
965
|
+
Arbitrary additional role keys are accepted, but only a name selected by one of
|
|
966
|
+
these routes affects a session.
|
|
967
|
+
|
|
968
|
+
A declared key that no route requests is resolved and validated like any other,
|
|
969
|
+
and then never used — `ab dispatch` reports it at startup, naming the key and
|
|
970
|
+
the keys valid for this configuration, and reports a deprecated skill-name key
|
|
971
|
+
with the step name to rename it to. Both stay warnings; neither blocks a
|
|
972
|
+
session or changes which runtime and model run. `kind = "check"` steps start no
|
|
973
|
+
session and so are not a route: naming a role after one does not make it
|
|
974
|
+
consumed. It is reported only when that check step is its sole apparent route —
|
|
975
|
+
a check step named `plan` leaves `[roles.plan]` consumed by the core `plan`
|
|
976
|
+
phase, and nothing is reported.
|
|
977
|
+
|
|
978
|
+
Resolver construction validates `default`, every declared role, and every
|
|
979
|
+
alternate eagerly and aggregates all unknown-runtime and incompatible-model
|
|
980
|
+
problems. Unknown-runtime diagnostics list every builtin and materialized
|
|
981
|
+
plugin runtime. A deliberately different reviewer model is valid and often
|
|
982
|
+
useful; mixed models are not a configuration inconsistency.
|
|
983
|
+
|
|
984
|
+
Each session attempt starts with its role's primary. Overload, rate limits, 5xx,
|
|
985
|
+
timeout, transport, unknown provider failures, and quota/usage/billing
|
|
986
|
+
exhaustion try alternates in declaration order inside that same phase attempt.
|
|
987
|
+
Authentication, permission, and local runtime-configuration failures do not.
|
|
988
|
+
Each target gets a separate session and transcript; a continuation that moves
|
|
989
|
+
to another target starts fresh from durable context and cannot inherit the
|
|
990
|
+
failed provider's conversation. Selection is not sticky: the next phase, next
|
|
991
|
+
review-loop continuation, and next attempt begin at the primary again.
|
|
992
|
+
|
|
993
|
+
Trying alternates does not consume additional phase attempts. Only an exhausted
|
|
994
|
+
or non-eligible chain writes `phase.failed`; its final failure controls retry
|
|
995
|
+
policy, so final quota/usage/billing exhaustion parks immediately while a final
|
|
996
|
+
availability failure consumes one existing bounded attempt. `session.started`
|
|
997
|
+
records each selected runtime/model and substitution cause, and an exhausted
|
|
998
|
+
failure records every tried target and verbatim error for policy escalation.
|
|
999
|
+
The chain applies to core phases, agent verify/finalize steps, and Harvest.
|
|
1000
|
+
Tool-free one-shot completions such as slug and upgrade use only the primary.
|
|
1001
|
+
|
|
1002
|
+
For build sessions, the kernel starts the captured role budget after the durable
|
|
1003
|
+
`session.started` event. Expiry aborts and ends the session best-effort, drops
|
|
1004
|
+
producer continuation state, and records `phase.failed` with
|
|
1005
|
+
`phase session budget expired after <seconds> seconds`. It does not select an
|
|
1006
|
+
alternate because it is kernel policy, not a provider failure. The ordinary
|
|
1007
|
+
phase-attempt guard retries from the primary and raises an answerable policy
|
|
1008
|
+
escalation when exhausted. Operator cancellation does not disable the captured
|
|
1009
|
+
deadline: a cooperative adapter still returns promptly, while an adapter that
|
|
1010
|
+
ignores cancellation is released when the deadline arrives. If operator abort
|
|
1011
|
+
arrived first, that release stays an abort control outcome, starts no alternate,
|
|
1012
|
+
and records no `phase.failed`. A typed terminal deposited before either boundary
|
|
1013
|
+
remains authoritative. Agent finalize post-step expiry instead records that
|
|
1014
|
+
failure-tolerant step as `ok = false` and files its follow-up observation.
|
|
1015
|
+
Harvest sessions and direct verify/finalize check commands are not covered by
|
|
1016
|
+
this setting.
|
|
1017
|
+
|
|
1018
|
+
## `[policy]`
|
|
1019
|
+
|
|
1020
|
+
Optional. Every field receives its own default. All are positive integers except
|
|
1021
|
+
`harvestMaxDrift`, which is nonnegative so zero can disable that trigger.
|
|
1022
|
+
|
|
1023
|
+
| Field | Default | Constraints | Purpose |
|
|
1024
|
+
|---|---:|---|---|
|
|
1025
|
+
| `sessionBudgetSeconds` | `3600` | positive integer | Wall-clock bound for each build agent session unless its logical role overrides it. |
|
|
1026
|
+
| `stallRounds` | `3` | positive integer | Escalate when the same review finding survives this many rounds. |
|
|
1027
|
+
| `maxVerifyAttempts` | `3` | positive integer | Bound failure-driven verify → implement retry cycles. |
|
|
1028
|
+
| `maxSetupAttempts` | `3` | positive integer | Bound consecutive workspace setup failures before human escalation. |
|
|
1029
|
+
| `maxInfrastructureAttempts` | `3` | positive integer | Bound consecutive provision/start/wait/cleanup failures before a setup-targeted policy escalation; a human `retry` answer re-arms this budget. |
|
|
1030
|
+
| `maxReconcileAttempts` | `3` | positive integer | Bound completed reconciles that leave the PR conflicted against an unchanged authoritative base. Moving-base races do not consume the bound. |
|
|
1031
|
+
| `maxReviewRounds` | `6` | positive integer | Default bound for each plan/review and implement/review convergence loop. An operator may replace it for one parked build loop and current spec revision with `ab answer --review-round-ceiling`; other builds are unaffected. |
|
|
1032
|
+
| `harvestThreshold` | `5` | positive integer | New unclaimed observation occurrences needed to start one harvest run. |
|
|
1033
|
+
| `harvestMaxDrift` | `3` | nonnegative integer | Other builds merged after the oldest unclaimed observation needed to start a run; `0` disables drift. |
|
|
1034
|
+
|
|
1035
|
+
For each repeat conflict, Autobuild compares the base merged by the most recent
|
|
1036
|
+
completed reconcile with a fresh authoritative base snapshot. An advanced base
|
|
1037
|
+
proves the attempt lost a race and permits another reconcile regardless of the
|
|
1038
|
+
attempt high-water. An unchanged base consumes `maxReconcileAttempts` and
|
|
1039
|
+
escalates when the bound is exhausted. Every current policy escalation carries
|
|
1040
|
+
a closed `policyCause`; reconcile no-progress uses `reconcile-no-progress`,
|
|
1041
|
+
while `round` remains occurrence scope. Answering a runner-failure cause re-arms
|
|
1042
|
+
only its matching phase and round. The phase-level `verify-failure-limit` cause
|
|
1043
|
+
explicitly re-arms all rounds for its verify target, while
|
|
1044
|
+
`reconcile-no-progress` and the setup-only cause reset no phase-runner failures.
|
|
1045
|
+
The closed cause map forces every future policy condition to choose one of these
|
|
1046
|
+
semantics instead of inheriting behavior from a missing `round`. Runner failures
|
|
1047
|
+
while checking progress or refreshing the base therefore remain counted after a
|
|
1048
|
+
roundless no-progress answer. If the unchanged-base bound is already exhausted,
|
|
1049
|
+
Autobuild raises the matching no-progress escalation before starting another
|
|
1050
|
+
reconcile. Cause-less historical raises retain their former round-shaped reset
|
|
1051
|
+
behavior, and roundless policy/reconcile raises retain their former no-progress
|
|
1052
|
+
meaning, on replay without migration. Post-reconcile verification still runs in
|
|
1053
|
+
full and remains independently bounded by `maxVerifyAttempts`.
|
|
1054
|
+
|
|
1055
|
+
Harvest is driven by repository pressure during dispatcher ticks, not a wall
|
|
1056
|
+
clock, and is independent of build `capacity`. A run starts when observation
|
|
1057
|
+
count reaches `harvestThreshold` **or** drift reaches `harvestMaxDrift`. Drift
|
|
1058
|
+
uses the oldest unclaimed observation, counts only later same-repository
|
|
1059
|
+
`pr.merged` facts from other builds, and ignores aborted and closed-unmerged
|
|
1060
|
+
builds. Whichever trigger fires, Harvest claims the complete current
|
|
1061
|
+
accumulation and records `count`, `drift`, or `both` on the durable start fact.
|
|
1062
|
+
The dashboard header reports
|
|
1063
|
+
`queue <depth> | active <current>/<limit> | observations <current>/<limit>`.
|
|
1064
|
+
The observation current value is the unclaimed occurrence count and its limit is
|
|
1065
|
+
`policy.harvestThreshold`. The interactive frontend refreshes that display-only
|
|
1066
|
+
count directly from the BuildStore, retains the last successful value with a
|
|
1067
|
+
diagnostic after a failed refresh, and appends no transport event. The separate
|
|
1068
|
+
`policy.harvestMaxDrift` trigger is configured here but is not shown in the
|
|
1069
|
+
header. The repository lease and fixed per-run recovery budget are implementation
|
|
1070
|
+
invariants, not additional configuration fields.
|
|
1071
|
+
|
|
1072
|
+
## `[tickets]`
|
|
1073
|
+
|
|
1074
|
+
Required in practice: `readyState` deliberately has no default, so omitting the
|
|
1075
|
+
table fails at `tickets.readyState` rather than allowing tickets from every
|
|
1076
|
+
state. Within a present table, `source` and `readyState` are required.
|
|
1077
|
+
|
|
1078
|
+
| Field | Default | Constraints | Purpose |
|
|
1079
|
+
|---|---:|---|---|
|
|
1080
|
+
| `source` | — | required nonblank builtin or plugin registration name | Select the ticket adapter after configured plugins load. |
|
|
1081
|
+
| `readyLabels` | source-aware | optional array of nonempty strings; `[]` allowed | Require every listed label in addition to the state gate. |
|
|
1082
|
+
| `readyState` | — | required nonblank string | The one workflow state eligible for dispatch. |
|
|
1083
|
+
| `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
|
+
| `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
|
+
| `createState` | provider default | optional nonempty string | Default state for newly created tickets when `ab ticket create` omits `--state`. |
|
|
1086
|
+
| `triageState` | Linear: `"Backlog"`; file/plugin: `"Triage"` | optional nonempty string | State used for spec-gate bounces, aborts, and closed-unmerged PRs. |
|
|
1087
|
+
| `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
|
+
| `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
|
+
|
|
1090
|
+
When `readyLabels` is absent, direct Linear uses `["autobuild"]`; hosted, file,
|
|
1091
|
+
and plugin sources use `[]`, meaning no host-imposed label gate. An explicit value always
|
|
1092
|
+
wins. A nonempty list is
|
|
1093
|
+
conjunctive: every configured label must be present. `readyState` remains
|
|
1094
|
+
mandatory regardless of labels. Linear compares state and label names exactly
|
|
1095
|
+
and case-sensitively. The file source accepts state names case-insensitively and
|
|
1096
|
+
canonicalizes them to `triage/`, `ready/`, `doing/`, or `done/`.
|
|
1097
|
+
|
|
1098
|
+
Source-specific validation is strict:
|
|
1099
|
+
|
|
1100
|
+
- Linear and hosted require `teamKey` and reject `dir`.
|
|
1101
|
+
- File rejects `teamKey` and `claimedState`; `dir` is optional.
|
|
1102
|
+
- `createState`, `triageState`, and `proposalState` are valid for every source,
|
|
1103
|
+
but the named state must exist in that provider when used.
|
|
1104
|
+
- Plugin sources receive the existing fields in this table unchanged and own
|
|
1105
|
+
any additional semantic validation. No untyped plugin-options table exists.
|
|
1106
|
+
|
|
1107
|
+
For Linear, omitting `createState` lets the team's default state apply. For the
|
|
1108
|
+
file source it defaults to `Triage`. A caller may override that default for one
|
|
1109
|
+
create with `ab ticket create --state <state>`; the value is passed unchanged to
|
|
1110
|
+
the selected source, which owns its workflow vocabulary and rejects unknown
|
|
1111
|
+
states before creating anything. An omitted Linear `triageState` uses `Backlog`,
|
|
1112
|
+
because every team has it while the optional Linear triage feature may be
|
|
1113
|
+
disabled. The file source uses `Triage`.
|
|
1114
|
+
|
|
1115
|
+
`proposalState` names the one state observation harvest files proposals into,
|
|
1116
|
+
and defaults to the resolved `triageState` — proposals wait for a human, which
|
|
1117
|
+
is the grooming gate the pipeline is built around. Naming `readyState` here
|
|
1118
|
+
waives that gate for this repository: every proposal the harvest loop approves
|
|
1119
|
+
enters the ordinary dispatch eligibility checks without being read, protected
|
|
1120
|
+
by that loop's own review and by the spec gate at dispatch. The producer and
|
|
1121
|
+
reviewer see scan-time existence and resolution for every distinct ticket that
|
|
1122
|
+
originated a claimed observation. Approved creates may carry evidence-backed
|
|
1123
|
+
source-local `blockedBy` ids for other prerequisites. Immediately before create,
|
|
1124
|
+
harvest refreshes declared ids and matching-source origins through the selected
|
|
1125
|
+
TicketSource. Unknown declared ids fail; missing or resolved origins are dropped;
|
|
1126
|
+
still-unresolved origins are deduplicated into `blockedBy` automatically.
|
|
1127
|
+
Harvest status and the filing report distinguish declared blockers from those
|
|
1128
|
+
derived from originating tickets. An unresolved harvested blocker still
|
|
1129
|
+
prevents claim until the source reports completion or the relationship is
|
|
1130
|
+
deliberately removed. It is a separate field precisely so
|
|
1131
|
+
the waiver stays narrow. Redirecting `triageState` instead would also send
|
|
1132
|
+
spec-gate bounces, aborts, and closed-unmerged PRs into the ready state, where
|
|
1133
|
+
a bounced ticket is reclaimed and bounced again on every tick.
|
|
1134
|
+
|
|
1135
|
+
The default file directory follows a selected local `AB_STORE` root and writes
|
|
1136
|
+
its own self-excluding `.gitignore`. An explicitly configured `dir` belongs to
|
|
1137
|
+
the repository owner and is not automatically ignored.
|
|
1138
|
+
|
|
1139
|
+
<!-- config-fragment:linear-tickets -->
|
|
1140
|
+
```toml
|
|
1141
|
+
[tickets]
|
|
1142
|
+
source = "linear"
|
|
1143
|
+
teamKey = "ENG"
|
|
1144
|
+
readyState = "Todo"
|
|
1145
|
+
readyLabels = ["autobuild"]
|
|
1146
|
+
claimedState = "In Progress"
|
|
1147
|
+
createState = "Backlog"
|
|
1148
|
+
triageState = "Backlog"
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
Linear credentials do not belong in this table. Set `LINEAR_API_KEY` in the
|
|
1152
|
+
environment or local `.env` file described below.
|
|
1153
|
+
|
|
1154
|
+
For a hosted backend, repository policy remains local while credentials remain
|
|
1155
|
+
at the service:
|
|
1156
|
+
|
|
1157
|
+
<!-- config-fragment:hosted-tickets -->
|
|
1158
|
+
```toml
|
|
1159
|
+
[tickets]
|
|
1160
|
+
source = "hosted"
|
|
1161
|
+
teamKey = "ENG"
|
|
1162
|
+
readyState = "Ready"
|
|
1163
|
+
readyLabels = []
|
|
1164
|
+
claimedState = "Doing"
|
|
1165
|
+
createState = "Triage"
|
|
1166
|
+
triageState = "Triage"
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
Set `AB_STORE` to the service's HTTP(S) URL and `AB_TOKEN` to a deployment
|
|
1170
|
+
operator token (`mint operator` without `--user`). The same source-agnostic wiring powers dispatch, harvest, ticket-aware
|
|
1171
|
+
skills, and all seven command families: `ab ticket create`, `update`, `block`,
|
|
1172
|
+
`unblock`, `list`, `show`, and `move`. See the
|
|
1173
|
+
[hosted ticket protocol](remote-ticket-protocol.md). In hosted Linear mode,
|
|
1174
|
+
`LINEAR_API_KEY` is configured only on the service; direct Linear remains
|
|
1175
|
+
supported.
|
|
1176
|
+
|
|
1177
|
+
When filing dependent tickets into the ready state, follow the
|
|
1178
|
+
[safe blocker-filing workflow](ticket-dependencies.md). Direct and hosted Linear
|
|
1179
|
+
create the issue before recording its blockers, so `create --blocked-by` is not
|
|
1180
|
+
an atomic publication guarantee. Stage outside ready, verify the relationships,
|
|
1181
|
+
then move to the intended ready state last.
|
|
1182
|
+
|
|
1183
|
+
A plugin registration is selected the same way. `ab dispatch` and every
|
|
1184
|
+
`ab ticket` subcommand load the plugin and route through that source. Within a
|
|
1185
|
+
dispatch process, one selected instance serves dependencies, claim/projections,
|
|
1186
|
+
harvest filing, and janitor completion. Unknown names fail with the available
|
|
1187
|
+
builtin and loaded plugin names. Missing descriptor credentials name both the
|
|
1188
|
+
source and each variable.
|
|
1189
|
+
|
|
1190
|
+
<!-- config-fragment:plugin-tickets -->
|
|
1191
|
+
```toml
|
|
1192
|
+
plugins = ["./plugins/company.ts"]
|
|
1193
|
+
|
|
1194
|
+
[tickets]
|
|
1195
|
+
source = "company"
|
|
1196
|
+
readyState = "Ready"
|
|
1197
|
+
claimedState = "Doing"
|
|
1198
|
+
createState = "Triage"
|
|
1199
|
+
triageState = "Triage"
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
Set `COMPANY_TICKET_TOKEN` in the environment, never in this table.
|
|
1203
|
+
|
|
1204
|
+
## `[orchestrator]`
|
|
1205
|
+
|
|
1206
|
+
Gates the operator-sandbox feature: a persistent, **credential-free**
|
|
1207
|
+
environment per operator × repository that an operator agent drives through the
|
|
1208
|
+
`sandbox.*` registry tools (served by `ab mcp` and every later binding). The
|
|
1209
|
+
table is closed to unknown keys and restart-classified as a whole.
|
|
1210
|
+
|
|
1211
|
+
| Field | Default | Constraints | Purpose |
|
|
1212
|
+
|---|---:|---|---|
|
|
1213
|
+
| `enabled` | `false` | boolean | Master gate. With the table absent or `enabled = false`, the sandbox tools are absent from every binding and no environment is ever provisioned. |
|
|
1214
|
+
| `sandbox` | — | strict subtable; absence reads as the defaults | Sandbox behavior knobs. |
|
|
1215
|
+
|
|
1216
|
+
`[orchestrator.sandbox]` fields:
|
|
1217
|
+
|
|
1218
|
+
| Field | Default | Constraints | Purpose |
|
|
1219
|
+
|---|---:|---|---|
|
|
1220
|
+
| `idleMinutes` | `30` | positive integer | Minutes without a sandbox tool call after which the dispatcher tick's janitor stops the environment, keeping its snapshot; the next tool call resumes it. |
|
|
1221
|
+
| `environmentVariables` | `[]` | array of nonblank names | Names of non-secret host variables forwarded into the sandbox — the only non-toolchain environment it ever sees, during provisioning/setup and tool exec/start alike. Unset means none. |
|
|
1222
|
+
|
|
1223
|
+
The credential-free rule is structural: the environment receives no store,
|
|
1224
|
+
forge, ticket-provider, or model credential, and naming any of `AB_STORE`,
|
|
1225
|
+
`AB_TOKEN`, `GITHUB_TOKEN`, `GH_TOKEN`, `LINEAR_API_KEY`, `VERCEL_OIDC_TOKEN`,
|
|
1226
|
+
`VERCEL_TOKEN`, `VERCEL_TEAM_ID`, `VERCEL_PROJECT_ID`, or `AI_GATEWAY_API_KEY`
|
|
1227
|
+
in `environmentVariables` is a config error. The typed operator tools remain
|
|
1228
|
+
the only route to build state from inside the sandbox. Tool bounds:
|
|
1229
|
+
`sandbox.exec` waits at most 300 seconds; stdout and stderr are each truncated
|
|
1230
|
+
at 65,536 bytes with a `[truncated by autobuild: output exceeded 65536 bytes]`
|
|
1231
|
+
marker; `sandbox.reset` and archiving an operator's last session are
|
|
1232
|
+
destructive (reset re-provisions from the current base head); there is at most
|
|
1233
|
+
one environment per operator and repository.
|
|
1234
|
+
|
|
1235
|
+
<!-- config-fragment:orchestrator -->
|
|
1236
|
+
```toml
|
|
1237
|
+
[orchestrator]
|
|
1238
|
+
enabled = true
|
|
1239
|
+
|
|
1240
|
+
[orchestrator.sandbox]
|
|
1241
|
+
idleMinutes = 30
|
|
1242
|
+
environmentVariables = ["MY_TOOL_CONFIG"]
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
Set `MY_TOOL_CONFIG` in the host environment of the `ab mcp` process; a missing
|
|
1246
|
+
value fails the tool call that needs it, named as a typed `environment` error.
|
|
1247
|
+
|
|
1248
|
+
## Complete example
|
|
1249
|
+
|
|
1250
|
+
The following deliberately exercises every fixed table and both verify/finalize
|
|
1251
|
+
step variants. Replace commands, paths, roles, and ticket states with values
|
|
1252
|
+
that exist in your repository and providers.
|
|
1253
|
+
|
|
1254
|
+
<!-- complete-config -->
|
|
1255
|
+
```toml
|
|
1256
|
+
baseBranch = "main"
|
|
1257
|
+
capacity = 2
|
|
1258
|
+
forge = "github"
|
|
1259
|
+
plugins = ["./plugins/company.ts", "@acme/autobuild-plugin"]
|
|
1260
|
+
|
|
1261
|
+
[workspace]
|
|
1262
|
+
provider = "company-container"
|
|
1263
|
+
|
|
1264
|
+
[workspace.config]
|
|
1265
|
+
image = "ghcr.io/acme/build:bun"
|
|
1266
|
+
|
|
1267
|
+
[pr.imageHost]
|
|
1268
|
+
provider = "github-release"
|
|
1269
|
+
repository = "acme/public-review-assets"
|
|
1270
|
+
releaseId = 123456
|
|
1271
|
+
|
|
1272
|
+
[commands]
|
|
1273
|
+
setup = "bun install"
|
|
1274
|
+
typecheck = "bun tsc --noEmit"
|
|
1275
|
+
test = "bun test"
|
|
1276
|
+
publish = "bun run publish"
|
|
1277
|
+
|
|
1278
|
+
[verify]
|
|
1279
|
+
steps = ["types", "unit", "e2e"]
|
|
1280
|
+
|
|
1281
|
+
[verify.types]
|
|
1282
|
+
kind = "check"
|
|
1283
|
+
command = "typecheck"
|
|
1284
|
+
always = true
|
|
1285
|
+
|
|
1286
|
+
[verify.unit]
|
|
1287
|
+
kind = "check"
|
|
1288
|
+
command = "test"
|
|
1289
|
+
|
|
1290
|
+
[verify.e2e]
|
|
1291
|
+
kind = "agent"
|
|
1292
|
+
skill = "verify-app-e2e" # repository-authored skill
|
|
1293
|
+
paths = ["web/**", "src/routes/**"]
|
|
1294
|
+
|
|
1295
|
+
[finalize]
|
|
1296
|
+
steps = ["publish", "release-notes"]
|
|
1297
|
+
|
|
1298
|
+
[finalize.publish]
|
|
1299
|
+
kind = "check"
|
|
1300
|
+
command = "publish"
|
|
1301
|
+
|
|
1302
|
+
[finalize.release-notes]
|
|
1303
|
+
kind = "agent"
|
|
1304
|
+
skill = "ab-release-notes"
|
|
1305
|
+
|
|
1306
|
+
[roles.default]
|
|
1307
|
+
runtime = "claude"
|
|
1308
|
+
args = []
|
|
1309
|
+
|
|
1310
|
+
[roles.plan]
|
|
1311
|
+
args = ["--effort", "high"]
|
|
1312
|
+
|
|
1313
|
+
[roles.code-review]
|
|
1314
|
+
runtime = "pi"
|
|
1315
|
+
model = "kimi-coding/k3"
|
|
1316
|
+
args = ["--extension", "./tools/web-access.js"]
|
|
1317
|
+
sessionBudgetSeconds = 7200
|
|
1318
|
+
|
|
1319
|
+
[policy]
|
|
1320
|
+
sessionBudgetSeconds = 3600
|
|
1321
|
+
stallRounds = 3
|
|
1322
|
+
maxVerifyAttempts = 3
|
|
1323
|
+
maxSetupAttempts = 3
|
|
1324
|
+
maxInfrastructureAttempts = 3
|
|
1325
|
+
maxReconcileAttempts = 3
|
|
1326
|
+
maxReviewRounds = 6
|
|
1327
|
+
harvestThreshold = 5
|
|
1328
|
+
harvestMaxDrift = 3
|
|
1329
|
+
|
|
1330
|
+
[tickets]
|
|
1331
|
+
source = "file"
|
|
1332
|
+
readyState = "ready"
|
|
1333
|
+
readyLabels = []
|
|
1334
|
+
createState = "Triage"
|
|
1335
|
+
triageState = "Triage"
|
|
1336
|
+
proposalState = "Triage"
|
|
1337
|
+
dir = "tickets"
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
This repository's [`autobuild.toml`](../autobuild.toml) is another worked
|
|
1341
|
+
example.
|
|
1342
|
+
|
|
1343
|
+
## What `ab init` generates
|
|
1344
|
+
|
|
1345
|
+
On the first `ab init [target]`, when `autobuild.toml` is absent, Autobuild
|
|
1346
|
+
writes a valid stack-neutral skeleton. Deterministic init code does not inspect
|
|
1347
|
+
package manifests, infer commands, or ask a fixed adapter survey. It vendors the
|
|
1348
|
+
editable skills, records their pristine bases, updates `.gitignore`, and probes
|
|
1349
|
+
every registered runtime for executable/authentication usability.
|
|
1350
|
+
|
|
1351
|
+
Init reports every probe result and chooses a setup launcher from the fixed
|
|
1352
|
+
product preference `claude`, then `codex`, then `pi`. That choice only starts
|
|
1353
|
+
setup; the temporary `[roles.default].runtime` skeleton value does not constrain
|
|
1354
|
+
the role runtimes or models the setup agent ultimately configures. When a
|
|
1355
|
+
shipped runtime is usable, no Claude discovery conflict remains, and both stdin
|
|
1356
|
+
and stdout are attached to a TTY, init starts the selected coding-agent CLI in
|
|
1357
|
+
the target repository with a short prompt pointing to the locally editable
|
|
1358
|
+
`.agents/skills/ab-guide/references/setup.md` guide reference. The user can
|
|
1359
|
+
answer its repository- and team-specific questions directly. When launched,
|
|
1360
|
+
the child exit status is init's exit status, and the direct handoff creates no
|
|
1361
|
+
build or BuildStore data.
|
|
1362
|
+
|
|
1363
|
+
When no discovery conflict exists but no shipped runtime is usable or either
|
|
1364
|
+
terminal stream is non-interactive, init still completes deterministic
|
|
1365
|
+
installation and exits successfully. It prints the same setup pointer prompt verbatim
|
|
1366
|
+
with instructions to run it in a coding agent; unusable runtime reports include
|
|
1367
|
+
their reasons. The setup agent reads the installed guide reference, derives
|
|
1368
|
+
commands and verification from the actual repository, chooses pipeline roles,
|
|
1369
|
+
configures ticket workflow and environment-only credentials, arranges suitable
|
|
1370
|
+
end-to-end verification (authoring a repository-owned agent verifier when
|
|
1371
|
+
needed), and leaves one groomed dispatchable ticket.
|
|
1372
|
+
|
|
1373
|
+
After installation, init reports `autobuild.toml: written|skipped`, counts all
|
|
1374
|
+
skill outcomes, names attention-worthy local edits, and prints runtime probe
|
|
1375
|
+
results. A distinct real `.claude/skills/ab-*` directory is an actionable
|
|
1376
|
+
discovery conflict rather than an alias: init processes every skill, summarizes
|
|
1377
|
+
all such conflicts with move/remove guidance, skips the setup handoff, and exits
|
|
1378
|
+
1. Redirected/non-TTY output is plain text.
|
|
1379
|
+
|
|
1380
|
+
The generated file is an active-only, schema-valid skeleton. It writes
|
|
1381
|
+
`baseBranch = "main"`, `capacity = 4`, empty `[commands]`, empty verify and
|
|
1382
|
+
finalize step arrays, all six default policy values, a valid local file-ticket
|
|
1383
|
+
gate, and the selected setup runtime as a temporary explicit role default. It
|
|
1384
|
+
contains no setup command, package-manager command, language assumption, or
|
|
1385
|
+
repository-derived value. Therefore repositories containing only `package.json`
|
|
1386
|
+
or only `Cargo.toml` receive byte-identical config on the same machine.
|
|
1387
|
+
|
|
1388
|
+
The config rule is intentionally one-way: once `autobuild.toml` exists,
|
|
1389
|
+
`ab init` does not reconcile or overwrite the file. This remains true with
|
|
1390
|
+
`--force`; that flag can overwrite locally edited vendored skills, never
|
|
1391
|
+
configuration. The setup prompt instead asks the agent to review and improve
|
|
1392
|
+
the existing config.
|
|
1393
|
+
|
|
1394
|
+
Re-running init still maintains the `.autobuild/` ignore rule and skill
|
|
1395
|
+
installation. `ab upgrade` does not migrate or rewrite `autobuild.toml`; by
|
|
1396
|
+
default it first updates a recognized Bun installation — from the npm registry
|
|
1397
|
+
for a registry install, or from GitHub Releases for a `github:` install — and
|
|
1398
|
+
then merges vendored skills from that distribution. For a local install, Bun may update the
|
|
1399
|
+
*owning* project's Autobuild dependency in `package.json` and `bun.lock`; this
|
|
1400
|
+
package-manager side effect is separate from target-repository configuration.
|
|
1401
|
+
Use `ab upgrade --no-self-update` for merge-only behavior.
|
|
1402
|
+
|
|
1403
|
+
By default, upgrade makes one local commit on the target's current HEAD after a
|
|
1404
|
+
successful merge. It stages only paths it owns: each reported skill's canonical
|
|
1405
|
+
`.agents` tree, `.ab-pristine` record, and `.claude` discovery path, plus the
|
|
1406
|
+
target repository's `package.json` and `bun.lock` when this run's local Bun
|
|
1407
|
+
self-update wrote them. A global Bun update changes no target path, and a no-op
|
|
1408
|
+
skill merge creates no commit. Additions, modifications, links, and deletions
|
|
1409
|
+
are included. The message identifies `ab upgrade` and pairs every skill whose
|
|
1410
|
+
bytes changed with its reported outcome; unrelated staged, unstaged, and
|
|
1411
|
+
untracked work remains untouched.
|
|
1412
|
+
|
|
1413
|
+
Use `ab upgrade --no-commit` to leave all output uncommitted for manual review.
|
|
1414
|
+
The flag is forwarded when self-update hands off to the replacement binary, and
|
|
1415
|
+
the default path likewise carries its pre-update Git baseline through that
|
|
1416
|
+
handoff. If a replacement child receives the handoff marker without that
|
|
1417
|
+
baseline — possible when an older parent launches a newer child — it suppresses
|
|
1418
|
+
the entire automatic commit, names the cross-version compatibility reason, and
|
|
1419
|
+
leaves all upgrade-owned changes uncommitted for the operator. Skill-upgrade
|
|
1420
|
+
results and the merge-derived exit status remain unchanged. Upgrade likewise
|
|
1421
|
+
suppresses the whole commit and names why when any skill or Claude discovery
|
|
1422
|
+
path conflicts, an owned path was already dirty, the target is not a Git
|
|
1423
|
+
repository, HEAD/worktree identity changes, or Git is mid-merge, mid-rebase, or
|
|
1424
|
+
mid-cherry-pick. If upgrade cannot snapshot the worktree's Git index, it warns
|
|
1425
|
+
and declines to stage. A failed staging or commit attempt restores that exact
|
|
1426
|
+
pre-attempt index and reports the original Git failure; merged worktree files
|
|
1427
|
+
remain in place and the merge's exit status is unchanged. Upgrade never pushes
|
|
1428
|
+
or rewrites history.
|
|
1429
|
+
|
|
1430
|
+
Autobuild now installs 11 skills; only `ab-spec`, `ab-tickets`, and `ab-guide`
|
|
1431
|
+
are model-invocable. The setup reference is an ordinary support file in the
|
|
1432
|
+
editable/pristine `ab-guide` tree and participates in the same three-way
|
|
1433
|
+
upgrade merge as every other vendored file.
|
|
1434
|
+
|
|
1435
|
+
Upgrade has two one-time retirement classifications for the former
|
|
1436
|
+
`ab-setup` and `ab-verify-e2e` defaults. `removed` means pristine provenance
|
|
1437
|
+
existed and either the unreferenced live tree matched it and was removed with
|
|
1438
|
+
its owned discovery link, or the canonical live tree was already missing and
|
|
1439
|
+
upgrade cleared provenance plus any owned dangling link. `kept` normally means
|
|
1440
|
+
the tree was customized, still configured, or could not be proved safe to
|
|
1441
|
+
remove; upgrade preserves it and clears obsolete pristine ownership. It also
|
|
1442
|
+
means an otherwise removable canonical tree was deleted while a user-owned
|
|
1443
|
+
`.claude/skills/<name>` discovery entry — a distinct real directory or foreign
|
|
1444
|
+
symlink — was preserved byte-for-byte and remains discoverable. For a symlink,
|
|
1445
|
+
its link text and target are unchanged. That entry enters the ordinary
|
|
1446
|
+
structured discovery conflict report, so upgrade exits nonzero. A same-named
|
|
1447
|
+
repository-authored skill with no pristine provenance is never removed, and a
|
|
1448
|
+
second upgrade neither recreates a dangling link nor resurrects or re-reports
|
|
1449
|
+
either retirement.
|
|
1450
|
+
|
|
1451
|
+
## Durable settings outside TOML
|
|
1452
|
+
|
|
1453
|
+
Four repository-wide operator choices intentionally live as facts in the
|
|
1454
|
+
BuildStore repository journal, not in `autobuild.toml`. They are latest-write
|
|
1455
|
+
wins, survive process restarts, and are sampled by every dispatcher on its
|
|
1456
|
+
poll. Editing TOML cannot change them.
|
|
1457
|
+
|
|
1458
|
+
| Setting | Fresh-repository default | Controls | Scope |
|
|
1459
|
+
|---|---:|---|---|
|
|
1460
|
+
| 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
|
+
| 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
|
+
| Claim-time auto-merge default | off | `ab dispatch --auto-merge` / `--no-auto-merge`; `m` on the global row | Seeds durable auto-merge intent only on builds claimed after the setting is enabled. Existing builds never change with the default. |
|
|
1463
|
+
| 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
|
+
|
|
1465
|
+
The opposite flag forms for each dispatch setting are mutually exclusive.
|
|
1466
|
+
Omitting both writes nothing and reuses the durable value. Per-build
|
|
1467
|
+
pause/resume and auto-merge controls are separate facts and do not alter these
|
|
1468
|
+
repository defaults. Pause all and resume all write two facts each — the
|
|
1469
|
+
repository pause first, then intake — so a partial failure still leaves the
|
|
1470
|
+
repository at least as quiesced as asked; `ab pause --all` reports on stderr
|
|
1471
|
+
which of them landed when a walk stops partway. The current release has no TOML
|
|
1472
|
+
field for either the harvest gate or the repository pause, and no command that
|
|
1473
|
+
sets the repository pause on its own without also moving intake; inspect the
|
|
1474
|
+
harvest gate with `ab harvest status`.
|
|
1475
|
+
|
|
1476
|
+
## Environment and credentials
|
|
1477
|
+
|
|
1478
|
+
Secrets and store selection accompany the file through environment variables:
|
|
1479
|
+
|
|
1480
|
+
| Variable | Used for | Notes |
|
|
1481
|
+
|---|---|---|
|
|
1482
|
+
| `LINEAR_API_KEY` | Direct Linear ticket source | Required and nonempty when `tickets.source = "linear"`; in hosted Linear mode it is service-only. |
|
|
1483
|
+
| `AB_STORE` | BuildStore and hosted ticket selection | A local path or HTTP(S) remote-store URL. `tickets.source = "hosted"` requires a nonblank HTTP(S) URL. A command's explicit `--store` wins for BuildStore selection, then nonblank `AB_STORE`, then the main checkout's `.autobuild/`. Relative local paths resolve from the main checkout. |
|
|
1484
|
+
| `AB_TOKEN` | Protected remote BuildStore and hosted tickets | Bearer credential forwarded to the service. Hosted tickets require deployment operator scope (`mint operator` without `--user`). Empty means no token; nonempty token bytes are treated as opaque. |
|
|
1485
|
+
|
|
1486
|
+
A local store selection relocates the state database, blobs, worktrees, and the
|
|
1487
|
+
default file-ticket directory together. With a remote store, Git worktrees and
|
|
1488
|
+
default file tickets remain under the repository's local `.autobuild/` root.
|
|
1489
|
+
An explicitly configured `tickets.dir` remains independent and resolves as
|
|
1490
|
+
described in its field row.
|
|
1491
|
+
|
|
1492
|
+
The `ab` binary loads exactly `<cwd>/.env` before routing a command. Its minimal
|
|
1493
|
+
parser accepts `KEY=VALUE`, an optional `export ` prefix, full-line `#`
|
|
1494
|
+
comments, and matching single or double quotes around the entire value. It
|
|
1495
|
+
trims surrounding whitespace, keeps additional `=` characters in values, does
|
|
1496
|
+
not perform interpolation or escape processing, and silently skips malformed
|
|
1497
|
+
lines. A missing or unreadable file is a no-op. Any key already present in the
|
|
1498
|
+
real process environment—even an empty string—wins over `.env`.
|
|
1499
|
+
|
|
1500
|
+
Variables such as `AB_BUILD`, `AB_PHASE`, `AB_SESSION`, and the harvest-session
|
|
1501
|
+
identity tuple are runner-owned ambient authorization. Operators should not
|
|
1502
|
+
set or copy them into `.env`; the runner stamps them for each session.
|
|
1503
|
+
|
|
1504
|
+
Forge and agent credentials remain adapter-owned. The `local-git` forge uses no
|
|
1505
|
+
credentials or network access: it keeps durable PR records under private Git
|
|
1506
|
+
refs, leaves each build at `refs/heads/ab/<slug>`, and squash-merges locally only
|
|
1507
|
+
after auto-merge consent. The squash author and committer come from the
|
|
1508
|
+
repository's ordinary Git identity, as resolved for a plain `git commit`; the
|
|
1509
|
+
landing is always unsigned even when `commit.gpgsign` is enabled. If Git cannot
|
|
1510
|
+
resolve that identity, the PR remains open and `ab build status <slug>` names
|
|
1511
|
+
`user.name` / `user.email` setup commands; dispatcher ticks retry automatically
|
|
1512
|
+
after the operator configures them. A dirty checked-out base does not by itself
|
|
1513
|
+
block the landing: tracked and untracked work on paths untouched by the squash
|
|
1514
|
+
survives.
|
|
1515
|
+
If the squash would overwrite operator work, the PR remains open, `ab build
|
|
1516
|
+
status <slug>` shows a path-bearing observation, and later dispatcher ticks retry
|
|
1517
|
+
automatically after the operator commits, stashes, or discards the collision;
|
|
1518
|
+
Autobuild never mutates that work. Inspect an open local change with `git diff
|
|
1519
|
+
main...ab/<slug>`. It has no review web UI and no image-hosting capability; attached artifacts use
|
|
1520
|
+
the existing text-download projection.
|
|
1521
|
+
|
|
1522
|
+
- for `forge = "github"`, either export a `GITHUB_TOKEN` or `GH_TOKEN` or run `gh auth login`:
|
|
1523
|
+
the kernel speaks the GitHub REST API directly, taking `GITHUB_TOKEN`, then `GH_TOKEN`, then the
|
|
1524
|
+
gh CLI's stored login (`gh auth token`) — so a local dispatcher needs only the gh login, while a
|
|
1525
|
+
host without gh (hosted or sandboxed dispatchers) must export a token. Separately make sure the
|
|
1526
|
+
Git remote can fetch/push with the process's Git credentials;
|
|
1527
|
+
- Claude sessions invoke the local `claude` CLI and use its configured login;
|
|
1528
|
+
install Claude Code, launch `claude`, and complete login before dispatching;
|
|
1529
|
+
- Codex sessions invoke the local `codex` CLI and use its configured login;
|
|
1530
|
+
install Codex, run `codex login`, and complete authentication before
|
|
1531
|
+
dispatching;
|
|
1532
|
+
- Pi sessions use Pi's provider authentication: start `pi` and run `/login`
|
|
1533
|
+
inside the interactive session, or, for non-interactive use, supply the
|
|
1534
|
+
provider credentials Pi supports (such as provider API keys in the environment).
|
|
1535
|
+
|
|
1536
|
+
No provider API key, GitHub token, or remote-store bearer token belongs in
|
|
1537
|
+
`autobuild.toml`.
|
|
1538
|
+
|
|
1539
|
+
## Troubleshooting
|
|
1540
|
+
|
|
1541
|
+
### Reading an invalid-config error
|
|
1542
|
+
|
|
1543
|
+
Start with `ab dispatch --once` when a real single tick is safe. A syntax error
|
|
1544
|
+
looks like:
|
|
1545
|
+
|
|
1546
|
+
```text
|
|
1547
|
+
/path/to/autobuild.toml: TOML syntax error: ...
|
|
1548
|
+
```
|
|
1549
|
+
|
|
1550
|
+
Fix TOML structure first. A schema failure starts with:
|
|
1551
|
+
|
|
1552
|
+
```text
|
|
1553
|
+
/path/to/autobuild.toml: invalid config
|
|
1554
|
+
verify.e2e.skill: ...
|
|
1555
|
+
tickets.teamKey: ...
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
Each indented path is independently actionable. Common causes are a root scalar
|
|
1559
|
+
placed after a table, a misspelled strict field, a step listed without its
|
|
1560
|
+
subtable (or vice versa), a check naming no `[commands]` key, a field used on
|
|
1561
|
+
the wrong step kind, or source-specific ticket fields used together.
|
|
1562
|
+
|
|
1563
|
+
Runtime routing failures use a separate heading and list all bad roles. Check
|
|
1564
|
+
the merged `default` plus child values, not just the child table: each axis
|
|
1565
|
+
inherits independently. Confirm the runtime name is shipped or registered by a configured plugin and
|
|
1566
|
+
use `ab models [query]` (or the plugin's documentation) to choose a model family
|
|
1567
|
+
that runtime serves.
|
|
1568
|
+
|
|
1569
|
+
### `tick: idle` with expected work
|
|
1570
|
+
|
|
1571
|
+
In plain/non-TTY mode, `tick: idle` means the pass recorded no dispatcher
|
|
1572
|
+
action. Check the gates in this order:
|
|
1573
|
+
|
|
1574
|
+
1. **Durable intake:** the dashboard header must show intake on, or explicitly
|
|
1575
|
+
run a future dispatcher with `--intake`. Intake off skips the ready scan
|
|
1576
|
+
even when tickets exist.
|
|
1577
|
+
2. **Repository pause:** a pause all holds every queued build, so the dashboard
|
|
1578
|
+
controls line shows `repository PAUSED` and an existing held build shows
|
|
1579
|
+
`(held)` beside its literal `QUEUED` status. Intake off alone shows neither
|
|
1580
|
+
pause indication. `r` on the dashboard's global row or `ab resume --all`
|
|
1581
|
+
releases the hold and restores intake.
|
|
1582
|
+
3. **Ready state:** the ticket must be in exactly `tickets.readyState`. Linear
|
|
1583
|
+
is case-sensitive; file tickets must physically be in the corresponding
|
|
1584
|
+
state directory.
|
|
1585
|
+
4. **Labels:** the ticket must carry every effective `readyLabels` value,
|
|
1586
|
+
including Linear's default `autobuild` label when the field is omitted.
|
|
1587
|
+
5. **Dependencies:** every `blockedBy` ticket must exist and be complete in the
|
|
1588
|
+
same source. This includes harvest proposals filed directly into the ready
|
|
1589
|
+
state. Plain dispatch output reports unresolved ids and cycles.
|
|
1590
|
+
6. **Capacity:** every nonterminal build for this repository—including paused
|
|
1591
|
+
and blocked builds—uses a slot. Inspect `ab builds --queued` and
|
|
1592
|
+
`ab build status <slug>` rather than looking only for a live process.
|
|
1593
|
+
7. **Duplicate work:** a ready ticket already represented by an active build is
|
|
1594
|
+
deliberately excluded from the queue.
|
|
1595
|
+
|
|
1596
|
+
If the expected work is observation harvesting instead of a ticket, check the
|
|
1597
|
+
acknowledged harvest gate, the dashboard's unclaimed `observations` count, and
|
|
1598
|
+
the configured triggers. Harvest starts when `policy.harvestThreshold`
|
|
1599
|
+
unclaimed observations exist or when `policy.harvestMaxDrift` other builds have
|
|
1600
|
+
merged since the oldest one; a drift limit of zero disables the second
|
|
1601
|
+
condition. The header shows count pressure against `harvestThreshold`; drift
|
|
1602
|
+
progress is not shown. Harvest does not consume build capacity.
|
|
1603
|
+
|
|
1604
|
+
### Authentication failures
|
|
1605
|
+
|
|
1606
|
+
- **Linear:** `LINEAR_API_KEY is not set` means the selected source was wired
|
|
1607
|
+
without a nonempty key. Set it in the real environment or `<cwd>/.env` and
|
|
1608
|
+
rerun. For API rejections, verify the personal key, `teamKey`, and that every
|
|
1609
|
+
configured workflow state exists in that team.
|
|
1610
|
+
- **GitHub and Git:** run `gh auth status` for PR, auto-merge, and release API
|
|
1611
|
+
calls; verify repository permissions and separately test the Git remote's
|
|
1612
|
+
fetch/push credentials. Image hosting additionally requires a public host
|
|
1613
|
+
repository, an existing published mutable release, the numeric release id,
|
|
1614
|
+
and Contents write permission.
|
|
1615
|
+
- **Agent runtime/provider:** authenticate the runtime selected by the merged
|
|
1616
|
+
role. A missing Codex adapter diagnostic names both `codex runtime` and the
|
|
1617
|
+
`codex` executable; install it and run `codex login`. A 401/402/403,
|
|
1618
|
+
permission, quota, or billing rejection is treated as a permanent provider
|
|
1619
|
+
failure rather than retried indefinitely. For Pi, confirm the
|
|
1620
|
+
provider-qualified model and provider login.
|
|
1621
|
+
- **Remote BuildStore:** confirm the effective `AB_STORE` URL and `AB_TOKEN`.
|
|
1622
|
+
A protected store reports 401 for a missing, invalid, or expired token and
|
|
1623
|
+
403 when the token is valid but scoped to another build/session.
|