@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.
Files changed (136) hide show
  1. package/README.md +3 -1
  2. package/SPEC.md +335 -31
  3. package/docs/architecture.md +125 -7
  4. package/docs/configuration.md +142 -21
  5. package/docs/hosted-dispatcher.md +17 -2
  6. package/docs/hosted-service.md +240 -0
  7. package/docs/mcp.md +8 -2
  8. package/docs/operator-api.md +97 -7
  9. package/docs/remote-store-protocol.md +199 -144
  10. package/docs/remote-ticket-protocol.md +249 -19
  11. package/docs/setup.md +199 -16
  12. package/docs/ticket-dependencies.md +1 -1
  13. package/docs/ticket-migration.md +64 -0
  14. package/package.json +7 -6
  15. package/packages/core/src/cli/attention.ts +209 -0
  16. package/packages/core/src/cli/build-control.ts +12 -1
  17. package/packages/core/src/cli/configured-store.ts +17 -0
  18. package/packages/core/src/cli/connect.ts +364 -0
  19. package/packages/core/src/cli/context.ts +7 -1
  20. package/packages/core/src/cli/dashboard/model.ts +4 -5
  21. package/packages/core/src/cli/dashboard/poll.ts +1 -1
  22. package/packages/core/src/cli/dispatch.ts +336 -41
  23. package/packages/core/src/cli/harvest.ts +13 -3
  24. package/packages/core/src/cli/help.ts +51 -16
  25. package/packages/core/src/cli/init-validation.ts +27 -2
  26. package/packages/core/src/cli/installation.ts +67 -13
  27. package/packages/core/src/cli/main.ts +60 -16
  28. package/packages/core/src/cli/mcp.ts +9 -2
  29. package/packages/core/src/cli/pr-summary-publication.ts +220 -0
  30. package/packages/core/src/cli/pr-summary.ts +2 -0
  31. package/packages/core/src/cli/project-credentials.ts +132 -0
  32. package/packages/core/src/cli/repo-state.ts +15 -2
  33. package/packages/core/src/cli/self-update.ts +15 -1
  34. package/packages/core/src/cli/store-opening.ts +22 -14
  35. package/packages/core/src/cli/terminals.ts +0 -10
  36. package/packages/core/src/cli/ticket-assets.ts +7 -2
  37. package/packages/core/src/cli/ticket-editor.ts +82 -0
  38. package/packages/core/src/cli/ticket.ts +172 -35
  39. package/packages/core/src/cli/upgrade-compatibility.ts +80 -0
  40. package/packages/core/src/cli/wait.ts +84 -17
  41. package/packages/core/src/cli/watch.ts +197 -60
  42. package/packages/core/src/config/live.ts +3 -0
  43. package/packages/core/src/config/schema.ts +90 -11
  44. package/packages/core/src/connection.ts +86 -0
  45. package/packages/core/src/events/catalog.ts +11 -2
  46. package/packages/core/src/events/payloads.ts +50 -0
  47. package/packages/core/src/events/repository.ts +13 -0
  48. package/packages/core/src/integration/harness.ts +37 -2
  49. package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +145 -83
  50. package/packages/core/src/kernel/engine.ts +24 -0
  51. package/packages/core/src/kernel/generators/build-log.ts +11 -1
  52. package/packages/core/src/kernel/incremental-fixtures.ts +13 -2
  53. package/packages/core/src/kernel/log-index.ts +17 -0
  54. package/packages/core/src/kernel/pr-summary.ts +89 -0
  55. package/packages/core/src/kernel/reducer-registry.ts +10 -0
  56. package/packages/core/src/kernel/reducer.ts +8 -0
  57. package/packages/core/src/ontology.ts +2 -0
  58. package/packages/core/src/operator/annotations.ts +12 -0
  59. package/packages/core/src/operator/index.ts +12 -0
  60. package/packages/core/src/operator/protocol.ts +19 -2
  61. package/packages/core/src/operator/query.ts +38 -16
  62. package/packages/core/src/operator/registry.ts +61 -8
  63. package/packages/core/src/operator/ticket-source-backend.ts +17 -20
  64. package/packages/core/src/operator/tickets.ts +136 -28
  65. package/packages/core/src/plugin-sdk/index.ts +7 -0
  66. package/packages/core/src/plugins/manifest.ts +5 -3
  67. package/packages/core/src/plugins/runtimes.ts +7 -1
  68. package/packages/core/src/ports/forge/fake.ts +13 -1
  69. package/packages/core/src/ports/forge/github-transport.ts +36 -1
  70. package/packages/core/src/ports/forge/github.ts +29 -5
  71. package/packages/core/src/ports/forge/local-git.ts +12 -4
  72. package/packages/core/src/ports/forge/summary-marker.ts +8 -0
  73. package/packages/core/src/ports/runner/session-env.ts +3 -0
  74. package/packages/core/src/ports/tickets/contract.ts +66 -1
  75. package/packages/core/src/ports/tickets/create.ts +54 -10
  76. package/packages/core/src/ports/tickets/fake.ts +30 -1
  77. package/packages/core/src/ports/tickets/file-assets.ts +159 -0
  78. package/packages/core/src/ports/tickets/file-codec.ts +90 -0
  79. package/packages/core/src/ports/tickets/file.ts +114 -120
  80. package/packages/core/src/ports/tickets/grants.ts +47 -0
  81. package/packages/core/src/ports/tickets/hosted-index.ts +17 -3
  82. package/packages/core/src/ports/tickets/identity.ts +20 -0
  83. package/packages/core/src/ports/tickets/lifecycle.ts +18 -0
  84. package/packages/core/src/ports/tickets/linear.ts +74 -9
  85. package/packages/core/src/ports/tickets/migration.ts +336 -0
  86. package/packages/core/src/ports/tickets/operations.ts +38 -7
  87. package/packages/core/src/ports/tickets/portable-assets.ts +54 -0
  88. package/packages/core/src/ports/tickets/remote-protocol.ts +96 -0
  89. package/packages/core/src/ports/tickets/remote.ts +61 -3
  90. package/packages/core/src/ports/tickets/requested-id.ts +46 -0
  91. package/packages/core/src/ports/tickets/revision.ts +62 -0
  92. package/packages/core/src/ports/tickets/states.ts +61 -0
  93. package/packages/core/src/ports/types.ts +40 -3
  94. package/packages/core/src/ports/workspace/build-execution.ts +6 -1
  95. package/packages/core/src/ports/workspace/create.ts +15 -2
  96. package/packages/core/src/ports/workspace/distribution-archive.ts +3 -5
  97. package/packages/core/src/ports/workspace/harvest-execution.ts +3 -1
  98. package/packages/core/src/ports/workspace/local-build-execution.ts +8 -1
  99. package/packages/core/src/ports/workspace/operator-sandbox.ts +2 -0
  100. package/packages/core/src/ports/workspace/vercel-sandbox.ts +51 -19
  101. package/packages/core/src/processes/build-execution-state.ts +1 -1
  102. package/packages/core/src/processes/build-runner.ts +87 -26
  103. package/packages/core/src/processes/dispatch-failure-budget.ts +83 -0
  104. package/packages/core/src/processes/dispatcher.ts +168 -41
  105. package/packages/core/src/processes/execution-no-progress.ts +176 -0
  106. package/packages/core/src/processes/harvest-child.ts +31 -4
  107. package/packages/core/src/processes/harvest-execution-state.ts +19 -2
  108. package/packages/core/src/processes/harvest-runner.ts +166 -104
  109. package/packages/core/src/processes/harvest.ts +34 -14
  110. package/packages/core/src/processes/phase-failure-budget.ts +2 -0
  111. package/packages/core/src/processes/publication-loss.ts +18 -1
  112. package/packages/core/src/processes/repo-view.ts +35 -0
  113. package/packages/core/src/store/contract.ts +32 -1
  114. package/packages/core/src/store/local/store.ts +1 -1
  115. package/packages/core/src/store/remote/client.ts +1 -1
  116. package/packages/core/src/store/remote/compatibility.ts +90 -0
  117. package/packages/core/src/store/remote/credentials.ts +28 -0
  118. package/packages/core/src/store/remote/index.ts +14 -1
  119. package/packages/core/src/store/remote/protocol.ts +25 -0
  120. package/packages/core/src/store/remote/token.ts +74 -13
  121. package/packages/core/src/store/ticket-asset-snapshots.ts +192 -0
  122. package/packages/core/src/store/ticket-assets.ts +86 -8
  123. package/packages/core/src/store/types.ts +1 -1
  124. package/packages/core/src/testing/index.ts +7 -0
  125. package/skills/guide/SKILL.md +232 -34
  126. package/skills/guide/references/operator-api.md +142 -13
  127. package/skills/guide/references/plugin-authoring.md +10 -2
  128. package/skills/guide/references/remote-store-protocol.md +199 -144
  129. package/skills/guide/references/remote-ticket-protocol.md +253 -18
  130. package/skills/guide/references/setup.md +199 -16
  131. package/skills/guide/references/ticket-dependencies.md +1 -1
  132. package/skills/guide/references/ticket-migration.md +64 -0
  133. package/skills/operate/SKILL.md +126 -36
  134. package/skills/tickets/SKILL.md +71 -0
  135. package/templates/hosted-service/package.json +13 -0
  136. package/templates/hosted-service/vercel.json +7 -0
@@ -333,7 +333,19 @@ head is a no-op; Git/Forge failures stay failure-tolerant observations.
333
333
  **Verify gating.** `packages/core/src/ontology.ts` owns the canonical
334
334
  `pass | fail | skipped` outcome (only `fail` routes back to implement or
335
335
  consumes attempts; a skip satisfies one step without being passing
336
- evidence). Two kernel-authored skip sources narrow the configured universe:
336
+ evidence). Three kernel-authored skip sources narrow the configured universe:
337
+ post-reconcile policy, approved plan selection, and changed paths.
338
+ The engine captures the ordered effective `afterReconcile` policy in
339
+ `verify.reconcile-policy-captured` at the action after reconciliation completes,
340
+ before verification starts. Log-index replay keeps it scoped to that completion;
341
+ subsequent config changes cannot replace it mid-cycle. Historical already-started
342
+ cycles without a snapshot retain rerun-all behavior. A later code-review approval
343
+ restores ordinary verification. Policy exclusions precede plan/path applicability
344
+ and carry `skipCause: "post-reconcile-policy"` plus a setting-specific reason.
345
+ The shared dashboard model renders that reason, as does the PR summary.
346
+ Excluded steps do not re-verify merged code, so combination-only breakage can
347
+ land; forge requirements and reconcile budgets remain independent.
348
+ For enabled steps,
337
349
  `packages/core/src/kernel/plan-verify-selection.ts` resolves an approved plan's front-matter
338
350
  selection (applied at the planner's `ab done` in `packages/core/src/cli/terminals.ts`,
339
351
  snapshotted by the engine at approval), and
@@ -341,7 +353,7 @@ snapshotted by the engine at approval), and
341
353
  selectors, resolved by the build runner via `git diff` against the initial
342
354
  branch-cut base (or the refreshed base promoted by a completed reconcile).
343
355
  This verify-only base is deliberately independent from implementation's
344
- focused review range. Both narrowing mechanisms produce the ordinary
356
+ focused review range. All narrowing mechanisms produce the ordinary
345
357
  queryable skipped outcome; Git failure is infrastructure and fails closed,
346
358
  never a synthetic skip. Verify escalation routing is also explicit in the
347
359
  engine: guidance answering an agent verifier's own `ab escalate` returns to the
@@ -352,6 +364,10 @@ once. Guidance answering the policy escalation after failed-report exhaustion
352
364
  instead routes to `implement` and outranks the pending report. A bare retry carries no
353
365
  feedback.
354
366
 
367
+ The dispatcher launch boundary uses `processes/execution-no-progress.ts` to
368
+ project clean empty execution streaks through `RepoViewStore` snapshots and
369
+ conditionally raise an ordinary setup policy escalation before a fourth launch.
370
+
355
371
  **Launch ownership.** `packages/core/src/cli/dispatch.ts` is the kernel owner and
356
372
  single-flights supervised build executions per slug within its process. It
357
373
  publishes effective config into the build artifact namespace, claims the exact
@@ -579,7 +595,7 @@ slug routing. Upgrade performs the same composition lazily at its first merge
579
595
  conflict.
580
596
 
581
597
  `packages/core/src/cli/repo-state.ts` owns repository identity and store precedence
582
- (`--store` > `AB_STORE` > `.autobuild/`); `packages/core/src/cli/store-opening.ts` is the
598
+ (`--store` > `AB_STORE` > `[store] url` > `.autobuild/`); `packages/core/src/cli/store-opening.ts` is the
583
599
  production store composition boundary shared by finite sessionless queries and
584
600
  phase sessions. Its shared scoping primitive preserves remote token composition
585
601
  and adds local ambient authority only for filesystem stores. The dedicated
@@ -599,7 +615,15 @@ phase-only commands report their complete runner context when run by hand.
599
615
  correlations, and pending cleanup without coupling to a producer or verify-step
600
616
  name. `packages/core/src/cli/pr-attachments.ts` performs optional image hosting through the
601
617
  narrow Forge capability, while `packages/core/src/cli/pr-summary.ts` renders the same complete
602
- text projection for finalize and late designations. The GitHub release transport
618
+ text projection for finalize and late designations. `cli/pr-summary-publication.ts`
619
+ coalesces these at completed phase boundaries with durable attempt, result,
620
+ cooldown, request, and disposition facts. Built-in forges upsert one hidden
621
+ per-build marker; legacy plugins keep the existing bounded `commentOnPr` API.
622
+ Oversized bodies become a compact pinned download of a full `pr-summary`
623
+ store artifact. Local runners publish under their lease; remote guests request
624
+ and yield, and dispatcher settlement fences ended execution and owns the
625
+ publication lease before launching replacement work. Summary failure never
626
+ changes phase outcomes. The GitHub release transport
603
627
  lives in `packages/core/src/ports/forge/github-pr-attachments.ts`; terminal-build reclamation
604
628
  and retry facts remain dispatcher janitor duty.
605
629
 
@@ -751,17 +775,24 @@ selected through the plugin ticket-source registry, the real filesystem and
751
775
  local-git adapters, the injected Claude and Codex CLI subprocess contracts,
752
776
  and the injected Pi CLI/RPC contract.
753
777
  Ticket assets (SPEC §7.1.3) are files attached to a ticket and kept in the
754
- BuildStore, never in the ticket source. `packages/core/src/store/ticket-assets.ts`
778
+ ticket service for hosted tickets, independently of build storage, and in the
779
+ BuildStore for other sources. `packages/core/src/store/ticket-assets.ts`
755
780
  holds the types, the limits, and the one validator every adapter calls;
756
781
  `packages/core/src/cli/ticket-assets.ts` holds the filesystem walk, the download
757
782
  writer, and the note builder; `ab ticket attach`, `asset get`, and `asset rm`
758
- live in `packages/core/src/cli/ticket.ts`. The only thing a ticket source sees is
759
- a note through `comment()`, so no source or plugin protocol changed.
783
+ live in `packages/core/src/cli/ticket.ts`. The optional hosted source asset
784
+ capability reuses RemoteBuildStore transport with the ticket URL and credential.
785
+ Other sources retain the existing store path.
760
786
  A claim freezes the ticket's current assets into the build (SPEC §6.3):
761
787
  `build.created.assets` (and, when a spec is revised from the ticket,
762
788
  `spec.revised.assets`) record the pinned revisions, `state.pinnedAssets` in the
763
789
  reducer projects them, `getPinnedTicketAsset` reads them back by pinned revision,
764
790
  and `ab context` writes them under `.ab/assets/`.
791
+ For hosted tickets, `store/ticket-asset-snapshots.ts` copies exact revisions
792
+ into chunked immutable repository artifacts in the build store before pinning.
793
+ Reads use explicit manifest/chunk revisions without a ticket connection. Old
794
+ pins retain ticket-row reads; existing service attachments need no migration,
795
+ and attachments in old local stores are not moved.
765
796
 
766
797
  Both `ab dispatch` and sessionless `ab ticket` load the repository's plugins
767
798
  before selecting their TicketSource; dispatch passes that one adapter instance
@@ -876,3 +907,90 @@ because they cannot be manufactured safely against a live account.
876
907
 
877
908
  Provisioning or scheduling these credentials/resources in CI is deliberately
878
909
  out of scope; live runs remain explicit.
910
+
911
+ ## Hosted project registry
912
+
913
+ `packages/postgres-store/src/projects.ts` owns a narrow project registry port
914
+ beside BuildStore. Schema v10 adds an independent append-only project journal
915
+ and singleton mutation/bootstrap lock and marker, with no build/ticket foreign
916
+ keys. Active entries reduce the latest sequence per origin; timestamps are
917
+ metadata, not ordering. Runtime opens assert schema and never run DDL.
918
+ `ab-hosted-store projects` administers registrations using database authority.
919
+ The service exposes one lazy live reader to the operator server, web gateway,
920
+ MCP and server-rendered repository picker. Initialization failures clear the
921
+ cached promise for retry. The dispatcher opens its own shared pool and queries
922
+ membership each invocation. See [owner workflow](setup.md#connected-projects).
923
+
924
+ ## Delegated execution authority
925
+
926
+ `store/remote/credentials.ts` owns the credential exchange client independently
927
+ of the BuildStore persistence port. The hosted service's `delegated-authority.ts`
928
+ validates issuance, durable lifecycle bindings and the 60-second terminal grace;
929
+ `machine-auth.ts` authenticates these signed version-2 grants as delegated
930
+ principals. `remote-store-server.ts` enforces resource mutations before deposits,
931
+ including body-selected and addressed-stream resources. Operator routes reject
932
+ these principals. Ticket routes deny build grants and allow harvest grants only the reads and
933
+ runner filing operations they require. Ticket sources and assets are partitioned
934
+ by the authenticated project.
935
+
936
+ Dispatch prepares harvest claims using `HarvestRunner.run(true)` before issuing
937
+ an execution grant. `harvest.execution.authorized` binds the selected run,
938
+ execution and adopted lease holder; the guest resumes only that prepared run.
939
+ Local completion records `harvest.execution.ended`; hosted completion retains
940
+ its provider release fact. Recovery controls remain trusted dispatcher work.
941
+ Execution launch auth is transient and separate from serialized identity. Runners
942
+ request exact phase grants after session-start facts and before runtime calls.
943
+ Workspace plugin factories receive an exchange callback and a scrubbed environment,
944
+ while local children and sandbox bootstrap/launch replace inherited authority.
945
+ Service requests reread durable state; detached executions need neither renewal
946
+ secrets nor their original dispatcher. Local stores retain their existing path.
947
+ Project onboarding uses `cli/connect.ts`, user-level `cli/project-credentials.ts`,
948
+ the shared `connection.ts` wire types, and Postgres `connections.ts`. Approval
949
+ commits registry membership, a credential digest, and the outcome atomically.
950
+ The web connection routes authenticate the existing Better Auth session and
951
+ reuse the sign-in/consent layout; CLI polling uses a separate private capability.
952
+
953
+ ## Revision-checked ticket writes
954
+
955
+ Every ticket read includes an opaque `revision`. Pass it as `expectedRevision`
956
+ (on MCP/operator requests) or `--expected-revision <opaque>` on `ab ticket update`,
957
+ `move`, `block` and `unblock`. A stale write fails without changing anything and
958
+ reports the current revision. Omission preserves unguarded behavior. Blocker
959
+ checks cover the entire batch. Dispatcher moves remain unguarded and change the
960
+ revision. Sources without guarded writes, including Linear, refuse supplied
961
+ revisions rather than ignoring them.
962
+
963
+ `ab ticket edit <id>` opens `$VISUAL`, falling back to `$EDITOR`, using TOML
964
+ frontmatter and a Markdown body. Only title, body and labels are editable;
965
+ id, blockers, creation key and reference comments are read-only. It saves only
966
+ changed fields with the opening revision. Unchanged or formatting-only edits
967
+ write nothing. On conflict, invalid input or editor failure, the error names an
968
+ absolute file path containing the edited text. Read the current ticket and
969
+ reapply the intended changes before retrying; conflicts are never merged.
970
+
971
+ File and Linear revisions fingerprint title, body, canonical label/blocker sets
972
+ and state; reverting identical content restores its fingerprint. File checks
973
+ re-read immediately before writing, with the existing no-lock race window.
974
+ Direct file edits and state-directory moves change revisions. File comments
975
+ change the body and revision; separate hosted comments do not. Hosted revisions
976
+ are monotonic decimal bigint strings, and checks run under the same row lock
977
+ and transaction as all requested changes. Hosted reads return a coherent
978
+ snapshot of fields, blockers and revision.
979
+
980
+ ### Independent hosted ticket service
981
+
982
+ `[tickets] url` selects hosted ticket transport independently of BuildStore selection;
983
+ without it, tickets use the selected remote build store. Local builds require an
984
+ explicit ticket URL. Project registration and ticket policy publication through
985
+ `ab connect --tickets URL` work before dispatch. Policy is a durable per-registration
986
+ record, outside BuildStore events/artifacts; legacy uploaded configuration remains
987
+ a compatibility fallback. Hosted lifecycle defaults are triage/ready/doing/done.
988
+ Independent harvest uses revocable, expiring, service-bound ticket grants with
989
+ read/create runner authority and read-only child authority. Build state and harvest
990
+ journals remain exclusively in the selected BuildStore.
991
+
992
+ Hosted ticket detail reads expose complete, oldest-first structured comments in
993
+ `ticket.comments`, preserving exact bodies. Scans do not fetch comments. Hosted
994
+ HTTP clients opt in with `x-autobuild-ticket-comments: 1` so historical strict
995
+ decoders retain compatible responses. File comments remain embedded in the body;
996
+ Linear comment reads are unchanged.
@@ -14,6 +14,18 @@ and sessionless `ab init --validate` readiness check. Examples labelled as
14
14
  fragments are intended to be added to an existing file; the [complete
15
15
  example](#complete-example) is valid on its own.
16
16
 
17
+ ## `[store]`
18
+
19
+ | Key | Required | Values / purpose |
20
+ |---|---|---|
21
+ | `url` | When `[store]` exists | Absolute HTTPS service base URL; loopback HTTP is allowed for development. No credentials, query or fragment. Trailing slashes are normalized. |
22
+
23
+ Selection is explicit `--store` > nonblank `AB_STORE` > `[store] url` > the main
24
+ checkout's `.autobuild/`. A configured service without a user credential fails
25
+ with `ab connect` guidance. `AB_TOKEN` overrides the saved service/project
26
+ credential. See [connecting a project](hosted-service.md#connect-a-project).
27
+ Changing the store requires restarting dispatch.
28
+
17
29
  ## Hosted service and PostgreSQL BuildStore environment
18
30
 
19
31
  The optional `@defrex/autobuild-hosted-store-service` and its
@@ -59,14 +71,14 @@ separate signed-user token described by the [operator API](operator-api.md).
59
71
  | `AB_HOST` | Optional, ordinary Bun host | Bind hostname; defaults to `0.0.0.0`. Vercel supplies its own listener. |
60
72
  | `PORT` | Optional, ordinary Bun host | Integer 1–65535; defaults to `3000`. Vercel supplies its own listener. |
61
73
  | `AB_STORE` | Remote client | Hosted service base URL used by the kernel, phase commands, and dashboard. |
62
- | `AB_TOKEN` | Authenticated remote client | Offline-minted bearer token; this is not the signing secret. |
74
+ | `AB_TOKEN` | Authenticated remote client | Explicit project bearer credential override for CI; otherwise use ab connect. This is not the signing secret. |
63
75
  | `BETTER_AUTH_SECRET` | Web dashboard | Independent server-only session secret with at least 32 high-entropy characters. |
64
76
  | `BETTER_AUTH_URL` | Web dashboard | Exact HTTP(S) origin, without credentials or a path; HTTPS is mandatory in production. |
65
77
  | `GITHUB_CLIENT_ID` | GitHub sign-in | OAuth application client id. |
66
78
  | `GITHUB_CLIENT_SECRET` | GitHub sign-in | Server-only OAuth application secret. |
67
79
  | `AB_WEB_AUTH_PROVIDERS` | Web dashboard | Comma-separated provider set; currently exactly `github`. |
68
80
  | `AB_WEB_ALLOWED_EMAILS` | Web dashboard | Nonempty comma-separated operator email allowlist; matching is case-insensitive. |
69
- | `AB_WEB_REPOSITORIES` | Web dashboard | Nonempty comma-separated repository allowlist exposed to signed-in operators. Entries are repository identities — normalized `https://` origins (ssh-like spellings such as `git@github.com:owner/repo.git` are accepted and normalized); they must match the Store's identity for the repository. |
81
+ | `AB_WEB_REPOSITORIES` | Hosted service | Deprecated once-per-database bootstrap CSV for the durable project registry. Use owner commands thereafter; see [connected projects](setup.md#connected-projects). |
70
82
 
71
83
  GitHub's callback is `{BETTER_AUTH_URL}/api/auth/callback/github`; the app needs
72
84
  the `user:email` scope (or GitHub App read-only email permission). Sessions are
@@ -190,6 +202,10 @@ dashboard notice row or on plain-mode stderr, and suppresses repeats while the
190
202
  same failure remains. Restoring `autobuild.toml` with valid configuration clears
191
203
  the rejection and ordinary live reload resumes without a dispatcher restart.
192
204
 
205
+ Dispatcher merge readiness, startup resume, and execution recovery use the same pinned
206
+ commands, verify, and finalize configuration as the build runner. Missing pins retain
207
+ the legacy live-configuration fallback.
208
+
193
209
  These fields hot-reload:
194
210
 
195
211
  | Field or table | Boundary where a new value applies |
@@ -198,8 +214,8 @@ These fields hot-reload:
198
214
  | `ticketScanIntervalSeconds` | Next tick; evaluates elapsed time against the new interval. |
199
215
  | `capacity` | Next dispatcher tick. |
200
216
  | `[pr]` | Next build creation. |
201
- | `[commands]` | Next setup, verify check, or finalize check selected for a build whose OWN branch carries it. A base-branch reload does not retarget an in-flight build. |
202
- | `[verify]` and `[finalize]` | Next engine step selection for a build whose OWN branch carries it. An approved plan's stored verify selection remains authoritative, and a base-branch reload does not retarget an in-flight build. |
217
+ | `[commands]` | Next setup, verify check, or finalize check selected for a build whose OWN branch carries it. Dispatcher readiness, resume, and recovery use the same pin. A base-branch reload does not retarget an in-flight build. |
218
+ | `[verify]` and `[finalize]` | Next engine step selection for a build whose OWN branch carries it. Dispatcher readiness, resume, and recovery use the same pin. An approved plan's stored verify selection remains authoritative, and a base-branch reload does not retarget an in-flight build. |
203
219
  | `[workspace]` provider and config (including `provisioning`) | Next launch of a build whose OWN branch carries it. A base-branch reload never re-provisions an in-flight build. |
204
220
  | `[roles]` | Next agent invocation for that role. Runtime, model, args, alternates, and the session budget are captured together and delivered live. |
205
221
  | `[policy]` | Next policy, convergence, or agent-session boundary, delivered live. A running session keeps its captured budget. |
@@ -212,6 +228,7 @@ remain pinned to their startup values and are reported explicitly:
212
228
 
213
229
  | Restart-required field | Reason |
214
230
  |---|---|
231
+ | `store` | Store selection and project authority are fixed when dispatch starts. |
215
232
  | `plugins` | Plugin modules and registration catalogs load before wiring. |
216
233
  | `forge` | Selects the constructed Forge adapter. |
217
234
  | `workspace.provider`, `workspace.config` | Select and configure the constructed workspace provider. |
@@ -357,7 +374,7 @@ manifest parse), and each `capabilities.requiredEnv` group must declare at
357
374
  least one of `dispatchMessage` / `validationMessage` — a group with neither is
358
375
  rejected at manifest parse, because the host would silently skip it at both
359
376
  enforcement sites. API 1.7 extends the workspace-provider factory context with
360
- the host-derived seams: `storeRef` and `storeToken` (guaranteed present under
377
+ the host-derived seams: `storeRef` and `delegateCredential` (guaranteed present under
361
378
  `capabilities.storeRequirements`), `runtimeReferences`, and the checkout-less
362
379
  `origin` and `remoteBranchHead` readers, all optional and ignored by factories
363
380
  that do not read them. A plugin reading the new seams should require `^1.7.0`.
@@ -631,7 +648,7 @@ no longer matches the identity the current system would deliver is refreshed
631
648
  by reinstalling the archive before reuse, so an upgraded dispatcher retrofits
632
649
  its persistent guests (see "Guest distribution version selection and delivery"
633
650
  in docs/setup.md for the release-and-upgrade delivery path and the hosted
634
- store's version-lockstep gate). Immediately
651
+ store's supported client window). Immediately
635
652
  before every build-child launch, the adapter verifies the
636
653
  provisioned absolute Bun executable and every referenced runtime again. A
637
654
  failure names the runtime, selecting role/alternate, stage, and provisioning
@@ -757,6 +774,28 @@ as written.
757
774
 
758
775
  ## `[verify]` and `[verify.<step>]`
759
776
 
777
+ Post-reconcile verification defaults to enabled: `[verify].afterReconcile = true`.
778
+ Set it to `false` to retain pre-reconcile verification and skip all steps after
779
+ reconciliation. An optional boolean `[verify.<step>].afterReconcile` overrides
780
+ the repository setting in either direction; overrides require a configured step.
781
+ For example, set `afterReconcile = false` in `[verify]` and
782
+ `afterReconcile = true` in `[verify.types]` to keep only that step re-running.
783
+
784
+ The kernel freezes the resolved ordered policy in
785
+ `verify.reconcile-policy-captured` at the next engine action after reconcile
786
+ completion. Live configuration changes apply at that boundary; a cycle already
787
+ started keeps its policy through recovery. Historical cycles already started
788
+ without a captured policy retain the rerun-all behavior. Initial verification
789
+ and verification after subsequent code review always use ordinary applicability.
790
+ Policy exclusion precedes plan selection and paths, including `always = true`.
791
+ Enabled steps still follow plan selection and path rules. Excluded steps record
792
+ `verify.completed` with outcome `skipped`, `skipCause = "post-reconcile-policy"`,
793
+ and a reason naming the setting. Dashboards and PR summaries call this
794
+ "skipped by post-reconcile policy"; it is never a pass. Forge-side required
795
+ checks and branch protections still gate merge. For excluded steps, merged code
796
+ is not re-verified, so breakage that only exists in the combination can land.
797
+
798
+
760
799
  `[verify].steps` defines the configured universe and execution order. It is
761
800
  optional and defaults to no verify phases. Every listed name needs one matching
762
801
  subtable, and every named subtable must be listed; a defined-but-unlisted step
@@ -776,6 +815,7 @@ There are two strict step variants:
776
815
  | `command` | — | required for check; nonempty key in `[commands]` | Deterministic shell verb to run. Forbidden for agent steps. |
777
816
  | `skill` | — | required for agent; nonempty string | Exact installed verifier skill. Forbidden for check steps. |
778
817
  | `paths` | omitted (unconditional) | optional nonempty array of positive repository-relative globs | Apply only when at least one changed path matches. Available to both variants. |
818
+ | `afterReconcile` | repository: `true`; step: inherited | boolean | Step override wins over `[verify].afterReconcile`; false skips this step after reconciliation only. |
779
819
  | `always` | omitted (`false`) | optional boolean | `true` makes the step mandatory in plan selection and bypasses path gating. Available to both variants. |
780
820
 
781
821
  <!-- config-fragment:verify -->
@@ -1132,6 +1172,7 @@ Optional. Every field receives its own default. All are positive integers except
1132
1172
  | `sessionBudgetSeconds` | `3600` | positive integer | Wall-clock bound for each build agent session unless its logical role overrides it. |
1133
1173
  | `stallRounds` | `3` | positive integer | Escalate when the same review finding survives this many rounds. |
1134
1174
  | `maxVerifyAttempts` | `3` | positive integer | Bound failure-driven verify → implement retry cycles. |
1175
+ | `maxDispatchAttempts` | `3` | positive integer | Bound consecutive dispatch failures before human escalation. |
1135
1176
  | `maxSetupAttempts` | `3` | positive integer | Bound consecutive workspace setup failures before human escalation. |
1136
1177
  | `maxInfrastructureAttempts` | `3` | positive integer | Bound consecutive provision/start/wait/cleanup failures before a setup-targeted policy escalation; a human `retry` answer re-arms this budget. |
1137
1178
  | `maxReconcileAttempts` | `3` | positive integer | Bound completed reconciles that leave the PR conflicted against an unchanged authoritative base. Moving-base races do not consume the bound. |
@@ -1139,6 +1180,30 @@ Optional. Every field receives its own default. All are positive integers except
1139
1180
  | `harvestThreshold` | `5` | positive integer | New unclaimed observation occurrences needed to start one harvest run. |
1140
1181
  | `harvestMaxDrift` | `3` | nonnegative integer | Other builds merged after the oldest unclaimed observation needed to start a run; `0` disables drift. |
1141
1182
 
1183
+ Dispatch failures share a `[policy].maxDispatchAttempts` budget (default 3) across
1184
+ create, workspace, spec, comment, and launch stages. Exhaustion raises one
1185
+ `dispatch-failure-limit` policy escalation naming the latest stage and error,
1186
+ and parks dispatch until answered. Both bare `ab answer <slug>` retries and
1187
+ text guidance grant a fresh budget; abort does not. Confirmed runner attachment
1188
+ clears the streak. Recovery-provider workspace failures belong exclusively to
1189
+ the infrastructure budget. Exhaustion is repaired from durable facts on restart
1190
+ before further dispatch work.
1191
+
1192
+ The dispatcher also applies a fixed, non-configurable limit of three consecutive
1193
+ executions that complete normally with exit code zero and no durable build
1194
+ progress. Execution start/end, runner attachment and workspace lifecycle
1195
+ bookkeeping do not count as progress. Pipeline, session, spec, publication/PR,
1196
+ observation and acknowledged operator facts reset the streak; infrastructure,
1197
+ budget and provider failures do not consume the allowance and retain their own
1198
+ retry limits. Before a fourth actionable launch, the dispatcher raises one
1199
+ setup-targeted `execution-no-progress` policy escalation naming the count and
1200
+ the next action it expected the runner to take. Waiting, paused and terminal
1201
+ builds are left parked, and pending operator commands remain executable.
1202
+ Answering that escalation with guidance or a bare `ab answer <slug>` retry grants
1203
+ three fresh executions. The guard is derived from durable facts, including on
1204
+ supervisor restart, and uses the ordinary watch, dashboard and operator API
1205
+ attention paths.
1206
+
1142
1207
  For each repeat conflict, Autobuild compares the base merged by the most recent
1143
1208
  completed reconcile with a fresh authoritative base snapshot. An advanced base
1144
1209
  proves the attempt lost a race and permits another reconcile regardless of the
@@ -1178,20 +1243,25 @@ invariants, not additional configuration fields.
1178
1243
 
1179
1244
  ## `[tickets]`
1180
1245
 
1246
+ To move an existing project between sources, follow the
1247
+ [ticket migration procedure](ticket-migration.md).
1248
+
1181
1249
  Required in practice: `readyState` deliberately has no default, so omitting the
1182
1250
  table fails at `tickets.readyState` rather than allowing tickets from every
1183
- state. Within a present table, `source` and `readyState` are required.
1251
+ state. Within a present table, `source` is required; hosted tickets default `readyState` to `ready`. Other sources require it.
1184
1252
 
1185
1253
  | Field | Default | Constraints | Purpose |
1186
1254
  |---|---:|---|---|
1187
1255
  | `source` | — | required nonblank builtin or plugin registration name | Select the ticket adapter after configured plugins load. |
1188
1256
  | `readyLabels` | source-aware | optional array of nonempty strings; `[]` allowed | Require every listed label in addition to the state gate. |
1189
- | `readyState` | — | required nonblank string | The one workflow state eligible for dispatch. |
1257
+ | `url` | selected build service | HTTPS service URL; HTTP allowed on loopback; no credentials, query or fragment; hosted only | Ticket service independent of [store] url and AB_STORE. Required with local builds. |
1258
+ | `readyState` | hosted: `"ready"` | nonblank string; required for other sources | The one workflow state eligible for dispatch. |
1190
1259
  | `readyAssignee` | — | optional; `"me"` or an email address; Linear only (rejected for file, hosted, and plugin sources) | Require the ticket to be assigned to one Linear user, in addition to the state and label gates. |
1191
1260
  | `teamKey` | — | required for Linear and hosted; forbidden for file; optional for plugins | Backend team key such as `"ENG"`, or an existing plugin config field. |
1192
- | `claimedState` | `"In Progress"` for direct Linear; hosted backend default otherwise | optional nonempty string; forbidden for file; allowed for plugins | Workflow state entered when a ticket is claimed. |
1261
+ | `claimedState` | `"In Progress"` for direct Linear; `"doing"` for hosted | optional nonempty string; forbidden for file; allowed for plugins | Workflow state entered when a ticket is claimed. |
1262
+ | `doneState` | hosted: `"done"` | optional nonblank string; hosted | Project completion state and merged-ticket transition. |
1193
1263
  | `createState` | provider default | optional nonempty string | Default state for newly created tickets when `ab ticket create` omits `--state`. |
1194
- | `triageState` | Linear: `"Backlog"`; file/plugin: `"Triage"` | optional nonempty string | State used for spec-gate bounces, aborts, and closed-unmerged PRs. |
1264
+ | `triageState` | Linear: `"Backlog"`; hosted: `"triage"`; file/plugin: `"Triage"` | optional nonempty string | State used for spec-gate bounces, aborts, and closed-unmerged PRs. |
1195
1265
  | `proposalState` | the resolved `triageState` | optional nonempty string | State harvest files its synthesized proposals into. Setting it to `readyState` waives the human grooming gate, not dependency eligibility. |
1196
1266
  | `dir` | file: selected local state root's `tickets/`; plugin: omitted | optional nonempty path; forbidden for Linear; allowed for plugins | Root containing file-source state directories, or an existing plugin config field. Relative file paths resolve from the repository. |
1197
1267
 
@@ -1210,7 +1280,7 @@ When `readyLabels` is absent, direct Linear uses `["autobuild"]`; hosted, file,
1210
1280
  and plugin sources use `[]`, meaning no host-imposed label gate. An explicit value always
1211
1281
  wins. A nonempty list is
1212
1282
  conjunctive: every configured label must be present. `readyState` remains
1213
- mandatory regardless of labels. Linear compares state and label names exactly
1283
+ mandatory regardless of labels (hosted defaults to `ready`). Linear compares state and label names exactly
1214
1284
  and case-sensitively. The file source matches state names case-insensitively against the directories
1215
1285
  under the tracker root: the four lifecycle directories (`triage/`, `ready/`,
1216
1286
  `doing/`, `done/`, always present) plus any directory the repository adds, such
@@ -1232,6 +1302,15 @@ Source-specific validation is strict:
1232
1302
  - Plugin sources receive the existing fields in this table unchanged and own
1233
1303
  any additional semantic validation. No untyped plugin-options table exists.
1234
1304
 
1305
+ Hosted states are durable service data scoped to a project. `ab ticket states`
1306
+ lists configured lifecycle roles first, then custom states alphabetically, with
1307
+ counts and optional purposes. `ab ticket state create <name> [--about <text>]`
1308
+ adds a state using the file source's lowercase name grammar. Hosted duplicates
1309
+ in any letter case are refused; file creation remains idempotent. Configured
1310
+ names always exist, preserve their spelling, and remain custom states after
1311
+ removal from config. Any existing state supports ticket create, move, and list;
1312
+ unknown names report the available states. Purposes must be one nonblank line.
1313
+
1235
1314
  For Linear, omitting `createState` lets the team's default state apply. For the
1236
1315
  file source it defaults to `Triage`. A caller may override that default for one
1237
1316
  create with `ab ticket create --state <state>`; the value is passed unchanged to
@@ -1303,17 +1382,18 @@ createState = "Triage"
1303
1382
  triageState = "Triage"
1304
1383
  ```
1305
1384
 
1306
- Set `AB_STORE` to the service's HTTP(S) URL and `AB_TOKEN` to a deployment
1307
- operator token (`mint operator` without `--user`). The same source-agnostic wiring powers dispatch, harvest, ticket-aware
1385
+ Set `AB_STORE` to the service's HTTP(S) URL and `AB_TOKEN` to a stored credential
1386
+ for that registered project (`credentials issue REPOSITORY --user IDENTITY`). The same source-agnostic wiring powers dispatch, harvest, ticket-aware
1308
1387
  skills, and all seven command families: `ab ticket create`, `update`, `block`,
1309
1388
  `unblock`, `list`, `show`, and `move`. See the
1310
- [hosted ticket protocol](remote-ticket-protocol.md). In hosted Linear mode,
1311
- `LINEAR_API_KEY` is configured only on the service; direct Linear remains
1312
- supported.
1389
+ [hosted ticket protocol](remote-ticket-protocol.md). Tickets are partitioned by
1390
+ the credential's registered project; lifecycle comes from its latest effective
1391
+ configuration. The service has no Linear backend and refuses ticket operations
1392
+ for projects using external sources. Direct Linear remains dispatcher-side.
1313
1393
 
1314
1394
  When filing dependent tickets into the ready state, follow the
1315
- [safe blocker-filing workflow](ticket-dependencies.md). Direct and hosted Linear
1316
- create the issue before recording its blockers, so `create --blocked-by` is not
1395
+ [safe blocker-filing workflow](ticket-dependencies.md). Direct Linear
1396
+ creates the issue before recording its blockers, so `create --blocked-by` is not
1317
1397
  an atomic publication guarantee. Stage outside ready, verify the relationships,
1318
1398
  then move to the intended ready state last.
1319
1399
 
@@ -1494,6 +1574,7 @@ sessionBudgetSeconds = 7200
1494
1574
  sessionBudgetSeconds = 3600
1495
1575
  stallRounds = 3
1496
1576
  maxVerifyAttempts = 3
1577
+ maxDispatchAttempts = 3
1497
1578
  maxSetupAttempts = 3
1498
1579
  maxInfrastructureAttempts = 3
1499
1580
  maxReconcileAttempts = 3
@@ -1653,9 +1734,9 @@ Secrets and store selection accompany the file through environment variables:
1653
1734
 
1654
1735
  | Variable | Used for | Notes |
1655
1736
  |---|---|---|
1656
- | `LINEAR_API_KEY` | Direct Linear ticket source | Required and nonempty when `tickets.source = "linear"`; in hosted Linear mode it is service-only. |
1657
- | `AB_STORE` | BuildStore and hosted ticket selection | A local path or HTTP(S) remote-store URL. `tickets.source = "hosted"` requires a nonblank HTTP(S) URL. A command's explicit `--store` wins for BuildStore selection, then nonblank `AB_STORE`, then the main checkout's `.autobuild/`. Relative local paths resolve from the main checkout. |
1658
- | `AB_TOKEN` | Protected remote BuildStore and hosted tickets | Bearer credential forwarded to the service. Hosted tickets require deployment operator scope (`mint operator` without `--user`). Empty means no token; nonempty token bytes are treated as opaque. |
1737
+ | `LINEAR_API_KEY` | Direct Linear ticket source | Required and nonempty when `tickets.source = "linear"`; never used by the hosted service. |
1738
+ | `AB_STORE` | BuildStore and hosted ticket selection | A local path or HTTP(S) remote-store URL. Hosted tickets use this only when `[tickets] url` is absent; local builds with hosted tickets require `[tickets] url`. A command's explicit `--store` wins for BuildStore selection, then nonblank `AB_STORE`, then `[store] url`, then the main checkout's `.autobuild/`. Relative local paths resolve from the main checkout. |
1739
+ | `AB_TOKEN` | Protected remote BuildStore and hosted tickets | Bearer credential forwarded to the service. Hosted tickets require a stored credential for a registered project. Owner admin is an explicit break-glass exception. Empty means no token; nonempty token bytes are treated as opaque. |
1659
1740
 
1660
1741
  A local store selection relocates the state database, blobs, worktrees, and the
1661
1742
  default file-ticket directory together. With a remote store, Git worktrees and
@@ -1795,3 +1876,43 @@ progress is not shown. Harvest does not consume build capacity.
1795
1876
  - **Remote BuildStore:** confirm the effective `AB_STORE` URL and `AB_TOKEN`.
1796
1877
  A protected store reports 401 for a missing, invalid, or expired token and
1797
1878
  403 when the token is valid but scoped to another build/session.
1879
+
1880
+ ### Hosted project selection
1881
+
1882
+ The durable PostgreSQL project registry controls dashboard, MCP and operator
1883
+ API access. Every allowlisted user sees every active entry. Administer it with
1884
+ `ab-hosted-store projects list|add|remove` using deployment database credentials;
1885
+ mutations require `--user IDENTITY` for declared attribution. See
1886
+ [connected projects](setup.md#connected-projects) for migration and retention.
1887
+ `AB_DISPATCHER_REPOSITORIES` optionally selects an ordered subset of current
1888
+ registrations. When unset, each authorized tick queries all active entries.
1889
+ Removed credential override keys are inert in dynamic mode; explicit-list
1890
+ configuration still rejects unknown override keys.
1891
+
1892
+ ## Hosted service client compatibility
1893
+
1894
+ A service accepts stable clients from its own `major.minor` release line and
1895
+ the two preceding minor lines in the same major. A major starts a new family:
1896
+ X.0 accepts only X.0, X.1 accepts X.0/X.1, and X.2 onward accepts three lines.
1897
+ Patch versions are interchangeable in both directions and must never change
1898
+ the protocol. Upgrade the service first, then projects independently within
1899
+ its window. Supported release lines retain their wire handlers and known
1900
+ protocol identities; an unknown protocol is refused independently of version.
1901
+ Malformed, missing, and prerelease identities are refused with HTTP 409.
1902
+ A newer client is told to upgrade the service; a client below the window is
1903
+ told to upgrade the project's Autobuild. Diagnostics name the service version
1904
+ and supported range.
1905
+
1906
+ Unauthenticated `GET /health` publishes `ok`, `autobuildVersion`,
1907
+ `protocolVersion`, and `supportedClientRange: { minVersion, maxVersionExclusive }`.
1908
+ The range is inclusive at the minimum and exclusive at the maximum; a 0.11.x
1909
+ service publishes `>=0.9.0 <0.12.0`. Health needs no credentials, version
1910
+ headers, or available backend.
1911
+
1912
+ Remote workspace factories must use `delegateCredential(storeRef, undefined,
1913
+ target)` to obtain per-resource grants before provisioning or launching guests.
1914
+ The callback keeps parent authority in host composition. `storeToken` is retired
1915
+ and is never supplied; factory environments also exclude `AB_TOKEN`. Launch
1916
+ credentials travel through the separate transient execution-auth argument, never
1917
+ through serialized execution identity. Existing plugins that forwarded
1918
+ `storeToken` must migrate to this callback and fail closed if it is unavailable.
@@ -14,6 +14,16 @@ it once a minute by default), so builds no longer depend on a maintainer's
14
14
  machine. A deployment that does not install the package serves the hosted
15
15
  store, ticket, operator, and web endpoints and never attempts a dispatch tick.
16
16
 
17
+ The [minimal hosted service deployment](hosted-service.md) defaults to store-only
18
+ mode and supplies no cron. The source-checkout deployment below retains the
19
+ opt-in dispatcher packing and shipping pipeline.
20
+
21
+ For a dispatcher running as a local user or service manager, first
22
+ [connect the project](hosted-service.md#connect-a-project). Origin-mode dispatch
23
+ can read the committed `[store] url` and the same user credential by origin.
24
+ A deployment running under another identity needs its own project credential;
25
+ `AB_TOKEN` remains the explicit CI override.
26
+
17
27
  ## Enabling the schedule
18
28
 
19
29
  1. Deploy the hosted service with the dispatcher package installed and the
@@ -255,6 +265,11 @@ presents (`sandboxAuth=oidc-header`, `oidc-env`, `token`, or `none`); `none`
255
265
  means the project has OIDC federation disabled (Settings → Security). No
256
266
  long-lived `VERCEL_TOKEN` belongs on the service. Guests never receive a Vercel
257
267
  token or forge credential; they authenticate to the Store with the short-TTL
258
- deployment operator token the dispatcher mints for the tick
268
+ stored project credential the dispatcher issues for each repository tick
259
269
  (`AB_DISPATCHER_TOKEN_TTL_SECONDS`, default 7 days — keep it longer than your
260
- largest `timeoutSeconds` guest lifetime).
270
+ largest `timeoutSeconds` guest lifetime). Its immutable dispatcher purpose
271
+ permits raw Store and hosted tickets only, never operator controls. The service
272
+ checks registration and revocation on each request. Tick return does not revoke
273
+ it; spawned builds may outlive the tick. Owner-issued project credentials and
274
+ the owner-only break-glass admin are documented in the
275
+ [remote Store protocol](remote-store-protocol.md#7-authentication-and-project-authority).