@defrex/autobuild 0.10.1 → 0.12.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 (120) hide show
  1. package/README.md +3 -1
  2. package/SPEC.md +237 -23
  3. package/bin/ab-dispatch-kernel.ts +1 -1
  4. package/docs/architecture.md +66 -4
  5. package/docs/configuration.md +137 -19
  6. package/docs/hosted-dispatcher.md +17 -2
  7. package/docs/hosted-service.md +214 -0
  8. package/docs/mcp.md +8 -2
  9. package/docs/operator-api.md +107 -7
  10. package/docs/remote-store-protocol.md +189 -144
  11. package/docs/remote-ticket-protocol.md +111 -18
  12. package/docs/setup.md +173 -16
  13. package/docs/ticket-dependencies.md +1 -1
  14. package/package.json +7 -6
  15. package/packages/core/src/cli/attention.ts +209 -0
  16. package/packages/core/src/cli/binary.ts +3 -3
  17. package/packages/core/src/cli/configured-store.ts +17 -0
  18. package/packages/core/src/cli/connect.ts +261 -0
  19. package/packages/core/src/cli/dashboard/model.ts +8 -5
  20. package/packages/core/src/cli/dashboard/render.ts +37 -7
  21. package/packages/core/src/cli/dispatch-frontend.ts +16 -1
  22. package/packages/core/src/cli/dispatch-process.ts +25 -7
  23. package/packages/core/src/cli/dispatch.ts +247 -47
  24. package/packages/core/src/cli/dispatcher-liveness.ts +91 -0
  25. package/packages/core/src/cli/help.ts +45 -13
  26. package/packages/core/src/cli/init-validation.ts +27 -2
  27. package/packages/core/src/cli/main.ts +41 -15
  28. package/packages/core/src/cli/mcp.ts +7 -1
  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/repository-status.ts +45 -5
  34. package/packages/core/src/cli/self-update.ts +10 -0
  35. package/packages/core/src/cli/store-opening.ts +22 -14
  36. package/packages/core/src/cli/terminals.ts +0 -10
  37. package/packages/core/src/cli/ticket.ts +8 -1
  38. package/packages/core/src/cli/upgrade-compatibility.ts +80 -0
  39. package/packages/core/src/cli/wait.ts +84 -17
  40. package/packages/core/src/cli/watch.ts +350 -68
  41. package/packages/core/src/config/live.ts +4 -0
  42. package/packages/core/src/config/schema.ts +65 -11
  43. package/packages/core/src/connection.ts +86 -0
  44. package/packages/core/src/events/catalog.ts +10 -2
  45. package/packages/core/src/events/globs.ts +8 -3
  46. package/packages/core/src/events/payloads.ts +44 -0
  47. package/packages/core/src/events/repository.ts +64 -2
  48. package/packages/core/src/integration/harness.ts +2 -1
  49. package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +148 -83
  50. package/packages/core/src/kernel/dispatch-status.ts +9 -1
  51. package/packages/core/src/kernel/dispatcher-liveness.ts +70 -0
  52. package/packages/core/src/kernel/engine.ts +24 -0
  53. package/packages/core/src/kernel/generators/build-log.ts +10 -0
  54. package/packages/core/src/kernel/log-index.ts +17 -0
  55. package/packages/core/src/kernel/pr-summary.ts +89 -0
  56. package/packages/core/src/kernel/reducer-registry.ts +12 -0
  57. package/packages/core/src/kernel/reducer.ts +8 -0
  58. package/packages/core/src/kernel/ticket-source-health.ts +30 -0
  59. package/packages/core/src/ontology.ts +1 -0
  60. package/packages/core/src/operator/index.ts +7 -0
  61. package/packages/core/src/operator/query.ts +44 -19
  62. package/packages/core/src/operator/ticket-source-backend.ts +1 -0
  63. package/packages/core/src/operator/tickets.ts +6 -8
  64. package/packages/core/src/plugin-sdk/index.ts +1 -0
  65. package/packages/core/src/plugins/manifest.ts +5 -3
  66. package/packages/core/src/plugins/runtimes.ts +3 -1
  67. package/packages/core/src/ports/forge/fake.ts +13 -1
  68. package/packages/core/src/ports/forge/github-transport.ts +36 -1
  69. package/packages/core/src/ports/forge/github.ts +29 -5
  70. package/packages/core/src/ports/forge/local-git.ts +12 -4
  71. package/packages/core/src/ports/forge/summary-marker.ts +8 -0
  72. package/packages/core/src/ports/runner/session-env.ts +1 -0
  73. package/packages/core/src/ports/tickets/contract.ts +25 -0
  74. package/packages/core/src/ports/tickets/create.ts +11 -3
  75. package/packages/core/src/ports/tickets/fake.ts +10 -8
  76. package/packages/core/src/ports/tickets/file.ts +1 -0
  77. package/packages/core/src/ports/tickets/hosted-index.ts +0 -3
  78. package/packages/core/src/ports/tickets/linear.ts +241 -49
  79. package/packages/core/src/ports/tickets/remote-protocol.ts +3 -0
  80. package/packages/core/src/ports/tickets/remote.ts +1 -0
  81. package/packages/core/src/ports/types.ts +17 -0
  82. package/packages/core/src/ports/workspace/build-execution.ts +5 -1
  83. package/packages/core/src/ports/workspace/create.ts +11 -2
  84. package/packages/core/src/ports/workspace/distribution-archive.ts +3 -5
  85. package/packages/core/src/ports/workspace/harvest-execution.ts +3 -1
  86. package/packages/core/src/ports/workspace/local-build-execution.ts +8 -1
  87. package/packages/core/src/ports/workspace/vercel-sandbox.ts +47 -19
  88. package/packages/core/src/processes/build-execution-state.ts +1 -1
  89. package/packages/core/src/processes/build-runner.ts +87 -26
  90. package/packages/core/src/processes/dispatch-failure-budget.ts +83 -0
  91. package/packages/core/src/processes/dispatcher.ts +336 -83
  92. package/packages/core/src/processes/harvest-child.ts +1 -0
  93. package/packages/core/src/processes/harvest-execution-state.ts +19 -2
  94. package/packages/core/src/processes/harvest-runner.ts +142 -100
  95. package/packages/core/src/processes/phase-failure-budget.ts +1 -0
  96. package/packages/core/src/processes/publication-loss.ts +18 -1
  97. package/packages/core/src/processes/repo-view.ts +2 -1
  98. package/packages/core/src/store/adapter.ts +1 -0
  99. package/packages/core/src/store/contract.ts +109 -0
  100. package/packages/core/src/store/digest.ts +14 -2
  101. package/packages/core/src/store/local/store.ts +18 -5
  102. package/packages/core/src/store/remote/client.ts +1 -1
  103. package/packages/core/src/store/remote/compatibility.ts +90 -0
  104. package/packages/core/src/store/remote/credentials.ts +28 -0
  105. package/packages/core/src/store/remote/index.ts +14 -1
  106. package/packages/core/src/store/remote/protocol.ts +26 -0
  107. package/packages/core/src/store/remote/token.ts +74 -13
  108. package/packages/core/src/store/repo-state-events.ts +49 -59
  109. package/packages/core/src/store/types.ts +5 -1
  110. package/packages/core/src/testing/index.ts +7 -0
  111. package/skills/guide/SKILL.md +175 -26
  112. package/skills/guide/references/operator-api.md +135 -13
  113. package/skills/guide/references/plugin-authoring.md +10 -2
  114. package/skills/guide/references/remote-store-protocol.md +189 -144
  115. package/skills/guide/references/remote-ticket-protocol.md +111 -18
  116. package/skills/guide/references/setup.md +173 -16
  117. package/skills/guide/references/ticket-dependencies.md +1 -1
  118. package/skills/operate/SKILL.md +159 -34
  119. package/templates/hosted-service/package.json +13 -0
  120. package/templates/hosted-service/vercel.json +7 -0
@@ -28,7 +28,7 @@ maintainer before selecting it:
28
28
  - a dispatcher-side push-capable `GITHUB_TOKEN` or `GH_TOKEN`;
29
29
  - for a private repository, a separate read-only clone identity named by
30
30
  `gitUsernameEnv` and `gitPasswordEnv`;
31
- - an HTTPS hosted `AB_STORE` reachable from Vercel and a scoped `AB_TOKEN`;
31
+ - an HTTPS hosted service reachable from Vercel, configured through `ab connect <url>`;
32
32
  - each selected role and alternate's runtime/model, its pinned
33
33
  `[workspace.config.runtimeProvisioning.<runtime>]` commands, and the API
34
34
  credential names it needs in `[workspace.config].environmentVariables`.
@@ -177,7 +177,7 @@ ab dispatch --repository https://github.com/owner/repository \\
177
177
  ```
178
178
 
179
179
  `--repository` (or `AB_REPOSITORY`) is the normalized origin. Origin mode
180
- requires an HTTPS `AB_STORE` plus `AB_TOKEN`, an exported `GITHUB_TOKEN` or
180
+ requires an HTTPS service selected through `[store] url` or `AB_STORE`, a saved project credential or `AB_TOKEN`, an exported `GITHUB_TOKEN` or
181
181
  `GH_TOKEN`, and `forge = "github"`; the interactive dashboard is unavailable
182
182
  (`--plain` or no TTY). Origin mode never consults the gh CLI: a checkout-less
183
183
  host has no gh and no keyring, and the only workspace provider it can run,
@@ -208,17 +208,34 @@ sources agree with the running system by construction:
208
208
  **running dispatcher's version** (read from the deployed distribution's
209
209
  `package.json`), so the guest runs the dispatcher's own release.
210
210
 
211
- The hosted remote store enforces exact **version lockstep**: every client
212
- request must carry an `x-autobuild-version` equal to the server's, and any
213
- mismatch — a guest older *or* newer than the store — is rejected with a 409
214
- `remote store version mismatch`. Guest, store, and dispatcher versions
215
- therefore cannot be mixed: a distribution fix reaches guests only after
216
- `tools/release.ts` cuts a release (credential-gated; requires GitHub
217
- credentials — a documented delivery dependency, not an automatic propagation)
218
- **and** the hosted store and dispatcher are upgraded to that release in the
219
- same lockstep window. If the release is cut but the store upgrade lags, guests
220
- carrying the new distribution fail loudly against the old store (409) rather
221
- than running silently stale.
211
+ A service accepts stable clients from its own `major.minor` release line and
212
+ the two preceding minor lines in the same major. A major starts a new family:
213
+ X.0 accepts only X.0, X.1 accepts X.0/X.1, and X.2 onward accepts three lines.
214
+ Patch versions are interchangeable in both directions and must never change
215
+ the protocol. Upgrade the service first, then projects independently within
216
+ its window. Supported release lines retain their wire handlers and known
217
+ protocol identities; an unknown protocol is refused independently of version.
218
+ Malformed, missing, and prerelease identities are refused with HTTP 409.
219
+ A newer client is told to upgrade the service; a client below the window is
220
+ told to upgrade the project's Autobuild. Diagnostics name the service version
221
+ and supported range.
222
+
223
+ Unauthenticated `GET /health` publishes `ok`, `autobuildVersion`,
224
+ `protocolVersion`, and `supportedClientRange: { minVersion, maxVersionExclusive }`.
225
+ The range is inclusive at the minimum and exclusive at the maximum; a 0.11.x
226
+ service publishes `>=0.9.0 <0.12.0`. Health needs no credentials, version
227
+ headers, or available backend.
228
+
229
+ `ab upgrade` probes this public range before installation or project changes.
230
+ It warns and continues when the target is outside the range, naming the
231
+ required service upgrade for a newer target or a compatible project range for
232
+ an older one. An unavailable or malformed probe warns that connectivity may
233
+ fail after upgrade. Legacy health without a range means exact-version matching;
234
+ upgrade that service to the compatibility-window release first.
235
+
236
+ A distribution fix reaches origin-mode guests after a release is cut and the
237
+ dispatcher is upgraded. Guests continue to run the dispatcher's own version;
238
+ the hosted service must support that release line.
222
239
 
223
240
  Provisioning records the installed distribution's identity — package version
224
241
  and remote-store protocol version — in the guest's
@@ -229,10 +246,10 @@ against the identity the current system would deliver and, when they disagree
229
246
  (which includes every guest provisioned before the marker existed), refreshes
230
247
  it in place (archive re-fetched, extracted, and production-installed) first,
231
248
  so an upgraded dispatcher retrofits its existing guests instead of resuming a
232
- stale distribution the hosted store would reject. A refresh failure deletes
249
+ stale distribution. A refresh failure deletes
233
250
  the sandbox so the next provisioning pass rematerializes it from scratch.
234
251
 
235
- After a lockstep upgrade, confirm a fix-bearing guest from the guest
252
+ After a dispatcher upgrade, confirm a fix-bearing guest from the guest
236
253
  perspective:
237
254
 
238
255
  1. `ab build status <slug> --json` with the ambient build identity for a
@@ -250,7 +267,7 @@ apart from the identity change, `ab dispatch` behaves as it did.
250
267
  A hosted-store deployment configures its web/auth variables once — see the
251
268
  hosted service README and the complete environment reference in the pinned
252
269
  distribution checkout: `BETTER_AUTH_URL`, `AB_WEB_ALLOWED_EMAILS`,
253
- `AB_WEB_REPOSITORIES`, and the optional `AB_WEB_MCP_RESOURCE` — the protected
270
+ and the optional `AB_WEB_MCP_RESOURCE` — the protected
254
271
  resource the hosted MCP server (docs/mcp.md) binds its OAuth tokens to,
255
272
  defaulting to `<BETTER_AUTH_URL>/mcp`. Set it only when a proxy or custom
256
273
  domain fronts the deployment under a different public URL than
@@ -258,6 +275,93 @@ domain fronts the deployment under a different public URL than
258
275
  production) or every MCP client's discovery binds tokens to the wrong
259
276
  resource.
260
277
 
278
+
279
+ ## Connect a project
280
+
281
+ From a checkout with an origin remote, run `ab connect https://YOUR_ORIGIN`.
282
+ The CLI prints a verification URL and short code and attempts to open a browser.
283
+ Sign in as an allowed owner, check the full repository identity and code, and
284
+ approve or deny. Another device can open the printed URL, or visit `/connect`
285
+ and enter the code. Approval requires the web-enabled service origin; a
286
+ standalone machine-only listener cannot present browser consent. Requests
287
+ expire after ten minutes. Denial and timeout leave local configuration unchanged.
288
+
289
+ On approval the service registers the project and issues a project credential.
290
+ The CLI keeps it in `$XDG_CONFIG_HOME/autobuild/credentials` (or
291
+ `~/.config/autobuild/credentials`), keyed by normalized service and origin,
292
+ with directory mode 0700 and file mode 0600. Credentials never belong in the
293
+ repository. It adds the non-secret `[store] url` to `autobuild.toml` when
294
+ absent; review and commit that change yourself. An existing different URL
295
+ must be changed explicitly before connecting. Reconnecting replaces the saved
296
+ credential only after approval and revokes the previous credential.
297
+
298
+ All worktrees share the main checkout's configuration and the user's saved
299
+ credential. A service manager running as that user uses the same HOME and
300
+ XDG_CONFIG_HOME, with no AB_STORE or AB_TOKEN exports. A fresh clone with
301
+ committed store configuration but no saved credential fails with `ab connect`
302
+ guidance; it never silently creates local state. Run connect on each host/user.
303
+
304
+ `ab connect` without a URL reports service, project, and credential status:
305
+ accepted, rejected, missing, or unreachable. `ab disconnect` revokes the saved
306
+ credential remotely before removing it locally, keeping the project registered,
307
+ its data, and store configuration. A network/server failure retains the local
308
+ entry for retry; an already revoked credential can be removed. If saving a new
309
+ connection fails, the CLI cancels/revokes it and retains the previous entry.
310
+ If remote cleanup cannot be confirmed, check the printed credential id with
311
+ the owner's administrative tools before retrying.
312
+
313
+ Store selection is `--store` > nonblank `AB_STORE` > `[store] url` > the
314
+ main checkout's `.autobuild/`. `AB_TOKEN` overrides the exact service/project's
315
+ saved credential for CI and other non-interactive hosts. A service override
316
+ never borrows another service's credential. Build and harvest sessions keep
317
+ their explicit scoped authority.
318
+
319
+ ## Connected projects
320
+
321
+ Migrate PostgreSQL from the pinned release before deploying (`bun run
322
+ postgres:migrate`). The service keeps an independent, durable project registry;
323
+ existing build or ticket data does not register a project. An administrator
324
+ with deployment database credentials manages it without redeploying:
325
+
326
+ ```sh
327
+ ab-hosted-store projects list
328
+ ab-hosted-store projects add https://github.com/owner/project --user owner@example.com
329
+ ab-hosted-store projects remove https://github.com/owner/project --user owner@example.com
330
+ ```
331
+
332
+ From a source checkout, substitute `bun packages/hosted-store-service/src/bin.ts`
333
+ for `ab-hosted-store`. Configure `AB_POSTGRES_URL` or `DATABASE_URL` in that
334
+ process. Database possession grants administration; `--user` is declared audit
335
+ attribution, not a verified login. Ordinary project/store tokens cannot manage
336
+ registrations. Each entry records the normalized origin URL, addition time,
337
+ and adding identity. SSH git spellings normalize to HTTPS; local paths and
338
+ credential-bearing HTTPS URLs are rejected. Duplicate adds preserve metadata;
339
+ remove is idempotent, and re-adding records a fresh addition.
340
+
341
+ Every allowlisted signed-in user owns every registered project. The dashboard,
342
+ MCP discovery and calls, and every operator API route consult current
343
+ registration. Removal hides a project from all these surfaces and subsequent
344
+ hosted dispatcher invocations; builds, events, artifacts and tickets remain,
345
+ and re-registration restores access. Already-started work is not cancelled.
346
+ Project credentials issued by connect authorize only their registered project. Administrative credential issuance remains available for non-interactive hosts.
347
+
348
+ Link directly to `/?repo=<URL-encoded normalized origin>`. The page selects the
349
+ first registered origin when the requested one is missing or removed, and an
350
+ empty registry shows no project data or controls. Registry errors fail closed;
351
+ there is no environment fallback on database failure.
352
+
353
+ `AB_WEB_REPOSITORIES` is deprecated. A nonempty CSV is imported exactly once per
354
+ database, on the first registry initialization with that variable configured,
355
+ with actor `system:AB_WEB_REPOSITORIES`. The import and durable marker commit
356
+ atomically across concurrent starts. An absent variable does not consume the
357
+ marker. Later restarts or edits to the variable never re-add removed projects;
358
+ use the owner commands for subsequent administration.
359
+
360
+ The hosted dispatcher queries current registrations on every authorized
361
+ invocation. With `AB_DISPATCHER_REPOSITORIES` unset, it serves all registered
362
+ projects; an explicit list preserves its order and is intersected with current
363
+ registration. An empty registry is a successful zero-project invocation.
364
+
261
365
  ## Authoring an agent verifier
262
366
 
263
367
  A repository-owned agent-verify skill is a verifier, not a reviewer: it drives
@@ -290,3 +394,56 @@ explanation, never a skip or silent pass. Use `skip` only when the complete step
290
394
  does not apply. If guidance still cannot make a verdict possible, escalate and
291
395
  explain what remains unresolved. Record out-of-scope work with `ab observe`;
292
396
  the verifier does not fix it.
397
+
398
+ ### Project-scoped hosted tickets
399
+
400
+ Hosted ticket queues belong to the credential's registered repository, even
401
+ when projects share a team key. The latest uploaded effective `[tickets]`
402
+ configuration owns lifecycle: hosted defaults are Backlog / Doing / Done,
403
+ `readyState` is required, `doneState` customizes completion, and create/proposal
404
+ default to triage. The union of configured roles is allowed; roles may overlap.
405
+ Owner admin requests require an explicit registered `context.repo`; project
406
+ credentials cannot select another project. Missing effective configuration is
407
+ an actionable refusal: run the project's dispatcher to publish it.
408
+
409
+ The service never contacts Linear. Projects with external sources (including
410
+ file and Linear) keep using those sources directly through their dispatcher;
411
+ service ticket HTTP, operator and MCP surfaces refuse with project/source
412
+ feedback rather than returning an empty queue. Removed `AB_TICKET_BACKEND` and
413
+ `LINEAR_API_KEY` values are ignored with one startup removal message, including
414
+ blank/unknown values. Removed `AB_TICKET_TRIAGE_STATE`, `AB_TICKET_READY_STATE`,
415
+ `AB_TICKET_DOING_STATE` and `AB_TICKET_DONE_STATE` values are ignored even when
416
+ malformed. None of these retired settings prevents startup.
417
+
418
+ The environment-aware migration imports `AB_WEB_REPOSITORIES` once before
419
+ ownership preflight, including released main-v9 deployments without registry
420
+ tables. A failed migration rolls back the import, main-schema promotion and
421
+ ticket changes together. Owner `projects add` and `tickets assign-team` commands
422
+ bootstrap only the registry/recovery journals and work while the main marker
423
+ is still v9. Without the retired environment, first register the intended
424
+ project with `ab-hosted-store projects add REPOSITORY --user IDENTITY`, then
425
+ assign the team and rerun migration. A successful one-time import is never
426
+ reapplied from later environment edits.
427
+
428
+ Ticket schema v1 migration assigns each whole legacy team to its sole claimant
429
+ among registered projects' latest effective configs, reading their actual blob
430
+ bytes. Projects with no dispatcher run or no effective-config reference make
431
+ no team claim and do not block other projects. Unclaimed teams still fail closed.
432
+ When inferring ownership, a selected configuration whose revision/blob is missing,
433
+ unreadable or invalid stops unchanged with recovery guidance. Restore the selected
434
+ configuration or assign every remaining legacy team explicitly; complete owner
435
+ assignments to registered projects bypass unrelated configuration reads. Conflicts
436
+ and asset ownership mismatches also stop unchanged. Resolve ownership, then rerun:
437
+
438
+ ```sh
439
+ ab-hosted-store tickets assign-team TEAM https://github.com/acme/app --user owner@example.com
440
+ ab-postgres-store migrate
441
+ ```
442
+
443
+ The assignment journal works before v2 assertion, retains the actor, and grants
444
+ no future cross-project access. Migration preserves ids, states, comments,
445
+ blockers, timestamps and assets. Populated-v1 migration and deployment builds
446
+ require the running service's blob backend, access mode, credentials and prefix
447
+ (`AB_BLOB_PREFIX`), not just PostgreSQL access. `deploy:build` inherits this
448
+ environment and migration failure stops Next build. Fresh databases, empty v1
449
+ queues and v2 reruns stay SQL-only and do not initialize blob storage.
@@ -2,7 +2,7 @@
2
2
 
3
3
  `ab ticket create --blocked-by` validates prerequisite ids and records blocking
4
4
  relationships, but a single CLI command is **not an atomic publication guarantee**.
5
- In particular, direct Linear and hosted Linear create the issue first and add
5
+ In particular, direct Linear creates the issue first and adds
6
6
  its blocker relationships in separate requests. If the issue is already in the
7
7
  ready state, a dispatcher can see it before those relationships exist. A
8
8
  successful create response containing every blocker does not prove that no build
@@ -24,20 +24,83 @@ after them apply to both.
24
24
 
25
25
  ## Running locally with the `ab` CLI
26
26
 
27
- Run the commands below from the repository root. Each acts with the operator's
28
- authority, so say what you are doing before you do anything destructive.
27
+ Each command acts with the operator's authority, so say what you are doing
28
+ before you do anything destructive.
29
+
30
+ ### Resolve the local invocation first
31
+
32
+ Before your first Autobuild command, read the repository's package manifests
33
+ and package-manager evidence. A direct `@defrex/autobuild` declaration in
34
+ `dependencies` or `devDependencies` requires the repository's installed
35
+ version. The manifest's own `name` and a transitive dependency are not declarations.
36
+
37
+ Determine the package manager from `packageManager` and the lockfile:
38
+ `bun.lock` / `bun.lockb`, `package-lock.json` / `npm-shrinkwrap.json`,
39
+ `pnpm-lock.yaml`, or `yarn.lock`. Resolve workspace context from the manifest
40
+ declaring the dependency and the repository's package-manager configuration.
41
+ If evidence conflicts or cannot establish the manager, diagnose and tell the
42
+ operator instead of guessing.
43
+
44
+ Verify that the dependency is already installed and that its installed package
45
+ exposes the `ab` binary you will execute. For node_modules installs, verify
46
+ that `./node_modules/.bin/ab` resolves to that installed
47
+ `@defrex/autobuild` package, not another package or a global binary. For Yarn
48
+ PnP, use Yarn's installed dependency and binary evidence to establish ownership.
49
+ Run from the resolved repository/workspace directory:
50
+
51
+ | Package manager | Local-only invocation |
52
+ | --- | --- |
53
+ | Bun | `bun run ./node_modules/.bin/ab …` |
54
+ | npm | `npm exec --offline --no --call './node_modules/.bin/ab …'` |
55
+ | pnpm | `pnpm exec ./node_modules/.bin/ab …` |
56
+ | Modern Yarn | `yarn run --binaries-only ab …` |
57
+ | Yarn Classic | `yarn run ab …` |
58
+
59
+ Replace the ellipsis with CLI arguments; for npm they go inside the quoted
60
+ call, with appropriate shell quoting. Do not add `--package` or an inferred
61
+ package name. The explicit path prevents global PATH fallback, and npm's
62
+ offline/no flags prevent registry installation. Modern Yarn's binary-only
63
+ selection supports installed PnP dependencies without assuming node_modules.
64
+ Before using Yarn Classic, verify its installed local binary and check that
65
+ `scripts.ab`, `scripts.preab`, and `scripts.postab` cannot intercept the
66
+ command. Report a script conflict instead of executing it.
67
+
68
+ These forms execute an already-installed dependency. A missing dependency,
69
+ unavailable binary, or failed local execution must be reported to the operator;
70
+ never hide it with global fallback or install dependencies automatically.
71
+ Never use bare `bunx ab`, `npx ab`, `dlx`, or other registry-fetching
72
+ variants: the registry package named `ab` is not Autobuild. Offline cache
73
+ availability alone does not prove a local installation.
74
+
75
+ Only when no local declaration exists may you use global `ab`. In either
76
+ case run the resolved invocation with `--version` and report the invocation,
77
+ working directory, and actual version when you start orchestrating.
78
+
79
+ Every `ab …` example in this skill means this resolved invocation, run from
80
+ the same resolved working directory.
29
81
 
30
82
  ### Watch for attention events
31
83
 
32
- `ab watch --json` streams the attention set (escalations, failures, finalize,
33
- PR conflicted/merged/closed, build completed/aborted) as newline-delimited
34
- records. Every record carries a `cursor`; a bounded watch ends with a trailing
35
- `{"cursor": …}` line. Resume with `--since <cursor>` and every event after it
36
- is delivered exactly once, including events that landed while nothing was
37
- watching. Name slugs to watch only those builds; with none, every active build
38
- (including ones created later) is watched. `--event <glob>` replaces the
39
- default filter, `--timeout <dur>` bounds it (default 30 minutes, `0` is
40
- unbounded), `--count <n>` ends it after n records.
84
+ `ab watch --json` is the standing monitor. Keep its default filter: every
85
+ record calls for action. It reports build escalations and merge decisions,
86
+ harvest escalations/recovery exhaustion, rejected configuration, reloads needing
87
+ restart, abnormal dispatcher stops/loss, degradation past reset (15 minutes
88
+ without reset), and five minutes of continuous tick failures. Advertised resets
89
+ wait for a post-reset tick to finish before requiring action; ordinary recovery
90
+ on that retry stays silent. Source grace and cooldown-driven failures do not
91
+ start the tick-failure clock. Routine outcomes and self-healing failures stay silent.
92
+
93
+ Every record carries a cursor; bounded watches end with a bare `{"cursor": …}`
94
+ line. Resume with `--since <cursor>`. Named watches end when all named builds
95
+ are terminal; add `--repository` for repository attention. With no slugs all
96
+ active builds and repository attention are included. Reconcile status after
97
+ arming because historical build attention is silently baselined.
98
+
99
+ Add an extra monitor only for an identified need, such as an operator asking
100
+ to hear when a particular build merges or an outcome triggering follow-up work.
101
+ Scope it to named slugs and explicit `--event` globs (for example,
102
+ `ab watch my-build --event pr.merged --json`), and retire it when that need ends.
103
+ An extra monitor supplements the default standing watch.
41
104
 
42
105
  `ab wait [<slug>...] (--for <condition> | --event <glob>) [--since <cursor>] [--json]`
43
106
  blocks until a condition holds and prints one record. `--for` state conditions
@@ -51,7 +114,44 @@ bare `{"cursor": …}` to resume from.
51
114
 
52
115
  Run long watches and waits in whatever background or monitor facility your
53
116
  harness provides, so the session stays responsive and you are woken when they
54
- emit. Never block a foreground command for their whole duration.
117
+ emit. Use precisely the same resolved invocation and working directory as
118
+ foreground commands, including inside background shells and harness monitors.
119
+ Do not rely on a shell alias or function that those facilities may lose.
120
+ Never block a foreground command for their whole duration.
121
+
122
+ Ticket-source degradation records mean dispatch intake cannot evaluate the source;
123
+ existing builds may still advance. Inspect `ab repository status --json` for the
124
+ active cause, operation, message, and expected reset. Reset times are advisory;
125
+ recovery means reads actually succeeded. Allow the existing cooldown and retry
126
+ behavior, investigate persistent ready-scan failures or a condition still active
127
+ after its reset estimate, and keep watching through recovery. Empty builds do
128
+ not establish idle intake; diagnose persistent trouble and report it to the operator.
129
+
130
+ Repository records have null build and state. Handle them with repository
131
+ status rather than build commands, keep their cursor, and re-arm. A legacy
132
+ cursor without a repository position baselines that stream at attachment and
133
+ cannot recover its earlier interval: reconcile current repository status.
134
+ `ab wait --for attention` remains build-only and does not replace a repository
135
+ attention watch.
136
+
137
+
138
+ Check dispatcher liveness with `ab repository status --json` after a watch
139
+ timeout or `dispatcher.lost` observation, or when dispatch appears unhealthy. Do not
140
+ use process lists to decide whether a dispatcher owns this repository. The
141
+ `dispatcher` projection distinguishes `held` (an unexpired lease), `lost`
142
+ (expired ownership without a recorded stop), `stopped`, and `absent`. Its holder
143
+ identity resolves a yield message to run, pid, host, start time, and heartbeat
144
+ age. Historical identity and a new holder's heartbeat may be unknown until its
145
+ first attributable renewal. Source degradation or an old tick alone does not
146
+ establish loss: a healthy lease can accompany a slow tick.
147
+
148
+ An unscoped watch, or a named watch with `--repository`, reports loss as a
149
+ read-only observation: `{repo, build:null, state:null, observation:{type:
150
+ "dispatcher.lost", run, observedAt, leaseExpiresAt, ...identity}, cursor}`.
151
+ It has no durable event envelope. Keep its cursor just as for event records;
152
+ resuming it suppresses repeated loss reports for that run. Inspect repository
153
+ status to diagnose the condition and report dispatcher trouble to the operator.
154
+ Neither a loss observation nor a later retry authorizes dispatcher intervention.
55
155
 
56
156
  ### Keep watching until the work is done
57
157
 
@@ -59,8 +159,8 @@ Checking once is not shepherding. When you take responsibility for builds:
59
159
 
60
160
  1. Record the set you are shepherding (`ab builds --all --json`, or the slugs
61
161
  you were given).
62
- 2. Arm an event-only watch and keep its cursor:
63
- `ab watch <slugs…> --json --since <cursor>` (omit `--since` the first
162
+ 2. Arm the default standing watch and keep its cursor:
163
+ `ab watch --json --since <cursor>` (or named slugs with `--repository`) (omit `--since` the first
64
164
  time). Events are delivered exactly once, so a build that already finished
65
165
  is never replayed. Do not loop on `ab wait --for terminal|attention|…`:
66
166
  state conditions are satisfied at once by any build already in that state,
@@ -72,14 +172,16 @@ Checking once is not shepherding. When you take responsibility for builds:
72
172
  current position and does not emit what already happened, so a build that
73
173
  merged, or an escalation already open, before registration would otherwise
74
174
  never reach you. Run `ab build status <slug> --json` for every slug in the
75
- set. Reading status after arming leaves no gap — anything earlier shows in
175
+ set, and `ab repository status --json` for source health, initially and after
176
+ every timeout or legacy-cursor attachment. Reading status after arming leaves no gap — anything earlier shows in
76
177
  the status, anything later arrives as an event. Treat a terminal status
77
178
  (merged, closed, aborted) as that build's outcome, and an open escalation
78
- or failed phase as attention to handle now.
179
+ or merge decision as attention to handle now.
79
180
  4. When a watch returns records, react: read the record, inspect the build,
80
- answer or escalate, then re-arm from the last cursor. Keep your own
81
- outcome table: mark a build done when you see `pr.merged`, `pr.closed`, or
82
- `build.aborted`, or when status shows it. A timeout (exit 3, or a watch
181
+ resolve attention using the shared rules; for a repository record inspect repository status and act
182
+ on its condition without assuming a build slug. Then re-arm from the last cursor. Keep your own
183
+ outcome table from status reconciliation after named-watch exit and bounded
184
+ watch timeouts. Default watches do not emit routine merge/completion records. A timeout (exit 3, or a watch
83
185
  that ends with only a `{"cursor": …}` line) is not completion — reconcile
84
186
  as in step 3, then re-arm from that cursor.
85
187
  5. Stop only when every build in the set has an outcome in your table.
@@ -206,19 +308,24 @@ report it, adapt, and carry on.
206
308
 
207
309
  ### The attention set
208
310
 
209
- When the session has wake settings, the dispatcher wakes you when a build
210
- event matching them lands. A wake turn's first message carries the event
211
- record and the build's reduced state as JSON. Typical wakes and what to do:
212
-
213
- - `escalation.raised` / `phase.failed` / `infrastructure.failed` — read the
214
- escalation and the build's recent events; if the operator notes or the
215
- conversation already answer it, say so; otherwise summarize and decide
216
- whether it needs the operator.
217
- - `finalize.completed` / `build.completed` / `pr.merged` / `pr.closed` /
218
- `pr.conflicted` / `build.aborted` — acknowledge outcome changes that the
219
- operator should know about, and file or move tickets when the outcome
220
- makes that obviously right.
221
- - Anything else matching the wake settings — read the event, then decide.
311
+ A wake turn's first message carries the configured event record and the
312
+ build's reduced state as JSON. Existing hosted wake settings are independent
313
+ of CLI watch defaults; reconcile current state before acting on their routine
314
+ outcomes or self-healing failures.
315
+
316
+ Treat every default-watch record as a call to act:
317
+
318
+ - `escalation.raised`: inspect the question and evidence, then resolve it using
319
+ the shared rules.
320
+ - `build.merge-decision`: inspect the PR and current merge gate, then apply
321
+ the operator's merge policy or request the decision that is missing.
322
+ - Harvest escalation or recovery exhaustion: inspect harvest status and resolve
323
+ the reported blocker.
324
+ - Dispatcher loss, abnormal stop, restart-required reload, rejected config, or
325
+ persistent tick failure: diagnose through repository status and report the
326
+ condition to the operator. Never start or run a dispatcher.
327
+ - Persistent source degradation: inspect the source cause and reset evidence,
328
+ diagnose and report the continuing intake problem.
222
329
 
223
330
  Do not re-derive history the build's events already tell you; read events
224
331
  with a cursor and keep it short.
@@ -227,6 +334,16 @@ with a cursor and keep it short.
227
334
 
228
335
  These apply in both settings. Where the text below says "plain streamed text", a local session means plain text to the human in the session.
229
336
 
337
+ ### Resolve attention by default
338
+
339
+ Make a best effort to resolve attention without operator input. Inspect the
340
+ escalation, spec, code, build artifacts/events, and applicable operator notes.
341
+ Answer questions supported by that evidence, retry when the evidence warrants
342
+ it, and file or move tickets where that is clearly right, within the existing
343
+ ready-state authorization boundary. Investigate before asking; the exceptions
344
+ below govern when the evidence or available tools cannot resolve the issue,
345
+ or a human decision is required.
346
+
230
347
  ### When to escalate instead of act
231
348
 
232
349
  Stop and ask the operator — in plain streamed text — whenever:
@@ -238,11 +355,19 @@ Stop and ask the operator — in plain streamed text — whenever:
238
355
  - A tool refusal repeats and you cannot resolve it with the tools you have.
239
356
  - The attention event is one you cannot act on with the tools you have at all.
240
357
 
241
- Filing a ticket IS acting — do it when the request is concrete. Asking is
242
- not a failure; an unasked destructive action is.
358
+ Filing a ticket is acting — do it when the request is concrete. Default to
359
+ resolving attention yourself within these boundaries; ask the operator only
360
+ for the exceptions above.
243
361
 
244
362
  ### Boundaries
245
363
 
364
+ - Never start, restart, or stop a dispatcher: no `ab dispatch` in any mode,
365
+ and no dispatcher process-management commands. This applies when the
366
+ dispatcher is lost, stopped, absent, or failing repeatedly. Its lease,
367
+ process, and configuration management belong to the operator. Diagnose
368
+ through repository status and report trouble to the operator; reporting
369
+ does not authorize process intervention, including on a later retry.
370
+
246
371
  - You never claim to have done anything outside the tools you have. If you cannot
247
372
  observe it with a tool, say you cannot observe it.
248
373
  - You never invent repository identity, ticket ids, or build slugs; every
@@ -0,0 +1,13 @@
1
+ {
2
+ "private": true,
3
+ "scripts": {
4
+ "build": "ab-hosted-service-build"
5
+ },
6
+ "dependencies": {
7
+ "@defrex/autobuild-hosted-store-service": "0.12.0",
8
+ "@defrex/autobuild": "0.12.0",
9
+ "next": "16.1.6",
10
+ "react": "19.2.4",
11
+ "react-dom": "19.2.4"
12
+ }
13
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "https://openapi.vercel.sh/vercel.json",
3
+ "framework": "nextjs",
4
+ "installCommand": "bun install --frozen-lockfile",
5
+ "buildCommand": "bun run build",
6
+ "bunVersion": "1.4.x"
7
+ }