@defrex/autobuild 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SPEC.md +313 -26
- package/bin/ab-build-runner.ts +8 -2
- package/docs/architecture.md +57 -14
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +151 -25
- package/docs/hosted-dispatcher.md +69 -13
- package/docs/operator-api.md +2 -2
- package/docs/remote-store-protocol.md +169 -16
- package/docs/remote-ticket-protocol.md +1 -1
- package/docs/setup.md +12 -9
- package/docs/website.md +10 -0
- package/package.json +43 -23
- package/packages/core/src/cli/artifact.ts +130 -6
- package/packages/core/src/cli/binary.ts +9 -1
- package/packages/core/src/cli/build-control.ts +110 -13
- package/packages/core/src/cli/context.ts +46 -0
- package/packages/core/src/cli/dashboard/model.ts +57 -7
- package/packages/core/src/cli/dashboard/poll.ts +69 -10
- package/packages/core/src/cli/dashboard/render.ts +34 -5
- package/packages/core/src/cli/dispatch-frontend.ts +54 -5
- package/packages/core/src/cli/dispatch.ts +320 -46
- package/packages/core/src/cli/harvest.ts +122 -8
- package/packages/core/src/cli/help.ts +47 -8
- package/packages/core/src/cli/init-readiness-remote.ts +110 -0
- package/packages/core/src/cli/init-readiness-shared.ts +72 -0
- package/packages/core/src/cli/init-validation.ts +248 -222
- package/packages/core/src/cli/main.ts +70 -11
- package/packages/core/src/cli/mcp.ts +23 -1
- package/packages/core/src/cli/plugin.ts +7 -4
- package/packages/core/src/cli/remote-poll.ts +277 -0
- package/packages/core/src/cli/repo-state.ts +22 -0
- package/packages/core/src/cli/repository-status.ts +39 -1
- package/packages/core/src/cli/status.ts +149 -27
- package/packages/core/src/cli/terminals.ts +2 -1
- package/packages/core/src/cli/ticket-assets.ts +247 -0
- package/packages/core/src/cli/ticket.ts +307 -7
- package/packages/core/src/cli/wait.ts +147 -39
- package/packages/core/src/cli/watch.ts +128 -166
- package/packages/core/src/config/live.ts +22 -0
- package/packages/core/src/config/pipeline-source.ts +376 -0
- package/packages/core/src/config/schema.ts +230 -19
- package/packages/core/src/events/catalog.ts +1 -0
- package/packages/core/src/events/globs.ts +47 -0
- package/packages/core/src/events/payloads.ts +52 -2
- package/packages/core/src/events/repository.ts +52 -1
- package/packages/core/src/events/sessions.ts +69 -10
- package/packages/core/src/events/workspace-remote.ts +12 -0
- package/packages/core/src/integration/harness.ts +2 -1
- package/packages/core/src/kernel/auto-merge-default.ts +123 -0
- package/packages/core/src/kernel/phases.ts +16 -2
- package/packages/core/src/kernel/reducer.ts +40 -1
- package/packages/core/src/operator/annotations.ts +6 -0
- package/packages/core/src/operator/control.ts +19 -5
- package/packages/core/src/operator/index.ts +71 -6
- package/packages/core/src/operator/query.ts +208 -19
- package/packages/core/src/operator/registry.ts +55 -10
- package/packages/core/src/operator/requests.ts +9 -6
- package/packages/core/src/operator/sandbox-host.ts +140 -0
- package/packages/core/src/operator/sandbox.ts +247 -14
- package/packages/core/src/operator/ticket-source-backend.ts +47 -0
- package/packages/core/src/orchestrator/approvals.ts +115 -0
- package/packages/core/src/orchestrator/conversation.ts +167 -0
- package/packages/core/src/orchestrator/model.ts +86 -0
- package/packages/core/src/orchestrator/prompt.ts +61 -0
- package/packages/core/src/orchestrator/tools.ts +117 -0
- package/packages/core/src/orchestrator/turn-runner.ts +457 -0
- package/packages/core/src/plugin-sdk/index.ts +51 -1
- package/packages/core/src/plugins/load.ts +135 -7
- package/packages/core/src/plugins/manifest.ts +183 -10
- package/packages/core/src/plugins/registry.ts +73 -4
- package/packages/core/src/ports/forge/create.ts +1 -1
- package/packages/core/src/ports/forge/fake.ts +31 -0
- package/packages/core/src/ports/forge/github-transport.ts +13 -2
- package/packages/core/src/ports/forge/github.ts +13 -2
- package/packages/core/src/ports/forge/local-git.ts +31 -1
- package/packages/core/src/ports/runner/claude.ts +2 -2
- package/packages/core/src/ports/runner/codex.ts +6 -2
- package/packages/core/src/ports/runner/fake.ts +7 -1
- package/packages/core/src/ports/runner/pi.ts +8 -6
- package/packages/core/src/ports/runner/provider-error.ts +13 -3
- package/packages/core/src/ports/tickets/{remote-index.ts → hosted-index.ts} +3 -2
- package/packages/core/src/ports/types.ts +17 -2
- package/packages/core/src/ports/workspace/builtin-capabilities.ts +114 -0
- package/packages/core/src/ports/workspace/create.ts +74 -39
- package/packages/core/src/ports/workspace/distribution-archive.ts +171 -12
- package/packages/core/src/ports/workspace/fake.ts +169 -4
- package/packages/core/src/ports/workspace/git-worktree.ts +246 -26
- package/packages/core/src/ports/workspace/github-origin.ts +36 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +7 -1
- package/packages/core/src/ports/workspace/provider-capabilities.ts +171 -0
- package/packages/core/src/ports/workspace/vercel-capabilities.ts +110 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +142 -114
- package/packages/core/src/processes/build-child.ts +17 -2
- package/packages/core/src/processes/build-execution-state.ts +37 -0
- package/packages/core/src/processes/build-runner.ts +54 -3
- package/packages/core/src/processes/dispatcher.ts +247 -41
- package/packages/core/src/processes/harvest-child.ts +5 -0
- package/packages/core/src/processes/harvest-runner.ts +66 -19
- package/packages/core/src/processes/harvest.ts +196 -24
- package/packages/core/src/processes/orchestrator-tick.ts +319 -0
- package/packages/core/src/processes/sandbox-state.ts +10 -0
- package/packages/core/src/store/adapter.ts +32 -0
- package/packages/core/src/store/build-scope.ts +64 -0
- package/packages/core/src/store/contract.ts +868 -0
- package/packages/core/src/store/digest.ts +49 -0
- package/packages/core/src/store/local/schema.ts +29 -2
- package/packages/core/src/store/local/store.ts +338 -4
- package/packages/core/src/store/memory.ts +179 -0
- package/packages/core/src/store/new-build.ts +17 -0
- package/packages/core/src/store/phase-session.ts +71 -0
- package/packages/core/src/store/remote/client.ts +260 -1
- package/packages/core/src/store/remote/index.ts +12 -2
- package/packages/core/src/store/remote/protocol.ts +100 -12
- package/packages/core/src/store/remote/version.ts +6 -2
- package/packages/core/src/store/repo-state-events.ts +140 -0
- package/packages/core/src/store/retention.ts +7 -0
- package/packages/core/src/store/session-handle.ts +64 -0
- package/packages/core/src/{sessions/reducer.ts → store/session-reducer.ts} +28 -9
- package/packages/core/src/store/streams/session-writer.ts +42 -2
- package/packages/core/src/store/streams/wait.ts +4 -5
- package/packages/core/src/store/ticket-assets.ts +425 -0
- package/packages/core/src/store/types.ts +122 -1
- package/packages/core/src/testing/index.ts +59 -0
- package/skills/code-review/SKILL.md +5 -1
- package/skills/guide/SKILL.md +126 -33
- package/skills/guide/references/operator-api.md +2 -2
- package/skills/guide/references/plugin-authoring.md +17 -0
- package/skills/guide/references/remote-store-protocol.md +169 -16
- package/skills/guide/references/remote-ticket-protocol.md +1 -1
- package/skills/guide/references/setup.md +12 -9
- package/skills/implement/SKILL.md +3 -0
- package/skills/operate/SKILL.md +126 -0
- package/skills/plan/SKILL.md +4 -0
- package/skills/plan-review/SKILL.md +5 -1
- package/skills/spec/SKILL.md +46 -3
- package/packages/core/src/cli/dashboard/frame-image.ts +0 -373
- package/packages/core/src/cli/store-opening.contract.ts +0 -289
- package/packages/core/src/cli/testkit.ts +0 -194
- package/packages/core/src/markdown.ts +0 -67
- package/packages/core/src/operator/client.ts +0 -311
- package/packages/core/src/operator/server.ts +0 -616
- package/packages/core/src/operator/sessions.ts +0 -223
- package/packages/core/src/ports/runner/live-contract-fixture.ts +0 -176
- package/packages/core/src/ports/tickets/remote-server.ts +0 -185
- package/packages/core/src/store/remote/server.ts +0 -923
- package/packages/core/src/testing/store-failures.ts +0 -32
package/skills/guide/SKILL.md
CHANGED
|
@@ -65,6 +65,13 @@ spec → plan ⇄ plan-review → implement ⇄ code-review → verify:* → fin
|
|
|
65
65
|
claims tickets that pass the `[tickets]` ready
|
|
66
66
|
gate, chooses a short immutable slug from the final conforming spec, and
|
|
67
67
|
starts builds up to the top-level `capacity` bound.
|
|
68
|
+
- **Ticket assets** — files or folders attached to a ticket (see
|
|
69
|
+
[Ticket assets](#ticket-assets)) are frozen into the build at claim and
|
|
70
|
+
appear under `.ab/assets/` in every phase, as inputs alongside the spec.
|
|
71
|
+
A `design` asset is part of the contract: `plan`, `plan-review`, `implement`,
|
|
72
|
+
`code-review`, and agent verify steps judge against it, as a contract on
|
|
73
|
+
observable visual and reference outcomes, not on implementation. Other kinds
|
|
74
|
+
are reference material.
|
|
68
75
|
- **`spec`** — the ticket's spec becomes the build's contract. The `ab-spec`
|
|
69
76
|
skill is the human-interactive surface for producing it, and it runs *before*
|
|
70
77
|
a build exists.
|
|
@@ -337,7 +344,7 @@ gitPasswordEnv = "AB_GIT_READ_TOKEN"
|
|
|
337
344
|
|
|
338
345
|
[workspace.config.runtimeProvisioning.pi]
|
|
339
346
|
install = "npm install --global --ignore-scripts @earendil-works/pi-coding-agent@0.84.4"
|
|
340
|
-
preflight = "test \"$(pi --version)\" = \"0.84.4\""
|
|
347
|
+
preflight = "test \"$(pi --version)\" = \"0.84.4\" && pi update --models"
|
|
341
348
|
```
|
|
342
349
|
|
|
343
350
|
`timeoutSeconds` is required (60–86400; Hobby currently caps at 2700), `vcpus`
|
|
@@ -413,6 +420,12 @@ Names must already exist in `[verify].steps` with matching tables; duplicates,
|
|
|
413
420
|
blanks, unknown names, malformed metadata, and omission of an `always = true`
|
|
414
421
|
step make the planner's `ab done` fail before `plan.completed` is appended.
|
|
415
422
|
|
|
423
|
+
An agent verify step is handed the build's pinned ticket assets under
|
|
424
|
+
`.ab/assets/` (listed in `.ab/context.json`) as well as the spec. A verify skill
|
|
425
|
+
should treat a `design` asset as part of the contract, a contract on observable
|
|
426
|
+
visual and reference outcomes, not on implementation, and judge the result
|
|
427
|
+
against it.
|
|
428
|
+
|
|
416
429
|
An agent verify step selects `[roles.<step>]` by its **logical step name** —
|
|
417
430
|
the name in `[verify].steps`, not the skill it runs. An agent finalize
|
|
418
431
|
post-step follows the same rule.
|
|
@@ -782,15 +795,21 @@ environment variable instead and say why.
|
|
|
782
795
|
|
|
783
796
|
### `[orchestrator]`
|
|
784
797
|
|
|
785
|
-
Gates the operator-
|
|
786
|
-
environment per operator × repository that an
|
|
787
|
-
`sandbox.*` registry tools (`ab mcp`, and
|
|
788
|
-
|
|
789
|
-
|
|
798
|
+
Gates the operator-agent feature (AUT-340, AUT-342): the persistent,
|
|
799
|
+
**credential-free** sandbox environment per operator × repository that an
|
|
800
|
+
operator agent drives through the `sandbox.*` registry tools (`ab mcp`, and
|
|
801
|
+
every later binding), and — when the embedded orchestrator is enabled — the
|
|
802
|
+
turn runner that answers operator sessions and wakes on build attention
|
|
803
|
+
events. The whole table is **closed to unknown keys** and restart-classified:
|
|
804
|
+
changing it requires a dispatcher restart.
|
|
790
805
|
|
|
791
806
|
| Field | Default | Allowed / constraints | Effect |
|
|
792
807
|
|---|---|---|---|
|
|
793
|
-
| `enabled` | `false` | boolean | Master gate. With the table absent or `enabled = false`, the sandbox tools are absent from every binding
|
|
808
|
+
| `enabled` | `false` | boolean | Master gate. With the table absent or `enabled = false`, the sandbox tools are absent from every binding, no environment is ever provisioned, and no dispatcher tick or message post ever runs orchestrator code — a local dispatcher is inert even when enabled, because the turn runner exists only in the hosted deployment. |
|
|
809
|
+
| `model` | — (required when `enabled = true`) | nonblank provider-qualified string, same vocabulary as `[roles]` | The turn runner's model, resolved through the deployment's gateway credential; a leading `vercel-ai-gateway/` prefix is accepted and stripped. |
|
|
810
|
+
| `invocationBudgetSeconds` | `240` | positive integer, clamped to 300 | Wall-clock budget for one turn invocation. A larger value is clamped — not rejected — to the 300-second hosted function limit the runner executes under. |
|
|
811
|
+
| `approvals` | `["builds.control:abort", "builds.control:discard", "builds.answer:revise-spec", "sandbox.publish", "tickets.move:ready"]` | array of `tool` or `tool:qualifier` strings | Registry tool calls that suspend a turn until the operator answers. Empty (`[]`) means none — autonomous operation. An entry naming an absent or unknown tool is **inert by design**, never an error. A qualified entry applies to one discriminator value only (`tickets.move:ready` approves moving a ticket to `ready`); for a tool without a discriminator, only the bare form matches. |
|
|
812
|
+
| `wake` | inherits the default | array of event-type globs | The attention events a **new** session wakes for. Absent inherits the default attention set (the events `ab watch` filters on by default); an explicit `[]` means never wake — message-only. Each glob must match at least one build event type. Existing sessions never retroactively inherit; an explicit wake update always wins. |
|
|
794
813
|
| `sandbox` | — | strict subtable | The sandbox behavior knobs; absence reads as the defaults below. |
|
|
795
814
|
|
|
796
815
|
`[orchestrator.sandbox]` fields:
|
|
@@ -814,6 +833,20 @@ and archiving an operator's last session are **destructive** (reset re-provision
|
|
|
814
833
|
from the current base head); there is at most **one** environment per operator
|
|
815
834
|
and repository.
|
|
816
835
|
|
|
836
|
+
Publication: `sandbox.publish` takes a title (1–200 characters), an optional
|
|
837
|
+
body (≤65,536 characters), and an optional explicit commit, and takes your
|
|
838
|
+
sandbox's current head commit to a pull request. It refuses when the checkout
|
|
839
|
+
has uncommitted changes — tracked or untracked (the sandbox's own provisioning
|
|
840
|
+
marker is excluded) — and when the commit is not a descendant of the base head
|
|
841
|
+
recorded when the sandbox was provisioned or last reset — a pre-existing environment without a recorded
|
|
842
|
+
base head asks you to run `sandbox.reset` first. The rule is PR only, never the
|
|
843
|
+
base branch: the sandbox itself never pushes and holds no credential — the push
|
|
844
|
+
and the PR creation are kernel plumbing through the workspace provider's
|
|
845
|
+
publication capability, to one deterministic operator branch per operator and
|
|
846
|
+
repository, so a later publish updates the same PR's head. A publication is a
|
|
847
|
+
repository-journal fact naming the operator, the session, the branch, the
|
|
848
|
+
commit, and the PR; a refused or failed attempt is journaled too.
|
|
849
|
+
|
|
817
850
|
## Setup and upgrades
|
|
818
851
|
|
|
819
852
|
**`ab init <target> [--force]`** runs *outside* build sessions — it takes a
|
|
@@ -827,7 +860,7 @@ repo path, needs no `AB_*` environment, and is safe to re-run. It:
|
|
|
827
860
|
`[commands]`, no verify or finalize steps, a valid local ticket gate, and one
|
|
828
861
|
explicit runtime solely as a schema placeholder. It reads no package or
|
|
829
862
|
language manifest and invents no command.
|
|
830
|
-
- Vendors
|
|
863
|
+
- Vendors 12 skills before handoff, including the locally editable
|
|
831
864
|
[repository setup reference](references/setup.md) in the installed `ab-guide`
|
|
832
865
|
tree. On an interactive terminal it starts the selected coding-agent CLI in
|
|
833
866
|
the target repository with a short prompt to read that reference and
|
|
@@ -1070,6 +1103,37 @@ subcommand accepts `--json`, which emits one bare JSON value and no prose: a
|
|
|
1070
1103
|
`Ticket[]` for `list`, and the complete resulting `Ticket` for `create`,
|
|
1071
1104
|
`update`, `block`, `unblock`, `show`, or `move`.
|
|
1072
1105
|
|
|
1106
|
+
### Ticket assets
|
|
1107
|
+
|
|
1108
|
+
A ticket asset is a file or folder attached to a ticket and stored in the
|
|
1109
|
+
BuildStore, never in the ticket source: a design export, reference screenshots,
|
|
1110
|
+
a sample payload. Each has a kind and a name.
|
|
1111
|
+
|
|
1112
|
+
- `ab ticket attach <id> <kind> <path> [--name <name>] [--json]` stores `<path>`
|
|
1113
|
+
(default name: its basename). Attaching the same kind and name again makes a
|
|
1114
|
+
new revision; earlier ones stay retrievable. Over the store's size limit
|
|
1115
|
+
(25 MiB by default, lower on a hosted deployment) it is refused. Attach and
|
|
1116
|
+
remove leave a note on the ticket naming the asset.
|
|
1117
|
+
- `ab ticket show <id>` lists the current assets; with `--json` the ticket
|
|
1118
|
+
gains an `assets` array.
|
|
1119
|
+
- `ab ticket asset get <id> <kind> <name> <dest> [--rev <n>]` downloads the
|
|
1120
|
+
exact bytes of the latest (or given) revision; `ab ticket asset rm <id> <kind>
|
|
1121
|
+
<name>` drops it from the current assets.
|
|
1122
|
+
- **Frozen at claim.** A claim pins the current revisions into the build. A
|
|
1123
|
+
later attach or remove leaves a running build alone; a human answer with
|
|
1124
|
+
`--revise-spec-from-ticket` pins again. `ab build asset get <slug> <kind>
|
|
1125
|
+
<name> <dest> [--rev <n>]` downloads what the build froze, also after it ends.
|
|
1126
|
+
- **Where phases find them.** `ab context` writes each pinned asset to
|
|
1127
|
+
`.ab/assets/<kind>/<name>/` (a file asset is the single file inside that
|
|
1128
|
+
directory) and lists them under `assets` in `.ab/context.json` with kind,
|
|
1129
|
+
name, revision, layout, size, and path. The key is absent when the build has
|
|
1130
|
+
none.
|
|
1131
|
+
|
|
1132
|
+
By convention the `design` kind is a visual or reference contract, exported to
|
|
1133
|
+
static files when the ticket is groomed, so no build converts a link. The spec
|
|
1134
|
+
cites it by kind and name. See [the lifecycle](#the-lifecycle) for how phases
|
|
1135
|
+
judge against it.
|
|
1136
|
+
|
|
1073
1137
|
## Retrieving build artifacts
|
|
1074
1138
|
|
|
1075
1139
|
`ab artifact download <build> <kind>[@rev] --output <file> [--store <ref>]`
|
|
@@ -1081,6 +1145,17 @@ Inside a phase, a complete ambient tuple permits only the ambient build; a
|
|
|
1081
1145
|
foreign build or malformed/partial identity is rejected. Use the exact pinned
|
|
1082
1146
|
`@rev` from a PR attachment command.
|
|
1083
1147
|
|
|
1148
|
+
Repository-scoped streams have their own form:
|
|
1149
|
+
`ab artifact download stream:<id>[@rev] --output <file> [--store <ref>]`. A
|
|
1150
|
+
stream id is store-assigned and globally unique, so no build argument is
|
|
1151
|
+
needed; the command resolves the stream, requires it to be repository-scoped
|
|
1152
|
+
and owned by this repository, and retrieves the artifact its close finalized
|
|
1153
|
+
(§7.6) — an open stream has no artifact yet. This form is strictly an operator
|
|
1154
|
+
read: under any complete ambient build or Harvest identity it fails closed
|
|
1155
|
+
(§8.2), since repository-scoped targets are never reachable by ambient
|
|
1156
|
+
own-build or own-harvest authority. Find stream ids in the session rows of
|
|
1157
|
+
`ab harvest status`.
|
|
1158
|
+
|
|
1084
1159
|
In a build session, `ab artifact put <kind> <file> --attach` atomically deposits
|
|
1085
1160
|
the exact bytes and designates that revision for the PR. A later designation of
|
|
1086
1161
|
the same kind replaces it; distinct kinds remain distinct attachments. The
|
|
@@ -1214,16 +1289,19 @@ on its first paint; clearing the hold removes both on the next poll. The hold
|
|
|
1214
1289
|
survives a restart and is not defeated by re-running `ab dispatch`.
|
|
1215
1290
|
|
|
1216
1291
|
`--auto-merge` and `--no-auto-merge` similarly set the durable repository
|
|
1217
|
-
|
|
1292
|
+
auto-merge default; omission reuses stored state, falling back to OFF only when
|
|
1218
1293
|
no fact exists. The forms are mutually exclusive independently of the intake
|
|
1219
1294
|
pair. Global-row `m` re-reads current state, appends the opposite value, and
|
|
1220
1295
|
posts a dispatcher notice. When on, each fresh dispatcher claim records the
|
|
1221
1296
|
existing human-authored `build.auto-merge-requested` fact immediately after
|
|
1222
1297
|
`build.created` and before runner launch. The first visible build frame therefore
|
|
1223
|
-
carries `auto merge`.
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
the
|
|
1298
|
+
carries `auto merge`. The default is also a bulk action over current builds:
|
|
1299
|
+
on each change the dispatcher reconciles every non-terminal build against the
|
|
1300
|
+
newest fact, requesting consent when it turns on and withdrawing it when it
|
|
1301
|
+
turns off, attributed to the toggling operator exactly as a row keypress would
|
|
1302
|
+
be. A per-build toggle overrides its build's state until the global row is
|
|
1303
|
+
toggled again, so a seeded or fanned build can still be cancelled while the
|
|
1304
|
+
global default stays on. Both repository settings are independently
|
|
1227
1305
|
last-write-wins by event sequence. They are stored in the BuildStore, not in
|
|
1228
1306
|
`autobuild.toml`; propagation uses polling, not a push channel.
|
|
1229
1307
|
|
|
@@ -1249,10 +1327,13 @@ settles every exhaustion barrier. Exhaustion and escalation remain terminal.
|
|
|
1249
1327
|
Escalation's row is dismissed only by a post-terminal human resume request
|
|
1250
1328
|
followed by its later kernel acknowledgement, never by the request alone.
|
|
1251
1329
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1330
|
+
Every build row renders an explicit `auto merge <state>` token, including off:
|
|
1331
|
+
cyan means requested locally but not yet applied on GitHub, green means native
|
|
1332
|
+
auto-merge is enabled, yellow means cancellation is in flight, and dim means
|
|
1333
|
+
off — turning red while the repository default reads ON, so the
|
|
1334
|
+
consent mismatch is visible without opening the detail view. A build parked on
|
|
1335
|
+
an open PR with no consent carries the reason on its merge step, naming the
|
|
1336
|
+
per-build control that requests it.
|
|
1256
1337
|
Merge conflicts recover automatically: the gate defers, the pipeline re-enters
|
|
1257
1338
|
reconcile, and verify re-runs before consent is re-examined — the reconcile
|
|
1258
1339
|
step on the build row is the only indication, and uncomputed mergeability
|
|
@@ -1509,10 +1590,12 @@ stream:<id>` retrieves the closed session's finalized document.
|
|
|
1509
1590
|
Harvest sessions stream the same protocol into *repository-scoped* streams:
|
|
1510
1591
|
the harvest runner's synthesize and review brackets each get one stream
|
|
1511
1592
|
(`phase` spelled `harvest:<step>`), with `harvest.session.started` carrying
|
|
1512
|
-
the stream id.
|
|
1513
|
-
|
|
1514
|
-
`
|
|
1515
|
-
|
|
1593
|
+
the stream id. `ab harvest status` shows each run's sessions the way build
|
|
1594
|
+
status does — one row per session with its stream id and open/closed status
|
|
1595
|
+
(`session hs_… (harvest, synthesize@1) stream st_… (open)`). Once a stream
|
|
1596
|
+
closes, its finalized document is a repository-scoped `stream:<id>` artifact,
|
|
1597
|
+
and `ab artifact download stream:<id>` retrieves it; the stream form is an
|
|
1598
|
+
operator-only read (ambient phase identity fails closed).
|
|
1516
1599
|
|
|
1517
1600
|
**`ab watch [<slug>...] [--repository] [--event <glob>]... [--since <cursor>]
|
|
1518
1601
|
[--timeout <dur>] [--interval <dur>] [--count <n>] [--json] [--store <ref>]`**
|
|
@@ -1553,7 +1636,10 @@ The watch ends with exit 0 when `--timeout` elapses (30 minutes by default;
|
|
|
1553
1636
|
gone terminal; with no slugs named, an empty set of active builds does not end
|
|
1554
1637
|
it. Usage errors, scope violations, unknown slugs, and cursor rejection exit 1
|
|
1555
1638
|
before any record. A store read that fails after the watch has started is
|
|
1556
|
-
reported
|
|
1639
|
+
reported on stderr once per failing read source — locally the poll cycle is
|
|
1640
|
+
one source, while against an `http(s)` store each tracked stream and the
|
|
1641
|
+
discovery pass carries its own failure streak — and retried without
|
|
1642
|
+
duplicating or skipping any event.
|
|
1557
1643
|
Against a local store the default cadence polls every 1 second. Against an
|
|
1558
1644
|
`http(s)` store each tracked stream long-polls instead — one held request per
|
|
1559
1645
|
stream waits up to 25 seconds for the next event, requests run concurrently,
|
|
@@ -1635,8 +1721,11 @@ condition (one record with `condition` null, showing how the build ended); 3 —
|
|
|
1635
1721
|
stdout is a bare `{"cursor": …}` object for the final position, plus one line
|
|
1636
1722
|
on stderr); 4 — interrupted by SIGINT before any condition was satisfied (same
|
|
1637
1723
|
cursor behavior as 3). The default poll interval and `--interval` match
|
|
1638
|
-
`ab watch`. A store read that fails while waiting is reported
|
|
1639
|
-
|
|
1724
|
+
`ab watch`. A store read that fails while waiting is reported on stderr once
|
|
1725
|
+
per failing read source — locally the poll cycle is one source, while
|
|
1726
|
+
against an `http(s)` store each tracked build and the discovery pass carries
|
|
1727
|
+
its own failure streak — and retried without missing or duplicating a
|
|
1728
|
+
condition. The command is
|
|
1640
1729
|
read-only — it appends no event, takes no lease, and creates no record — and
|
|
1641
1730
|
deciding what to do about a match stays with the caller's skill.
|
|
1642
1731
|
|
|
@@ -1770,12 +1859,16 @@ default, when you need to know what this repo's version says).
|
|
|
1770
1859
|
| `ab-code-review` | `code-review` phase | Fresh skeptic: review the implementation diff against spec and plan, same verdict vocabulary. |
|
|
1771
1860
|
| `ab-reconcile` | `reconcile` phase (epilogue) | Resolve a conflicted PR with one merge commit, base merged *into* the build branch. Never rebases. |
|
|
1772
1861
|
| `ab-finalize` | `finalize` phase | Write the PR description for a green build; the kernel opens the PR. |
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
`ab-
|
|
1776
|
-
`disable-model-invocation: true` — do not invoke a phase
|
|
1777
|
-
not remove that key to make one convenient to call. A
|
|
1778
|
-
phase by pattern-matching a description is exactly
|
|
1779
|
-
three exceptions drive no phase, which is
|
|
1780
|
-
`ab-spec` and `ab-tickets` are
|
|
1781
|
-
`ab-guide` is read-only reference
|
|
1862
|
+
| `ab-operate` | hosted only | The embedded orchestrator's own operating manual: its role, the registry tool surface with the approval semantics, the attention set, wake-trigger input, budget/approval suspension, and when to escalate to the operator rather than act. Invoked by the embedded orchestrator, never vendored into a build session. |
|
|
1863
|
+
|
|
1864
|
+
Autobuild installs 12 skills. Everything except `ab-spec`, `ab-tickets`, and
|
|
1865
|
+
`ab-guide` carries `disable-model-invocation: true` — do not invoke a phase
|
|
1866
|
+
skill yourself, and do not remove that key to make one convenient to call. A
|
|
1867
|
+
model starting a pipeline phase by pattern-matching a description is exactly
|
|
1868
|
+
what the flag prevents. Those three exceptions drive no build phase, which is
|
|
1869
|
+
the criterion for membership (§16.3): `ab-spec` and `ab-tickets` are
|
|
1870
|
+
human/agent-facing pre-build surfaces, and `ab-guide` is read-only reference
|
|
1871
|
+
including the setup handoff guidance. The fourth skill outside the
|
|
1872
|
+
model-invocable set, `ab-operate`, is not a build-phase skill either: it is the
|
|
1873
|
+
embedded orchestrator's own operating manual, read by the hosted turn runner as
|
|
1874
|
+
its system prompt and never invoked by a model in a build session.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Operator API v1
|
|
2
2
|
|
|
3
3
|
The hosted service exposes the polling-oriented operator surface at
|
|
4
|
-
`/operator/v1/repos/{repo}`. It is versioned with remote store protocol **
|
|
4
|
+
`/operator/v1/repos/{repo}`. It is versioned with remote store protocol **3**.
|
|
5
5
|
Every request must send `X-Autobuild-Version`,
|
|
6
|
-
`X-Autobuild-Protocol-Version:
|
|
6
|
+
`X-Autobuild-Protocol-Version: 3`, and `Authorization: Bearer …`. Identity is
|
|
7
7
|
checked before authentication or resource lookup.
|
|
8
8
|
|
|
9
9
|
Mint a token offline; the signed user is used as the human actor on every write:
|
|
@@ -38,6 +38,17 @@ the durable provider locator as `BuildExecutionStart.workspaceRef`; local PID
|
|
|
38
38
|
supervision remains host-owned. A workspace plugin relying on these fields
|
|
39
39
|
should declare an API range containing `^1.5.0`.
|
|
40
40
|
|
|
41
|
+
Plugin API 1.7 extends the workspace-provider factory context with the
|
|
42
|
+
host-derived seams a store-requiring remote provider needs. Workspace-provider
|
|
43
|
+
factories additionally receive `storeRef` and `storeToken` (guaranteed present
|
|
44
|
+
when the registration declares `capabilities.storeRequirements`),
|
|
45
|
+
`runtimeReferences` (the host's effective runtime routes; absent at call sites
|
|
46
|
+
that reference no runtimes), and the checkout-less `origin` and
|
|
47
|
+
`remoteBranchHead` readers (absent means fall back to host `git` from
|
|
48
|
+
`repoRoot`). All five are optional, and a factory that ignores them changes
|
|
49
|
+
nothing. A plugin reading the new seams should declare an API range containing
|
|
50
|
+
`^1.7.0`.
|
|
51
|
+
|
|
41
52
|
`BuildStore` and `BlobStore` contract types are exported for remote-server
|
|
42
53
|
authors, but BuildStore is **not** an in-process manifest map. Implement the
|
|
43
54
|
colocated language-neutral
|
|
@@ -113,6 +124,12 @@ Every module must default-export one strict `AutobuildPluginManifest`:
|
|
|
113
124
|
absolute consuming-repository root. Ticket sources receive the existing
|
|
114
125
|
`[tickets]` fields, workspace providers receive `[workspace.config]`, and
|
|
115
126
|
runtime and forge factories currently receive an empty `config` object.
|
|
127
|
+
Workspace providers additionally receive the host-derived seams (API 1.7):
|
|
128
|
+
`storeRef` and `storeToken` — guaranteed strings when the registration
|
|
129
|
+
declares `capabilities.storeRequirements` — plus `runtimeReferences`,
|
|
130
|
+
`origin`, and `remoteBranchHead`. Each seam is optional and absent when the
|
|
131
|
+
host call site supplies no such value; absent `origin`/`remoteBranchHead`
|
|
132
|
+
fall back to host `git` from `repoRoot`.
|
|
116
133
|
|
|
117
134
|
Add the module specifier at the TOML root, before any table. Repository-path
|
|
118
135
|
specifiers resolve from the config-bearing root, which is the immutable build
|
|
@@ -20,7 +20,7 @@ document and the package's executable contracts together.
|
|
|
20
20
|
`AB_STORE`; the shipped client appends the routes below.
|
|
21
21
|
- Every `/builds`, `/repos`, and `/sessions` request sends
|
|
22
22
|
`X-Autobuild-Version` with the exact client package version and
|
|
23
|
-
`X-Autobuild-Protocol-Version` with the remote protocol version (`
|
|
23
|
+
`X-Autobuild-Protocol-Version` with the remote protocol version (`3` in this
|
|
24
24
|
distribution). Before authentication,
|
|
25
25
|
body parsing, or resource lookup, the server compares both exact strings.
|
|
26
26
|
Missing or different values receive `409 {"kind":"conflict","error":"…"}`;
|
|
@@ -290,7 +290,7 @@ it to exist. An unknown build returns `404 not-found`.
|
|
|
290
290
|
|
|
291
291
|
| BuildStore operation | HTTP route | Request | Success |
|
|
292
292
|
|---|---|---|---|
|
|
293
|
-
| `createBuild` | `POST /builds` | `{slug: string, repo: string, repoOrigin?: string, ticket?: TicketRef, branch?: string}`; `slug`, `repo`, and a supplied `branch` are nonempty | `201` + `BuildRecord`; duplicate slug is `409 conflict` |
|
|
293
|
+
| `createBuild` | `POST /builds` | `{slug: string, repo: string, repoOrigin?: string, ticket?: TicketRef, branch?: string, created?: event write}`; `slug`, `repo`, and a supplied `branch` are nonempty. `created`, when present, must be a valid `build.created` write; the server appends it as seq 1 atomically with the record (both exist or neither), and an invalid one is `422` with nothing created | `201` + `BuildRecord`; duplicate slug is `409 conflict` |
|
|
294
294
|
| `listBuilds` | `GET /builds` | none | `200` + `BuildRecord[]`; list order is unspecified |
|
|
295
295
|
| `getBuild` | `GET /builds/{slug}` | none | `200` + `BuildRecord`; absent is `404` (the shipped client maps this to `null`) |
|
|
296
296
|
| `append` | `POST /builds/{slug}/events` | event write | `201` + build event envelope |
|
|
@@ -357,7 +357,8 @@ requires. A held request's authentication, token scope, and version-skew rules
|
|
|
357
357
|
are identical to the immediate form: a `401`, `403`, or `409` identity failure
|
|
358
358
|
is answered immediately, never held.
|
|
359
359
|
|
|
360
|
-
The parameter is purely additive
|
|
360
|
+
The parameter is purely additive in the version it landed in (protocol `2`,
|
|
361
|
+
where the event-wait rules were introduced): a server
|
|
361
362
|
that ignores `wait` remains conforming — its immediate empty answers only raise
|
|
362
363
|
the client's request rate — and a client that sends `wait` to such a server
|
|
363
364
|
observes no other difference. This is the same reasoning the stream and session
|
|
@@ -368,7 +369,11 @@ waits apply. There is no push transport; a held request is the whole mechanism.
|
|
|
368
369
|
Repository journals are separate resources with independent event sequences,
|
|
369
370
|
artifacts, and leases. `{repo}` is one percent-encoded repository id; encode
|
|
370
371
|
embedded `/` characters. All resource routes authorize and require the
|
|
371
|
-
repository to exist. An unknown repository returns `404 not-found
|
|
372
|
+
repository to exist. An unknown repository returns `404 not-found` — with one
|
|
373
|
+
carve-out: the build-digest batch read of this section is keyed to build
|
|
374
|
+
records, not the repository journal record, so it authorizes the repo token
|
|
375
|
+
but answers `200` (possibly empty) even for an unknown repository, a repository
|
|
376
|
+
without a journal record, or a repository without builds.
|
|
372
377
|
|
|
373
378
|
| BuildStore operation | HTTP route | Request | Success |
|
|
374
379
|
|---|---|---|---|
|
|
@@ -376,10 +381,12 @@ repository to exist. An unknown repository returns `404 not-found`.
|
|
|
376
381
|
| `getRepo` | `GET /repos/{repo}` | none | `200` + `RepositoryRecord`; absent is `404` (the shipped client maps this to `null`) |
|
|
377
382
|
| `appendRepo` | `POST /repos/{repo}/events` | event write | `201` + repository event envelope |
|
|
378
383
|
| `getRepoEvents` | `GET /repos/{repo}/events?since={n}&wait={s}` | optional `since` query value, parsed as in section 3; absence defaults to `0`. Optional `wait` in whole seconds, per the event-wait rules of section 3 | `200` + envelopes with `seq >` parsed `since`, in increasing sequence order |
|
|
384
|
+
| `getRepoStateEvents` | `GET /repos/{repo}/state-events` | none | `200` + the bounded repository-journal subset (AUT-489): every durable event type (`harvest.*`, `orchestrator.sandbox.*`, the three dispatcher setting types) plus, when the journal has one, the tail from the latest `dispatcher.run-started`, in increasing sequence order; served inside the repository-existence gate like `GET events`, so an unknown repository is `404` |
|
|
379
385
|
| `appendRepoWithArtifacts` | `POST /repos/{repo}/deposits` | atomic deposit request | `201` + `{event, artifacts}` using repository shapes |
|
|
380
386
|
| `putRepoArtifact` | `POST /repos/{repo}/artifacts` | artifact input | `201` + repository artifact metadata |
|
|
381
387
|
| `getRepoArtifact` | `GET /repos/{repo}/artifacts?kind={kind}&rev={n}` | required nonempty `kind`; optional `rev` is parsed as in section 3 | `200` + artifact read; latest only when the `rev` parameter is absent; missing kind/revision is `200 null` |
|
|
382
388
|
| `listRepoArtifacts` | `GET /repos/{repo}/artifact-list?kind={kind}` | optional `kind`; absence means all kinds | `200` + metadata ordered by kind and then increasing revision |
|
|
389
|
+
| `getRepoBuildDigests` | `GET /repos/{repo}/build-digests` | none | `200` + `BuildDigest[]`, one entry per build record of the repository (possibly empty), ordered by slug; handled before the repository-existence gate, so an unknown or journal-less repository answers `200 []` |
|
|
383
390
|
| `claimRepoLease` | `POST /repos/{repo}/lease/claim` | claim request | `200 {"ok": boolean}` |
|
|
384
391
|
| `heartbeatRepo` | `POST /repos/{repo}/lease/heartbeat` | holder request | `200 {"ok": boolean}` |
|
|
385
392
|
| `releaseRepoLease` | `POST /repos/{repo}/lease/release` | holder request | `200 {"ok": true}`; a wrong holder is a successful no-op |
|
|
@@ -389,10 +396,153 @@ creation operation and must not reset timestamps, events, artifacts, or lease
|
|
|
389
396
|
state when the repository already exists. Event sequence numbering starts at 1
|
|
390
397
|
for each repository independently of every build and other repository.
|
|
391
398
|
|
|
399
|
+
A `BuildDigest` is the projection of one build's log onto its terminal fact
|
|
400
|
+
and its `observation.recorded` occurrences:
|
|
401
|
+
|
|
402
|
+
```jsonc
|
|
403
|
+
// BuildDigest
|
|
404
|
+
{
|
|
405
|
+
"slug": "ab-fix-login-3",
|
|
406
|
+
"terminal": "done", // optional: "done" | "aborted", omitted while the log has neither
|
|
407
|
+
"observations": [ // ascending-seq pairs for the log's observation.recorded events
|
|
408
|
+
{ "seq": 1, "ts": "2026-07-15T12:00:01.000Z" },
|
|
409
|
+
{ "seq": 4, "ts": "2026-07-15T12:00:04.000Z" }
|
|
410
|
+
],
|
|
411
|
+
"merged": "2026-07-15T12:00:06.000Z" // optional: ts of the log's latest pr.merged, omitted when none
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
The digest is derived from the event log on every call — the server persists
|
|
416
|
+
nothing and there is no refresh step. Protocol version `3` reshaped the
|
|
417
|
+
digest's `observations` from bare sequence numbers to `{seq, ts}` pairs and
|
|
418
|
+
added the optional `merged` fact, so the harvest pressure gate can evaluate
|
|
419
|
+
launch decisions from digests and the repository journal alone instead of
|
|
420
|
+
reading every build's full history per evaluation. That is a breaking wire
|
|
421
|
+
change, not an additive one: a server that predates it returns the old shape
|
|
422
|
+
and would fail the client's schema parse mid-request, which is why the version
|
|
423
|
+
moved — a stale peer is now rejected at the handshake (`409 conflict`) instead.
|
|
424
|
+
This is the same discipline the transport's exact-match version rules promise:
|
|
425
|
+
wire shapes that independently implemented servers must reproduce update this
|
|
426
|
+
document and the protocol version together.
|
|
427
|
+
|
|
392
428
|
The build-stream query, artifact, ordering, timestamp, and validation rules
|
|
393
429
|
apply symmetrically to repository journals, including the event-read bounded
|
|
394
430
|
wait of section 3.
|
|
395
431
|
|
|
432
|
+
### Ticket assets
|
|
433
|
+
|
|
434
|
+
A ticket asset is a named, versioned bundle of files attached to
|
|
435
|
+
a ticket, keyed by `(repo, ticketId, kind, name)`. These routes live under the
|
|
436
|
+
repository but, unlike the journal routes, **do not require a repository
|
|
437
|
+
record**: assets can precede any journal row, so the repository-existence gate
|
|
438
|
+
does not apply. `{repo}` and `{id}` are percent-encoded path segments. Token
|
|
439
|
+
authorization follows the one-matching-repo column of the matrix (a repo token
|
|
440
|
+
for its own repository, or admin); build and session tokens receive `403 auth`.
|
|
441
|
+
The family is additive, so `REMOTE_STORE_PROTOCOL_VERSION` did not change.
|
|
442
|
+
|
|
443
|
+
A build also reads the assets it froze at its claim through its own build-scoped
|
|
444
|
+
routes, which a build token may use (no ticket-asset authority needed). The
|
|
445
|
+
server reduces the build's events to find the pinned revision, then returns that
|
|
446
|
+
exact revision, so a later replace or removal never changes the answer and a
|
|
447
|
+
finished build still answers:
|
|
448
|
+
|
|
449
|
+
| BuildStore operation | HTTP route | Request | Success |
|
|
450
|
+
|---|---|---|---|
|
|
451
|
+
| `getPinnedTicketAsset` (manifest) | `GET /builds/{slug}/ticket-assets?kind=&name=[&rev=]` | required `kind` and `name`; optional `rev` | `200` + `TicketAssetMeta` for the latest pinned revision (or the named pinned `rev`), or `200 null` when the build never pinned it |
|
|
452
|
+
| `getPinnedTicketAsset` (one file) | `GET /builds/{slug}/ticket-assets/file?kind=&name=&rev=&path=` | required `kind`, `name`, `path`; optional `rev` | `200` + `{"contentBase64": string}`, or `200 null` when not pinned; `404` when the revision has no such file |
|
|
453
|
+
|
|
454
|
+
| BuildStore operation | HTTP route | Request | Success |
|
|
455
|
+
|---|---|---|---|
|
|
456
|
+
| `ticketAssetLimits` | `GET /repos/{repo}/ticket-asset-limits` | none | `200` + `{maxBytes, maxEntries, maxRequestBytes?}` — the effective per-revision ceilings and, when the deployment has one, the largest request or response body it can carry. A server without this route answers `404`; clients treat that as the defaults |
|
|
457
|
+
| `putTicketAsset` | `POST /repos/{repo}/tickets/{id}/assets` | `{kind, name, layout, entries}` | `201` + `TicketAssetMeta` (with its manifest) |
|
|
458
|
+
| `getTicketAsset` (manifest) | `GET /repos/{repo}/tickets/{id}/assets?kind=&name=[&rev=]` | required `kind` and `name`; optional `rev` | `200` + `TicketAssetMeta` — **the manifest only, no bytes** — or `200 null` when the asset is absent or removed (latest) or the revision does not exist |
|
|
459
|
+
| `getTicketAsset` (one file) | `GET /repos/{repo}/tickets/{id}/assets/file?kind=&name=&rev=&path=` | required `kind`, `name`, `path`; optional `rev` | `200` + `{"contentBase64": string}`, or `200 null` when the asset is absent; `404` when the revision has no such file |
|
|
460
|
+
| `listTicketAssets` | `GET /repos/{repo}/tickets/{id}/assets/list[?revisions=1]` | optional `revisions=1` | `200` + `TicketAssetSummary[]` ordered by kind, name, revision: the latest live revision per kind and name, or with `revisions=1` every real revision (no tombstones). Summaries carry no manifest |
|
|
461
|
+
| `removeTicketAsset` | `POST /repos/{repo}/tickets/{id}/assets/remove` | `{kind, name}` | `200` + the removed `TicketAssetMeta`, or `200 null` when no live asset exists |
|
|
462
|
+
|
|
463
|
+
Wire types:
|
|
464
|
+
|
|
465
|
+
```jsonc
|
|
466
|
+
// put request: entries are files (content as base64) or explicit directories
|
|
467
|
+
{ "kind": "design", "name": "home", "layout": "tree", // layout: "file" | "tree"
|
|
468
|
+
"entries": [
|
|
469
|
+
{ "type": "file", "path": "index.html", "contentBase64": "PGgxPg==" },
|
|
470
|
+
{ "type": "dir", "path": "img/empty" }
|
|
471
|
+
] }
|
|
472
|
+
|
|
473
|
+
// TicketAssetMeta: dir entries carry no size or blobRef
|
|
474
|
+
{ "repo": "…", "ticketId": "AUT-1", "kind": "design", "name": "home",
|
|
475
|
+
"revision": 0, "layout": "tree", "size": 1234, // size = total file bytes
|
|
476
|
+
"entries": [
|
|
477
|
+
{ "type": "file", "path": "index.html", "size": 1234, "blobRef": "<sha256 hex>" },
|
|
478
|
+
{ "type": "dir", "path": "img/empty" }
|
|
479
|
+
],
|
|
480
|
+
"createdAt": "2026-10-01T12:00:00.000Z" }
|
|
481
|
+
|
|
482
|
+
// TicketAssetSummary (list): the same without entries
|
|
483
|
+
{ "repo": "…", "ticketId": "AUT-1", "kind": "design", "name": "home",
|
|
484
|
+
"revision": 0, "layout": "tree", "size": 1234,
|
|
485
|
+
"fileCount": 1, "dirCount": 1, "createdAt": "…" }
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Semantics every server must reproduce:
|
|
489
|
+
|
|
490
|
+
- **Revisions** are 0-based per `(repo, ticketId, kind, name)`. Putting the same
|
|
491
|
+
kind and name again creates the next revision; earlier revisions stay readable
|
|
492
|
+
by explicit `rev`.
|
|
493
|
+
- **Removal is a tombstone** that takes the next revision number with an empty
|
|
494
|
+
manifest. The latest row decides liveness: a read without `rev` and the
|
|
495
|
+
default list skip a removed asset, while an explicit earlier `rev` keeps
|
|
496
|
+
working (a build that pinned that revision still retrieves it). Re-attaching
|
|
497
|
+
takes the next revision, so numbers may skip the tombstone. `revisions=1`
|
|
498
|
+
lists real revisions only.
|
|
499
|
+
- **Validation** (`422 validation`, nothing stored). `kind` and `name` match
|
|
500
|
+
`[A-Za-z0-9._-]+`, at most 64 characters. Paths are relative and
|
|
501
|
+
`/`-separated, with no empty, `.`, or `..` segment and no NUL, at most 256
|
|
502
|
+
UTF-8 bytes (128 per segment); paths are unique across files and directories
|
|
503
|
+
and no file is the ancestor of another entry. `layout: "file"` has exactly one
|
|
504
|
+
top-level `file` entry and no directory; `layout: "tree"` may hold any number
|
|
505
|
+
of entries, including zero (an empty folder; its root is implicit). A revision
|
|
506
|
+
holds at most 1000 entries (files plus directories) and **26214400 bytes (25
|
|
507
|
+
MiB)** of file bytes; the error names the limit, for example `ticket asset
|
|
508
|
+
exceeds the 26214400-byte (25 MiB) limit`.
|
|
509
|
+
- File bytes are content-addressed (`blobRef` is the sha256 hex) in the same
|
|
510
|
+
blob store as artifacts; the database holds only the manifest.
|
|
511
|
+
|
|
512
|
+
**Deployment ceilings.** A platform may reject oversized request or response
|
|
513
|
+
bodies before any application code runs (Vercel's functions cap them at 4.5
|
|
514
|
+
MB), so validating on the server alone cannot enforce a limit. A server that
|
|
515
|
+
has such a ceiling advertises it as `maxRequestBytes` on the limits route
|
|
516
|
+
together with the effective `maxBytes`, computed from it as follows. Paths and
|
|
517
|
+
entry counts are bounded, so the worst-case wire overhead is a constant:
|
|
518
|
+
`overhead = 1024 + 1000 × 320 = 321024` bytes, and
|
|
519
|
+
`maxBytes = min(26214400, max(0, floor((maxRequestBytes − overhead) × 3 / 4)))`
|
|
520
|
+
(base64 inflates content by 4/3); `maxEntries` stays 1000. The hosted service
|
|
521
|
+
sets `maxRequestBytes` to 4 MiB, giving an effective per-asset limit of 2904960
|
|
522
|
+
bytes (about 2.8 MiB). The formula is a safe lower bound, not the final
|
|
523
|
+
authority:
|
|
524
|
+
|
|
525
|
+
- The shipped **client** reads the limits once per store handle and, before any
|
|
526
|
+
upload, validates against `maxBytes` and `maxEntries` and then compares the
|
|
527
|
+
exact UTF-8 length of the serialized request with `maxRequestBytes`. A bundle
|
|
528
|
+
that does not fit is refused locally with `ticket asset exceeds this store's
|
|
529
|
+
N-byte limit (deployment request ceiling M bytes)` and **no upload request is
|
|
530
|
+
made**.
|
|
531
|
+
- The **server** re-validates every put against its effective limits and checks
|
|
532
|
+
every ticket-asset body against `maxRequestBytes` itself, answering `413` (a
|
|
533
|
+
request) or `422` with a limit-naming message where the platform lets the
|
|
534
|
+
request through.
|
|
535
|
+
- **Downloads never need a bundle-sized response**: the client reads the
|
|
536
|
+
manifest and then each file by its own request. The manifest, file, and list
|
|
537
|
+
routes check the exact serialized length of their response against
|
|
538
|
+
`maxRequestBytes` and fail with a `413 validation` error naming the asset (or
|
|
539
|
+
the file, its size, and the ceiling) rather than letting the platform reject
|
|
540
|
+
it. This matters for a revision stored under an older or higher ceiling:
|
|
541
|
+
its manifest and smaller files remain retrievable; a file that cannot fit can
|
|
542
|
+
only be fetched from a store with a higher ceiling. Chunked or ranged transfer
|
|
543
|
+
is not part of the protocol, so a deployment's effective per-asset limit is
|
|
544
|
+
its advertised `maxBytes`.
|
|
545
|
+
|
|
396
546
|
## 5. Operator-session operations
|
|
397
547
|
|
|
398
548
|
Operator sessions are a third resource kind: durable orchestrator-conversation
|
|
@@ -430,6 +580,7 @@ identity is already the actor.
|
|
|
430
580
|
| `listSessions` | `GET /repos/{repo}/sessions` | none | `200` + `SessionRecord[]`, creation order (`createdAt`, then the store's creation counter for same-millisecond ties) |
|
|
431
581
|
| `getSession` | `GET /sessions/{id}` | none | `200` + `SessionRecord`; absent is `404` (the shipped client maps this to `null`) |
|
|
432
582
|
| `appendSessionEvent` | `POST /sessions/{id}/events` | event write | `201` + session event envelope (same envelope shape with `"session"` in place of `"build"`) |
|
|
583
|
+
| `appendSessionEventIfCurrent` | `POST /sessions/{id}/events/conditional` | `{"expectedSeq": nonnegative integer, "event": event write}` | appended: `201` + session event envelope; session advanced: `200 null` (mirroring section 3's build compare-and-append) |
|
|
433
584
|
| `getSessionEvents` | `GET /sessions/{id}/events?since={n}&wait={s}` | optional `since` query value, parsed as in section 3; absence defaults to `0`. Optional `wait` in whole seconds, per the event-wait rules of section 3 | `200` + session event envelopes with `seq >` parsed `since`, in increasing sequence order |
|
|
434
585
|
| `appendSessionWithArtifacts` | `POST /sessions/{id}/deposits` | atomic deposit request | `201` + `{event, artifacts}` using session shapes; the substitution algorithm of section 8 applies unchanged |
|
|
435
586
|
| `putSessionArtifact` | `POST /sessions/{id}/artifacts` | artifact input | `201` + session artifact metadata |
|
|
@@ -599,7 +750,8 @@ by this rule (their retention belongs to the open archival thread), and open
|
|
|
599
750
|
streams are never pruned. A read of a stream whose chunks were pruned returns
|
|
600
751
|
no chunks, `status: "closed"`, and the artifact reference.
|
|
601
752
|
|
|
602
|
-
These additions
|
|
753
|
+
These additions were purely additive in the version they landed in (protocol
|
|
754
|
+
`2`, where the stream routes were introduced), and a
|
|
603
755
|
conforming server implements the stream routes together with this document's
|
|
604
756
|
rules exactly as it does for the event and artifact routes.
|
|
605
757
|
|
|
@@ -675,12 +827,12 @@ resource access; an explicit resource whose id is `"*"` is not admin.
|
|
|
675
827
|
|
|
676
828
|
### Resource authorization matrix
|
|
677
829
|
|
|
678
|
-
| Token resource | `/builds` create/list | one matching build | `/repos` ensure | one matching repo | `/repos/{repo}/sessions` create/list | one matching session |
|
|
679
|
-
|
|
680
|
-
| admin (`build: "*"`) | yes | any | yes | any | yes | any |
|
|
681
|
-
| build | no | exact id only | no | no | no | no |
|
|
682
|
-
| repo | no | no | no | exact id only | yes | no |
|
|
683
|
-
| session | no | no | no | no | no | exact id only |
|
|
830
|
+
| Token resource | `/builds` create/list | one matching build | `/repos` ensure | one matching repo | `/repos/{repo}/sessions` create/list | `/repos/{repo}/build-digests` | `/repos/{repo}/state-events` | `/repos/{repo}/tickets/{id}/assets` and `/repos/{repo}/ticket-asset-limits` | one matching session |
|
|
831
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
832
|
+
| admin (`build: "*"`) | yes | any | yes | any | yes | any | any | any | any |
|
|
833
|
+
| build | no | exact id only | no | no | no | no | no | no | no |
|
|
834
|
+
| repo | no | no | no | exact id only | yes | exact id only | exact id only | exact repo only | no |
|
|
835
|
+
| session | no | no | no | no | no | no | no | no | exact id only |
|
|
684
836
|
|
|
685
837
|
A valid token used for the wrong resource receives `403 auth`. Resource scope
|
|
686
838
|
gates all operations, including reads, artifact operations, and leases. A
|
|
@@ -793,8 +945,8 @@ The shipped server maps failures as follows:
|
|
|
793
945
|
| `403` | `auth` | Valid token with the wrong resource scope or event-session attribution |
|
|
794
946
|
| `404` | `not-found` | Unknown route, unsupported method, unknown build, unknown repository, unknown session, or an unknown or foreign-scoped stream |
|
|
795
947
|
| `409` | `conflict` | Missing/mismatched package or protocol identity, duplicate build creation, appending to a closed stream (`stream "…" is closed`), or a backing conflict reported as already existing |
|
|
796
|
-
| `413` | `validation` | Decoded artifact or serialized stream batch exceeds the configured ceiling; the message names the ceiling |
|
|
797
|
-
| `422` | `validation` | `EventValidationError` from build, repository, or session catalog validation; its message is preserved verbatim |
|
|
948
|
+
| `413` | `validation` | Decoded artifact or serialized stream batch exceeds the configured ceiling; a ticket-asset request or response exceeds the deployment's request ceiling; the message names the ceiling |
|
|
949
|
+
| `422` | `validation` | `EventValidationError` from build, repository, or session catalog validation, or a ticket-asset validation failure (including the 25 MiB limit); its message is preserved verbatim |
|
|
798
950
|
| `500` | `internal` | Any other unexpected backing or server failure; the thrown error message is returned |
|
|
799
951
|
|
|
800
952
|
Authentication runs before resource lookup, and session authorization runs
|
|
@@ -916,11 +1068,12 @@ Four shipped behaviors do not add `BuildStore` routes:
|
|
|
916
1068
|
store lifecycle; there is no close endpoint.
|
|
917
1069
|
- `GET /health` is outside the `BuildStore` interface. It is always open and
|
|
918
1070
|
returns exactly `200 {"ok":true,"autobuildVersion":"<package>",
|
|
919
|
-
"protocolVersion":"
|
|
1071
|
+
"protocolVersion":"3"}`. It reports HTTP-process availability and identity,
|
|
920
1072
|
not a deeper backing-store transaction or migration check.
|
|
921
1073
|
|
|
922
|
-
There is no push/WebSocket subscription protocol,
|
|
923
|
-
|
|
1074
|
+
There is no push/WebSocket subscription protocol, repository listing, artifact
|
|
1075
|
+
deletion, or server deployment API. The one repo-scoped batch read is the
|
|
1076
|
+
build-digest route of section 4; no other batch-read route exists.
|
|
924
1077
|
|
|
925
1078
|
## 12. Conformance
|
|
926
1079
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The hosted ticket protocol exposes the complete `TicketSource` port without
|
|
4
4
|
exposing backend credentials. It shares the Autobuild and remote protocol
|
|
5
5
|
version headers with the remote BuildStore. Both must exactly match the service;
|
|
6
|
-
this release uses remote protocol version `
|
|
6
|
+
this release uses remote protocol version `3`.
|
|
7
7
|
|
|
8
8
|
## Authentication and context
|
|
9
9
|
|
|
@@ -59,7 +59,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y chromium""" },
|
|
|
59
59
|
|
|
60
60
|
[workspace.config.runtimeProvisioning.pi]
|
|
61
61
|
install = "npm install --global --ignore-scripts @earendil-works/pi-coding-agent@0.84.4"
|
|
62
|
-
preflight = "test \"$(pi --version)\" = \"0.84.4\""
|
|
62
|
+
preflight = "test \"$(pi --version)\" = \"0.84.4\" && pi update --models"
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
Use a `vercel-ai-gateway/...` Pi model with `AI_GATEWAY_API_KEY` in the
|
|
@@ -220,14 +220,17 @@ same lockstep window. If the release is cut but the store upgrade lags, guests
|
|
|
220
220
|
carrying the new distribution fail loudly against the old store (409) rather
|
|
221
221
|
than running silently stale.
|
|
222
222
|
|
|
223
|
-
Provisioning records the installed distribution's
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
223
|
+
Provisioning records the installed distribution's identity — package version
|
|
224
|
+
and remote-store protocol version — in the guest's
|
|
225
|
+
`/opt/autobuild/.distribution-version` marker. Every path that launches guest
|
|
226
|
+
code in an existing sandbox — provisioning reuse, harvest reuse, the operator
|
|
227
|
+
sandbox, and build execution restart after a lease sweep — compares the marker
|
|
228
|
+
against the identity the current system would deliver and, when they disagree
|
|
229
|
+
(which includes every guest provisioned before the marker existed), refreshes
|
|
230
|
+
it in place (archive re-fetched, extracted, and production-installed) first,
|
|
231
|
+
so an upgraded dispatcher retrofits its existing guests instead of resuming a
|
|
232
|
+
stale distribution the hosted store would reject. A refresh failure deletes
|
|
233
|
+
the sandbox so the next provisioning pass rematerializes it from scratch.
|
|
231
234
|
|
|
232
235
|
After a lockstep upgrade, confirm a fix-bearing guest from the guest
|
|
233
236
|
perspective:
|