@defrex/autobuild 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/SPEC.md +2269 -0
  4. package/bin/ab-build-runner.ts +47 -0
  5. package/bin/ab-dev.ts +27 -0
  6. package/bin/ab-dispatch-kernel.ts +92 -0
  7. package/bin/ab-harvest-runner.ts +29 -0
  8. package/bin/ab-init-probe.ts +19 -0
  9. package/bin/ab-process-group-reaper.ts +32 -0
  10. package/bin/ab.ts +6 -0
  11. package/bin/agent/ab +3 -0
  12. package/docs/answering-blocked-builds.md +89 -0
  13. package/docs/architecture.md +726 -0
  14. package/docs/assets/headline-wide.png +0 -0
  15. package/docs/configuration.md +1623 -0
  16. package/docs/hosted-dispatcher-cutover-validation.md +178 -0
  17. package/docs/hosted-dispatcher.md +204 -0
  18. package/docs/mcp.md +119 -0
  19. package/docs/operator-api.md +186 -0
  20. package/docs/remote-store-protocol.md +972 -0
  21. package/docs/remote-ticket-protocol.md +79 -0
  22. package/docs/setup.md +289 -0
  23. package/docs/spec-standard.md +63 -0
  24. package/docs/ticket-dependencies.md +59 -0
  25. package/docs/vercel-sandbox-rollout-probe.md +7 -0
  26. package/package.json +135 -0
  27. package/packages/core/src/cli/args.ts +74 -0
  28. package/packages/core/src/cli/artifact.ts +182 -0
  29. package/packages/core/src/cli/binary.ts +198 -0
  30. package/packages/core/src/cli/build-control.ts +789 -0
  31. package/packages/core/src/cli/build-progress.ts +55 -0
  32. package/packages/core/src/cli/bulk-control.ts +385 -0
  33. package/packages/core/src/cli/context.ts +470 -0
  34. package/packages/core/src/cli/dashboard/actions.ts +59 -0
  35. package/packages/core/src/cli/dashboard/cells.ts +92 -0
  36. package/packages/core/src/cli/dashboard/composer.ts +230 -0
  37. package/packages/core/src/cli/dashboard/detail.ts +100 -0
  38. package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
  39. package/packages/core/src/cli/dashboard/live.ts +152 -0
  40. package/packages/core/src/cli/dashboard/model.ts +1173 -0
  41. package/packages/core/src/cli/dashboard/poll.ts +196 -0
  42. package/packages/core/src/cli/dashboard/render.ts +1568 -0
  43. package/packages/core/src/cli/dashboard/selection.ts +66 -0
  44. package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
  45. package/packages/core/src/cli/dashboard/session-view.ts +506 -0
  46. package/packages/core/src/cli/dashboard/transcript.ts +108 -0
  47. package/packages/core/src/cli/dev.ts +46 -0
  48. package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
  49. package/packages/core/src/cli/dispatch-process.ts +259 -0
  50. package/packages/core/src/cli/dispatch.ts +3692 -0
  51. package/packages/core/src/cli/dotenv.ts +48 -0
  52. package/packages/core/src/cli/env.ts +206 -0
  53. package/packages/core/src/cli/harvest.ts +698 -0
  54. package/packages/core/src/cli/help.ts +713 -0
  55. package/packages/core/src/cli/init-agent.ts +106 -0
  56. package/packages/core/src/cli/init-validation.ts +583 -0
  57. package/packages/core/src/cli/init.ts +756 -0
  58. package/packages/core/src/cli/installation.ts +333 -0
  59. package/packages/core/src/cli/keyboard.ts +261 -0
  60. package/packages/core/src/cli/main.ts +1399 -0
  61. package/packages/core/src/cli/mcp.ts +279 -0
  62. package/packages/core/src/cli/models.ts +65 -0
  63. package/packages/core/src/cli/observe.ts +55 -0
  64. package/packages/core/src/cli/plugin.ts +227 -0
  65. package/packages/core/src/cli/pr-attachments.ts +138 -0
  66. package/packages/core/src/cli/pr-summary.ts +124 -0
  67. package/packages/core/src/cli/publication-settlement.ts +188 -0
  68. package/packages/core/src/cli/repo-state.ts +213 -0
  69. package/packages/core/src/cli/repository-status.ts +80 -0
  70. package/packages/core/src/cli/self-update.ts +443 -0
  71. package/packages/core/src/cli/status.ts +895 -0
  72. package/packages/core/src/cli/store-opening.contract.ts +289 -0
  73. package/packages/core/src/cli/store-opening.ts +167 -0
  74. package/packages/core/src/cli/store-ref.ts +33 -0
  75. package/packages/core/src/cli/terminal-restore.ts +174 -0
  76. package/packages/core/src/cli/terminal.ts +361 -0
  77. package/packages/core/src/cli/terminals.ts +995 -0
  78. package/packages/core/src/cli/testkit.ts +194 -0
  79. package/packages/core/src/cli/ticket.ts +484 -0
  80. package/packages/core/src/cli/upgrade-agent.ts +180 -0
  81. package/packages/core/src/cli/upgrade-commit.ts +468 -0
  82. package/packages/core/src/cli/upgrade-notice.ts +91 -0
  83. package/packages/core/src/cli/upgrade-progress.ts +110 -0
  84. package/packages/core/src/cli/upgrade.ts +888 -0
  85. package/packages/core/src/cli/wait.ts +587 -0
  86. package/packages/core/src/cli/watch.ts +932 -0
  87. package/packages/core/src/config/live.ts +255 -0
  88. package/packages/core/src/config/load.ts +98 -0
  89. package/packages/core/src/config/roles.ts +438 -0
  90. package/packages/core/src/config/schema.ts +774 -0
  91. package/packages/core/src/distribution.ts +24 -0
  92. package/packages/core/src/events/catalog.ts +201 -0
  93. package/packages/core/src/events/envelope.ts +59 -0
  94. package/packages/core/src/events/payloads.ts +618 -0
  95. package/packages/core/src/events/repository.ts +559 -0
  96. package/packages/core/src/events/sessions.ts +180 -0
  97. package/packages/core/src/harvest/schema.ts +147 -0
  98. package/packages/core/src/ids.ts +45 -0
  99. package/packages/core/src/integration/harness.ts +681 -0
  100. package/packages/core/src/kernel/auto-merge.ts +259 -0
  101. package/packages/core/src/kernel/converge.ts +117 -0
  102. package/packages/core/src/kernel/dispatch-settings.ts +60 -0
  103. package/packages/core/src/kernel/dispatch-status.ts +141 -0
  104. package/packages/core/src/kernel/engine.ts +1047 -0
  105. package/packages/core/src/kernel/harvest.ts +823 -0
  106. package/packages/core/src/kernel/origin.ts +69 -0
  107. package/packages/core/src/kernel/phases.ts +196 -0
  108. package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
  109. package/packages/core/src/kernel/pr-attachments.ts +82 -0
  110. package/packages/core/src/kernel/reducer.ts +752 -0
  111. package/packages/core/src/kernel/stall.ts +155 -0
  112. package/packages/core/src/kernel/verify-applicability.ts +52 -0
  113. package/packages/core/src/markdown.ts +67 -0
  114. package/packages/core/src/ontology.ts +288 -0
  115. package/packages/core/src/open-map.ts +203 -0
  116. package/packages/core/src/operator/annotations.ts +187 -0
  117. package/packages/core/src/operator/client.ts +311 -0
  118. package/packages/core/src/operator/control.ts +117 -0
  119. package/packages/core/src/operator/index.ts +62 -0
  120. package/packages/core/src/operator/presentation.ts +22 -0
  121. package/packages/core/src/operator/protocol.ts +98 -0
  122. package/packages/core/src/operator/query.ts +201 -0
  123. package/packages/core/src/operator/registry.ts +1043 -0
  124. package/packages/core/src/operator/requests.ts +138 -0
  125. package/packages/core/src/operator/sandbox.ts +458 -0
  126. package/packages/core/src/operator/server.ts +616 -0
  127. package/packages/core/src/operator/sessions.ts +223 -0
  128. package/packages/core/src/operator/tickets.ts +177 -0
  129. package/packages/core/src/plugin-sdk/index.ts +174 -0
  130. package/packages/core/src/plugins/contract-entry.ts +74 -0
  131. package/packages/core/src/plugins/contract-env.ts +3 -0
  132. package/packages/core/src/plugins/load.ts +259 -0
  133. package/packages/core/src/plugins/manifest.ts +236 -0
  134. package/packages/core/src/plugins/registry.ts +274 -0
  135. package/packages/core/src/plugins/runtimes.ts +75 -0
  136. package/packages/core/src/ports/forge/contract.ts +258 -0
  137. package/packages/core/src/ports/forge/create.ts +74 -0
  138. package/packages/core/src/ports/forge/fake.ts +488 -0
  139. package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
  140. package/packages/core/src/ports/forge/github-transport.ts +352 -0
  141. package/packages/core/src/ports/forge/github.ts +1162 -0
  142. package/packages/core/src/ports/forge/local-git.ts +568 -0
  143. package/packages/core/src/ports/runner/claude.ts +873 -0
  144. package/packages/core/src/ports/runner/cli-stream.ts +90 -0
  145. package/packages/core/src/ports/runner/codex.ts +873 -0
  146. package/packages/core/src/ports/runner/contract.ts +492 -0
  147. package/packages/core/src/ports/runner/fake.ts +213 -0
  148. package/packages/core/src/ports/runner/json-record.ts +2 -0
  149. package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
  150. package/packages/core/src/ports/runner/one-shot.ts +29 -0
  151. package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
  152. package/packages/core/src/ports/runner/pi-bridge.js +6 -0
  153. package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
  154. package/packages/core/src/ports/runner/pi.ts +617 -0
  155. package/packages/core/src/ports/runner/production.ts +74 -0
  156. package/packages/core/src/ports/runner/provider-error.ts +121 -0
  157. package/packages/core/src/ports/runner/routing.ts +234 -0
  158. package/packages/core/src/ports/runner/runtime.ts +214 -0
  159. package/packages/core/src/ports/runner/session-env.ts +34 -0
  160. package/packages/core/src/ports/runner/stream-parts.ts +103 -0
  161. package/packages/core/src/ports/tickets/contract.ts +570 -0
  162. package/packages/core/src/ports/tickets/create.ts +135 -0
  163. package/packages/core/src/ports/tickets/fake.ts +215 -0
  164. package/packages/core/src/ports/tickets/file.ts +559 -0
  165. package/packages/core/src/ports/tickets/linear.ts +811 -0
  166. package/packages/core/src/ports/tickets/operations.ts +161 -0
  167. package/packages/core/src/ports/tickets/remote-index.ts +13 -0
  168. package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
  169. package/packages/core/src/ports/tickets/remote-server.ts +185 -0
  170. package/packages/core/src/ports/tickets/remote.ts +130 -0
  171. package/packages/core/src/ports/tickets/update.ts +51 -0
  172. package/packages/core/src/ports/types.ts +528 -0
  173. package/packages/core/src/ports/workspace/build-execution.ts +123 -0
  174. package/packages/core/src/ports/workspace/contract.ts +136 -0
  175. package/packages/core/src/ports/workspace/create.ts +132 -0
  176. package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
  177. package/packages/core/src/ports/workspace/fake.ts +432 -0
  178. package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
  179. package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
  180. package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
  181. package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
  182. package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
  183. package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
  184. package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
  185. package/packages/core/src/processes/build-child-exit.ts +78 -0
  186. package/packages/core/src/processes/build-child.ts +119 -0
  187. package/packages/core/src/processes/build-execution-state.ts +95 -0
  188. package/packages/core/src/processes/build-parent-watch.ts +35 -0
  189. package/packages/core/src/processes/build-runner.ts +2614 -0
  190. package/packages/core/src/processes/dispatcher.ts +2825 -0
  191. package/packages/core/src/processes/execution-settlement.ts +171 -0
  192. package/packages/core/src/processes/harvest-child.ts +115 -0
  193. package/packages/core/src/processes/harvest-execution-state.ts +121 -0
  194. package/packages/core/src/processes/harvest-runner.ts +1656 -0
  195. package/packages/core/src/processes/harvest.ts +595 -0
  196. package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
  197. package/packages/core/src/processes/phase-failure-budget.ts +52 -0
  198. package/packages/core/src/processes/process-group-reaper.ts +35 -0
  199. package/packages/core/src/processes/process-group.ts +65 -0
  200. package/packages/core/src/processes/publication-loss.ts +62 -0
  201. package/packages/core/src/processes/publication-state.ts +93 -0
  202. package/packages/core/src/processes/sandbox-state.ts +101 -0
  203. package/packages/core/src/registry.ts +24 -0
  204. package/packages/core/src/sessions/reducer.ts +185 -0
  205. package/packages/core/src/skills.ts +7 -0
  206. package/packages/core/src/spec-standard.ts +47 -0
  207. package/packages/core/src/store/adapter.ts +80 -0
  208. package/packages/core/src/store/build-scope.ts +272 -0
  209. package/packages/core/src/store/contract.ts +2581 -0
  210. package/packages/core/src/store/local/blobs.ts +38 -0
  211. package/packages/core/src/store/local/schema.ts +180 -0
  212. package/packages/core/src/store/local/store.ts +1713 -0
  213. package/packages/core/src/store/memory.ts +1138 -0
  214. package/packages/core/src/store/phase-session.ts +425 -0
  215. package/packages/core/src/store/remote/client.ts +780 -0
  216. package/packages/core/src/store/remote/index.ts +12 -0
  217. package/packages/core/src/store/remote/protocol.ts +348 -0
  218. package/packages/core/src/store/remote/server.ts +923 -0
  219. package/packages/core/src/store/remote/token.ts +137 -0
  220. package/packages/core/src/store/remote/version.ts +10 -0
  221. package/packages/core/src/store/retention.ts +91 -0
  222. package/packages/core/src/store/session-handle.ts +265 -0
  223. package/packages/core/src/store/streams/assemble.ts +79 -0
  224. package/packages/core/src/store/streams/lock.ts +42 -0
  225. package/packages/core/src/store/streams/session-writer.ts +306 -0
  226. package/packages/core/src/store/streams/types.ts +171 -0
  227. package/packages/core/src/store/streams/wait.ts +127 -0
  228. package/packages/core/src/store/subscribe.ts +105 -0
  229. package/packages/core/src/store/types.ts +466 -0
  230. package/packages/core/src/testing/fixed.ts +44 -0
  231. package/packages/core/src/testing/store-failures.ts +32 -0
  232. package/packages/core/src/zod-issues.ts +91 -0
  233. package/skills/code-review/SKILL.md +100 -0
  234. package/skills/finalize/SKILL.md +44 -0
  235. package/skills/guide/SKILL.md +1781 -0
  236. package/skills/guide/references/operator-api.md +164 -0
  237. package/skills/guide/references/plugin-authoring.md +354 -0
  238. package/skills/guide/references/remote-store-protocol.md +972 -0
  239. package/skills/guide/references/remote-ticket-protocol.md +79 -0
  240. package/skills/guide/references/setup.md +289 -0
  241. package/skills/guide/references/spec-standard.md +63 -0
  242. package/skills/guide/references/ticket-dependencies.md +59 -0
  243. package/skills/harvest/SKILL.md +66 -0
  244. package/skills/harvest-review/SKILL.md +63 -0
  245. package/skills/implement/SKILL.md +82 -0
  246. package/skills/plan/SKILL.md +123 -0
  247. package/skills/plan-review/SKILL.md +121 -0
  248. package/skills/reconcile/SKILL.md +65 -0
  249. package/skills/spec/SKILL.md +132 -0
  250. package/skills/tickets/SKILL.md +139 -0
  251. package/templates/autobuild.toml +36 -0
@@ -0,0 +1,726 @@
1
+ # Architecture
2
+
3
+ The contributor-facing map: how the codebase is organized and where the
4
+ seams are. `SPEC.md` is the source of truth for the design and terminology;
5
+ this document maps it to the code. For the user journey — install, configure,
6
+ operate — see [`README.md`](../README.md); for the complete declarative surface,
7
+ see [`docs/configuration.md`](configuration.md). The hosted sessionless control
8
+ surface is specified in the [operator API reference](operator-api.md). Behavioral detail beyond what
9
+ a map needs lives with the code and its tests; when this document and the code
10
+ disagree, the code is authoritative.
11
+
12
+ ## Constitution
13
+
14
+ 1. **Judgment in skills, determinism in code.** Agents never decide phase
15
+ transitions, signal identity, or state. Narrow non-phase judgment such as
16
+ slug naming and skill-conflict proposals remains behind deterministic
17
+ validation and fail-safe fallback.
18
+ 2. **Resumability is not a feature.** Re-running `ab dispatch` attempts every
19
+ actionable current build; each phase resumes as a function of durable state.
20
+ Open escalations remain parked until a human answer is recorded, while
21
+ pending operator commands remain actionable.
22
+ 3. **Ingesters propose, humans dispatch.** Nothing auto-generated passes
23
+ Triage without a human grooming it to Ready, unless the repository waives
24
+ that gate explicitly with `[tickets].proposalState`.
25
+ 4. **Every step leaves a paper trail** — queryable, not carried in the repo.
26
+
27
+ ## Pipeline
28
+
29
+ ```
30
+ spec → plan ⇄ plan-review → implement ⇄ code-review → verify:* → finalize
31
+ └────────────────── epilogue: (pr.conflicted → reconcile → verify:*)* → merged
32
+ ```
33
+
34
+ The grammar is fixed; `verify:*` and `finalize:*` are the only extension
35
+ points, declared per-repo in `autobuild.toml`. Observation harvest is
36
+ adjacent, never a build phase:
37
+
38
+ ```
39
+ K unclaimed observation.recorded events
40
+ → scan → synthesize ⇄ review → file approved proposals in proposalState
41
+ (Triage by default)
42
+ ```
43
+
44
+ ## Workspace ownership
45
+
46
+ The repository is a Bun 1.4 workspace. The private root package, `autobuild`, is
47
+ still the sole Git-installed compatibility distribution: it owns the public and
48
+ private `bin/` entries, the `@defrex/autobuild/plugin-sdk` export, and the shipped
49
+ `skills/`, `templates/`, and documentation. Its implementation is included as
50
+ unbundled TypeScript from the private `@defrex/autobuild-core` workspace at
51
+ `packages/core`; the root repeats core's runtime dependencies so a packed or
52
+ Git installation never requires a separately published core package.
53
+
54
+ `packages/core` owns the CLI, kernel, adapters, shared types, dashboard
55
+ projection, plugin SDK, and reusable contract suites. The optional
56
+ `packages/hosted-store-service` workspace composes the public remote-server
57
+ surface with `packages/postgres-store`; neither package enters an ordinary CLI
58
+ installation's runtime dependency closure. `tools/` is repository-only
59
+ maintainer tooling. All workspace manifests share one version because the
60
+ remote-store protocol requires matching client and server versions.
61
+
62
+ ## Layout
63
+
64
+ | Path | Contents | SPEC |
65
+ |---|---|---|
66
+ | `package.json` | Private `autobuild` compatibility distribution and Bun workspace orchestrator; owns bins and shipped assets | — |
67
+ | `packages/core/package.json` | Private `@defrex/autobuild-core` implementation workspace | — |
68
+ | `packages/core/src/ontology.ts` | The shared nouns — findings, verdicts, phases, refs, the canonical verify outcome | §4 |
69
+ | `packages/core/src/events/` | Separate build, repository, and operator-session envelopes/catalogs, frozen payload schemas, actor validation, the `via` attribution marker | §15 |
70
+ | `packages/core/src/harvest/` | Structured occurrence, scan packet, proposal, and ledger schemas | §12 |
71
+ | `packages/core/src/sessions/` | The operator-session reducer: status, open turn, pending approval, wake settings and cursors, and the turn list, reduced purely from the session event log | §7.1.1 |
72
+ | `packages/core/src/store/` | BuildStore contract spanning builds, the repository journal, and operator sessions; interface-enforced build, operator-session, and local ambient-session scope wrappers; memory, SQLite/blob, and remote HTTP adapters | §7 |
73
+ | `packages/core/src/store/streams/` | The stream primitive's shared core (§7.6): record/chunk types and the `ai-ui-message-stream/v1` constants, SDK-backed close-time `UIMessage[]` assembly, and the uniform bounded-wait read loop | §7.6 |
74
+ | `packages/hosted-store-service/` | Environment-only hosted Fetch handler, lazy PostgreSQL/blob composition, offline token binary, tests, and deployment guide | §7.2, §18 |
75
+ | `server.ts`, `vercel.json` | One host-neutral Bun listener used locally and by Vercel's Bun preset | §7.2 |
76
+ | `packages/core/src/kernel/` | Phase table, build reducer, engine; pure harvest, dispatcher-settings, dispatcher-status, and PR-attachment selectors; converge, stall detection, verify gating | §5, §7.5, §10, §12, §14, §15.4–15.5 |
77
+ | `packages/core/src/ports/` | TicketSource / Workspace / Forge / AgentRunner / Telemetry interfaces, adapters, and fakes; registry-aware builtin/plugin construction; eager primary/alternate runtime routing and provider-failure classification under `ports/runner/` | §3.2, §9, §13 |
78
+ | `packages/core/src/plugins/` | Strict versioned plugin manifests, dual-root repository/package Bun loading, owner-aware adapter registration, contract/credential metadata, and runtime-factory materialization | §3.2.1, §9 |
79
+ | `packages/core/src/plugin-sdk/` | The sole supported `@defrex/autobuild/plugin-sdk` barrel: port/manifest types, contract suites, and reference fakes | §3.2.1 |
80
+ | `packages/core/src/processes/` | build-runner and standalone child composition, durable execution config/diagnostics, dispatcher (+ janitor duty and harvest trigger), harvest deterministic core + runner | §3.3, §12, §15.7 |
81
+ | `packages/core/src/cli/` and `bin/` | The `ab` CLI — the only agent↔store channel — plus the shared presentation-only durable-progress projection, init/upgrade, the Store-only dispatch frontend, its private supervised kernel entry, and `bin/ab-build-runner.ts` (one child per build) | §8, §14, §16.3 |
82
+ | `packages/core/src/cli/dashboard/` | `ab dispatch`'s fixed live frame: pure projection, renderer, poll cache, and deterministic image renderer | §14 |
83
+ | `bin/agent/ab` | Private launcher placed first on agent-session `PATH`; delegates to the canonical `bin/ab.ts` | §8.1 |
84
+ | `packages/core/src/config/` | `autobuild.toml` parsing and strict validation, plus the pure role-key consumability diagnostics `ab dispatch` reports at startup (`roles.ts`); user reference in `docs/configuration.md` | §9, §16.1 |
85
+ | `packages/core/src/integration/` | End-to-end harness and product scenarios | — |
86
+ | `tools/` | This repository's local maintainer tooling, including verification, dashboard capture, and release cutting; not shipped product behavior | — |
87
+ | `skills/` | Canonical defaults; `ab init` vendors them to `.agents/skills/ab-*` and links `.claude/skills/ab-*` | §16.3 |
88
+ | `skills/guide/` | `ab-guide` — the model-invocable reference for the lifecycle and full config surface; `references/` is the shared installed-documentation seam for every vendored skill. Update it when config or shared guidance changes; `packages/core/src/cli/guide-skill.test.ts` guards schema coverage and `packages/core/src/cli/skill-self-containment.test.ts` guards installed references | §16.3 |
89
+ | `docs/spec-standard.md` | The standalone definition of "buildable" every ticket surface uses; kept byte-identical with `skills/guide/references/spec-standard.md` for installed agents | §6.1 |
90
+ | `templates/` | Valid setup-only config baseline with comment anchors rendered by `ab init` | §16.3 |
91
+
92
+ ## Key boundaries
93
+
94
+ **Hosted store service.** `@defrex/autobuild/remote-store` is the supported protocol
95
+ server/client/token export. The hosted workspace validates its environment,
96
+ serves health without touching persistence, and lazily retains one
97
+ `openPostgresBuildStoreFromEnv` promise per warm process. Its Fetch handler has
98
+ no Vercel branch; root `server.ts` is the sole `Bun.serve()` composition point.
99
+ The fixed 1 MiB decoded artifact policy is enforced before backing mutation.
100
+ The `ab-hosted-store` binary signs admin or build/session tokens offline from
101
+ `AB_STORE_SECRET`; the running service exposes no mint endpoint. Machine routes
102
+ and later server-session human routes intentionally share one deployment.
103
+
104
+
105
+ Where each mechanism lives, and the one rule worth knowing at the seam. The
106
+ full behavior is specified by each owner's colocated tests.
107
+
108
+ **Events and state.** `packages/core/src/events/payloads.ts`, `packages/core/src/events/repository.ts`,
109
+ and `packages/core/src/events/sessions.ts`
110
+ are the frozen catalogs; every write passes `validateEventWrite` /
111
+ `validateRepositoryEventWrite` / `validateSessionEventWrite`. `packages/core/src/kernel/reducer.ts` derives all build
112
+ status; `packages/core/src/kernel/harvest.ts` and `packages/core/src/kernel/dispatch-settings.ts` reduce
113
+ the repository journal independently of each other; `packages/core/src/sessions/reducer.ts` reduces an operator
114
+ session's status, open turn, pending approval, and wake cursors from its own
115
+ closed catalog. No decision anywhere
116
+ consults a snapshot in place of the append-only log.
117
+
118
+ **Session Store authority.** `packages/core/src/cli/binary.ts` validates a complete build or
119
+ Harvest ambient tuple before opening its phase Store through
120
+ `packages/core/src/cli/store-opening.ts`. `packages/core/src/cli/env.ts` also classifies optional ambient
121
+ identity for the finite `builds`, `build status`, and `artifact download` read
122
+ shells: no identity retains operator authority, while any partial, malformed,
123
+ shared-only, or mixed identity fails closed before Store opening. Remote
124
+ references keep the existing token-backed client unchanged. Local references
125
+ are wrapped by `packages/core/src/store/phase-session.ts`, which permits only the exact
126
+ ambient build or repository resource (this is the phase-session wrapper, not the
127
+ operator-session scope handle in `session-handle.ts`) and rejects
128
+ agent-attributed event writes
129
+ for any session other than `AB_SESSION`. The wrapper deliberately allows
130
+ non-agent actors because phase terminals also run trusted kernel plumbing in
131
+ that CLI process; resource authority still applies to every operation. Generic
132
+ sessionless opening remains unwrapped; only those three reads use the dedicated
133
+ ambient-aware finite opener.
134
+
135
+ **Phase decisions.** `packages/core/src/kernel/phases.ts` owns the phase table,
136
+ `packages/core/src/kernel/engine.ts` the deterministic transitions;
137
+ `packages/core/src/processes/build-runner.ts` executes the decisions. Repeated phase starts
138
+ left by recovery are execution evidence, not policy counters: logical verdict
139
+ rounds, `phase.failed`, failed verify completions, and distinct completed
140
+ reconcile attempts own their respective budgets. Agents reach state only
141
+ through `packages/core/src/cli/` terminals, which convert artifact deposits into event facts
142
+ atomically — the engine never reads blobs. Engine crash-gap and
143
+ exhaustion guards query the event log for a later escalation raise with the
144
+ exact same source/class and target phase; the triggering sequence remains the
145
+ re-arm boundary. `BuildRunner` applies the same independent targeting at the
146
+ setup seam, where only a setup-targeted policy raise can satisfy setup
147
+ exhaustion.
148
+
149
+ **Phase-session budgets.** `packages/core/src/ports/runner/routing.ts` resolves one inherited
150
+ `sessionBudgetSeconds` value for each logical role, with the policy default as
151
+ its final fallback. `BuildRunner` captures it at the session action boundary,
152
+ starts an unref'ed timer after `session.started`, and composes expiry with the
153
+ existing operator `AbortController`. Operator abort and deadline remain
154
+ independent: abort cancels promptly, while the captured deadline still stops
155
+ waiting if the adapter ignores that cancellation. First cause owns
156
+ classification, so an operator-first deadline release returns to abort
157
+ acknowledgement without `phase.failed` or provider substitution. Ordinary
158
+ expiry best-effort ends an available or late-returning handle, drops producer
159
+ continuation state, and reuses retryable `phase.failed`; it does not enter the
160
+ provider-alternate classifier. The event log is checked before failure so a
161
+ typed terminal racing either boundary remains authoritative. Finalize agent
162
+ post-steps reuse the timer but drain expiry through their existing failure-tolerant
163
+ completion and observation path. Harvest and deterministic commands do not use
164
+ this build-phase timer.
165
+
166
+ **Human guidance delivery.** The routing fact is an `escalation.answered`
167
+ event carrying `resolution: "guidance"`; nothing else routes an answer. `ab
168
+ answer` and the dashboard's blocked-build resume control are two surfaces over
169
+ the one `packages/core/src/cli/build-control.ts` operation that appends it, so delivery is
170
+ identical whichever a human used.
171
+ `packages/core/src/kernel/engine.ts` routes answers from `plan` and `plan-review` to the next
172
+ `plan` round, and answers from `implement` and `code-review` to the next
173
+ `implement` round. An agent verifier's own escalation is the deliberate
174
+ exception to producer routing: its answer returns to that same step on
175
+ `verify.started.feedback`, and `PHASE_SPECS.inputs.currentFeedback` makes `ab
176
+ context` the delivery channel. When a failed verify report instead exhausts
177
+ policy, guidance answering that policy escalation takes precedence over the
178
+ pending report as the failure routes to the next `implement` round. For these
179
+ engine-routed destinations, `engine.ts` chooses the newest answered guidance
180
+ for the requested destination before checking whether its durable carrier
181
+ reached a matching session launch. That ordering makes supersession durable:
182
+ delivering the winner cannot reveal an older same-destination answer, while
183
+ plan, code, and exact-verifier destinations remain independent and a new answer
184
+ after delivery remains eligible. The projection uses only event history, so
185
+ restart and decision replay agree.
186
+ `finalize` and `reconcile` have no producer round, so
187
+ `PHASE_SPECS.inputs.answeredGuidance` makes `ab context` their delivery channel
188
+ for the latest answer addressed to that phase. On every receiving path,
189
+ `packages/core/src/cli/context.ts` materializes `.ab/guidance.json` with the escalation id and
190
+ answer text. For producer and verifier feedback routes, `packages/core/src/kernel/engine.ts`
191
+ indexes each guidance-bearing phase start as a durable carrier and pairs only
192
+ the latest carrier for an exact phase plus round/attempt with a subsequent
193
+ matching `session.started`. An unpaired carrier remains deliverable across
194
+ recovery; the matching launch consumes it once without process-local state. A
195
+ producer round's feedback is a discriminated union — findings, a
196
+ failed verify report, or guidance — and a round may carry none, so a round with
197
+ guidance writes no `.ab/findings.json`. The receiving skills document their
198
+ corresponding input, and `packages/core/src/cli/skill-guidance.test.ts` derives that
199
+ requirement from the phase table.
200
+
201
+ **Finalize publication.** Content-producing `finalize:*` checks or agents
202
+ select and commit files locally and leave a clean worktree. `build-runner.ts`
203
+ derives the last published head from event facts, rejects a non-descendant
204
+ `HEAD`, and uses the `Forge.pushBranch` port for a regular kernel-side push
205
+ before checkpointing the new head on `finalize.step-completed`. An unchanged
206
+ head is a no-op; Git/Forge failures stay failure-tolerant observations.
207
+
208
+ **Verify gating.** `packages/core/src/ontology.ts` owns the canonical
209
+ `pass | fail | skipped` outcome (only `fail` routes back to implement or
210
+ consumes attempts; a skip satisfies one step without being passing
211
+ evidence). Two kernel-authored skip sources narrow the configured universe:
212
+ `packages/core/src/kernel/plan-verify-selection.ts` resolves an approved plan's front-matter
213
+ selection (applied at the planner's `ab done` in `packages/core/src/cli/terminals.ts`,
214
+ snapshotted by the engine at approval), and
215
+ `packages/core/src/kernel/verify-applicability.ts` matches changed paths against a step's
216
+ selectors, resolved by the build runner via `git diff` against the initial
217
+ branch-cut base (or the refreshed base promoted by a completed reconcile).
218
+ This verify-only base is deliberately independent from implementation's
219
+ focused review range. Both narrowing mechanisms produce the ordinary
220
+ queryable skipped outcome; Git failure is infrastructure and fails closed,
221
+ never a synthetic skip. Verify escalation routing is also explicit in the
222
+ engine: guidance answering an agent verifier's own `ab escalate` returns to the
223
+ same step on `verify.started.feedback`, which `ab context` materializes as
224
+ `.ab/guidance.json`; that cited start remains recoverable before launch, and the
225
+ matching `session.started` for the same step and attempt consumes the answer
226
+ once. Guidance answering the policy escalation after failed-report exhaustion
227
+ instead routes to `implement` and outranks the pending report. A bare retry carries no
228
+ feedback.
229
+
230
+ **Launch ownership.** `packages/core/src/cli/dispatch.ts` is the kernel owner and
231
+ single-flights supervised build executions per slug within its process. It
232
+ publishes effective config into the build artifact namespace, claims the exact
233
+ execution-instance lease, then calls the workspace-adjacent `BuildExecution`
234
+ capability; it never constructs a `BuildRunner`. The shipped local capability
235
+ starts `bin/ab-build-runner.ts` with ignored stdio as a detached POSIX
236
+ session/process-group leader. That child immediately scopes its Store handle, discovers
237
+ its workspace from durable events, and composes the existing runner. Config and
238
+ instance-correlated diagnostics remain build artifacts. Local completion first
239
+ reaps the complete inherited process group, escalating group-wide from SIGTERM
240
+ through the existing bounded delay to SIGKILL; only then is it a liveness signal
241
+ and only then does the kernel release the exact lease. Interactive `ab dispatch` runs the kernel owner in the
242
+ private `bin/ab-dispatch-kernel.ts` child;
243
+ `dispatch-frontend.ts` owns only terminal/Store adapters and
244
+ `dispatch-process.ts` owns child supervision. SIGINT, SIGHUP, SIGQUIT, and
245
+ SIGTERM restore terminal modes immediately and begin one supervised drain;
246
+ persistent frontend and child handlers keep repeated signals graceful until the
247
+ kernel child is reaped, after which a non-SIGINT terminating signal is replayed
248
+ in the frontend. A parent-liveness watch remains the detached-kernel fallback.
249
+ Timeout escalation is held while a durable `tick-started` fact remains open,
250
+ because force-killing the ticket claim before build creation would not be
251
+ recoverable. After a build child claims a takeover lease, it reduces one
252
+ captured event prefix and appends transcriptless reclaimed `session.ended`
253
+ facts for every session open there before `runner.attached` cites that same
254
+ boundary. Partial closure is retry-safe and an observed attachment therefore
255
+ proves all pre-boundary sessions closed; historical orphan-only logs keep their
256
+ old projection until an actual takeover records those facts. The supervisor
257
+ rechecks kernel-child liveness at the force boundary
258
+ and projects the same normal, forced, or abnormal result (with available
259
+ process/error detail) that the repository run-stopped evidence records. Ordinary
260
+ kernel teardown stops and reaps all build groups. Every build-child terminal
261
+ path — natural completion, failure, SIGINT/SIGTERM, and parent-watch death —
262
+ first starts exactly one detached `bin/ab-process-group-reaper.ts` while the
263
+ leader still pins its process-group identity. The helper targets only that
264
+ negative group id and applies the same bounded TERM-then-KILL teardown, so it
265
+ survives abrupt kernel death. The live local supervisor independently awaits
266
+ full-group disappearance before publishing completion and releasing its exact
267
+ lease; lease expiry remains the durable recovery fallback. The BuildStore lease
268
+ remains the cross-process and workspace-release gate. `packages/core/src/processes/dispatcher.ts`
269
+ counts actual schedules, not suppressed polls. Open session history is never a
270
+ lock: a legacy dead session may remain open, while an actual takeover closes it
271
+ explicitly.
272
+
273
+ **Store authority.** `packages/core/src/store/build-scope.ts` wraps every adapter with one
274
+ build's authority. Own-build record/event/artifact/lease/subscription calls
275
+ delegate; foreign-build, collection/admin, close, and repository-journal calls
276
+ raise `BuildScopeError`. The shared Store suite applies these checks to memory,
277
+ SQLite, and remote clients, while HTTP token scope remains defense in depth.
278
+
279
+ **Live configuration.** `packages/core/src/config/live.ts` owns the running dispatcher's
280
+ immutable last-valid main-checkout snapshot, exhaustive hot/restart field
281
+ classification, and eager role resolver. The watch loop refreshes it before
282
+ each tick and publishes the exact accepted TOML and the composed effective JSON
283
+ atomically with a run-correlated `dispatcher.config-reloaded` repository fact
284
+ before making the snapshot visible. Startup similarly publishes the effective
285
+ artifact before the first tick. Missing, unreadable, malformed, and
286
+ routing-invalid candidates retain that snapshot with a durable deduplicated
287
+ operator notice; restoring a valid file resumes ordinary reload. Dispatcher
288
+ ticks and build/harvest actions capture one snapshot at their own boundary; the
289
+ dashboard validates and projects only the referenced effective artifact, so an in-progress
290
+ turn or command is never interrupted. Scoped phase CLI processes retain their
291
+ separate build-worktree config boundary. Startup-built plugin, forge, ticket,
292
+ and workspace adapter fields stay pinned; changed values are durable restart
293
+ notices rather than partial adapter swaps. `--once` retains static startup
294
+ configuration.
295
+
296
+ **Harvest.** The dispatcher owns the threshold trigger and starts runs
297
+ fire-and-forget; `packages/core/src/processes/harvest.ts` is the deterministic core (scan,
298
+ source-aware originating-ticket lifecycle projection, filing-time blocker
299
+ resolution, occurrence identity, and the exhaustion partition),
300
+ `harvest-runner.ts` executes the staged workflow under the heartbeated
301
+ repository lease, records declared/derived blocker provenance, and
302
+ `packages/core/src/kernel/harvest.ts` reduces runs, claims, recovery history, and the
303
+ committed ledger with ordered parked/exhaustion/open selectors. Its pure
304
+ creation selectors pair reservation/filing facts, bound unmatched keys to
305
+ active runs, and conservatively union reservations first seen between the
306
+ repository snapshots bracketing a ready listing. `packages/core/src/processes/dispatcher.ts`
307
+ correlates those keys before dependency reads or claims, retaining queue depth
308
+ and emitting creation-specific tick diagnostics. The recovery
309
+ invariants are SPEC §12; the mechanics live in the reducer and its tests.
310
+
311
+ **Ticket sources.** `packages/core/src/ports/tickets/`. `listReady` is an explicit
312
+ partial-listing seam: individually malformed records come back as
313
+ diagnostics (surfaced by the dispatcher's tick report and `ab ticket list`
314
+ stderr) while tracker-wide invariant violations stay fatal — one broken
315
+ ticket never blocks unrelated dispatch, but nothing that could permit double
316
+ dispatch is tolerated. `Ticket.creationKey` is the optional stable external
317
+ create/adoption correlation, distinct from `Ticket.ref.id`; Linear projects its
318
+ issue UUID and file/fake preserve their idempotency metadata through create,
319
+ adoption, get, and list paths. Legacy/plugin tickets may omit it and dispatch
320
+ unchanged.
321
+
322
+ **Workspace, execution, and review base selection.**
323
+ `packages/core/src/ports/workspace/create.ts` resolves `[workspace].provider` against the
324
+ builtin-plus-plugin registry once during production wiring and pairs it with a
325
+ `BuildExecution` capability. A provider-supplied capability substitutes at
326
+ that seam; otherwise the shipped local subprocess capability is used. The builtin stays
327
+ store-root-aware; selected plugin factories receive their nested config,
328
+ environment, and absolute repository root. `WorkspaceHandle.ref` remains a
329
+ provider identifier, `path` is absolute inside the execution environment, and
330
+ optional `localPath` explicitly identifies dispatcher reachability. Local Forge
331
+ operations fall back to the main checkout when `localPath` is absent.
332
+ `packages/core/src/ports/workspace/git-worktree.ts` selects the branch-cut base once
333
+ at first creation, fetching into a build-scoped private ref; re-provisioning
334
+ resumes at the branch tip and never re-cuts, so the first provisioning fact
335
+ remains immutable provenance. Separately, each successful implementation terminal in
336
+ `packages/core/src/cli/terminals.ts` asks the Forge to snapshot its authoritative base when
337
+ that optional capability exists; otherwise it privately refreshes the frozen
338
+ target branch from `origin` and records
339
+ the unique merge-base of that snapshot and `HEAD` in `implement.completed`.
340
+ It fails before publication/deposit on fetch, ref, ancestry, or ambiguity
341
+ errors and writes neither `FETCH_HEAD` nor operator refs. Reconcile's
342
+ execution-time target refresh in `packages/core/src/processes/build-runner.ts` remains a
343
+ third, deliberately separate boundary and also fails closed.
344
+
345
+ The builtin `vercel-sandbox` adapter provisions an exact GitHub revision into a
346
+ fixed guest path. Fresh-environment bootstrap scrubs clone authority, installs
347
+ and verifies pinned Bun, serially runs repository-declared named system
348
+ provisioning through `sh -c` with provider root authority, installs the running
349
+ Autobuild distribution and repository dependencies, and only then writes the
350
+ readiness marker. Thus marked environments run the list once, while every new
351
+ replacement generation reproduces it before `[commands].setup` or any phase.
352
+ Step failures retain command/status/stdout/stderr, leave no marker, and flow
353
+ through durable `infrastructure.failed`, its bounded retry budget, shared status
354
+ and dashboard projection, and setup-targeted escalation; nested cleanup failure
355
+ does not erase the original diagnostic. Readiness uses the same bootstrap helper
356
+ in an always-deleted temporary sandbox and reports declared step names before
357
+ guest setup checks. The adapter then starts the same private build child with
358
+ environment supervision. Local children carry a positive parent PID and retain their
359
+ watchdog/process-group reaper; remote children cannot see a meaningful host PID,
360
+ so the detached SDK command plus VM stop owns full teardown. Scoped Store facts
361
+ are the only state channel. Remote terminals append `publication.requested` and
362
+ park. Only confirmed VM teardown plus execution-lease release permits the local
363
+ supervisor to use the provider's exact SHA/branch publication capability; a
364
+ rejected/uncertain executor completion leaves the request pending. The supervisor
365
+ verifies the remote head and records the ordinary completion; PR API operations
366
+ remain on the dispatcher. A failed finalize post-step publication records the
367
+ ordinary failure-tolerant step outcome and follow-up observation rather than
368
+ wedging the green build. Every guest launch first reasserts the normal
369
+ receive-pack-free network policy, so even a failed publication-policy restore
370
+ fails closed before setup or plugin code runs. Normal VM sessions never receive
371
+ Forge credentials.
372
+
373
+ **Forge credentials.** `packages/core/src/ports/forge/github-transport.ts` owns the
374
+ REST transport seam and credential resolution for the builtin GitHub forge:
375
+ `GITHUB_TOKEN`, then `GH_TOKEN`, then the gh CLI's stored login read through
376
+ `gh auth token --hostname github.com` under a bounded deadline. A transport resolves
377
+ lazily on its first request and asks its resolver again at most once per bounded
378
+ interval — after a miss, or after a `401` past that interval (a rotated login) — so a
379
+ miss is never frozen into anonymous access and nothing is re-probed per request. Nothing is probed at construction time; a request that had to
380
+ go out anonymously carries the miss reason (gh missing, not logged in to github.com,
381
+ timed out) in its error, in every process that holds a forge. Origin-mode dispatch — and therefore the hosted
382
+ dispatcher — requires an exported token before any side effect and never consults gh:
383
+ a checkout-less host has neither gh nor a keyring, and `vercel-sandbox` publication
384
+ injects that same token.
385
+
386
+ **Agent runtimes.** `packages/core/src/ports/runner/`: `runtime.ts` (capability-carrying
387
+ registry plus boundary validation), `routing.ts` (eager role resolver),
388
+ `production.ts` (shipped Claude/Codex/Pi registrations), `codex.ts` (direct
389
+ Codex `exec --json` subprocess protocol with native thread resume), `pi.ts` and
390
+ `pi-rpc.ts` (the local pi CLI prerequisite/auth probe and long-lived headless
391
+ RPC protocol, minimum version 0.84.3), `one-shot.ts` (optional tool-free
392
+ non-phase completions — slug naming via `packages/core/src/cli/dispatch.ts`, skill-conflict
393
+ proposals via `packages/core/src/cli/upgrade-agent.ts`), `provider-error.ts` (positive-only
394
+ permanent-failure classifier), and `session-env.ts` (per-turn environment merge
395
+ that fronts `bin/agent/ab` on `PATH`). The pi CLI owns its login and model
396
+ catalog; the shipped bridge limits tools and refreshes each turn's subprocess
397
+ environment without writing session state into the workspace. Adapters own
398
+ native error extraction; processes own durable failure policy — the transcript
399
+ is always deposited, and a turn's typed terminal always beats a late failure
400
+ signal.
401
+
402
+ **Plugin bootstrap and CLI composition.** `packages/core/src/plugins/load.ts` resolves
403
+ repository-path modules from the config-bearing root and bare packages from an
404
+ explicit package root (defaulting to that same root), validates each default
405
+ manifest/API range, and atomically registers normalized factories, provenance,
406
+ ticket credential metadata, and optional contract descriptors before production
407
+ wiring or the first dispatch tick. Its structured single-module attempt feeds
408
+ two policies: `loadPlugins` remains fail-fast for dispatch, while
409
+ `diagnosePlugins` collects ordered failures and retains later healthy
410
+ registrations for `ab plugin doctor`. `packages/core/src/cli/plugin.ts` owns the sessionless
411
+ list/doctor/test grammar and live gate; `packages/core/src/plugins/contract-entry.ts` reloads
412
+ one selected registration inside a real `bun test` process and registers exactly
413
+ one unchanged port suite.
414
+
415
+ `packages/core/src/ports/forge/create.ts` resolves the root `forge` selector, constructs the
416
+ shipped GitHub or local-git adapter, or lazily invokes a registered plugin
417
+ factory, and preserves the returned adapter's optional capabilities. The
418
+ local-git adapter in `packages/core/src/ports/forge/local-git.ts` stores versioned JSON PR
419
+ records as blobs behind private `refs/autobuild/local-git/` refs in the shared
420
+ Git database. It computes mergeability with `git merge-tree`, leaves the build
421
+ branch reachable, and lands a guarded single-parent squash locally. Immediately
422
+ before offering an ungated landing candidate, `git var GIT_AUTHOR_IDENT` and
423
+ `git var GIT_COMMITTER_IDENT` prove that ordinary Git identity resolution can
424
+ succeed; a failure becomes a reason-bearing, retryable consent deferral. The
425
+ `git commit-tree` seam inherits that resolved identity without injecting an
426
+ Autobuild author or committer, while `-c commit.gpgSign=false` keeps the landing
427
+ unsigned. For a checked-out base, dry-run and real
428
+ `git read-tree -m -u <old> <landed>` calls
429
+ let Git carry forward non-overlapping operator changes and report colliding
430
+ paths. The pending landing precedes the guarded ref update; a later poll retries
431
+ the same two-tree checkout transition across the crash window and observes the
432
+ PR as merged only after synchronization succeeds. Forge-native base snapshotting lets
433
+ it pin the current local base without `origin`, while capability-less adapters
434
+ retain the legacy private-ref fetch path. Dispatch constructs one
435
+ selected adapter before opening the store and threads it through runners,
436
+ epilogue, and janitor work. Scoped `packages/core/src/cli/binary.ts` processes independently
437
+ load immutable config and repository-path plugins from the build worktree, use
438
+ `resolveMainRepo` to locate the consuming checkout only for bare-package lookup,
439
+ and resolve the same adapter name for phase terminal plumbing. This keeps
440
+ package availability independent of local-store/worktree placement without
441
+ redirecting branch-owned plugin source or factory `repoRoot` away from the
442
+ worktree. `packages/core/src/ports/workspace/create.ts` similarly resolves
443
+ `[workspace].provider`, retaining host-owned git-worktree construction while
444
+ passing plugin config, environment, and repository root to registered
445
+ factories. Ticket-source selection is registry-backed in
446
+ `packages/core/src/ports/tickets/create.ts`; dispatch and sessionless `ab ticket` commands
447
+ load plugins before constructing it.
448
+
449
+ `packages/core/src/plugins/runtimes.ts` invokes plugin runtime factories in registration
450
+ order, validates their capability-bearing registrations, and returns one fresh
451
+ registry containing builtins and plugins. Dispatch performs that composition
452
+ before eager role resolution and shares the result with build, harvest, and
453
+ slug routing. Upgrade performs the same composition lazily at its first merge
454
+ conflict.
455
+
456
+ `packages/core/src/cli/repo-state.ts` owns repository identity and store precedence
457
+ (`--store` > `AB_STORE` > `.autobuild/`); `packages/core/src/cli/store-opening.ts` is the
458
+ production store composition boundary shared by finite sessionless queries and
459
+ phase sessions. Its shared scoping primitive preserves remote token composition
460
+ and adds local ambient authority only for filesystem stores. The dedicated
461
+ ambient-read opener classifies identity before repository or Store opening and
462
+ is adopted only by `ab builds`, `ab build status`, and `ab artifact download`;
463
+ all other sessionless commands keep the generic unwrapped opener.
464
+ `packages/core/src/cli/repository-status.ts` consumes that boundary and the kernel's
465
+ `reduceDispatchSettings` projection to report journal-backed dispatcher
466
+ controls without ensuring a repository stream or starting dispatcher work.
467
+ `packages/core/src/cli/args.ts` parses command-scoped flag contracts; `packages/core/src/cli/binary.ts`
468
+ classifies build/harvest session tuples and routes sessionless invocations, so
469
+ phase-only commands report their complete runner context when run by hand.
470
+
471
+ **PR attachments.** `packages/core/src/cli/artifact.ts` atomically turns an explicit
472
+ `artifact put --attach` into an exact artifact plus designation fact.
473
+ `packages/core/src/kernel/pr-attachments.ts` selects current designations, hosted
474
+ correlations, and pending cleanup without coupling to a producer or verify-step
475
+ name. `packages/core/src/cli/pr-attachments.ts` performs optional image hosting through the
476
+ narrow Forge capability, while `packages/core/src/cli/pr-summary.ts` renders the same complete
477
+ text projection for finalize and late designations. The GitHub release transport
478
+ lives in `packages/core/src/ports/forge/github-pr-attachments.ts`; terminal-build reclamation
479
+ and retry facts remain dispatcher janitor duty.
480
+
481
+ **Abort control and cleanup.** CLI and dashboard call the same
482
+ `packages/core/src/cli/build-control.ts` service. A confirmed dashboard `a` action appends only
483
+ the durable abort request. After claiming the build lease, `build-runner.ts`
484
+ acknowledges an already-pending pause or abort before workspace setup, without
485
+ appending `runner.attached`; resume and workspace-using decisions stay behind
486
+ successful setup. For a request arriving during an AgentRunner turn, the runner
487
+ observes the BuildStore subscription, forwards an `AbortSignal`, deposits/ends
488
+ the session without a phase failure, and acknowledges abort. The execution
489
+ supervisor keeps the lease until every local descendant is reaped. `dispatcher.ts`
490
+ defers destructive abort, discard, and terminal-PR cleanup while that lease is
491
+ live, then resumes the event-checkpointed janitor saga that closes an unmerged PR,
492
+ releases the workspace, deletes exact remote/local branch
493
+ refs, unions `autobuild:aborted` into current ticket labels, returns the ticket to
494
+ Triage, and appends abandoned completion last. Forge close/delete operations are
495
+ optional API 1.2 capabilities so older plugins load but leave cleanup visibly due.
496
+ AgentRunner failure causes and the exhaustion contract fixture are additive
497
+ plugin API 1.3 surfaces; legacy failures without a cause retain permanent-bit
498
+ behavior.
499
+
500
+ **Dashboard.** `packages/core/src/cli/dispatch-frontend.ts` is a Store-only terminal adapter:
501
+ it has no ticket, forge, workspace-provider, LiveConfig, or runtime dependency.
502
+ It follows one dispatch run's repository facts incrementally through
503
+ `packages/core/src/kernel/dispatch-status.ts`, retaining only low-volume settings/Harvest
504
+ facts for their replay reducers, validates/caches its effective-config artifact,
505
+ and polls build streams independently while elapsed paints continue from cached
506
+ intervals. The same polling path calls the canonical
507
+ `scanUnclaimedObservations` BuildStore reduction for the header's current count
508
+ and pairs it with the effective config's `policy.harvestThreshold`. That sample
509
+ is process-local presentation state: a failed refresh preserves the last
510
+ successful value and adds a local diagnostic, while an initial failure delays
511
+ the complete frame rather than inventing zero. No repository fact transports
512
+ it, and the headless child does not scan for presentation. Navigation is
513
+ synchronous presentation state; only Store reads and writes enter its UI action
514
+ queue. The supervised kernel child owns every slow
515
+ adapter operation, so slug naming, provisioning, runners, and Harvest cannot
516
+ block input.
517
+
518
+ `packages/core/src/cli/build-progress.ts` projects the exact last-event, heartbeat, and lease
519
+ inputs shared by the diagnostic `ab builds` and `ab build status` surfaces. Its
520
+ one-hour `diverged` predicate is presentation-only: reducer status, engine
521
+ routing, and lease health remain independent. `packages/core/src/cli/dashboard/model.ts` is
522
+ the event-derived build-row projection; `poll.ts` reprojects rows for event or
523
+ effective-config changes and reuses them across heartbeat/lease-only renewals
524
+ while retaining the cached log and reduction.
525
+ `detail.ts` projects chronological session history from the same retained log,
526
+ including a distinct transcriptless `reclaimed` ending, and `transcript.ts`
527
+ heuristically presents ordinary opaque transcript artifacts with a raw
528
+ fallback. `render.ts` composes the list, build-detail, and transcript as
529
+ cell-honest Unicode frames; `keyboard.ts` owns Kitty keyboard-protocol
530
+ negotiation and CSI-u decoding, while `live.ts` owns normal teardown and
531
+ sequences its push and pop with the alternate-screen region because the
532
+ terminal's keyboard flag stack is per-screen. Every mode enters and leaves through
533
+ the declarations and active ledger in `terminal-restore.ts`; `binary.ts` installs a dispatch-only synchronous
534
+ process-boundary fallback over that same ledger for faults and terminating
535
+ signals that bypass normal teardown. `poll.ts` is a display-only incremental
536
+ cache (the logs remain authoritative — cache loss just
537
+ rehydrates); `frame-image.ts` renders a deterministic PNG with pinned fonts.
538
+ `composer.ts` owns the text geometry the blocked-resume panel edits against —
539
+ display-cell wrapping and caret layout, plus cursor motions expressed as UTF-16
540
+ string offsets normalized to extended-grapheme boundaries — as pure, ANSI-free
541
+ arithmetic shared by `render.ts` and `dispatch.ts`.
542
+ Nested navigation, session selection, and pinned artifact retrieval are
543
+ read-only process-local UI concerns. Build actions still use the shared control
544
+ service and append human facts; the header shows acknowledged durable state,
545
+ never optimistic intent. Forge mutation stays in dispatcher plumbing.
546
+
547
+ **Agent tool registry.** `packages/core/src/operator/registry.ts` is the agent-facing
548
+ face of the operator services: one closed table of typed tools whose handlers call
549
+ the same control, query, ticket, and artifact services the operator API routes call
550
+ (`operator/control.ts`, `operator/query.ts`, `operator/tickets.ts`), with the
551
+ route-level glue re-derived beside it in `operator/requests.ts` and tool/route parity
552
+ proven per tool by `operator/registry.contract.test.ts`. Every binding is generated
553
+ from the table and never widens beyond the operator services: the shipped one is
554
+ `packages/core/src/cli/mcp.ts` (`ab mcp`, a stdio MCP server over the registry that
555
+ fails closed inside a phase, exactly as repository-wide `ab builds` does); the
556
+ embedded orchestrator and later transports register from the same table. Checked-in
557
+ risk classes live in `operator/annotations.ts` (SPEC §8.9).
558
+
559
+ **Init and upgrade.** `packages/core/src/cli/init.ts` owns deterministic skill vendoring,
560
+ ignore maintenance, runtime probes, and the stack-neutral first config. It then
561
+ launches an interactive agent CLI, or prints the identical short prompt, telling
562
+ the setup agent to read the installed
563
+ `.agents/skills/ab-guide/references/setup.md`; this bypasses all build and
564
+ BuildStore session plumbing. Existing config is never reconciled, even with
565
+ `--force`. `packages/core/src/cli/upgrade.ts` owns the pristine × local × incoming skill merge,
566
+ the provenance-safe fixed retirement of obsolete defaults, and all writes:
567
+ agent output is an untrusted proposal validated before anything touches disk,
568
+ and every failure path leaves live and pristine byte-untouched.
569
+
570
+ ## Development
571
+
572
+ ```sh
573
+ bun install
574
+ bun run check # Biome lint + format, zero diagnostics
575
+ bun test # unit tests, colocated *.test.ts
576
+ bun typecheck # tsc --noEmit
577
+ ```
578
+
579
+ For dashboard presentation work, run the repository-only hot CLI:
580
+
581
+ ```sh
582
+ bun run dev -- dispatch
583
+ # Generic form: bun run dev -- <ab arguments>
584
+ ```
585
+
586
+ Bun keeps the original CLI promise and its `DispatchLoop` alive while hot
587
+ module evaluation replaces only the renderer used by the next repaint. Edits
588
+ to `packages/core/src/cli/dashboard/render.ts` and presentation-only dependencies imported
589
+ by it appear without restarting runners, releasing leases, or stacking input
590
+ handlers. Changes to dispatcher logic, dashboard model/controller logic,
591
+ keyboard handling, or build-runner code still require a restart. The
592
+ installed `ab` binary remains the non-watching production entry.
593
+
594
+ ### Contract suites
595
+
596
+ The seams are the contract. Five reusable contract families run the same
597
+ behavioral assertions against every implementation:
598
+
599
+ - `packages/core/src/store/contract.ts` — `BuildStore` and `BlobStore`, including the streams block (§7.6) every adapter runs;
600
+ - `packages/core/src/ports/tickets/contract.ts` — `TicketSource`;
601
+ - `packages/core/src/ports/workspace/contract.ts` — `WorkspaceProvider`;
602
+ - `packages/core/src/ports/forge/contract.ts` — `Forge`, including idempotent PR close and
603
+ branch deletion with merged-race preservation;
604
+ - `packages/core/src/ports/runner/contract.ts` — `AgentRunner` session/continuation,
605
+ transcript metadata and usage, typed failure permanence/cause, per-turn
606
+ ambient environment refresh and cancellation, distribution-managed `ab`
607
+ resolution, and the optional tool-free one-shot capability.
608
+
609
+ `packages/core/src/ports/runner/routing.ts` eagerly resolves every role's primary, ordered
610
+ alternate targets, and independently inherited build-session budget.
611
+ `build-runner.ts` and `harvest-runner.ts` own the deterministic
612
+ primary-first attempt loop: each substitution opens a new session bracket,
613
+ records provenance on its start, and only complete-chain exhaustion consumes an
614
+ existing phase/session attempt. This keeps runtime availability routing out of
615
+ agent judgment while preserving transcript and artifact actor attribution.
616
+
617
+ A normal `bun test` runs the memory/fake/local registrations, including a fake
618
+ selected through the plugin ticket-source registry, the real filesystem and
619
+ local-git adapters, the injected Claude and Codex CLI subprocess contracts,
620
+ and the injected Pi CLI/RPC contract.
621
+ Both `ab dispatch` and sessionless `ab ticket` load the repository's plugins
622
+ before selecting their TicketSource; dispatch passes that one adapter instance
623
+ through readiness, dependency, harvest, and completion paths. The Linear,
624
+ GitHub, and real-runtime AgentRunner registrations are present in the same run
625
+ but reported as skipped: live provider access requires credentials/resources
626
+ and an explicit opt-in. When
627
+ adding an adapter, start from its contract suite, not only the interface.
628
+
629
+ To run the Linear contract manually against a destructive scratch target:
630
+
631
+ ```sh
632
+ AB_RUN_LIVE_PORT_CONTRACTS=1 \
633
+ LINEAR_API_KEY=… \
634
+ AB_LINEAR_CONTRACT_TEAM_KEY=SCRATCH \
635
+ AB_LINEAR_CONTRACT_PROJECT_ID=… \
636
+ bun test packages/core/src/ports/tickets/linear.live.test.ts
637
+ ```
638
+
639
+ The token must be able to create, update, relate, and archive issues in the
640
+ configured project, and to create and delete issue labels in the configured
641
+ team. The team needs a claimable `unstarted` or `backlog` state, a `started`
642
+ state, a `completed` or `canceled` state, and at least one issue label the
643
+ contract can replace/clear. Every issue gets a reserved UUID and is attached to
644
+ that project. Cleanup archives those issues and deletes the fresh team labels
645
+ created by the unknown-label contract cases; failures are reported so leaked
646
+ fixtures can be removed manually. Use a project with no real work in it.
647
+
648
+ To run the GitHub contract manually:
649
+
650
+ ```sh
651
+ AB_RUN_LIVE_PORT_CONTRACTS=1 \
652
+ GH_TOKEN=… \
653
+ AB_GITHUB_CONTRACT_REPO=owner/destructive-scratch-repo \
654
+ bun test packages/core/src/ports/forge/github.live.test.ts
655
+ ```
656
+
657
+ `GITHUB_TOKEN` may be used instead of `GH_TOKEN`. The repository must have
658
+ native auto-merge enabled, an initialized default branch, no inherited
659
+ merge-blocking rule that catches the UUID-namespaced contract branches, and a
660
+ token with repository admin, contents, pull-request, comment, and branch
661
+ protection permissions. The fixture creates and deletes temporary branches,
662
+ PRs, comments, and a required-check protection rule; it never pushes to or
663
+ merges into the default branch. Use a dedicated scratch repository only.
664
+
665
+ The deterministic Claude, Codex, and Pi adapter contracts run offline in every
666
+ normal test run. Their live registrations add real transport/authentication smoke
667
+ coverage for successful start, continue, end, environment refresh, managed CLI
668
+ execution, and one-shot completion:
669
+
670
+ ```sh
671
+ AB_RUN_LIVE_PORT_CONTRACTS=1 \
672
+ AB_CLAUDE_CONTRACT_MODEL=claude-sonnet-4-… \
673
+ bun test packages/core/src/ports/runner/claude.live.test.ts
674
+
675
+ AB_RUN_LIVE_PORT_CONTRACTS=1 \
676
+ AB_CODEX_CONTRACT_MODEL=gpt-… \
677
+ bun test packages/core/src/ports/runner/codex.live.test.ts
678
+
679
+ AB_RUN_LIVE_PORT_CONTRACTS=1 \
680
+ AB_PI_CONTRACT_MODEL=openai/gpt-… \
681
+ bun test packages/core/src/ports/runner/pi.live.test.ts
682
+ ```
683
+
684
+ Claude uses the locally installed Claude Code CLI and its configured login; the
685
+ live command therefore requires an installed `claude` executable whose browser
686
+ login has already completed. Its offline suite injects a subprocess executor
687
+ and covers direct argv, native resume, stream-json parsing, usage, transcripts,
688
+ and failure classification without launching the CLI. The one-shot argument
689
+ set was checked against Claude Code 2.1.245 `--help` and [Anthropic's CLI
690
+ reference](https://code.claude.com/docs/en/cli-reference): `-p` produces one
691
+ non-interactive result, `--no-session-persistence` prevents resumable storage,
692
+ `--tools ""` disables built-in tools, and `--disallowedTools "mcp__*"` denies
693
+ MCP tools. This is compatibility evidence, not a repository-wide minimum
694
+ version. Codex similarly uses the locally installed `codex` executable after
695
+ `codex login`; its offline suite
696
+ pins direct argv, `$skill` invocation, JSONL parsing, native thread resume,
697
+ startup diagnostics, and tool-free one-shot rejection.
698
+
699
+ Pi requires a locally installed `pi` executable at version 0.84.3 or newer. The
700
+ full AgentRunner smoke uses that installation's login and model catalog for the
701
+ authenticated provider named in `AB_PI_CONTRACT_MODEL`. The same live file also
702
+ checks production extension gating against two packages installed and enabled
703
+ in the operator's user-visible Pi settings:
704
+
705
+ ```sh
706
+ pi install npm:pi-subagents
707
+ pi install npm:pi-web-access
708
+ ```
709
+
710
+ That package probe does not invoke a provider and can be selected without
711
+ `AB_PI_CONTRACT_MODEL`:
712
+
713
+ ```sh
714
+ AB_RUN_LIVE_PORT_CONTRACTS=1 \
715
+ bun test packages/core/src/ports/runner/pi.live.test.ts -t 'gates tools discovered'
716
+ ```
717
+
718
+ Once opted in, the probe fails with the missing expected tool and its exact
719
+ `pi install` remediation if either package is not discoverable; it never skips
720
+ and implies package coverage ran. The live fixtures create isolated temporary
721
+ project skills, extensions, and probe files and remove them after each run;
722
+ provider failures remain in the deterministic injected adapter contracts
723
+ because they cannot be manufactured safely against a live account.
724
+
725
+ Provisioning or scheduling these credentials/resources in CI is deliberately
726
+ out of scope; live runs remain explicit.