@defrex/autobuild 0.11.0 → 0.13.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 +3 -1
- package/SPEC.md +335 -31
- package/docs/architecture.md +125 -7
- package/docs/configuration.md +142 -21
- package/docs/hosted-dispatcher.md +17 -2
- package/docs/hosted-service.md +240 -0
- package/docs/mcp.md +8 -2
- package/docs/operator-api.md +97 -7
- package/docs/remote-store-protocol.md +199 -144
- package/docs/remote-ticket-protocol.md +249 -19
- package/docs/setup.md +199 -16
- package/docs/ticket-dependencies.md +1 -1
- package/docs/ticket-migration.md +64 -0
- package/package.json +7 -6
- package/packages/core/src/cli/attention.ts +209 -0
- package/packages/core/src/cli/build-control.ts +12 -1
- package/packages/core/src/cli/configured-store.ts +17 -0
- package/packages/core/src/cli/connect.ts +364 -0
- package/packages/core/src/cli/context.ts +7 -1
- package/packages/core/src/cli/dashboard/model.ts +4 -5
- package/packages/core/src/cli/dashboard/poll.ts +1 -1
- package/packages/core/src/cli/dispatch.ts +336 -41
- package/packages/core/src/cli/harvest.ts +13 -3
- package/packages/core/src/cli/help.ts +51 -16
- package/packages/core/src/cli/init-validation.ts +27 -2
- package/packages/core/src/cli/installation.ts +67 -13
- package/packages/core/src/cli/main.ts +60 -16
- package/packages/core/src/cli/mcp.ts +9 -2
- package/packages/core/src/cli/pr-summary-publication.ts +220 -0
- package/packages/core/src/cli/pr-summary.ts +2 -0
- package/packages/core/src/cli/project-credentials.ts +132 -0
- package/packages/core/src/cli/repo-state.ts +15 -2
- package/packages/core/src/cli/self-update.ts +15 -1
- package/packages/core/src/cli/store-opening.ts +22 -14
- package/packages/core/src/cli/terminals.ts +0 -10
- package/packages/core/src/cli/ticket-assets.ts +7 -2
- package/packages/core/src/cli/ticket-editor.ts +82 -0
- package/packages/core/src/cli/ticket.ts +172 -35
- package/packages/core/src/cli/upgrade-compatibility.ts +80 -0
- package/packages/core/src/cli/wait.ts +84 -17
- package/packages/core/src/cli/watch.ts +197 -60
- package/packages/core/src/config/live.ts +3 -0
- package/packages/core/src/config/schema.ts +90 -11
- package/packages/core/src/connection.ts +86 -0
- package/packages/core/src/events/catalog.ts +11 -2
- package/packages/core/src/events/payloads.ts +50 -0
- package/packages/core/src/events/repository.ts +13 -0
- package/packages/core/src/integration/harness.ts +37 -2
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +145 -83
- package/packages/core/src/kernel/engine.ts +24 -0
- package/packages/core/src/kernel/generators/build-log.ts +11 -1
- package/packages/core/src/kernel/incremental-fixtures.ts +13 -2
- package/packages/core/src/kernel/log-index.ts +17 -0
- package/packages/core/src/kernel/pr-summary.ts +89 -0
- package/packages/core/src/kernel/reducer-registry.ts +10 -0
- package/packages/core/src/kernel/reducer.ts +8 -0
- package/packages/core/src/ontology.ts +2 -0
- package/packages/core/src/operator/annotations.ts +12 -0
- package/packages/core/src/operator/index.ts +12 -0
- package/packages/core/src/operator/protocol.ts +19 -2
- package/packages/core/src/operator/query.ts +38 -16
- package/packages/core/src/operator/registry.ts +61 -8
- package/packages/core/src/operator/ticket-source-backend.ts +17 -20
- package/packages/core/src/operator/tickets.ts +136 -28
- package/packages/core/src/plugin-sdk/index.ts +7 -0
- package/packages/core/src/plugins/manifest.ts +5 -3
- package/packages/core/src/plugins/runtimes.ts +7 -1
- package/packages/core/src/ports/forge/fake.ts +13 -1
- package/packages/core/src/ports/forge/github-transport.ts +36 -1
- package/packages/core/src/ports/forge/github.ts +29 -5
- package/packages/core/src/ports/forge/local-git.ts +12 -4
- package/packages/core/src/ports/forge/summary-marker.ts +8 -0
- package/packages/core/src/ports/runner/session-env.ts +3 -0
- package/packages/core/src/ports/tickets/contract.ts +66 -1
- package/packages/core/src/ports/tickets/create.ts +54 -10
- package/packages/core/src/ports/tickets/fake.ts +30 -1
- package/packages/core/src/ports/tickets/file-assets.ts +159 -0
- package/packages/core/src/ports/tickets/file-codec.ts +90 -0
- package/packages/core/src/ports/tickets/file.ts +114 -120
- package/packages/core/src/ports/tickets/grants.ts +47 -0
- package/packages/core/src/ports/tickets/hosted-index.ts +17 -3
- package/packages/core/src/ports/tickets/identity.ts +20 -0
- package/packages/core/src/ports/tickets/lifecycle.ts +18 -0
- package/packages/core/src/ports/tickets/linear.ts +74 -9
- package/packages/core/src/ports/tickets/migration.ts +336 -0
- package/packages/core/src/ports/tickets/operations.ts +38 -7
- package/packages/core/src/ports/tickets/portable-assets.ts +54 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +96 -0
- package/packages/core/src/ports/tickets/remote.ts +61 -3
- package/packages/core/src/ports/tickets/requested-id.ts +46 -0
- package/packages/core/src/ports/tickets/revision.ts +62 -0
- package/packages/core/src/ports/tickets/states.ts +61 -0
- package/packages/core/src/ports/types.ts +40 -3
- package/packages/core/src/ports/workspace/build-execution.ts +6 -1
- package/packages/core/src/ports/workspace/create.ts +15 -2
- package/packages/core/src/ports/workspace/distribution-archive.ts +3 -5
- package/packages/core/src/ports/workspace/harvest-execution.ts +3 -1
- package/packages/core/src/ports/workspace/local-build-execution.ts +8 -1
- package/packages/core/src/ports/workspace/operator-sandbox.ts +2 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +51 -19
- package/packages/core/src/processes/build-execution-state.ts +1 -1
- package/packages/core/src/processes/build-runner.ts +87 -26
- package/packages/core/src/processes/dispatch-failure-budget.ts +83 -0
- package/packages/core/src/processes/dispatcher.ts +168 -41
- package/packages/core/src/processes/execution-no-progress.ts +176 -0
- package/packages/core/src/processes/harvest-child.ts +31 -4
- package/packages/core/src/processes/harvest-execution-state.ts +19 -2
- package/packages/core/src/processes/harvest-runner.ts +166 -104
- package/packages/core/src/processes/harvest.ts +34 -14
- package/packages/core/src/processes/phase-failure-budget.ts +2 -0
- package/packages/core/src/processes/publication-loss.ts +18 -1
- package/packages/core/src/processes/repo-view.ts +35 -0
- package/packages/core/src/store/contract.ts +32 -1
- package/packages/core/src/store/local/store.ts +1 -1
- package/packages/core/src/store/remote/client.ts +1 -1
- package/packages/core/src/store/remote/compatibility.ts +90 -0
- package/packages/core/src/store/remote/credentials.ts +28 -0
- package/packages/core/src/store/remote/index.ts +14 -1
- package/packages/core/src/store/remote/protocol.ts +25 -0
- package/packages/core/src/store/remote/token.ts +74 -13
- package/packages/core/src/store/ticket-asset-snapshots.ts +192 -0
- package/packages/core/src/store/ticket-assets.ts +86 -8
- package/packages/core/src/store/types.ts +1 -1
- package/packages/core/src/testing/index.ts +7 -0
- package/skills/guide/SKILL.md +232 -34
- package/skills/guide/references/operator-api.md +142 -13
- package/skills/guide/references/plugin-authoring.md +10 -2
- package/skills/guide/references/remote-store-protocol.md +199 -144
- package/skills/guide/references/remote-ticket-protocol.md +253 -18
- package/skills/guide/references/setup.md +199 -16
- package/skills/guide/references/ticket-dependencies.md +1 -1
- package/skills/guide/references/ticket-migration.md +64 -0
- package/skills/operate/SKILL.md +126 -36
- package/skills/tickets/SKILL.md +71 -0
- package/templates/hosted-service/package.json +13 -0
- package/templates/hosted-service/vercel.json +7 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ticket assets (SPEC §7.1): named, versioned bundles of files attached to a
|
|
3
|
-
* ticket and kept in
|
|
4
|
-
*
|
|
3
|
+
* ticket and kept in its service (hosted) or BuildStore (other sources), keyed by `(repo, ticketId, kind, name)`.
|
|
4
|
+
* Sources also get a human-readable note through
|
|
5
5
|
* `TicketSource.comment()` (SPEC §13).
|
|
6
6
|
*
|
|
7
7
|
* This module is the shared, adapter-independent half: the types, the limits,
|
|
8
8
|
* and the one validator every adapter calls before any write.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { readAssetSnapshot, prepareAssetSnapshots } from './ticket-asset-snapshots'
|
|
12
|
+
import type { TicketSource, TicketAssetStore } from '../ports/types'
|
|
11
13
|
import type { AbEvent } from '../events/catalog'
|
|
12
14
|
import type { PinnedAsset } from '../events/payloads'
|
|
13
15
|
import { defineReducer } from '../kernel/incremental'
|
|
@@ -56,7 +58,8 @@ export interface TicketAssetMeta {
|
|
|
56
58
|
ticketId: string
|
|
57
59
|
kind: string
|
|
58
60
|
name: string
|
|
59
|
-
/**
|
|
61
|
+
/** Increasing per `(repo, ticketId, kind, name)` (normally from zero; native
|
|
62
|
+
* file revisions reserve a separate range for legacy compatibility). A removal consumes a number
|
|
60
63
|
* too (a tombstone), so revisions may skip. */
|
|
61
64
|
revision: number
|
|
62
65
|
layout: TicketAssetLayout
|
|
@@ -380,7 +383,27 @@ export async function samplePinnedAssets(
|
|
|
380
383
|
return (await store.listTicketAssets(repo, ticketId)).map(pinnedAssetOf)
|
|
381
384
|
}
|
|
382
385
|
|
|
383
|
-
export
|
|
386
|
+
export async function preparePinnedAssets(
|
|
387
|
+
source: TicketSource,
|
|
388
|
+
store: BuildStore,
|
|
389
|
+
repo: string,
|
|
390
|
+
ticketId: string,
|
|
391
|
+
): Promise<PinnedAsset[]> {
|
|
392
|
+
if (!source.assets) {
|
|
393
|
+
if (source.name === 'hosted') throw new Error('hosted ticket source has no asset capability')
|
|
394
|
+
return samplePinnedAssets(store, repo, ticketId)
|
|
395
|
+
}
|
|
396
|
+
const ticket = await source.get(ticketId)
|
|
397
|
+
return prepareAssetSnapshots(
|
|
398
|
+
composeTicketAssets(source, store),
|
|
399
|
+
store,
|
|
400
|
+
repo,
|
|
401
|
+
ticketId,
|
|
402
|
+
ticket?.ref.id ?? ticketId,
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export const PINNED_ASSETS_REDUCER_VERSION = 2
|
|
384
407
|
|
|
385
408
|
/** The asset sets recorded by every `build.created`/`spec.revised` pin event
|
|
386
409
|
* that carries one, in array order. Each pin event replaces the set, so the
|
|
@@ -443,7 +466,7 @@ export function findPinnedRevision(
|
|
|
443
466
|
* needed, so a finished build still answers. Null when the build never pinned
|
|
444
467
|
* `kind/name` (or the revision). Rejects an unknown build. */
|
|
445
468
|
export async function resolvePinnedAsset(
|
|
446
|
-
store: Pick<BuildStore, 'getBuild' | 'getEvents' | 'getTicketAsset'>,
|
|
469
|
+
store: Pick<BuildStore, 'getBuild' | 'getEvents' | 'getTicketAsset' | 'getRepoArtifact'>,
|
|
447
470
|
slug: string,
|
|
448
471
|
kind: string,
|
|
449
472
|
name: string,
|
|
@@ -452,7 +475,62 @@ export async function resolvePinnedAsset(
|
|
|
452
475
|
const record = await store.getBuild(slug)
|
|
453
476
|
if (record === null) throw new Error(`unknown build "${slug}"`)
|
|
454
477
|
if (record.ticket === undefined) return null
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
478
|
+
const events = await store.getEvents(slug)
|
|
479
|
+
const sets = pinnedAssetsReducer.reduce(events).pins
|
|
480
|
+
const pin =
|
|
481
|
+
rev === undefined
|
|
482
|
+
? sets.at(-1)?.find((a) => a.kind === kind && a.name === name)
|
|
483
|
+
: sets.flat().find((a) => a.kind === kind && a.name === name && a.revision === rev)
|
|
484
|
+
if (!pin) return null
|
|
485
|
+
if (pin.snapshot) {
|
|
486
|
+
const created = events.find((event) => event.type === 'build.created')
|
|
487
|
+
const asset = await readAssetSnapshot(
|
|
488
|
+
store,
|
|
489
|
+
record.repo,
|
|
490
|
+
record.ticket.id,
|
|
491
|
+
pin,
|
|
492
|
+
created?.type === 'build.created' ? created.payload.ticket?.id : undefined,
|
|
493
|
+
)
|
|
494
|
+
return { ...asset, meta: { ...asset.meta, ticketId: record.ticket.id } }
|
|
495
|
+
}
|
|
496
|
+
return store.getTicketAsset(record.repo, record.ticket.id, kind, name, pin.revision)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Native writes; merged reads preserve legacy file attachments and recorded revisions. */
|
|
500
|
+
export function composeTicketAssets(
|
|
501
|
+
source: TicketSource,
|
|
502
|
+
legacy: TicketAssetStore,
|
|
503
|
+
): TicketAssetStore {
|
|
504
|
+
const native = source.assets
|
|
505
|
+
if (!native) return legacy
|
|
506
|
+
if (!native.hasTicketAssetHistory) return native
|
|
507
|
+
const owns = (repo: string, id: string, kind: string, name: string) =>
|
|
508
|
+
native.hasTicketAssetHistory!(repo, id, kind, name)
|
|
509
|
+
return {
|
|
510
|
+
ticketAssetLimits: native.ticketAssetLimits.bind(native),
|
|
511
|
+
putTicketAsset: native.putTicketAsset.bind(native),
|
|
512
|
+
async getTicketAsset(repo, id, kind, name, rev) {
|
|
513
|
+
if (rev !== undefined)
|
|
514
|
+
return (
|
|
515
|
+
(await native.getTicketAsset(repo, id, kind, name, rev)) ??
|
|
516
|
+
legacy.getTicketAsset(repo, id, kind, name, rev)
|
|
517
|
+
)
|
|
518
|
+
return (await owns(repo, id, kind, name))
|
|
519
|
+
? native.getTicketAsset(repo, id, kind, name)
|
|
520
|
+
: legacy.getTicketAsset(repo, id, kind, name)
|
|
521
|
+
},
|
|
522
|
+
async listTicketAssets(repo, id) {
|
|
523
|
+
const current = await native.listTicketAssets(repo, id)
|
|
524
|
+
for (const item of await legacy.listTicketAssets(repo, id)) {
|
|
525
|
+
if (!(await owns(repo, id, item.kind, item.name))) current.push(item)
|
|
526
|
+
}
|
|
527
|
+
return current
|
|
528
|
+
},
|
|
529
|
+
async removeTicketAsset(repo, id, kind, name) {
|
|
530
|
+
const previous = await this.getTicketAsset(repo, id, kind, name)
|
|
531
|
+
if (!previous) return null
|
|
532
|
+
await native.removeTicketAsset(repo, id, kind, name)
|
|
533
|
+
return previous?.meta ?? null
|
|
534
|
+
},
|
|
535
|
+
}
|
|
458
536
|
}
|
|
@@ -412,7 +412,7 @@ export interface BuildStore {
|
|
|
412
412
|
listRepoArtifacts(repo: string, kind?: string): Promise<RepositoryArtifactMeta[]>
|
|
413
413
|
// ── Ticket assets (SPEC §7.1 — files attached to a ticket) ─────────────
|
|
414
414
|
// Keyed by (repo, ticketId, kind, name); revisions are 0-based. The store
|
|
415
|
-
// owns
|
|
415
|
+
// owns these bytes; hosted current assets use the ticket service store (SPEC §13).
|
|
416
416
|
/** The effective ceilings this store accepts for `repo` (a remote
|
|
417
417
|
* deployment may advertise lower ones than the defaults). */
|
|
418
418
|
ticketAssetLimits(repo: string): Promise<TicketAssetLimits>
|
|
@@ -57,3 +57,10 @@ export {
|
|
|
57
57
|
type E2eHarness,
|
|
58
58
|
type SkillHandlers,
|
|
59
59
|
} from '../integration/harness'
|
|
60
|
+
|
|
61
|
+
// Harvest lifecycle and CLI seams for delegated-credential integration tests.
|
|
62
|
+
export { HarvestRunner } from '../processes/harvest-runner'
|
|
63
|
+
export { resolveHarvestCliEnv } from '../cli/env'
|
|
64
|
+
export { buildHarvestContext, submitHarvestProposals, submitHarvestVerdict } from '../cli/harvest'
|
|
65
|
+
export { abConnect, abDisconnect } from '../cli/connect'
|
|
66
|
+
export { ProjectCredentials } from '../cli/project-credentials'
|
package/skills/guide/SKILL.md
CHANGED
|
@@ -197,7 +197,9 @@ The distinctions that change an administrator's answer:
|
|
|
197
197
|
agent turn or command already running. Missing, unreadable, malformed, and
|
|
198
198
|
routing-invalid candidates retain that snapshot with deduplicated operator
|
|
199
199
|
notice, and restoring a valid file resumes reload. Scoped phase CLI processes
|
|
200
|
-
still read the build worktree's branch-owned file.
|
|
200
|
+
still read the build worktree's branch-owned file. Dispatcher merge readiness,
|
|
201
|
+
resume, and execution recovery use the runner's pinned commands, verify, and
|
|
202
|
+
finalize configuration; builds without a pin retain the live fallback.
|
|
201
203
|
- **Workspaces** are provisioned per build and remain the locally reachable
|
|
202
204
|
execution context for that build.
|
|
203
205
|
- **Ticket providers** (`linear`, `file`) sit behind one port; the dispatcher
|
|
@@ -215,6 +217,22 @@ The distinctions that change an administrator's answer:
|
|
|
215
217
|
guidance and later dispatcher ticks retry. Without consent it remains open for
|
|
216
218
|
ordinary `git diff` inspection; it has no review UI or image host.
|
|
217
219
|
|
|
220
|
+
For moving a project between sources, follow the [ticket migration procedure](references/ticket-migration.md).
|
|
221
|
+
|
|
222
|
+
## Independent hosted tickets
|
|
223
|
+
|
|
224
|
+
Hosted tickets support local builds. Configure `[tickets] source = "hosted"`,
|
|
225
|
+
`teamKey`, and `url`, or run `ab connect --tickets URL` to register the project,
|
|
226
|
+
save its credential and publish ticket policy without changing the build store.
|
|
227
|
+
Without `tickets.url`, tickets use the selected remote build service; local builds
|
|
228
|
+
require an explicit ticket URL. No dispatcher is needed for ticket commands.
|
|
229
|
+
Hosted defaults are `triage`, `ready`, `doing`, `done`; create/proposal use triage.
|
|
230
|
+
Other sources still require readyState. Explicit state names retain their spelling.
|
|
231
|
+
Legacy clients without independent policy retain uploaded configuration defaults.
|
|
232
|
+
Harvest ticket grants are service-bound read/create authority; child phases are
|
|
233
|
+
read-only and cannot publish policy. See [setup](references/setup.md) and the
|
|
234
|
+
[ticket protocol](references/remote-ticket-protocol.md).
|
|
235
|
+
|
|
218
236
|
## `autobuild.toml` reference
|
|
219
237
|
|
|
220
238
|
One declarative file at the repo root. **Declarative, not executable** —
|
|
@@ -283,6 +301,19 @@ through the strict versioned manifest and environment-only secrets, to the
|
|
|
283
301
|
unchanged shared contract suite and optional npm publication—read
|
|
284
302
|
[`references/plugin-authoring.md`](references/plugin-authoring.md).
|
|
285
303
|
|
|
304
|
+
### `[store]`
|
|
305
|
+
|
|
306
|
+
| Key | Required | Values / purpose |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| `url` | When `[store]` exists | Absolute HTTPS service base URL; loopback HTTP is allowed for development. No credentials, query or fragment. Trailing slashes are normalized. |
|
|
309
|
+
|
|
310
|
+
Selection is explicit `--store` > nonblank `AB_STORE` > `[store] url` > the main
|
|
311
|
+
checkout's `.autobuild/`. A configured service without a user credential fails
|
|
312
|
+
with `ab connect` guidance. `AB_TOKEN` overrides the saved service/project
|
|
313
|
+
credential. See [repository setup](references/setup.md#connect-a-project).
|
|
314
|
+
Changing the store requires restarting dispatch.
|
|
315
|
+
|
|
316
|
+
|
|
286
317
|
### `[pr]`
|
|
287
318
|
|
|
288
319
|
Optional PR-projection settings. Omission leaves artifact attachments text-only.
|
|
@@ -408,6 +439,28 @@ manager.
|
|
|
408
439
|
|
|
409
440
|
### `[verify]`
|
|
410
441
|
|
|
442
|
+
Post-reconcile verification defaults to enabled: `[verify].afterReconcile = true`.
|
|
443
|
+
Set it to `false` to retain pre-reconcile verification and skip all steps after
|
|
444
|
+
reconciliation. An optional boolean `[verify.<step>].afterReconcile` overrides
|
|
445
|
+
the repository setting in either direction; overrides require a configured step.
|
|
446
|
+
For example, set `afterReconcile = false` in `[verify]` and
|
|
447
|
+
`afterReconcile = true` in `[verify.types]` to keep only that step re-running.
|
|
448
|
+
|
|
449
|
+
The kernel freezes the resolved ordered policy in
|
|
450
|
+
`verify.reconcile-policy-captured` at the next engine action after reconcile
|
|
451
|
+
completion. Live configuration changes apply at that boundary; a cycle already
|
|
452
|
+
started keeps its policy through recovery. Historical cycles already started
|
|
453
|
+
without a captured policy retain the rerun-all behavior. Initial verification
|
|
454
|
+
and verification after subsequent code review always use ordinary applicability.
|
|
455
|
+
Policy exclusion precedes plan selection and paths, including `always = true`.
|
|
456
|
+
Enabled steps still follow plan selection and path rules. Excluded steps record
|
|
457
|
+
`verify.completed` with outcome `skipped`, `skipCause = "post-reconcile-policy"`,
|
|
458
|
+
and a reason naming the setting. Dashboards and PR summaries call this
|
|
459
|
+
"skipped by post-reconcile policy"; it is never a pass. Forge-side required
|
|
460
|
+
checks and branch protections still gate merge. For excluded steps, merged code
|
|
461
|
+
is not re-verified, so breakage that only exists in the combination can land.
|
|
462
|
+
|
|
463
|
+
|
|
411
464
|
`steps` orders the verify phases; each entry `"<step>"` becomes a
|
|
412
465
|
`verify:<step>` phase and needs its own `[verify.<step>]` subtable. Those
|
|
413
466
|
subtables are part of this section — their fields are listed here.
|
|
@@ -419,6 +472,7 @@ subtables are part of this section — their fields are listed here.
|
|
|
419
472
|
| `command` | — | **required when `kind = "check"`**, nonempty string | Ref into `[commands]` — the key, not a shell string. Pass/fail is the command's exit status. |
|
|
420
473
|
| `skill` | — | **required when `kind = "agent"`**, nonempty string | Installed skill name to run (e.g. a repository-authored `"verify-app-e2e"`). |
|
|
421
474
|
| `paths` | — | optional nonempty array of positive repository-relative globs | Makes either step kind apply when any changed path matches any selector. Omitted means unconditional. |
|
|
475
|
+
| `afterReconcile` | repository: `true`; step: inherited | boolean | Step override wins over `[verify].afterReconcile`; false skips this step after reconciliation only. |
|
|
422
476
|
| `always` | — | optional boolean | `true` makes the step unconditional and mandatory (a plan cannot deselect it); `false` is equivalent to omission. |
|
|
423
477
|
|
|
424
478
|
A plan may begin with strict TOML front matter selecting the complete set of
|
|
@@ -488,7 +542,7 @@ A selected step's `paths` are evaluated immediately before the step against
|
|
|
488
542
|
current `HEAD`, relative to the initial branch-cut SHA or the refreshed base
|
|
489
543
|
from the latest completed reconcile. It uses a NUL-delimited, no-rename Git diff, so
|
|
490
544
|
adds/modifications/deletions and both rename sides participate. A reconcile
|
|
491
|
-
starts a fresh cycle and evaluation repeats; upstream-only merged paths are
|
|
545
|
+
starts a fresh cycle and evaluation repeats for policy-enabled steps; upstream-only merged paths are
|
|
492
546
|
excluded while build-owned resolutions remain visible. A miss starts no
|
|
493
547
|
command, agent, or session and records `skipped` with
|
|
494
548
|
`excluded by [verify.<step>].paths: no changed path matched <JSON paths>`.
|
|
@@ -688,6 +742,7 @@ nonnegative integer so zero can disable that trigger.
|
|
|
688
742
|
| `sessionBudgetSeconds` | `3600` | positive integer | Wall-clock bound for each build agent session unless its logical role overrides it. |
|
|
689
743
|
| `stallRounds` | `3` | positive integer | The same finding surviving this many review rounds auto-escalates to a human — the anti-loop guard. |
|
|
690
744
|
| `maxVerifyAttempts` | `3` | positive integer | Caps the `verify → implement → verify` cycle before escalation. |
|
|
745
|
+
| `maxDispatchAttempts` | `3` | positive integer | Caps consecutive dispatch failures before a setup-targeted `dispatch-failure-limit` escalation. |
|
|
691
746
|
| `maxSetupAttempts` | `3` | positive integer | Caps consecutive `[commands].setup` failures before a setup-targeted human escalation. |
|
|
692
747
|
| `maxInfrastructureAttempts` | `3` | positive integer | Caps consecutive remote provider lifecycle failures before a setup-targeted human escalation; a `retry` answer re-arms the budget. |
|
|
693
748
|
| `maxReconcileAttempts` | `3` | positive integer | Caps completed reconciles that leave the PR conflicted against an unchanged authoritative base. Moving-base races do not consume the budget. |
|
|
@@ -695,6 +750,26 @@ nonnegative integer so zero can disable that trigger.
|
|
|
695
750
|
| `harvestThreshold` | `5` | positive integer | Newly unclaimed `observation.recorded` occurrences required to start one repository harvest run. |
|
|
696
751
|
| `harvestMaxDrift` | `3` | nonnegative integer | Other builds merged after the oldest unclaimed observation required to start a run; `0` disables drift. |
|
|
697
752
|
|
|
753
|
+
Dispatch failures share one budget across stages until runner attachment. At
|
|
754
|
+
exhaustion dispatch parks durably; bare `ab answer <slug>` and text guidance
|
|
755
|
+
both grant a fresh budget, while abort terminates without retrying. Workspace
|
|
756
|
+
failures owned by provider infrastructure recovery use only its existing budget.
|
|
757
|
+
|
|
758
|
+
The dispatcher also applies a fixed, non-configurable limit of three consecutive
|
|
759
|
+
executions that complete normally with exit code zero and no durable build
|
|
760
|
+
progress. Execution start/end, runner attachment and workspace lifecycle
|
|
761
|
+
bookkeeping do not count as progress. Pipeline, session, spec, publication/PR,
|
|
762
|
+
observation and acknowledged operator facts reset the streak; infrastructure,
|
|
763
|
+
budget and provider failures do not consume the allowance and retain their own
|
|
764
|
+
retry limits. Before a fourth actionable launch, the dispatcher raises one
|
|
765
|
+
setup-targeted `execution-no-progress` policy escalation naming the count and
|
|
766
|
+
the next action it expected the runner to take. Waiting, paused and terminal
|
|
767
|
+
builds are left parked, and pending operator commands remain executable.
|
|
768
|
+
Answering that escalation with guidance or a bare `ab answer <slug>` retry grants
|
|
769
|
+
three fresh executions. The guard is derived from durable facts, including on
|
|
770
|
+
supervisor restart, and uses the ordinary watch, dashboard and operator API
|
|
771
|
+
attention paths.
|
|
772
|
+
|
|
698
773
|
For each repeat conflict, Autobuild compares the base merged by the latest
|
|
699
774
|
completed reconcile with a fresh authoritative base snapshot. An advanced base
|
|
700
775
|
proves the attempt lost a race and permits another reconcile regardless of the
|
|
@@ -712,7 +787,7 @@ roundless no-progress answer. An already exhausted no-progress budget raises its
|
|
|
712
787
|
matching cause separately before another reconcile. Cause-less historical
|
|
713
788
|
raises retain their former round-shaped reset behavior, and roundless
|
|
714
789
|
policy/reconcile raises retain their former no-progress meaning, on replay
|
|
715
|
-
without migration. Post-reconcile verification
|
|
790
|
+
without migration. Post-reconcile verification follows the captured policy and remains
|
|
716
791
|
independently bounded by `maxVerifyAttempts`.
|
|
717
792
|
|
|
718
793
|
`stallRounds` counts *persistence chains*, which reviewers mark and the kernel
|
|
@@ -746,12 +821,14 @@ state eligible.
|
|
|
746
821
|
|---|---|---|---|
|
|
747
822
|
| `source` | — | **required**, nonblank builtin or plugin registration name | Which provider backs all ticket operations. Unknown names report every available builtin and plugin source. |
|
|
748
823
|
| `readyLabels` | — (source-aware) | optional; array of nonempty strings | A ticket must carry **every** listed label to be dispatchable. `[]` = **no label gate**. Absent uses the source default below. |
|
|
749
|
-
| `
|
|
824
|
+
| `url` | selected build service | HTTPS URL (loopback HTTP allowed), no userinfo/query/fragment; hosted only | Ticket service independent of builds; required for local builds. |
|
|
825
|
+
| `readyState` | hosted: `ready` | non-blank string; required for other sources | The one workflow state a ticket must sit in to be dispatchable. Linear matches exactly and case-sensitively; file matches it case-insensitively to a state directory (`ready` → `ready/`). There is no any-state mode. |
|
|
750
826
|
| `readyAssignee` | — | optional; `"me"` or an email; `linear` only | Also require the ticket to be assigned to that Linear user (`me` = owner of `LINEAR_API_KEY`); unassigned tickets are never ready. |
|
|
751
|
-
| `teamKey` | — | required by `linear`, forbidden by `file`, allowed for plugins | The Linear team key (e.g. `"ENG"`) or
|
|
827
|
+
| `teamKey` | — | required by `linear` and `hosted`, forbidden by `file`, allowed for plugins | The Linear team key (e.g. `"ENG"`), the captured prefix for new hosted ticket ids (`ENG-1`), or a plugin configuration field. Hosted numbers are project-local and continue across prefix changes; legacy ids remain aliases and readable inputs are case-insensitive. |
|
|
752
828
|
| `claimedState` | — | optional nonempty string; forbidden by `file`, allowed for plugins | Workflow state `claim()` moves a ticket to when a build starts. |
|
|
829
|
+
| `doneState` | hosted: `"done"` | optional nonblank string | Project completion state and merged-ticket transition; hosted tickets only. |
|
|
753
830
|
| `createState` | — | optional, nonempty string | Default state for new tickets when a create does not name one. Absent = the provider's default (Linear: the team's default, e.g. Backlog; file: Triage). |
|
|
754
|
-
| `triageState` | — | optional, nonempty string | State the dispatcher hands tickets back to for human triage — spec-gate bounces, aborted builds, closed-unmerged PRs. Absent = Linear: Backlog; file/plugin: Triage. Must name a state the tracker actually has — a Linear team only has "Triage" when its triage feature is enabled. |
|
|
831
|
+
| `triageState` | — | optional, nonempty string | State the dispatcher hands tickets back to for human triage — spec-gate bounces, aborted builds, closed-unmerged PRs. Absent = Linear: Backlog; hosted: triage; file/plugin: Triage. Must name a state the tracker actually has — a Linear team only has "Triage" when its triage feature is enabled. |
|
|
755
832
|
| `proposalState` | — | optional, nonempty string | State observation harvest files approved proposals into. Absent = the resolved `triageState`, which keeps the human grooming gate. Setting it to `readyState` waives that gate: harvested proposals dispatch without being read. Keep it distinct from `triageState` — see below. |
|
|
756
833
|
| `dir` | file: `.autobuild/tickets`; plugin: — | optional nonempty string; forbidden by `linear`, allowed for plugins | Root holding file state directories, or an existing plugin configuration field. |
|
|
757
834
|
|
|
@@ -829,7 +906,7 @@ changing it requires a dispatcher restart.
|
|
|
829
906
|
| `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. |
|
|
830
907
|
| `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. |
|
|
831
908
|
| `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. |
|
|
832
|
-
| `wake` | inherits the default | array of event-type globs | The attention events a **new** session wakes for. Absent inherits the
|
|
909
|
+
| `wake` | inherits the default | array of event-type globs | The attention events a **new** session wakes for. Absent inherits the legacy hosted wake event set, independent of CLI watch defaults; 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. |
|
|
833
910
|
| `sandbox` | — | strict subtable | The sandbox behavior knobs; absence reads as the defaults below. |
|
|
834
911
|
|
|
835
912
|
`[orchestrator.sandbox]` fields:
|
|
@@ -1049,7 +1126,7 @@ There is no home-directory fallback: a repository with no state tree starts
|
|
|
1049
1126
|
empty.
|
|
1050
1127
|
|
|
1051
1128
|
Every command uses the same precedence: explicit `--store <ref>` > nonempty
|
|
1052
|
-
`AB_STORE` > `<main-repo>/.autobuild`. Relative local overrides are normalized
|
|
1129
|
+
`AB_STORE` > `[store] url` > `<main-repo>/.autobuild`. Relative local overrides are normalized
|
|
1053
1130
|
against the main checkout. A local override relocates the whole state tree,
|
|
1054
1131
|
including `worktrees/` and default file tickets; an explicit `[tickets].dir`
|
|
1055
1132
|
remains repo-relative. An HTTP(S) URL still selects the remote store unchanged,
|
|
@@ -1057,6 +1134,46 @@ while worktrees and default file tickets remain under the repository-default
|
|
|
1057
1134
|
`.autobuild/` directory. The dispatcher passes its normalized selection to
|
|
1058
1135
|
every agent session as `AB_STORE`.
|
|
1059
1136
|
|
|
1137
|
+
## Connect a project
|
|
1138
|
+
|
|
1139
|
+
From a checkout with an origin remote, run `ab connect https://YOUR_ORIGIN`.
|
|
1140
|
+
The CLI prints a verification URL and short code and attempts to open a browser.
|
|
1141
|
+
Sign in as an allowed owner, check the full repository identity and code, and
|
|
1142
|
+
approve or deny. Another device can open the printed URL, or visit `/connect`
|
|
1143
|
+
and enter the code. Approval requires the web-enabled service origin; a
|
|
1144
|
+
standalone machine-only listener cannot present browser consent. Requests
|
|
1145
|
+
expire after ten minutes. Denial and timeout leave local configuration unchanged.
|
|
1146
|
+
|
|
1147
|
+
On approval the service registers the project and issues a project credential.
|
|
1148
|
+
The CLI keeps it in `$XDG_CONFIG_HOME/autobuild/credentials` (or
|
|
1149
|
+
`~/.config/autobuild/credentials`), keyed by normalized service and origin,
|
|
1150
|
+
with directory mode 0700 and file mode 0600. Credentials never belong in the
|
|
1151
|
+
repository. It adds the non-secret `[store] url` to `autobuild.toml` when
|
|
1152
|
+
absent; review and commit that change yourself. An existing different URL
|
|
1153
|
+
must be changed explicitly before connecting. Reconnecting replaces the saved
|
|
1154
|
+
credential only after approval and revokes the previous credential.
|
|
1155
|
+
|
|
1156
|
+
All worktrees share the main checkout's configuration and the user's saved
|
|
1157
|
+
credential. A service manager running as that user uses the same HOME and
|
|
1158
|
+
XDG_CONFIG_HOME, with no AB_STORE or AB_TOKEN exports. A fresh clone with
|
|
1159
|
+
committed store configuration but no saved credential fails with `ab connect`
|
|
1160
|
+
guidance; it never silently creates local state. Run connect on each host/user.
|
|
1161
|
+
|
|
1162
|
+
`ab connect` without a URL reports service, project, and credential status:
|
|
1163
|
+
accepted, rejected, missing, or unreachable. `ab disconnect` revokes the saved
|
|
1164
|
+
credential remotely before removing it locally, keeping the project registered,
|
|
1165
|
+
its data, and store configuration. A network/server failure retains the local
|
|
1166
|
+
entry for retry; an already revoked credential can be removed. If saving a new
|
|
1167
|
+
connection fails, the CLI cancels/revokes it and retains the previous entry.
|
|
1168
|
+
If remote cleanup cannot be confirmed, check the printed credential id with
|
|
1169
|
+
the owner's administrative tools before retrying.
|
|
1170
|
+
|
|
1171
|
+
Store selection is `--store` > nonblank `AB_STORE` > `[store] url` > the
|
|
1172
|
+
main checkout's `.autobuild/`. `AB_TOKEN` overrides the exact service/project's
|
|
1173
|
+
saved credential for CI and other non-interactive hosts. A service override
|
|
1174
|
+
never borrows another service's credential. Build and harvest sessions keep
|
|
1175
|
+
their explicit scoped authority.
|
|
1176
|
+
|
|
1060
1177
|
## Source-agnostic ticket operations
|
|
1061
1178
|
|
|
1062
1179
|
The sessionless `ab ticket` namespace constructs whichever TicketSource the
|
|
@@ -1111,7 +1228,7 @@ to a ticket that has already been claimed into a build does not stop that build.
|
|
|
1111
1228
|
For dependent tickets, follow the
|
|
1112
1229
|
[safe blocker-filing workflow](references/ticket-dependencies.md): determine the
|
|
1113
1230
|
intended final destination, stage outside ready, verify all blocker relationships,
|
|
1114
|
-
and move to ready last. Direct
|
|
1231
|
+
and move to ready last. Direct Linear creates the issue and its blockers
|
|
1115
1232
|
in separate requests; one `create --blocked-by` call is not atomic publication.
|
|
1116
1233
|
Harvest's creation reservations cover Harvest only, not interactive CLI creates.
|
|
1117
1234
|
Dependency order and JSON id extraction are still required, but do not alone
|
|
@@ -1131,16 +1248,19 @@ transitions, so an invalid state fails with the
|
|
|
1131
1248
|
source's known states. A missing id fails nonzero with an error naming both the
|
|
1132
1249
|
id and configured source.
|
|
1133
1250
|
|
|
1134
|
-
Human-readable output is the default and must not be parsed for ids.
|
|
1135
|
-
|
|
1251
|
+
Human-readable output is the default and must not be parsed for ids. The CRUD
|
|
1252
|
+
subcommands accept `--json`, which emits one bare JSON value and no prose: a
|
|
1136
1253
|
`Ticket[]` for `list`, and the complete resulting `Ticket` for `create`,
|
|
1137
1254
|
`update`, `block`, `unblock`, `show`, or `move`.
|
|
1138
1255
|
|
|
1139
1256
|
### Ticket assets
|
|
1140
1257
|
|
|
1141
|
-
A ticket asset is a file or folder attached to a ticket
|
|
1142
|
-
|
|
1143
|
-
|
|
1258
|
+
A ticket asset is a file or folder attached to a ticket: a design export,
|
|
1259
|
+
reference screenshots, or a sample payload. Hosted tickets store assets in their
|
|
1260
|
+
ticket service, independently of the build store; file tickets use native portable
|
|
1261
|
+
assets composed with legacy BuildStore attachments. Sources without native assets
|
|
1262
|
+
use the BuildStore. Existing service assets need no migration. Old local hosted-ticket
|
|
1263
|
+
assets are not moved and are not a fallback. Each has a kind and a name.
|
|
1144
1264
|
|
|
1145
1265
|
- `ab ticket attach <id> <kind> <path> [--name <name>] [--json]` stores `<path>`
|
|
1146
1266
|
(default name: its basename). Attaching the same kind and name again makes a
|
|
@@ -1152,7 +1272,10 @@ a sample payload. Each has a kind and a name.
|
|
|
1152
1272
|
- `ab ticket asset get <id> <kind> <name> <dest> [--rev <n>]` downloads the
|
|
1153
1273
|
exact bytes of the latest (or given) revision; `ab ticket asset rm <id> <kind>
|
|
1154
1274
|
<name>` drops it from the current assets.
|
|
1155
|
-
- **Frozen at claim.** A claim pins the current revisions into the build.
|
|
1275
|
+
- **Frozen at claim.** A claim pins the current revisions into the build.
|
|
1276
|
+
Hosted revisions are copied into immutable snapshots in the build store,
|
|
1277
|
+
preserving revision numbers and allowing reads without the ticket service.
|
|
1278
|
+
Old pins without snapshots keep reading their original ticket rows. A
|
|
1156
1279
|
later attach or remove leaves a running build alone; a human answer with
|
|
1157
1280
|
`--revise-spec-from-ticket` pins again. `ab build asset get <slug> <kind>
|
|
1158
1281
|
<name> <dest> [--rev <n>]` downloads what the build froze, also after it ends.
|
|
@@ -1173,7 +1296,7 @@ judge against it.
|
|
|
1173
1296
|
is the read-only binary retrieval form. Operators need no session identity: it
|
|
1174
1297
|
works after a build is terminal, writes exact bytes (creating parent
|
|
1175
1298
|
directories), verifies that the build belongs to this repository, and uses the
|
|
1176
|
-
standard explicit `--store` > nonblank `AB_STORE` > repository-local selection.
|
|
1299
|
+
standard explicit `--store` > nonblank `AB_STORE` > `[store] url` > repository-local selection.
|
|
1177
1300
|
Inside a phase, a complete ambient tuple permits only the ambient build; a
|
|
1178
1301
|
foreign build or malformed/partial identity is rejected. Use the exact pinned
|
|
1179
1302
|
`@rev` from a PR attachment command.
|
|
@@ -1507,7 +1630,7 @@ Every command requires the target to exist in this repository and be active
|
|
|
1507
1630
|
`ab answer` additionally requires an open escalation. A stale, missing, done,
|
|
1508
1631
|
aborted, or otherwise inapplicable target is an error and gets no new event. Attribution uses `USER`, then `USERNAME`, then the
|
|
1509
1632
|
same stable fallback on both surfaces. All five commands run sessionless and
|
|
1510
|
-
accept `--store <ref>` with the usual explicit flag > `AB_STORE` > repository
|
|
1633
|
+
accept `--store <ref>` with the usual explicit flag > `AB_STORE` > `[store] url` > repository
|
|
1511
1634
|
local precedence. If nonblank `AB_SESSION` and matching `AB_BUILD` identify the
|
|
1512
1635
|
caller's own phase build, the command refuses it; a phase cannot pause, resume,
|
|
1513
1636
|
auto-merge, answer, or abort itself. `ab pause --all` and `ab resume --all`
|
|
@@ -1553,7 +1676,7 @@ repository-wide pause (`paused`, default `false`), and the claim-time auto-merge
|
|
|
1553
1676
|
default (`defaultAutoMerge`, default `false`). Those defaults also describe a
|
|
1554
1677
|
repository with no journal row; the read does not create one. Human output is
|
|
1555
1678
|
the default. `--json` emits one bare object with those exact fields plus the
|
|
1556
|
-
canonical `repo`, and `--store` follows the usual explicit flag > `AB_STORE` >
|
|
1679
|
+
canonical `repo`, and `--store` follows the usual explicit flag > `AB_STORE` > `[store] url` >
|
|
1557
1680
|
repository-local precedence. The command uses the same reducer as the
|
|
1558
1681
|
dispatcher and dashboard, writes no state, claims no ticket, attaches no runner,
|
|
1559
1682
|
and starts no dispatcher work.
|
|
@@ -1636,19 +1759,51 @@ streams matching events as they land instead of answering once. With no slug it
|
|
|
1636
1759
|
follows every nonterminal build in the repository — a build created while the
|
|
1637
1760
|
watch runs joins it without a restart — and naming one or more slugs watches
|
|
1638
1761
|
exactly those builds, ending the watch when all of them reach a terminal status.
|
|
1639
|
-
|
|
1640
|
-
repository
|
|
1641
|
-
|
|
1642
|
-
The default
|
|
1643
|
-
|
|
1644
|
-
`
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
`harvest.escalated`, `harvest.
|
|
1648
|
-
`dispatcher.config-rejected`, `dispatcher.
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1762
|
+
With no slugs the repository journal is included; named watches add it with
|
|
1763
|
+
`--repository`.
|
|
1764
|
+
|
|
1765
|
+
The default watch reports actionable conditions only:
|
|
1766
|
+
|
|
1767
|
+
- Build `escalation.raised`, or `build.merge-decision`: a finalized open PR
|
|
1768
|
+
with auto-merge off or a current actionable merge-gate deferral. Armed consent
|
|
1769
|
+
awaiting application is silent. One merge decision is reported per build/PR.
|
|
1770
|
+
- Repository `harvest.escalated`, `harvest.recovery-exhausted`,
|
|
1771
|
+
`dispatcher.config-rejected`, `dispatcher.config-reloaded` with nonempty
|
|
1772
|
+
`restartRequired`, abnormal `dispatcher.run-stopped`, and `dispatcher.lost`.
|
|
1773
|
+
- `dispatcher.ticket-source-persistent`: degradation past its advertised
|
|
1774
|
+
reset time, once a tick started at or after reset has completed or failed
|
|
1775
|
+
with the episode still open; crossing reset alone is silent. Without a reset,
|
|
1776
|
+
the deadline is 15 minutes after onset. Repeated evidence never postpones
|
|
1777
|
+
the deadline; recovery ends the episode.
|
|
1778
|
+
- `dispatcher.tick-failure-persistent`: continuously failing completed ticks
|
|
1779
|
+
for five minutes in one run. A successful tick, new run, or matching stop
|
|
1780
|
+
ends the episode. Source grace and cooldown-driven failures do not start
|
|
1781
|
+
this clock; after grace, the first qualifying failure starts a new five-minute
|
|
1782
|
+
interval. Each persistence episode is reported once.
|
|
1783
|
+
|
|
1784
|
+
Repository conditions are included with no slugs or with `--repository`.
|
|
1785
|
+
Routine outcomes and self-healing failures are silent. Repeatable
|
|
1786
|
+
`--event <glob>` replaces the default and selects exactly matching catalog
|
|
1787
|
+
events (plus the existing `dispatcher.lost` observation); use explicit globs
|
|
1788
|
+
to opt into failures, finalize, merge, or completion events.
|
|
1789
|
+
|
|
1790
|
+
Derived records use `observation` instead of `event`, without inventing a
|
|
1791
|
+
durable sequence or actor. Merge observations carry `type`, `pr`, `reason`,
|
|
1792
|
+
and `observedAt`, plus the build slug and normal reduced state. Persistence
|
|
1793
|
+
observations carry source/cause/message/resetAt when advertised (or
|
|
1794
|
+
run/firstFailure/error), `episodeSeq`, and `observedAt`; repository records
|
|
1795
|
+
have `repo` and null build/state. Source observations also report
|
|
1796
|
+
`dependencyLimited`.
|
|
1797
|
+
|
|
1798
|
+
Every record carries a cursor, including timer-only records. Resuming suppresses
|
|
1799
|
+
delivered merge decisions and persistence episodes even when stream positions
|
|
1800
|
+
did not advance. Old v1 cursors remain valid: historical eligible merge
|
|
1801
|
+
decisions are treated as consumed; active overdue repository episodes may
|
|
1802
|
+
report because old watches had no persistence notification. Fresh attachment
|
|
1803
|
+
silently baselines historical direct events and merge decisions, but retains
|
|
1804
|
+
active repository timers. Reconcile existing conditions through status after
|
|
1805
|
+
arming the watch. Named watches still end when every named build is terminal;
|
|
1806
|
+
learn their outcomes from status reconciliation at exit.
|
|
1652
1807
|
|
|
1653
1808
|
With `--json`, stdout is newline-delimited JSON — exactly one object per
|
|
1654
1809
|
matching event, flushed as soon as it is written — with fields `build` (slug,
|
|
@@ -1745,7 +1900,7 @@ vocabulary is closed: `blocked`, `paused`, `running`, `done`, `aborted`,
|
|
|
1745
1900
|
`terminal` (done or aborted), `pr=open`, `pr=merged`, `pr=closed`,
|
|
1746
1901
|
`pr=conflicted`, `phase=<phase>` (a core phase — `plan`, `plan-review`,
|
|
1747
1902
|
`implement`, `code-review`, `finalize`, `reconcile` — or `verify:<step>`), and
|
|
1748
|
-
`attention` (
|
|
1903
|
+
`attention` (an open escalation or a build waiting for a merge decision). Repeatable `--event`
|
|
1749
1904
|
globs follow the `ab watch` rules. Multiple values are alternatives: the first
|
|
1750
1905
|
one satisfied ends the wait — all `--for` values in supply order, then all
|
|
1751
1906
|
globs in supply order — and the record names what matched.
|
|
@@ -1756,8 +1911,11 @@ means an open escalation exactly as everywhere else. In the manner of `kubectl
|
|
|
1756
1911
|
wait`, a condition that already holds when the command starts returns
|
|
1757
1912
|
immediately with that build's latest event (`--since` does not suppress this);
|
|
1758
1913
|
otherwise the first later event whose reduction produces it ends the wait.
|
|
1759
|
-
`attention`
|
|
1760
|
-
|
|
1914
|
+
`attention` is also a current-state condition: an already open escalation or
|
|
1915
|
+
merge decision returns immediately, including with `--since`. Merge matches
|
|
1916
|
+
use the same observation as watch. During replay, only new escalation events
|
|
1917
|
+
or undelivered eligible merge decisions satisfy attention. `--event` conditions
|
|
1918
|
+
fire only on new events or a cursor-resumed backlog.
|
|
1761
1919
|
|
|
1762
1920
|
The record is the `ab watch` record plus a `condition` field naming the
|
|
1763
1921
|
satisfied `--for` value or `--event` glob. Unlike `ab watch`, there is no
|
|
@@ -1920,3 +2078,43 @@ build phase, which is the criterion for membership (§16.3): `ab-spec` and
|
|
|
1920
2078
|
`ab-tickets` are human/agent-facing pre-build surfaces, `ab-guide` is
|
|
1921
2079
|
read-only reference including the setup handoff guidance, and `ab-operate` is
|
|
1922
2080
|
the manual for the orchestrator role, which any agent may take on.
|
|
2081
|
+
|
|
2082
|
+
## Revision-checked ticket writes
|
|
2083
|
+
|
|
2084
|
+
Every ticket read includes an opaque `revision`. Pass it as `expectedRevision`
|
|
2085
|
+
(on MCP/operator requests) or `--expected-revision <opaque>` on `ab ticket update`,
|
|
2086
|
+
`move`, `block` and `unblock`. A stale write fails without changing anything and
|
|
2087
|
+
reports the current revision. Omission preserves unguarded behavior. Blocker
|
|
2088
|
+
checks cover the entire batch. Dispatcher moves remain unguarded and change the
|
|
2089
|
+
revision. Sources without guarded writes, including Linear, refuse supplied
|
|
2090
|
+
revisions rather than ignoring them.
|
|
2091
|
+
|
|
2092
|
+
`ab ticket edit <id>` opens `$VISUAL`, falling back to `$EDITOR`, using TOML
|
|
2093
|
+
frontmatter and a Markdown body. Only title, body and labels are editable;
|
|
2094
|
+
id, blockers, creation key and reference comments are read-only. It saves only
|
|
2095
|
+
changed fields with the opening revision. Unchanged or formatting-only edits
|
|
2096
|
+
write nothing. On conflict, invalid input or editor failure, the error names an
|
|
2097
|
+
absolute file path containing the edited text. Read the current ticket and
|
|
2098
|
+
reapply the intended changes before retrying; conflicts are never merged.
|
|
2099
|
+
|
|
2100
|
+
File and Linear revisions fingerprint title, body, canonical label/blocker sets
|
|
2101
|
+
and state; reverting identical content restores its fingerprint. File checks
|
|
2102
|
+
re-read immediately before writing, with the existing no-lock race window.
|
|
2103
|
+
Direct file edits and state-directory moves change revisions. File revisions also
|
|
2104
|
+
cover structured comments and portable attachments. Legacy file comments change
|
|
2105
|
+
the body; structured comments preserve it. Separate hosted comments do not change
|
|
2106
|
+
ticket revisions. Hosted revisions
|
|
2107
|
+
are monotonic decimal bigint strings, and checks run under the same row lock
|
|
2108
|
+
and transaction as all requested changes. Hosted reads return a coherent
|
|
2109
|
+
snapshot of fields, blockers and revision.
|
|
2110
|
+
|
|
2111
|
+
Read before writing, and copy the opaque revision verbatim:
|
|
2112
|
+
|
|
2113
|
+
```sh
|
|
2114
|
+
ab ticket show AUT-8 --json
|
|
2115
|
+
ab ticket update AUT-8 --title 'Revised title' --expected-revision '<revision from show>'
|
|
2116
|
+
ab ticket edit AUT-8
|
|
2117
|
+
```
|
|
2118
|
+
|
|
2119
|
+
Use `expectedRevision` on MCP `tickets.update`, `tickets.move`, `tickets.block`
|
|
2120
|
+
and `tickets.unblock` with the revision from `tickets.get` or `tickets.list`.
|