@defrex/autobuild 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +3 -1
  2. package/SPEC.md +335 -31
  3. package/docs/architecture.md +125 -7
  4. package/docs/configuration.md +142 -21
  5. package/docs/hosted-dispatcher.md +17 -2
  6. package/docs/hosted-service.md +240 -0
  7. package/docs/mcp.md +8 -2
  8. package/docs/operator-api.md +97 -7
  9. package/docs/remote-store-protocol.md +199 -144
  10. package/docs/remote-ticket-protocol.md +249 -19
  11. package/docs/setup.md +199 -16
  12. package/docs/ticket-dependencies.md +1 -1
  13. package/docs/ticket-migration.md +64 -0
  14. package/package.json +7 -6
  15. package/packages/core/src/cli/attention.ts +209 -0
  16. package/packages/core/src/cli/build-control.ts +12 -1
  17. package/packages/core/src/cli/configured-store.ts +17 -0
  18. package/packages/core/src/cli/connect.ts +364 -0
  19. package/packages/core/src/cli/context.ts +7 -1
  20. package/packages/core/src/cli/dashboard/model.ts +4 -5
  21. package/packages/core/src/cli/dashboard/poll.ts +1 -1
  22. package/packages/core/src/cli/dispatch.ts +336 -41
  23. package/packages/core/src/cli/harvest.ts +13 -3
  24. package/packages/core/src/cli/help.ts +51 -16
  25. package/packages/core/src/cli/init-validation.ts +27 -2
  26. package/packages/core/src/cli/installation.ts +67 -13
  27. package/packages/core/src/cli/main.ts +60 -16
  28. package/packages/core/src/cli/mcp.ts +9 -2
  29. package/packages/core/src/cli/pr-summary-publication.ts +220 -0
  30. package/packages/core/src/cli/pr-summary.ts +2 -0
  31. package/packages/core/src/cli/project-credentials.ts +132 -0
  32. package/packages/core/src/cli/repo-state.ts +15 -2
  33. package/packages/core/src/cli/self-update.ts +15 -1
  34. package/packages/core/src/cli/store-opening.ts +22 -14
  35. package/packages/core/src/cli/terminals.ts +0 -10
  36. package/packages/core/src/cli/ticket-assets.ts +7 -2
  37. package/packages/core/src/cli/ticket-editor.ts +82 -0
  38. package/packages/core/src/cli/ticket.ts +172 -35
  39. package/packages/core/src/cli/upgrade-compatibility.ts +80 -0
  40. package/packages/core/src/cli/wait.ts +84 -17
  41. package/packages/core/src/cli/watch.ts +197 -60
  42. package/packages/core/src/config/live.ts +3 -0
  43. package/packages/core/src/config/schema.ts +90 -11
  44. package/packages/core/src/connection.ts +86 -0
  45. package/packages/core/src/events/catalog.ts +11 -2
  46. package/packages/core/src/events/payloads.ts +50 -0
  47. package/packages/core/src/events/repository.ts +13 -0
  48. package/packages/core/src/integration/harness.ts +37 -2
  49. package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +145 -83
  50. package/packages/core/src/kernel/engine.ts +24 -0
  51. package/packages/core/src/kernel/generators/build-log.ts +11 -1
  52. package/packages/core/src/kernel/incremental-fixtures.ts +13 -2
  53. package/packages/core/src/kernel/log-index.ts +17 -0
  54. package/packages/core/src/kernel/pr-summary.ts +89 -0
  55. package/packages/core/src/kernel/reducer-registry.ts +10 -0
  56. package/packages/core/src/kernel/reducer.ts +8 -0
  57. package/packages/core/src/ontology.ts +2 -0
  58. package/packages/core/src/operator/annotations.ts +12 -0
  59. package/packages/core/src/operator/index.ts +12 -0
  60. package/packages/core/src/operator/protocol.ts +19 -2
  61. package/packages/core/src/operator/query.ts +38 -16
  62. package/packages/core/src/operator/registry.ts +61 -8
  63. package/packages/core/src/operator/ticket-source-backend.ts +17 -20
  64. package/packages/core/src/operator/tickets.ts +136 -28
  65. package/packages/core/src/plugin-sdk/index.ts +7 -0
  66. package/packages/core/src/plugins/manifest.ts +5 -3
  67. package/packages/core/src/plugins/runtimes.ts +7 -1
  68. package/packages/core/src/ports/forge/fake.ts +13 -1
  69. package/packages/core/src/ports/forge/github-transport.ts +36 -1
  70. package/packages/core/src/ports/forge/github.ts +29 -5
  71. package/packages/core/src/ports/forge/local-git.ts +12 -4
  72. package/packages/core/src/ports/forge/summary-marker.ts +8 -0
  73. package/packages/core/src/ports/runner/session-env.ts +3 -0
  74. package/packages/core/src/ports/tickets/contract.ts +66 -1
  75. package/packages/core/src/ports/tickets/create.ts +54 -10
  76. package/packages/core/src/ports/tickets/fake.ts +30 -1
  77. package/packages/core/src/ports/tickets/file-assets.ts +159 -0
  78. package/packages/core/src/ports/tickets/file-codec.ts +90 -0
  79. package/packages/core/src/ports/tickets/file.ts +114 -120
  80. package/packages/core/src/ports/tickets/grants.ts +47 -0
  81. package/packages/core/src/ports/tickets/hosted-index.ts +17 -3
  82. package/packages/core/src/ports/tickets/identity.ts +20 -0
  83. package/packages/core/src/ports/tickets/lifecycle.ts +18 -0
  84. package/packages/core/src/ports/tickets/linear.ts +74 -9
  85. package/packages/core/src/ports/tickets/migration.ts +336 -0
  86. package/packages/core/src/ports/tickets/operations.ts +38 -7
  87. package/packages/core/src/ports/tickets/portable-assets.ts +54 -0
  88. package/packages/core/src/ports/tickets/remote-protocol.ts +96 -0
  89. package/packages/core/src/ports/tickets/remote.ts +61 -3
  90. package/packages/core/src/ports/tickets/requested-id.ts +46 -0
  91. package/packages/core/src/ports/tickets/revision.ts +62 -0
  92. package/packages/core/src/ports/tickets/states.ts +61 -0
  93. package/packages/core/src/ports/types.ts +40 -3
  94. package/packages/core/src/ports/workspace/build-execution.ts +6 -1
  95. package/packages/core/src/ports/workspace/create.ts +15 -2
  96. package/packages/core/src/ports/workspace/distribution-archive.ts +3 -5
  97. package/packages/core/src/ports/workspace/harvest-execution.ts +3 -1
  98. package/packages/core/src/ports/workspace/local-build-execution.ts +8 -1
  99. package/packages/core/src/ports/workspace/operator-sandbox.ts +2 -0
  100. package/packages/core/src/ports/workspace/vercel-sandbox.ts +51 -19
  101. package/packages/core/src/processes/build-execution-state.ts +1 -1
  102. package/packages/core/src/processes/build-runner.ts +87 -26
  103. package/packages/core/src/processes/dispatch-failure-budget.ts +83 -0
  104. package/packages/core/src/processes/dispatcher.ts +168 -41
  105. package/packages/core/src/processes/execution-no-progress.ts +176 -0
  106. package/packages/core/src/processes/harvest-child.ts +31 -4
  107. package/packages/core/src/processes/harvest-execution-state.ts +19 -2
  108. package/packages/core/src/processes/harvest-runner.ts +166 -104
  109. package/packages/core/src/processes/harvest.ts +34 -14
  110. package/packages/core/src/processes/phase-failure-budget.ts +2 -0
  111. package/packages/core/src/processes/publication-loss.ts +18 -1
  112. package/packages/core/src/processes/repo-view.ts +35 -0
  113. package/packages/core/src/store/contract.ts +32 -1
  114. package/packages/core/src/store/local/store.ts +1 -1
  115. package/packages/core/src/store/remote/client.ts +1 -1
  116. package/packages/core/src/store/remote/compatibility.ts +90 -0
  117. package/packages/core/src/store/remote/credentials.ts +28 -0
  118. package/packages/core/src/store/remote/index.ts +14 -1
  119. package/packages/core/src/store/remote/protocol.ts +25 -0
  120. package/packages/core/src/store/remote/token.ts +74 -13
  121. package/packages/core/src/store/ticket-asset-snapshots.ts +192 -0
  122. package/packages/core/src/store/ticket-assets.ts +86 -8
  123. package/packages/core/src/store/types.ts +1 -1
  124. package/packages/core/src/testing/index.ts +7 -0
  125. package/skills/guide/SKILL.md +232 -34
  126. package/skills/guide/references/operator-api.md +142 -13
  127. package/skills/guide/references/plugin-authoring.md +10 -2
  128. package/skills/guide/references/remote-store-protocol.md +199 -144
  129. package/skills/guide/references/remote-ticket-protocol.md +253 -18
  130. package/skills/guide/references/setup.md +199 -16
  131. package/skills/guide/references/ticket-dependencies.md +1 -1
  132. package/skills/guide/references/ticket-migration.md +64 -0
  133. package/skills/operate/SKILL.md +126 -36
  134. package/skills/tickets/SKILL.md +71 -0
  135. package/templates/hosted-service/package.json +13 -0
  136. package/templates/hosted-service/vercel.json +7 -0
@@ -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) and repository attention are watched. Named watches need `--repository` to also receive repository attention. `--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,10 @@ 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.
55
121
 
56
122
  Ticket-source degradation records mean dispatch intake cannot evaluate the source;
57
123
  existing builds may still advance. Inspect `ab repository status --json` for the
@@ -59,7 +125,7 @@ active cause, operation, message, and expected reset. Reset times are advisory;
59
125
  recovery means reads actually succeeded. Allow the existing cooldown and retry
60
126
  behavior, investigate persistent ready-scan failures or a condition still active
61
127
  after its reset estimate, and keep watching through recovery. Empty builds do
62
- not establish idle intake; repeatedly restarting dispatch does not clear it.
128
+ not establish idle intake; diagnose persistent trouble and report it to the operator.
63
129
 
64
130
  Repository records have null build and state. Handle them with repository
65
131
  status rather than build commands, keep their cursor, and re-arm. A legacy
@@ -69,8 +135,8 @@ cannot recover its earlier interval: reconcile current repository status.
69
135
  attention watch.
70
136
 
71
137
 
72
- Check dispatcher liveness with `ab repository status --json` before starting another
73
- `ab dispatch`, and after a watch timeout or `dispatcher.lost` observation. Do not
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
74
140
  use process lists to decide whether a dispatcher owns this repository. The
75
141
  `dispatcher` projection distinguishes `held` (an unexpired lease), `lost`
76
142
  (expired ownership without a recorded stop), `stopped`, and `absent`. Its holder
@@ -84,7 +150,8 @@ read-only observation: `{repo, build:null, state:null, observation:{type:
84
150
  "dispatcher.lost", run, observedAt, leaseExpiresAt, ...identity}, cursor}`.
85
151
  It has no durable event envelope. Keep its cursor just as for event records;
86
152
  resuming it suppresses repeated loss reports for that run. Inspect repository
87
- status before deciding how to recover; watch never restarts a dispatcher.
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.
88
155
 
89
156
  ### Keep watching until the work is done
90
157
 
@@ -92,7 +159,7 @@ Checking once is not shepherding. When you take responsibility for builds:
92
159
 
93
160
  1. Record the set you are shepherding (`ab builds --all --json`, or the slugs
94
161
  you were given).
95
- 2. Arm an event-only watch and keep its cursor:
162
+ 2. Arm the default standing watch and keep its cursor:
96
163
  `ab watch --json --since <cursor>` (or named slugs with `--repository`) (omit `--since` the first
97
164
  time). Events are delivered exactly once, so a build that already finished
98
165
  is never replayed. Do not loop on `ab wait --for terminal|attention|…`:
@@ -109,12 +176,12 @@ Checking once is not shepherding. When you take responsibility for builds:
109
176
  every timeout or legacy-cursor attachment. Reading status after arming leaves no gap — anything earlier shows in
110
177
  the status, anything later arrives as an event. Treat a terminal status
111
178
  (merged, closed, aborted) as that build's outcome, and an open escalation
112
- or failed phase as attention to handle now.
179
+ or merge decision as attention to handle now.
113
180
  4. When a watch returns records, react: read the record, inspect the build,
114
- answer or escalate; for a repository record inspect repository status and act
181
+ resolve attention using the shared rules; for a repository record inspect repository status and act
115
182
  on its condition without assuming a build slug. Then re-arm from the last cursor. Keep your own
116
- outcome table: mark a build done when you see `pr.merged`, `pr.closed`, or
117
- `build.aborted`, or when status shows it. A timeout (exit 3, or a watch
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
118
185
  that ends with only a `{"cursor": …}` line) is not completion — reconcile
119
186
  as in step 3, then re-arm from that cursor.
120
187
  5. Stop only when every build in the set has an outcome in your table.
@@ -241,19 +308,24 @@ report it, adapt, and carry on.
241
308
 
242
309
  ### The attention set
243
310
 
244
- When the session has wake settings, the dispatcher wakes you when a build
245
- event matching them lands. A wake turn's first message carries the event
246
- record and the build's reduced state as JSON. Typical wakes and what to do:
247
-
248
- - `escalation.raised` / `phase.failed` / `infrastructure.failed` — read the
249
- escalation and the build's recent events; if the operator notes or the
250
- conversation already answer it, say so; otherwise summarize and decide
251
- whether it needs the operator.
252
- - `finalize.completed` / `build.completed` / `pr.merged` / `pr.closed` /
253
- `pr.conflicted` / `build.aborted` — acknowledge outcome changes that the
254
- operator should know about, and file or move tickets when the outcome
255
- makes that obviously right.
256
- - 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.
257
329
 
258
330
  Do not re-derive history the build's events already tell you; read events
259
331
  with a cursor and keep it short.
@@ -262,6 +334,16 @@ with a cursor and keep it short.
262
334
 
263
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.
264
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
+
265
347
  ### When to escalate instead of act
266
348
 
267
349
  Stop and ask the operator — in plain streamed text — whenever:
@@ -273,11 +355,19 @@ Stop and ask the operator — in plain streamed text — whenever:
273
355
  - A tool refusal repeats and you cannot resolve it with the tools you have.
274
356
  - The attention event is one you cannot act on with the tools you have at all.
275
357
 
276
- Filing a ticket IS acting — do it when the request is concrete. Asking is
277
- 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.
278
361
 
279
362
  ### Boundaries
280
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
+
281
371
  - You never claim to have done anything outside the tools you have. If you cannot
282
372
  observe it with a tool, say you cannot observe it.
283
373
  - You never invent repository identity, ticket ids, or build slugs; every
@@ -9,6 +9,16 @@ The local ticket tracker is a directory of markdown files. `ls` and `mv` are
9
9
  the state/backlog UI; source-agnostic `ab ticket` commands create and edit
10
10
  content and blocker relationships. The default file source needs no secret.
11
11
 
12
+ ## Hosted ticket ids
13
+
14
+ When `[tickets].source = "hosted"`, use readable ids such as `ENG-12` with
15
+ `ab ticket show`, `update`, `move`, `block`, and `unblock`. Inputs accept any
16
+ letter case and old UUID ids remain aliases. Output shows canonical readable
17
+ ids; blocker references use the same identity. Numbering belongs to each project
18
+ and never resets or reuses deleted numbers. A `teamKey` change affects future
19
+ prefixes only. File and Linear id rules are unchanged. See the
20
+ [hosted ticket protocol](../ab-guide/references/remote-ticket-protocol.md).
21
+
12
22
  ## Where it lives
13
23
 
14
24
  `.autobuild/tickets/` by default (if `autobuild.toml` has a `[tickets] dir`,
@@ -189,3 +199,64 @@ the state directories) are file-tracker-only. If this repo's
189
199
  guidance: state and labels live in Linear. The source-agnostic `ab ticket block`
190
200
  and `ab ticket unblock` commands still edit blocker relationships through the
191
201
  configured source.
202
+
203
+ ## Hosted ticket states
204
+
205
+ For `[tickets].source = "hosted"`, states are project-owned service data.
206
+ Run `ab ticket states` before creating a state, and reuse an existing purpose
207
+ when it fits. `ab ticket state create icebox --about "Deferred work"` creates
208
+ an empty state with an optional one-line, nonblank purpose. Custom names use
209
+ one to forty lowercase letters, digits, or hyphens, starting with a letter.
210
+ Hosted creation refuses an existing name in any letter case and never replaces
211
+ its purpose; file creation remains idempotent and can update the purpose.
212
+
213
+ Use `ab ticket create --state <name>`, `ab ticket move <id> <name>`, and
214
+ `ab ticket list --state <name>` with any existing state. Unknown states are
215
+ refused with the available names. Configured states are added automatically;
216
+ former configuration roles remain custom states with their tickets intact.
217
+ Listing puts the configured triage, ready, doing, and done roles first, then
218
+ other states alphabetically, including empty states and purposes. Configured
219
+ names keep their spelling; lookup prefers an exact match, then a unique
220
+ case-insensitive match. States belong to one project.
221
+
222
+ The directory `ls` and `mv` instructions above apply only to the file source.
223
+ For MCP, use `tickets.states` and `tickets.state.create`; the latter requires
224
+ an attributed operator identity and normal project authorization.
225
+
226
+ ## Revision-checked ticket writes
227
+
228
+ Every ticket read includes an opaque `revision`. Pass it as `expectedRevision`
229
+ (on MCP/operator requests) or `--expected-revision <opaque>` on `ab ticket update`,
230
+ `move`, `block` and `unblock`. A stale write fails without changing anything and
231
+ reports the current revision. Omission preserves unguarded behavior. Blocker
232
+ checks cover the entire batch. Dispatcher moves remain unguarded and change the
233
+ revision. Sources without guarded writes, including Linear, refuse supplied
234
+ revisions rather than ignoring them.
235
+
236
+ `ab ticket edit <id>` opens `$VISUAL`, falling back to `$EDITOR`, using TOML
237
+ frontmatter and a Markdown body. Only title, body and labels are editable;
238
+ id, blockers, creation key and reference comments are read-only. It saves only
239
+ changed fields with the opening revision. Unchanged or formatting-only edits
240
+ write nothing. On conflict, invalid input or editor failure, the error names an
241
+ absolute file path containing the edited text. Read the current ticket and
242
+ reapply the intended changes before retrying; conflicts are never merged.
243
+
244
+ File and Linear revisions fingerprint title, body, canonical label/blocker sets
245
+ and state; reverting identical content restores its fingerprint. File checks
246
+ re-read immediately before writing, with the existing no-lock race window.
247
+ Direct file edits and state-directory moves change revisions. File comments
248
+ change the body and revision; separate hosted comments do not. Hosted revisions
249
+ are monotonic decimal bigint strings, and checks run under the same row lock
250
+ and transaction as all requested changes. Hosted reads return a coherent
251
+ snapshot of fields, blockers and revision.
252
+
253
+ Read before writing, and copy the opaque revision verbatim:
254
+
255
+ ```sh
256
+ ab ticket show AUT-8 --json
257
+ ab ticket update AUT-8 --title 'Revised title' --expected-revision '<revision from show>'
258
+ ab ticket edit AUT-8
259
+ ```
260
+
261
+ Use `expectedRevision` on MCP `tickets.update`, `tickets.move`, `tickets.block`
262
+ and `tickets.unblock` with the revision from `tickets.get` or `tickets.list`.
@@ -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.13.0",
8
+ "@defrex/autobuild": "0.13.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
+ }