@defrex/autobuild 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SPEC.md +323 -26
- package/bin/ab-build-runner.ts +8 -2
- package/docs/architecture.md +166 -14
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +175 -28
- package/docs/hosted-dispatcher.md +69 -13
- package/docs/operator-api.md +2 -2
- package/docs/remote-store-protocol.md +204 -16
- package/docs/remote-ticket-protocol.md +1 -1
- package/docs/setup.md +12 -9
- package/docs/website.md +14 -0
- package/package.json +45 -23
- package/packages/core/src/cli/artifact.ts +130 -6
- package/packages/core/src/cli/binary.ts +9 -1
- package/packages/core/src/cli/build-control.ts +110 -13
- package/packages/core/src/cli/context.ts +46 -0
- package/packages/core/src/cli/dashboard/facts.ts +215 -0
- package/packages/core/src/cli/dashboard/frame-parse.ts +223 -0
- package/packages/core/src/cli/dashboard/model.ts +141 -153
- package/packages/core/src/cli/dashboard/poll.ts +76 -12
- package/packages/core/src/cli/dashboard/render.ts +34 -5
- package/packages/core/src/cli/dispatch-frontend.ts +54 -5
- package/packages/core/src/cli/dispatch-process.ts +1 -13
- package/packages/core/src/cli/dispatch.ts +392 -75
- package/packages/core/src/cli/harvest.ts +122 -8
- package/packages/core/src/cli/help.ts +62 -9
- package/packages/core/src/cli/init-readiness-remote.ts +110 -0
- package/packages/core/src/cli/init-readiness-shared.ts +72 -0
- package/packages/core/src/cli/init-validation.ts +266 -222
- package/packages/core/src/cli/installation.ts +50 -8
- package/packages/core/src/cli/main.ts +70 -11
- package/packages/core/src/cli/mcp.ts +23 -1
- package/packages/core/src/cli/plugin.ts +7 -4
- package/packages/core/src/cli/remote-poll.ts +277 -0
- package/packages/core/src/cli/repo-state.ts +22 -0
- package/packages/core/src/cli/repository-status.ts +39 -1
- package/packages/core/src/cli/self-update.ts +77 -2
- package/packages/core/src/cli/status.ts +150 -28
- package/packages/core/src/cli/terminals.ts +2 -1
- package/packages/core/src/cli/ticket-assets.ts +247 -0
- package/packages/core/src/cli/ticket.ts +453 -8
- package/packages/core/src/cli/wait.ts +157 -41
- package/packages/core/src/cli/watch.ts +138 -168
- package/packages/core/src/config/live.ts +23 -0
- package/packages/core/src/config/pipeline-source.ts +388 -0
- package/packages/core/src/config/schema.ts +251 -19
- package/packages/core/src/events/catalog.ts +1 -0
- package/packages/core/src/events/globs.ts +47 -0
- package/packages/core/src/events/payloads.ts +52 -2
- package/packages/core/src/events/repository.ts +52 -1
- package/packages/core/src/events/sessions.ts +69 -10
- package/packages/core/src/events/workspace-remote.ts +12 -0
- package/packages/core/src/integration/harness.ts +115 -2
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +1369 -0
- package/packages/core/src/kernel/auto-merge-default.ts +157 -0
- package/packages/core/src/kernel/auto-merge.ts +99 -22
- package/packages/core/src/kernel/dispatch-settings.ts +42 -17
- package/packages/core/src/kernel/dispatch-status.ts +23 -0
- package/packages/core/src/kernel/engine.ts +9 -438
- package/packages/core/src/kernel/generators/build-log.ts +259 -0
- package/packages/core/src/kernel/generators/repository-journals.ts +206 -0
- package/packages/core/src/kernel/generators/session-log.ts +72 -0
- package/packages/core/src/kernel/harvest.ts +62 -18
- package/packages/core/src/kernel/incremental-contract.ts +75 -0
- package/packages/core/src/kernel/incremental-fixtures.ts +211 -0
- package/packages/core/src/kernel/incremental.ts +53 -0
- package/packages/core/src/kernel/log-index.ts +620 -0
- package/packages/core/src/kernel/phases.ts +16 -2
- package/packages/core/src/kernel/pr-attachments.ts +164 -46
- package/packages/core/src/kernel/reducer-registry.ts +164 -0
- package/packages/core/src/kernel/reducer.ts +254 -73
- package/packages/core/src/operator/annotations.ts +6 -0
- package/packages/core/src/operator/control.ts +19 -5
- package/packages/core/src/operator/index.ts +71 -6
- package/packages/core/src/operator/query.ts +253 -24
- package/packages/core/src/operator/read-view.ts +98 -0
- package/packages/core/src/operator/registry.ts +55 -10
- package/packages/core/src/operator/requests.ts +9 -6
- package/packages/core/src/operator/sandbox-host.ts +140 -0
- package/packages/core/src/operator/sandbox.ts +247 -14
- package/packages/core/src/operator/ticket-source-backend.ts +47 -0
- package/packages/core/src/orchestrator/approvals.ts +115 -0
- package/packages/core/src/orchestrator/conversation.ts +167 -0
- package/packages/core/src/orchestrator/model.ts +86 -0
- package/packages/core/src/orchestrator/prompt.ts +61 -0
- package/packages/core/src/orchestrator/tools.ts +117 -0
- package/packages/core/src/orchestrator/turn-runner.ts +457 -0
- package/packages/core/src/plugin-sdk/index.ts +51 -1
- package/packages/core/src/plugins/load.ts +135 -7
- package/packages/core/src/plugins/manifest.ts +183 -10
- package/packages/core/src/plugins/registry.ts +73 -4
- package/packages/core/src/ports/forge/create.ts +1 -1
- package/packages/core/src/ports/forge/fake.ts +31 -0
- package/packages/core/src/ports/forge/github-transport.ts +13 -2
- package/packages/core/src/ports/forge/github.ts +13 -2
- package/packages/core/src/ports/forge/local-git.ts +31 -1
- package/packages/core/src/ports/runner/claude.ts +2 -2
- package/packages/core/src/ports/runner/codex.ts +6 -2
- package/packages/core/src/ports/runner/fake.ts +7 -1
- package/packages/core/src/ports/runner/pi.ts +8 -6
- package/packages/core/src/ports/runner/provider-error.ts +13 -3
- package/packages/core/src/ports/tickets/fake.ts +5 -1
- package/packages/core/src/ports/tickets/file.ts +169 -48
- package/packages/core/src/ports/tickets/{remote-index.ts → hosted-index.ts} +3 -2
- package/packages/core/src/ports/tickets/linear.ts +33 -1
- package/packages/core/src/ports/tickets/operations.ts +1 -1
- package/packages/core/src/ports/tickets/remote.ts +8 -1
- package/packages/core/src/ports/types.ts +49 -3
- package/packages/core/src/ports/workspace/builtin-capabilities.ts +114 -0
- package/packages/core/src/ports/workspace/create.ts +74 -39
- package/packages/core/src/ports/workspace/distribution-archive.ts +171 -12
- package/packages/core/src/ports/workspace/fake.ts +169 -4
- package/packages/core/src/ports/workspace/git-worktree.ts +246 -26
- package/packages/core/src/ports/workspace/github-origin.ts +36 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +7 -1
- package/packages/core/src/ports/workspace/provider-capabilities.ts +171 -0
- package/packages/core/src/ports/workspace/vercel-capabilities.ts +110 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +142 -114
- package/packages/core/src/processes/build-child.ts +17 -2
- package/packages/core/src/processes/build-execution-state.ts +68 -15
- package/packages/core/src/processes/build-runner.ts +238 -93
- package/packages/core/src/processes/dispatcher-selectors.ts +123 -0
- package/packages/core/src/processes/dispatcher.ts +501 -201
- package/packages/core/src/processes/execution-settlement.ts +72 -39
- package/packages/core/src/processes/harvest-child.ts +5 -0
- package/packages/core/src/processes/harvest-execution-state.ts +41 -29
- package/packages/core/src/processes/harvest-runner.ts +66 -19
- package/packages/core/src/processes/harvest.ts +196 -24
- package/packages/core/src/processes/infrastructure-failure-budget.ts +31 -18
- package/packages/core/src/processes/orchestrator-tick.ts +360 -0
- package/packages/core/src/processes/publication-state.ts +164 -52
- package/packages/core/src/processes/repo-view.ts +1436 -0
- package/packages/core/src/processes/sandbox-state.ts +36 -2
- package/packages/core/src/processes/tick-state.ts +33 -0
- package/packages/core/src/store/adapter.ts +41 -0
- package/packages/core/src/store/build-scope.ts +90 -0
- package/packages/core/src/store/contract.ts +1205 -0
- package/packages/core/src/store/digest.ts +102 -0
- package/packages/core/src/store/local/schema.ts +46 -2
- package/packages/core/src/store/local/store.ts +445 -4
- package/packages/core/src/store/memory.ts +246 -0
- package/packages/core/src/store/new-build.ts +17 -0
- package/packages/core/src/store/phase-session.ts +94 -0
- package/packages/core/src/store/remote/client.ts +310 -2
- package/packages/core/src/store/remote/index.ts +14 -2
- package/packages/core/src/store/remote/protocol.ts +115 -12
- package/packages/core/src/store/remote/version.ts +6 -2
- package/packages/core/src/store/repo-state-events.ts +205 -0
- package/packages/core/src/store/retention.ts +7 -0
- package/packages/core/src/store/session-handle.ts +94 -0
- package/packages/core/src/{sessions/reducer.ts → store/session-reducer.ts} +79 -19
- package/packages/core/src/store/snapshots.ts +88 -0
- package/packages/core/src/store/streams/session-writer.ts +42 -2
- package/packages/core/src/store/streams/wait.ts +4 -5
- package/packages/core/src/store/ticket-assets.ts +458 -0
- package/packages/core/src/store/types.ts +160 -1
- package/packages/core/src/testing/index.ts +59 -0
- package/skills/code-review/SKILL.md +5 -1
- package/skills/guide/SKILL.md +152 -40
- package/skills/guide/references/operator-api.md +2 -2
- package/skills/guide/references/plugin-authoring.md +17 -0
- package/skills/guide/references/remote-store-protocol.md +204 -16
- package/skills/guide/references/remote-ticket-protocol.md +1 -1
- package/skills/guide/references/setup.md +12 -9
- package/skills/implement/SKILL.md +3 -0
- package/skills/operate/SKILL.md +126 -0
- package/skills/plan/SKILL.md +4 -0
- package/skills/plan-review/SKILL.md +5 -1
- package/skills/spec/SKILL.md +46 -3
- package/skills/tickets/SKILL.md +57 -5
- package/packages/core/src/cli/dashboard/frame-image.ts +0 -373
- package/packages/core/src/cli/store-opening.contract.ts +0 -289
- package/packages/core/src/cli/testkit.ts +0 -194
- package/packages/core/src/markdown.ts +0 -67
- package/packages/core/src/operator/client.ts +0 -311
- package/packages/core/src/operator/server.ts +0 -616
- package/packages/core/src/operator/sessions.ts +0 -223
- package/packages/core/src/ports/runner/live-contract-fixture.ts +0 -176
- package/packages/core/src/ports/tickets/remote-server.ts +0 -185
- package/packages/core/src/store/remote/server.ts +0 -923
- package/packages/core/src/testing/store-failures.ts +0 -32
package/README.md
CHANGED
package/SPEC.md
CHANGED
|
@@ -218,10 +218,12 @@ Small, independently runnable, crash-safe:
|
|
|
218
218
|
repo, so re-running `ab dispatch` resumes durable work rather than only
|
|
219
219
|
looking for new tickets. Every ordinary tick also completes queued dispatch
|
|
220
220
|
records whose pre-run sequence was interrupted: it reconstructs a missing
|
|
221
|
-
`build.created` from the immutable record
|
|
221
|
+
`build.created` from the immutable record (only records an older dispatcher
|
|
222
|
+
left with an empty stream: new claims write it atomically with the record), executes only missing workspace,
|
|
222
223
|
spec, and ticket-notification boundaries, and records each failed attempt as
|
|
223
224
|
`dispatch.failed`. `build.created` retains any claim-time auto-merge
|
|
224
|
-
attribution
|
|
225
|
+
attribution and the sampled default fact's seq until the human-authored
|
|
226
|
+
command fact is materialized, while
|
|
225
227
|
`dispatch.comment-posted` prevents retries from duplicating the ticket notice.
|
|
226
228
|
It also owns observation back-pressure: settling
|
|
227
229
|
outstanding recoverable harvest runs takes priority over starting new scans
|
|
@@ -239,6 +241,16 @@ same BuildStore. Consequences, by design:
|
|
|
239
241
|
|
|
240
242
|
- **State is a reduction of events.** Any state snapshot is a cache, never
|
|
241
243
|
the source of truth. Resumability falls out.
|
|
244
|
+
- **Reducer snapshots are a cache the log regenerates, never an authority.**
|
|
245
|
+
The store may keep, per scope (one build log or the repository journal) and
|
|
246
|
+
reducer, that reducer's serialized state as of a cursor, keyed by scope,
|
|
247
|
+
reducer, reducer version, and cursor, so a process with no hot memory loads
|
|
248
|
+
it, reads only the newer events, and advances. A missing snapshot, one
|
|
249
|
+
written by a different reducer version, or one ahead of the log is ignored
|
|
250
|
+
and the state is rebuilt by full replay with identical results; stored
|
|
251
|
+
cursors never decrease; deleting every snapshot changes no observable
|
|
252
|
+
output. A store without snapshot storage behaves as if every snapshot were
|
|
253
|
+
missing.
|
|
242
254
|
- **The UI layer is a subscriber** plus a command channel back. TUI, web —
|
|
243
255
|
same adapter pattern. The shipped interactive terminal owns a supervised
|
|
244
256
|
kernel child, and the BuildStore is their only changing-state boundary.
|
|
@@ -386,6 +398,27 @@ raises an `escalation`; a human uses `ab answer --revise-spec` (or
|
|
|
386
398
|
`--revise-spec-from-ticket`), the spec gets rev N+1, and the build restarts
|
|
387
399
|
from `plan` (cheap — downstream was invalidated anyway).
|
|
388
400
|
|
|
401
|
+
**Ticket assets are frozen with the spec.** Claiming a ticket pins the
|
|
402
|
+
ticket's current assets (§7.1.3) into the build the same way the spec becomes
|
|
403
|
+
revision 0: `build.created.assets` lists exactly which `(kind, name, revision)`
|
|
404
|
+
the build sees, omitted when the ticket has none. The set is sampled
|
|
405
|
+
immediately after the claim succeeds and before spec authoring, and it is
|
|
406
|
+
written atomically with the build record (`createBuild` takes the initial
|
|
407
|
+
`build.created` event), so an edit made while a spec is being authored, or a
|
|
408
|
+
crash between the two writes, can never change or lose the pin; interrupted
|
|
409
|
+
dispatch recovery re-reads it from the log. Attaching, replacing, or removing
|
|
410
|
+
an asset afterwards leaves the running build alone, because a build reads its
|
|
411
|
+
assets by pinned revision, never "latest". `ab ticket attach` and `asset rm`
|
|
412
|
+
warn when the ticket has an active build. Revising the spec **from the ticket**
|
|
413
|
+
(`ab answer --revise-spec-from-ticket`) re-pins the ticket's then-current
|
|
414
|
+
assets together with its body, as `spec.revised.assets` (present, possibly
|
|
415
|
+
empty, means "re-pin to this set"); the set is sampled once, when the body is
|
|
416
|
+
read, and recorded on the `escalation.answered` authorization too, so a retry
|
|
417
|
+
after a crash publishes the originally authorized set. Revising from a supplied
|
|
418
|
+
body file leaves the pinned assets unchanged. The one case this cannot cover is
|
|
419
|
+
a record an older dispatcher created with an empty stream: recovery can only
|
|
420
|
+
sample the ticket's current assets for it.
|
|
421
|
+
|
|
389
422
|
## 7. The build store
|
|
390
423
|
|
|
391
424
|
The v1 repo-bloat and no-observability problems share one root cause: the
|
|
@@ -407,6 +440,8 @@ artifacts build_id, kind, revision, blobRef, metadata
|
|
|
407
440
|
repo_streams repo, created/updated, lease + heartbeat
|
|
408
441
|
repo_events repo, seq, timestamp, actor, type, payload (JSON) — append-only
|
|
409
442
|
repo_artifacts repo, kind, revision, blobRef, metadata
|
|
443
|
+
ticket_assets repo, ticket_id, kind, name, revision, layout, manifest (JSON), size,
|
|
444
|
+
removed, created — one row per revision
|
|
410
445
|
```
|
|
411
446
|
|
|
412
447
|
#### 7.1.1 Operator sessions (the third resource kind)
|
|
@@ -464,6 +499,141 @@ over the remote protocol the read is
|
|
|
464
499
|
stream read's ([docs/remote-store-protocol.md](docs/remote-store-protocol.md),
|
|
465
500
|
§5).
|
|
466
501
|
|
|
502
|
+
#### 7.1.2 The embedded orchestrator (turns)
|
|
503
|
+
|
|
504
|
+
The orchestrator is the agent that files tickets, watches builds, unblocks
|
|
505
|
+
them, and discusses direction — played in the hosted service by a stateless
|
|
506
|
+
turn runner over the operator tool registry, not by an agent living in a
|
|
507
|
+
sandbox (parity with MCP; no credential plumbing) and not by an external
|
|
508
|
+
workflow engine (durability comes from the store; the tick pattern already
|
|
509
|
+
exists). Posting a message to a session of an enabled repository starts a
|
|
510
|
+
turn in the same invocation: an AI SDK tool-loop agent, running with the
|
|
511
|
+
registry bound in process, reads the conversation from the session's durable
|
|
512
|
+
state, streams its output as protocol parts onto the turn's session-scoped
|
|
513
|
+
stream (§7.6), calls tools with the operator's attributed identity — the
|
|
514
|
+
session operator as the human actor, `via: {kind: "session", id}` —
|
|
515
|
+
checkpoints as it goes, and completes or suspends.
|
|
516
|
+
|
|
517
|
+
**Turn lifecycle.** `turn.started` (naming a new session-scoped stream and
|
|
518
|
+
the trigger — a message or a wake) → running → either `turn.completed`
|
|
519
|
+
(with input/output token usage and the step count), `turn.suspended`
|
|
520
|
+
(cause `budget` or `approval`), or typed `turn.failed`. A suspended turn
|
|
521
|
+
resumes via `turn.resumed` — the sole resume fact, recorded by the runner
|
|
522
|
+
when it actually picks the turn up; `approval.answered` only clears the
|
|
523
|
+
pending approval, so an answered approval whose resuming invocation died
|
|
524
|
+
stays recoverable. The stream stays open across a suspension and is the
|
|
525
|
+
resume checkpoint; it closes `completed` or `aborted` at the outcome.
|
|
526
|
+
Conversation state is reconstructed from durable state alone — the finalized
|
|
527
|
+
artifacts of earlier turns, the live stream of the open turn, and the session
|
|
528
|
+
events — so a turn resumed in a fresh invocation continues from the last
|
|
529
|
+
checkpointed step with identical model input; no process memory survives
|
|
530
|
+
between invocations. A turn's assembled messages attach at its `turn.started`
|
|
531
|
+
event, so a message posted during a suspension reads as the next user message
|
|
532
|
+
after the checkpoint, never interleaved into it.
|
|
533
|
+
|
|
534
|
+
**Budget suspension.** `[orchestrator].invocationBudgetSeconds` (default 240,
|
|
535
|
+
clamped to the 300-second hosted function limit) bounds one invocation. When
|
|
536
|
+
elapsed time approaches the budget — or a hard abort at the deadline
|
|
537
|
+
interrupts a runaway step — the runner records `turn.suspended` cause
|
|
538
|
+
`budget` and ends the invocation; a new invocation resumes it within 60
|
|
539
|
+
seconds (immediately when the service can self-trigger, otherwise on the
|
|
540
|
+
next dispatcher tick). The dispatcher tick's orchestrator step runs LAST,
|
|
541
|
+
after every build stage, against min(configured, remaining tick budget) with
|
|
542
|
+
a 30-second floor: it can only consume slack, and skipping is always safe
|
|
543
|
+
because suspension state is durable.
|
|
544
|
+
|
|
545
|
+
**Approval suspension.** A call to a tool named in
|
|
546
|
+
`[orchestrator].approvals` records `approval.requested`, emits the protocol's
|
|
547
|
+
tool-approval-request part, and suspends the turn with cause `approval`. An
|
|
548
|
+
`approve` answer resumes the turn and executes the call; a `deny` answer
|
|
549
|
+
resumes it with a denied tool output. Tools not on the list run without
|
|
550
|
+
pause. Entries are `tool` or `tool:qualifier`; an entry naming an absent or
|
|
551
|
+
unknown tool is inert by design.
|
|
552
|
+
|
|
553
|
+
**Wake rule.** The dispatcher tick's orchestrator step wakes each idle
|
|
554
|
+
session with non-empty wake settings: it scans the repository's builds for
|
|
555
|
+
build events after the session's per-build wake cursor and the repository
|
|
556
|
+
journal for events after the session's journal wake cursor — both filtered
|
|
557
|
+
by the same compiled settings, which may name build event types,
|
|
558
|
+
repository-journal event types, or both — and starts one turn per session
|
|
559
|
+
per tick with a `wake` trigger naming the winning event's source: the build,
|
|
560
|
+
seq, and type for a build-log wake, or the journal flag, seq, and type for a
|
|
561
|
+
repository-journal wake. A build wake delivers the event record and the
|
|
562
|
+
build's reduced state — frozen into the `turn.started` fact — as the turn's
|
|
563
|
+
input; a journal wake delivers the event record only (the turn's operator
|
|
564
|
+
registry already exposes bounded repository reads). Each source's cursor
|
|
565
|
+
advances only through a recorded trigger naming that source, so a crash
|
|
566
|
+
between scan and start cannot skip or duplicate a wake, and other builds' —
|
|
567
|
+
and the journal's — matching events re-trigger on later ticks. Sessions with
|
|
568
|
+
empty wake settings are never woken. The default wake settings span both
|
|
569
|
+
halves of the attention set: the build events `ab watch` filters on by
|
|
570
|
+
default plus the repository-journal events `ab watch --repository` follows.
|
|
571
|
+
|
|
572
|
+
**Crash recovery.** A turn invocation that dies without an outcome leaves the
|
|
573
|
+
session `running` with a silent open stream; the tick's reaper fails such a
|
|
574
|
+
turn (`turn.failed`, kind `internal`) after 15 minutes of stream silence,
|
|
575
|
+
closing the stream `aborted` and leaving the session `idle`. The reaper never
|
|
576
|
+
touches suspended or awaiting-approval sessions — a turn suspended for
|
|
577
|
+
approval is precisely an open stream that goes quiet for however long the
|
|
578
|
+
operator takes.
|
|
579
|
+
|
|
580
|
+
**Typed failures.** Model failures are typed on `turn.failed`:
|
|
581
|
+
`provider-unavailable`, `exhausted`, `credentials`, `configuration` (plus
|
|
582
|
+
`internal` for non-model failures) — never retried unboundedly; a failed
|
|
583
|
+
turn leaves the session `idle` with the error class visible in the session's
|
|
584
|
+
reduced state.
|
|
585
|
+
|
|
586
|
+
**Opt-in guarantee.** The whole feature is off unless a repository's
|
|
587
|
+
effective config enables it, and it is doubly gated: the tick step is
|
|
588
|
+
constructed only in origin mode (a local `ab dispatch` never constructs it —
|
|
589
|
+
enabled = true in a local autobuild.toml lists no sessions, builds no
|
|
590
|
+
registry, and calls no model), and in origin mode it returns immediately
|
|
591
|
+
unless the effective config enables the orchestrator. The system prompt is
|
|
592
|
+
the canonical `ab-operate` skill shipped in `skills/operate/` — self-contained
|
|
593
|
+
per the vendored-skill rule, teaching the tool surface, the attention set,
|
|
594
|
+
and when to escalate to the operator rather than act — plus the current
|
|
595
|
+
`operator-notes` artifact, the session's wake settings, and the session's
|
|
596
|
+
repository identity. The model never supplies a repository argument: the
|
|
597
|
+
tools' model-facing schemas strip `repo`, and the runner injects the
|
|
598
|
+
session's repository before the registry re-validates. A message posted
|
|
599
|
+
while a turn is open is durable and enters the conversation at the next
|
|
600
|
+
resume or turn — no second turn, no concurrent turns, no subagents.
|
|
601
|
+
|
|
602
|
+
#### 7.1.3 Ticket assets
|
|
603
|
+
|
|
604
|
+
A **ticket asset** is a named, versioned bundle of files attached to a ticket —
|
|
605
|
+
a design export, a reference screenshot, a sample payload — that shapes a change
|
|
606
|
+
but belongs neither in the repository (where it goes stale once the change
|
|
607
|
+
ships) nor in the ticket tool (which build agents cannot reach). It is keyed by
|
|
608
|
+
`(repo, ticketId, kind, name)` and works identically whichever ticket source is
|
|
609
|
+
configured. It is a fourth resource family beside builds, repository journals,
|
|
610
|
+
and sessions, but a deliberately thin one: no event log, because nothing here
|
|
611
|
+
needs reduction, and no retention pruning, because assets live as long as the
|
|
612
|
+
ticket's other durable data and never expire on their own.
|
|
613
|
+
|
|
614
|
+
Each put creates a new 0-based **revision**; earlier revisions stay retrievable.
|
|
615
|
+
An asset is a `file` (exactly one file) or a `tree` (a folder: its root is
|
|
616
|
+
implicit, so a tree with zero entries is a valid empty folder). The manifest is
|
|
617
|
+
a list of entries — `file` entries with size and content address, and `dir`
|
|
618
|
+
entries so empty directories survive. File bytes live in the `BlobStore`; the
|
|
619
|
+
database holds only the manifest, the same rule as artifacts. **Removal is a
|
|
620
|
+
tombstone**: a row taking the next revision number with an empty manifest. The
|
|
621
|
+
latest row decides liveness, so the default read and list skip a removed asset,
|
|
622
|
+
while an explicit earlier revision stays readable — a build that pinned revision
|
|
623
|
+
N keeps retrieving it. Re-attaching after a removal takes the next revision, so
|
|
624
|
+
revision numbers may skip the tombstone.
|
|
625
|
+
|
|
626
|
+
Every adapter runs one shared validator before any write. `kind` and `name` are
|
|
627
|
+
`[A-Za-z0-9._-]+` and at most 64 characters, so they are safe to print in a
|
|
628
|
+
command line. Entry paths are relative, `/`-separated, with no empty, `.`, or
|
|
629
|
+
`..` segment, at most 256 bytes each and 128 per segment, with no duplicates and
|
|
630
|
+
no file as an ancestor of another entry. A revision holds at most 1000 entries
|
|
631
|
+
(files plus directories) and **25 MiB** of file bytes; a larger put is refused
|
|
632
|
+
with an error that names the limit, and nothing is stored. A remote deployment
|
|
633
|
+
may advertise a lower effective limit (§7.2): a platform that rejects oversized
|
|
634
|
+
request bodies before application code runs cannot rely on server-side
|
|
635
|
+
validation alone, so the client enforces the advertised limit before uploading.
|
|
636
|
+
|
|
467
637
|
### 7.2 Interface and adapters
|
|
468
638
|
|
|
469
639
|
Deliberately narrow: build runners need `append(event)`, `putArtifact`,
|
|
@@ -476,11 +646,23 @@ snapshot or side ledger. Sequence 0 names an empty stream, candidates receive
|
|
|
476
646
|
ordinary event validation, unknown builds reject, and a comparison miss leaves
|
|
477
647
|
the log and build timestamps untouched.
|
|
478
648
|
The same contract has repository-scoped `ensureRepo`, event/artifact deposit
|
|
479
|
-
and read methods, plus a repository lease.
|
|
649
|
+
and read methods, plus a repository lease. Ticket assets (§7.1) add
|
|
650
|
+
`putTicketAsset`, `getTicketAsset` (latest live revision, or an explicit
|
|
651
|
+
revision), `listTicketAssets` (summaries without manifests: the latest live
|
|
652
|
+
revision per kind and name, or every real revision), `removeTicketAsset`, and
|
|
653
|
+
`ticketAssetLimits(repo)`, the effective ceilings the store accepts. Scoped
|
|
654
|
+
build and session handles reject all of them. Before execution, a runner obtains
|
|
480
655
|
an interface-enforced handle scoped to exactly its build. Own-build records,
|
|
481
656
|
stream, artifacts, lease, and subscription remain available; another build,
|
|
482
657
|
collection/admin operations, nested foreign scope, and every repository-journal
|
|
483
|
-
operation reject.
|
|
658
|
+
operation reject. The one ticket-asset read a build handle keeps is
|
|
659
|
+
`getPinnedTicketAsset(slug, kind, name, rev?)`: the asset as that build froze it
|
|
660
|
+
(the latest pinned revision, or any revision the build ever pinned), found by
|
|
661
|
+
reducing the build's own events and then reading that exact revision. It needs
|
|
662
|
+
no lease, so it answers for a finished build, and remote build tokens reach it
|
|
663
|
+
through `GET /builds/:slug/ticket-assets`. `createBuild` optionally takes the
|
|
664
|
+
build's initial `build.created` event and appends it as seq 1 in the same atomic
|
|
665
|
+
step as the record on every adapter. This guard applies identically to every adapter; remote tokens
|
|
484
666
|
carry the same authority over the wire as defense in depth rather than creating
|
|
485
667
|
it. Two implementations of one contract (`packages/core/src/store/contract.ts` is the shared
|
|
486
668
|
conformance suite):
|
|
@@ -515,6 +697,27 @@ conformance suite):
|
|
|
515
697
|
`subscribe` is specced in the interface; the v2.0 implementation is polling
|
|
516
698
|
`getEvents(since)`. True push comes later.
|
|
517
699
|
|
|
700
|
+
Operator UIs additionally have a repository-scoped batch read,
|
|
701
|
+
`getRepoBuildDigests(repo)`: the projection of every build's log onto its
|
|
702
|
+
terminal fact, its `observation.recorded` occurrences with their event
|
|
703
|
+
timestamps, and the ts of its latest `pr.merged`, derived from the event log on
|
|
704
|
+
each call. It lets a dashboard snapshot count unclaimed observations and gate
|
|
705
|
+
rows without reading the full history of finished builds (§16.1), and it backs
|
|
706
|
+
the harvest-launch pressure gate (dispatcher and harvest runner alike): launch
|
|
707
|
+
decisions evaluate from digests and the repository journal alone, so
|
|
708
|
+
gate-evaluation cost is flat in accumulated build history, with the full
|
|
709
|
+
per-build scan paid only when a gate actually trips and a scan packet is built.
|
|
710
|
+
|
|
711
|
+
Stateless repository-level readers also have a bounded journal read,
|
|
712
|
+
`getRepoStateEvents(repo)` (AUT-489): every durable, slow-growing event type
|
|
713
|
+
(all harvest facts, operator-sandbox facts, and the three dispatcher setting
|
|
714
|
+
types) plus, when the journal has one, the tail from the latest
|
|
715
|
+
`dispatcher.run-started`. Reducing this subset with the state readers yields
|
|
716
|
+
exactly what a full replay yields, so the cost of reading repository state no
|
|
717
|
+
longer grows with the number of past dispatcher invocations. The journal
|
|
718
|
+
itself stays append-only: full replay remains the normative truth, and
|
|
719
|
+
`ab watch` still sees every recorded fact.
|
|
720
|
+
|
|
518
721
|
### 7.3 Persistence granularity
|
|
519
722
|
|
|
520
723
|
Required phase outputs persist at **phase/round boundaries**; a killed phase
|
|
@@ -782,11 +985,19 @@ A phase-scoped hydration into the gitignored scratch dir:
|
|
|
782
985
|
findings.json # current feedback, when round > 1
|
|
783
986
|
history/ # prior-round artifacts where the phase needs them
|
|
784
987
|
verify/ # failure reports routed back to implement
|
|
988
|
+
assets/ # the ticket assets the build froze (§6.3), every phase:
|
|
989
|
+
# assets/<kind>/<name>/…, folders keep their trees
|
|
785
990
|
```
|
|
786
991
|
|
|
787
992
|
The manifest tells the agent its contract — `required` deposits and
|
|
788
993
|
`allowedTerminals` — so skills are self-checking against the same data the
|
|
789
|
-
CLI validates with.
|
|
994
|
+
CLI validates with. When the build pinned ticket assets, every phase also gets
|
|
995
|
+
them under `.ab/assets/<kind>/<name>/` and the manifest lists each as
|
|
996
|
+
`assets: [{kind, name, revision, layout, size, path}]`; a `file` asset is the
|
|
997
|
+
single file inside its directory. A build with no assets gets no `assets` key
|
|
998
|
+
and no directory. `ab build asset get <slug> <kind> <name> <dest>` downloads a
|
|
999
|
+
pinned asset by build, also after the build has finished. Per-phase inputs and
|
|
1000
|
+
terminals:
|
|
790
1001
|
|
|
791
1002
|
| Phase | Materialized inputs | Terminal |
|
|
792
1003
|
|---|---|---|
|
|
@@ -817,7 +1028,7 @@ Provider/runtime-declared turn failures are a separate `AgentRunner` result
|
|
|
817
1028
|
alternates, availability failures (overload, rate limit, 5xx, timeout,
|
|
818
1029
|
transport, and unclassified provider failure) and provider exhaustion (quota,
|
|
819
1030
|
usage limit, and billing) start the next target inside the same phase attempt.
|
|
820
|
-
Authentication, permission, and local runtime-configuration failures do not.
|
|
1031
|
+
Authentication, permission, and local runtime-configuration failures do not, with one exception: a runtime whose CLI executable is not installed is skipped like any other eligible failure, so the phase runs on the first declared target whose CLI is present. An unsupported installed version is still a stopping configuration failure. If no target's CLI is installed, the chain ends in one non-retried `configuration` failure carrying the last target's missing-CLI message.
|
|
821
1032
|
Each failed target's transcript is deposited. Only a stopped or exhausted chain
|
|
822
1033
|
emits one `phase.failed` with the final provider message verbatim; that final
|
|
823
1034
|
failure controls retry policy. Provider exhaustion skips the retry budget and
|
|
@@ -867,7 +1078,12 @@ A no-op step has no push. Agents only ever commit locally. Consequences: forge
|
|
|
867
1078
|
credentials **never enter the sandbox** (load-bearing once builds run on
|
|
868
1079
|
remote sandboxes), history is extended rather than force-pushed, and the
|
|
869
1080
|
push-at-boundary rule from [D3] is enforced by construction rather than
|
|
870
|
-
convention.
|
|
1081
|
+
convention. The same rule bounds the orchestrator's small fixes: an operator
|
|
1082
|
+
sandbox publishes work only through the `sandbox.publish` tool, whose push and
|
|
1083
|
+
PR creation are kernel-side plumbing executed by the sandbox service through
|
|
1084
|
+
the workspace provider's publication capability — the only publication path
|
|
1085
|
+
from an operator sandbox, always to a dedicated operator branch and a pull
|
|
1086
|
+
request, never to the base branch.
|
|
871
1087
|
|
|
872
1088
|
### 8.7 Walkthroughs
|
|
873
1089
|
|
|
@@ -912,7 +1128,8 @@ implementer: ab context (findings.json now materialized) → …
|
|
|
912
1128
|
### 8.8 Outer-loop namespace
|
|
913
1129
|
|
|
914
1130
|
Human/pre-build ticket grooming uses one configured-source namespace
|
|
915
|
-
(`ab ticket create|update|block|unblock|list|show|move
|
|
1131
|
+
(`ab ticket create|update|block|unblock|list|show|move`, plus the ticket-asset
|
|
1132
|
+
forms below). These commands are
|
|
916
1133
|
sessionless, source-agnostic, and never available as a mid-build spec
|
|
917
1134
|
mutation path. Every subcommand accepts `--json` and then writes exactly one
|
|
918
1135
|
bare value: `list` emits `Ticket[]`; every other form emits the complete
|
|
@@ -929,6 +1146,27 @@ creates tickets directly in its ready state must therefore create a dependency
|
|
|
929
1146
|
chain in dependency order, carrying each predecessor id into the next create,
|
|
930
1147
|
before a later ticket can be claimed.
|
|
931
1148
|
|
|
1149
|
+
**Ticket assets** (§7.1) attach files to a ticket without touching the ticket
|
|
1150
|
+
source's storage:
|
|
1151
|
+
|
|
1152
|
+
- `ab ticket attach <id> <kind> <path> [--name <name>] [--store <ref>] [--json]`
|
|
1153
|
+
stores a file or a whole folder (its relative tree preserved) as an asset.
|
|
1154
|
+
`--name` defaults to the path's basename. Attaching again under the same kind
|
|
1155
|
+
and name creates a new revision. The CLI asks the source first, so an unknown
|
|
1156
|
+
id stores nothing; it refuses symlinks and non-regular files, and refuses a
|
|
1157
|
+
bundle over the store's limit from `stat` sizes before reading any bytes. The
|
|
1158
|
+
store write precedes the note: if the note then fails, the error says the
|
|
1159
|
+
asset was stored as revision N, and the write is not rolled back.
|
|
1160
|
+
- `ab ticket asset get <id> <kind> <name> <dest> [--rev <n>]` writes the exact
|
|
1161
|
+
bytes of the latest (or a named) revision: a file asset to `<dest>` (or into
|
|
1162
|
+
`<dest>` when it is an existing directory), a folder asset into the directory
|
|
1163
|
+
`<dest>`, empty directories included. Stored paths are re-checked against
|
|
1164
|
+
traversal on write.
|
|
1165
|
+
- `ab ticket asset rm <id> <kind> <name>` removes it from the ticket's current
|
|
1166
|
+
assets; revisions a build has pinned stay retrievable.
|
|
1167
|
+
- `ab ticket show <id>` lists the current assets (kind/name, revision, size);
|
|
1168
|
+
with `--json` the `Ticket` is extended with an `assets` array.
|
|
1169
|
+
|
|
932
1170
|
Observation harvest uses a separate typed, repository-scoped namespace
|
|
933
1171
|
(`ab harvest context|submit|verdict|status`), mirroring the build session
|
|
934
1172
|
commands: context hydration, a producer terminal, a reviewer terminal, and a
|
|
@@ -936,7 +1174,7 @@ sessionless read-only status projection.
|
|
|
936
1174
|
|
|
937
1175
|
`ab repository status [--json] [--store <ref>]` is the sessionless read-only
|
|
938
1176
|
projection of the repository journal's dispatcher controls: ticket intake, the
|
|
939
|
-
repository-wide pause, and the
|
|
1177
|
+
repository-wide pause, and the auto-merge default. It uses the same
|
|
940
1178
|
dispatch-settings reducer as dispatcher decisions and the dashboard. An absent
|
|
941
1179
|
repository stream reduces as an empty journal and reports intake on, repository
|
|
942
1180
|
pause off, and auto-merge default off; the query does not create that stream,
|
|
@@ -990,6 +1228,9 @@ distinction from `no-terminal`. `failure.message` preserves provider text and
|
|
|
990
1228
|
and local runtime configuration. `permanent` remains the retry-policy and
|
|
991
1229
|
legacy-plugin compatibility bit: exhaustion and credential/configuration
|
|
992
1230
|
failures are permanent, while availability uses existing bounded retry. A
|
|
1231
|
+
`configuration` failure flagged `missingExecutable` (the runtime's CLI is not
|
|
1232
|
+
installed) is the one configuration failure that is alternate-eligible; it is
|
|
1233
|
+
still permanent and never retried. A
|
|
993
1234
|
legacy plugin may omit `cause`; `permanent: false` remains alternate-eligible
|
|
994
1235
|
and `permanent: true` remains a stopping failure. A failed `start` still returns
|
|
995
1236
|
an endable session handle, guaranteeing transcript deposition.
|
|
@@ -1078,7 +1319,8 @@ deterministic fail-safe.
|
|
|
1078
1319
|
runtime touches only the adapter registry, never the kernel. Mixing models
|
|
1079
1320
|
across roles is intentional — a different reviewer catches more. The
|
|
1080
1321
|
Within one agent-session attempt, eligible failures try the primary then each
|
|
1081
|
-
alternate in order without spending another phase attempt.
|
|
1322
|
+
alternate in order without spending another phase attempt. A runtime whose CLI executable is not installed is skipped the same way;
|
|
1323
|
+
credential, permission, and other configuration failures stop the chain. Each target has a
|
|
1082
1324
|
fresh session bracket. A failed producer continuation may therefore continue
|
|
1083
1325
|
on a different runtime only by rehydrating durable context, not by inheriting
|
|
1084
1326
|
the failed conversation. Selection is non-sticky: every later phase, loop
|
|
@@ -1204,6 +1446,10 @@ escalation after an exhausted failed verify report feeds `implement`, where its
|
|
|
1204
1446
|
guidance outranks
|
|
1205
1447
|
the pending report.
|
|
1206
1448
|
|
|
1449
|
+
Answering with `--revise-spec-from-ticket` also re-pins the build's ticket assets
|
|
1450
|
+
(§6.3): the sampled set rides on the answer and on `spec.revised`. A supplied
|
|
1451
|
+
body file changes the spec only.
|
|
1452
|
+
|
|
1207
1453
|
Only an operator answering an open `review-round-limit` policy escalation may
|
|
1208
1454
|
set `escalation.answered.reviewRoundCeiling`. The same event both clears the
|
|
1209
1455
|
human gate and replaces the cap; no standalone or pre-emptive form exists. A
|
|
@@ -1366,6 +1612,16 @@ posted as a comment, final summary, status transitions) flow outward only.
|
|
|
1366
1612
|
This keeps the abstraction honest: a file-based TicketSource with nowhere to
|
|
1367
1613
|
put blobs must be fully workable.
|
|
1368
1614
|
|
|
1615
|
+
**Ticket assets follow the same rule.** The contents of a ticket asset (§7.1)
|
|
1616
|
+
live in the BuildStore and the ticket source never stores them. Attaching,
|
|
1617
|
+
replacing, or removing an asset instead leaves a note on the ticket through the
|
|
1618
|
+
existing `comment()` port — the outward projection every source, including the
|
|
1619
|
+
plugin surface, already supports. The note names the asset, its revision, file
|
|
1620
|
+
count, and size, and gives the `ab ticket asset get` command that fetches it, so
|
|
1621
|
+
a person reading the ticket in Linear, the hosted service, or a local file knows
|
|
1622
|
+
the asset exists. The note is a projection outward only; nothing reads it back,
|
|
1623
|
+
and no TicketSource port or plugin-protocol change is needed.
|
|
1624
|
+
|
|
1369
1625
|
**Claim lifecycle.** Claim acquires nonterminal work: it returns `true` and
|
|
1370
1626
|
moves a claimable ticket to the source's claimed state. It returns `false` for
|
|
1371
1627
|
an unknown ticket, an already-claimed or contested ticket, and a ticket that
|
|
@@ -1455,12 +1711,15 @@ signal, and error evidence. `--plain`, non-TTY,
|
|
|
1455
1711
|
and `--once` kernel semantics remain the line-oriented/direct compatibility
|
|
1456
1712
|
path; `--once` still performs one tick and drains its in-flight work.
|
|
1457
1713
|
|
|
1458
|
-
Durable operator settings (intake, the repository-wide pause, the
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1714
|
+
Durable operator settings (intake, the repository-wide pause, the auto-merge
|
|
1715
|
+
default, the harvest gate) are repository-journal facts: they survive restarts,
|
|
1716
|
+
propagate between dispatchers by ordinary polling, and are never optimistically
|
|
1717
|
+
rendered — the UI shows acknowledged state. The repository-wide pause holds
|
|
1718
|
+
every queued build: while it is set, no dispatcher tick may attach a runner to
|
|
1719
|
+
a build that does not have one yet. The auto-merge default both seeds new
|
|
1720
|
+
claims and, on each change, fans out onto every current non-terminal build of
|
|
1721
|
+
the repository as a bulk request or withdrawal attributed to the toggling
|
|
1722
|
+
operator (§15.7).
|
|
1464
1723
|
|
|
1465
1724
|
The **operator sandbox** is the third execution kind beside build and harvest
|
|
1466
1725
|
executions: a persistent, credential-free, per-operator-per-repository
|
|
@@ -1473,7 +1732,11 @@ is repository-journal fact: provisioned/resumed/activity/stopped/released facts
|
|
|
1473
1732
|
bracket one deterministic environment per operator × repository, the
|
|
1474
1733
|
dispatcher tick's idle settlement stops an environment whose evidence is
|
|
1475
1734
|
stale (settling an orphan exactly as harvest executions are settled), and
|
|
1476
|
-
`ab repository status` reports each operator's sandbox.
|
|
1735
|
+
`ab repository status` reports each operator's sandbox. An operator's work
|
|
1736
|
+
leaves the sandbox only through `sandbox.publish`, which takes the checkout's
|
|
1737
|
+
head (or an explicit commit) to a deterministic operator branch and a pull
|
|
1738
|
+
request against the base branch and records published/publish-failed journal
|
|
1739
|
+
facts.
|
|
1477
1740
|
|
|
1478
1741
|
The operator's job across many concurrent builds: see status at a glance,
|
|
1479
1742
|
act on a selected build, find blocked builds, answer escalations, and inspect
|
|
@@ -1587,9 +1850,9 @@ with illustrative members:
|
|
|
1587
1850
|
|
|
1588
1851
|
| Family | Examples |
|
|
1589
1852
|
|---|---|
|
|
1590
|
-
| Build lifecycle | `build.created`, `workspace.provisioned`, `workspace.released`, `execution.started`, `execution.ended`, `infrastructure.failed`, `infrastructure.cleanup-attempted`, `dispatch.comment-posted`, `dispatch.failed`, `runner.attached`, `runner.setup-failed`, `abort.remote-branch-deleted`, `abort.local-branch-deleted`, `abort.ticket-returned`, `build.completed` |
|
|
1591
|
-
| Operator commands [D2] | `build.pause-requested` → `build.paused`; `build.discard-requested`; `build.auto-merge-requested`; `escalation.answered` (optionally carrying a validated `reviewRoundCeiling`) |
|
|
1592
|
-
| Spec | `spec.imported`, `spec.authored`, `spec.revised` |
|
|
1853
|
+
| Build lifecycle | `build.created` (carrying `assets`, the claim-time pin, §6.3), `workspace.provisioned`, `workspace.released`, `execution.started`, `execution.ended`, `infrastructure.failed`, `infrastructure.cleanup-attempted`, `dispatch.comment-posted`, `dispatch.failed`, `runner.attached`, `runner.setup-failed`, `abort.remote-branch-deleted`, `abort.local-branch-deleted`, `abort.ticket-returned`, `build.completed` |
|
|
1854
|
+
| Operator commands [D2] | `build.pause-requested` → `build.paused`; `build.discard-requested`; `build.auto-merge-requested`; `escalation.answered` (optionally carrying a validated `reviewRoundCeiling`, or the `assets` sampled with a revise-from-ticket authorization) |
|
|
1855
|
+
| Spec | `spec.imported`, `spec.authored`, `spec.revised` (optionally carrying `assets`, a re-pin of the ticket's assets, §6.3) |
|
|
1593
1856
|
| Sessions | `session.started`; `session.ended` with transcript ref and usage (ordinary completion — the analysis corpus), or `session.ended {outcome: reclaimed, reclaimedBy: {instance, resumedFromSeq}}` (explicit transcriptless takeover) |
|
|
1594
1857
|
| Plan/code loops | `plan.started` … `plan-review.verdict`; `implement.started` … `code-review.verdict` |
|
|
1595
1858
|
| Verify/finalize | `verify.started {step, attempt, feedback?}`, `verify.completed {step, outcome}`, `finalize.completed {pr}`, `finalize.step-completed {step, ok, headSha?}` |
|
|
@@ -1954,13 +2217,18 @@ Decisions here continue the series: **[D9]** declarative repo config and
|
|
|
1954
2217
|
One declarative file at the repo root. A running dispatcher owns an accepted
|
|
1955
2218
|
snapshot read from the **main checkout** and refreshes it before each dispatch
|
|
1956
2219
|
tick. Before launch and after each accepted revision, the dispatcher deposits
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2220
|
+
one composed snapshot into each affected build's artifact namespace: its
|
|
2221
|
+
build-owned sections come from the build's own branch, and its deployment- and
|
|
2222
|
+
dispatcher-owned sections from the live snapshot (the three ownership classes
|
|
2223
|
+
are defined below). The child samples that durable artifact at setup and
|
|
2224
|
+
pipeline-step boundaries, so a valid change to a deployment-owned section may
|
|
2225
|
+
affect the next action of an in-flight build but never interrupts an agent turn
|
|
2226
|
+
or deterministic command already running, and a change to a build-owned section
|
|
2227
|
+
never retargets an in-flight build at all. Scoped phase CLI processes separately
|
|
1961
2228
|
read the build worktree's branch-owned file. Because the file is repo-versioned,
|
|
1962
|
-
changes still flow through the pipeline itself: once
|
|
1963
|
-
adopt a retuned configuration without restarting
|
|
2229
|
+
changes still flow through the pipeline itself: once a build's own branch
|
|
2230
|
+
carries them, the system can adopt a retuned configuration without restarting
|
|
2231
|
+
the dispatcher.
|
|
1964
2232
|
|
|
1965
2233
|
A missing or unreadable file and a malformed or routing-invalid candidate are
|
|
1966
2234
|
rejected atomically: the last valid snapshot, resolver, and revision remain in
|
|
@@ -2072,6 +2340,35 @@ existing ticket table as factory config, and fails with the complete available
|
|
|
2072
2340
|
name set when unregistered. Plugin-declared `requiredEnv` values are never TOML
|
|
2073
2341
|
fields: secrets stay in the process environment or local `.env`.
|
|
2074
2342
|
|
|
2343
|
+
`autobuild.toml` sections belong to one of three ownership classes, which
|
|
2344
|
+
decide where a build's effective configuration is composed from:
|
|
2345
|
+
|
|
2346
|
+
| Class | Sections | Source at deposit |
|
|
2347
|
+
|---|---|---|
|
|
2348
|
+
| Build-owned (pinned) | `[verify]` and `[verify.<step>]`, `[finalize]` and `[finalize.<step>]`, `[commands]`, `[workspace]` (provider and config, including `provisioning` and `runtimeProvisioning`) | The build's own branch: its branch head once it has published commits, otherwise the recorded base commit its workspace was cut from. The exact commit is recorded once per deposit in the effective-config artifact metadata — except in checkout mode when the worktree read cannot be attributed to the branch-head commit: because the worktree file has uncommitted edits, because the clean/dirty comparison could not be performed (non-git workspace or a transient git error), or because the branch head never carried the file. The deposit then records the source `worktree-dirty` and no commit, because no commit claims the worktree bytes (the build still runs them). |
|
|
2349
|
+
| Deployment-owned (live) | `[roles]` (runtime, model, args, alternates, per-role `sessionBudgetSeconds`) and `[policy]` (budgets, retry bounds, stall and harvest knobs) | The dispatcher's live snapshot; an accepted reload delivers these to active builds. |
|
|
2350
|
+
| Dispatcher-owned | `baseBranch`, `capacity`, `forge`, `plugins`, `pr`, `tickets`, `orchestrator` | The dispatcher's live snapshot; the build runner never interprets them. |
|
|
2351
|
+
|
|
2352
|
+
A build's pipeline definition is therefore composed from the build's own
|
|
2353
|
+
branch: in origin mode the dispatcher reads `autobuild.toml` at the build's
|
|
2354
|
+
recorded base commit (or its branch head once the build has published commits)
|
|
2355
|
+
through the forging adapter; in checkout mode it reads the workspace's file. The
|
|
2356
|
+
effective-config artifact records the commit the build-owned sections were
|
|
2357
|
+
taken from — except in checkout mode when the worktree read cannot be
|
|
2358
|
+
attributed to the branch-head commit: because the worktree file has uncommitted
|
|
2359
|
+
edits, because the clean/dirty comparison could not be performed (non-git
|
|
2360
|
+
workspace or a transient git error), or because the branch head never carried
|
|
2361
|
+
the file. The artifact then records the source `worktree-dirty` and no commit,
|
|
2362
|
+
because attributing worktree bytes to a commit that has not been shown to carry
|
|
2363
|
+
them would be false provenance. A base-branch change to `[verify]`, `[finalize]`, `[commands]`, or
|
|
2364
|
+
the workspace provisioning after a build's base was cut does not change that
|
|
2365
|
+
build's verify universe, finalize steps, commands, or provisioning; a build
|
|
2366
|
+
that publishes such a change to its own branch picks it up at its next launch.
|
|
2367
|
+
A live reload delivers only deployment-owned sections to active builds, so the
|
|
2368
|
+
artifact deposited on reload changes only those sections. In-flight
|
|
2369
|
+
environments are never re-provisioned; a fresh build from the new base is the
|
|
2370
|
+
path for a changed provisioning definition.
|
|
2371
|
+
|
|
2075
2372
|
Two configurable narrowing mechanisms govern which verify steps run, both
|
|
2076
2373
|
resolving to the ordinary `skipped` outcome so exclusions stay queryable:
|
|
2077
2374
|
|
package/bin/ab-build-runner.ts
CHANGED
|
@@ -19,6 +19,12 @@ try {
|
|
|
19
19
|
if (input === undefined) process.exit(2)
|
|
20
20
|
|
|
21
21
|
let exitCode = 0
|
|
22
|
+
// Guest tolerance (AUT-517): an environment-supervised build child is
|
|
23
|
+
// structurally a guest — it never constructs workspace providers — so a
|
|
24
|
+
// configured provider plugin it cannot resolve is skipped with a notice
|
|
25
|
+
// instead of failing the build. A local-parent child runs on the host with
|
|
26
|
+
// the repository's full dependency tree, so it stays strict.
|
|
27
|
+
const guest = input.supervision.kind === 'environment'
|
|
22
28
|
if (input.supervision.kind === 'local-parent') {
|
|
23
29
|
// Every local exit first transfers group teardown to an owner outside this
|
|
24
30
|
// session. Environment-owned execution deliberately installs neither this
|
|
@@ -30,7 +36,7 @@ if (input.supervision.kind === 'local-parent') {
|
|
|
30
36
|
watchBuildParent(input.supervision.parentPid, () => terminal.terminate(143)),
|
|
31
37
|
)
|
|
32
38
|
try {
|
|
33
|
-
await runBuildChild(input, process.env)
|
|
39
|
+
await runBuildChild(input, process.env, undefined, { guest })
|
|
34
40
|
} catch {
|
|
35
41
|
exitCode = 1
|
|
36
42
|
}
|
|
@@ -39,7 +45,7 @@ if (input.supervision.kind === 'local-parent') {
|
|
|
39
45
|
// Vercel owns teardown of the complete VM session. Addressing a POSIX group
|
|
40
46
|
// here could leave the SDK believing a still-running environment completed.
|
|
41
47
|
try {
|
|
42
|
-
await runBuildChild(input, process.env)
|
|
48
|
+
await runBuildChild(input, process.env, undefined, { guest })
|
|
43
49
|
} catch {
|
|
44
50
|
exitCode = 1
|
|
45
51
|
}
|