@defrex/autobuild 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SPEC.md +119 -15
- package/docs/architecture.md +59 -3
- package/docs/configuration.md +41 -9
- package/docs/hosted-service.md +31 -5
- package/docs/operator-api.md +7 -0
- package/docs/remote-store-protocol.md +10 -0
- package/docs/remote-ticket-protocol.md +151 -9
- package/docs/setup.md +33 -7
- package/docs/ticket-migration.md +64 -0
- package/package.json +3 -3
- package/packages/core/src/cli/build-control.ts +12 -1
- package/packages/core/src/cli/connect.ts +141 -38
- package/packages/core/src/cli/context.ts +7 -1
- package/packages/core/src/cli/dashboard/poll.ts +1 -1
- package/packages/core/src/cli/dispatch.ts +156 -3
- package/packages/core/src/cli/harvest.ts +13 -3
- package/packages/core/src/cli/help.ts +14 -8
- package/packages/core/src/cli/installation.ts +67 -13
- package/packages/core/src/cli/main.ts +20 -2
- package/packages/core/src/cli/mcp.ts +2 -1
- package/packages/core/src/cli/self-update.ts +5 -1
- package/packages/core/src/cli/ticket-assets.ts +7 -2
- package/packages/core/src/cli/ticket-editor.ts +82 -0
- package/packages/core/src/cli/ticket.ts +164 -34
- package/packages/core/src/config/schema.ts +35 -3
- package/packages/core/src/connection.ts +3 -3
- package/packages/core/src/events/catalog.ts +3 -2
- package/packages/core/src/events/payloads.ts +6 -0
- package/packages/core/src/integration/harness.ts +37 -2
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +15 -0
- package/packages/core/src/kernel/generators/build-log.ts +1 -1
- package/packages/core/src/kernel/incremental-fixtures.ts +13 -2
- package/packages/core/src/kernel/reducer-registry.ts +2 -0
- package/packages/core/src/ontology.ts +1 -0
- package/packages/core/src/operator/annotations.ts +12 -0
- package/packages/core/src/operator/index.ts +5 -0
- package/packages/core/src/operator/protocol.ts +19 -2
- package/packages/core/src/operator/registry.ts +61 -8
- package/packages/core/src/operator/ticket-source-backend.ts +17 -21
- package/packages/core/src/operator/tickets.ts +130 -20
- package/packages/core/src/plugin-sdk/index.ts +6 -0
- package/packages/core/src/plugins/runtimes.ts +5 -1
- package/packages/core/src/ports/runner/session-env.ts +2 -0
- package/packages/core/src/ports/tickets/contract.ts +66 -1
- package/packages/core/src/ports/tickets/create.ts +48 -12
- package/packages/core/src/ports/tickets/fake.ts +30 -1
- package/packages/core/src/ports/tickets/file-assets.ts +159 -0
- package/packages/core/src/ports/tickets/file-codec.ts +90 -0
- package/packages/core/src/ports/tickets/file.ts +114 -120
- package/packages/core/src/ports/tickets/grants.ts +47 -0
- package/packages/core/src/ports/tickets/hosted-index.ts +17 -0
- package/packages/core/src/ports/tickets/identity.ts +20 -0
- package/packages/core/src/ports/tickets/lifecycle.ts +18 -0
- package/packages/core/src/ports/tickets/linear.ts +74 -9
- package/packages/core/src/ports/tickets/migration.ts +336 -0
- package/packages/core/src/ports/tickets/operations.ts +38 -7
- package/packages/core/src/ports/tickets/portable-assets.ts +54 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +95 -0
- package/packages/core/src/ports/tickets/remote.ts +60 -3
- package/packages/core/src/ports/tickets/requested-id.ts +46 -0
- package/packages/core/src/ports/tickets/revision.ts +62 -0
- package/packages/core/src/ports/tickets/states.ts +61 -0
- package/packages/core/src/ports/types.ts +40 -3
- package/packages/core/src/ports/workspace/build-execution.ts +1 -0
- package/packages/core/src/ports/workspace/create.ts +5 -1
- package/packages/core/src/ports/workspace/operator-sandbox.ts +2 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +8 -4
- package/packages/core/src/processes/dispatcher.ts +110 -32
- package/packages/core/src/processes/execution-no-progress.ts +176 -0
- package/packages/core/src/processes/harvest-child.ts +30 -4
- package/packages/core/src/processes/harvest-runner.ts +24 -4
- package/packages/core/src/processes/harvest.ts +34 -14
- package/packages/core/src/processes/phase-failure-budget.ts +1 -0
- package/packages/core/src/processes/repo-view.ts +35 -0
- package/packages/core/src/store/contract.ts +32 -1
- package/packages/core/src/store/local/store.ts +1 -1
- package/packages/core/src/store/ticket-asset-snapshots.ts +192 -0
- package/packages/core/src/store/ticket-assets.ts +86 -8
- package/packages/core/src/store/types.ts +1 -1
- package/skills/guide/SKILL.md +91 -11
- package/skills/guide/references/operator-api.md +7 -0
- package/skills/guide/references/remote-store-protocol.md +10 -0
- package/skills/guide/references/remote-ticket-protocol.md +151 -9
- package/skills/guide/references/setup.md +33 -7
- package/skills/guide/references/ticket-migration.md +64 -0
- package/skills/tickets/SKILL.md +71 -0
- package/templates/hosted-service/package.json +2 -2
package/skills/guide/SKILL.md
CHANGED
|
@@ -197,7 +197,9 @@ The distinctions that change an administrator's answer:
|
|
|
197
197
|
agent turn or command already running. Missing, unreadable, malformed, and
|
|
198
198
|
routing-invalid candidates retain that snapshot with deduplicated operator
|
|
199
199
|
notice, and restoring a valid file resumes reload. Scoped phase CLI processes
|
|
200
|
-
still read the build worktree's branch-owned file.
|
|
200
|
+
still read the build worktree's branch-owned file. Dispatcher merge readiness,
|
|
201
|
+
resume, and execution recovery use the runner's pinned commands, verify, and
|
|
202
|
+
finalize configuration; builds without a pin retain the live fallback.
|
|
201
203
|
- **Workspaces** are provisioned per build and remain the locally reachable
|
|
202
204
|
execution context for that build.
|
|
203
205
|
- **Ticket providers** (`linear`, `file`) sit behind one port; the dispatcher
|
|
@@ -215,6 +217,22 @@ The distinctions that change an administrator's answer:
|
|
|
215
217
|
guidance and later dispatcher ticks retry. Without consent it remains open for
|
|
216
218
|
ordinary `git diff` inspection; it has no review UI or image host.
|
|
217
219
|
|
|
220
|
+
For moving a project between sources, follow the [ticket migration procedure](references/ticket-migration.md).
|
|
221
|
+
|
|
222
|
+
## Independent hosted tickets
|
|
223
|
+
|
|
224
|
+
Hosted tickets support local builds. Configure `[tickets] source = "hosted"`,
|
|
225
|
+
`teamKey`, and `url`, or run `ab connect --tickets URL` to register the project,
|
|
226
|
+
save its credential and publish ticket policy without changing the build store.
|
|
227
|
+
Without `tickets.url`, tickets use the selected remote build service; local builds
|
|
228
|
+
require an explicit ticket URL. No dispatcher is needed for ticket commands.
|
|
229
|
+
Hosted defaults are `triage`, `ready`, `doing`, `done`; create/proposal use triage.
|
|
230
|
+
Other sources still require readyState. Explicit state names retain their spelling.
|
|
231
|
+
Legacy clients without independent policy retain uploaded configuration defaults.
|
|
232
|
+
Harvest ticket grants are service-bound read/create authority; child phases are
|
|
233
|
+
read-only and cannot publish policy. See [setup](references/setup.md) and the
|
|
234
|
+
[ticket protocol](references/remote-ticket-protocol.md).
|
|
235
|
+
|
|
218
236
|
## `autobuild.toml` reference
|
|
219
237
|
|
|
220
238
|
One declarative file at the repo root. **Declarative, not executable** —
|
|
@@ -737,6 +755,21 @@ exhaustion dispatch parks durably; bare `ab answer <slug>` and text guidance
|
|
|
737
755
|
both grant a fresh budget, while abort terminates without retrying. Workspace
|
|
738
756
|
failures owned by provider infrastructure recovery use only its existing budget.
|
|
739
757
|
|
|
758
|
+
The dispatcher also applies a fixed, non-configurable limit of three consecutive
|
|
759
|
+
executions that complete normally with exit code zero and no durable build
|
|
760
|
+
progress. Execution start/end, runner attachment and workspace lifecycle
|
|
761
|
+
bookkeeping do not count as progress. Pipeline, session, spec, publication/PR,
|
|
762
|
+
observation and acknowledged operator facts reset the streak; infrastructure,
|
|
763
|
+
budget and provider failures do not consume the allowance and retain their own
|
|
764
|
+
retry limits. Before a fourth actionable launch, the dispatcher raises one
|
|
765
|
+
setup-targeted `execution-no-progress` policy escalation naming the count and
|
|
766
|
+
the next action it expected the runner to take. Waiting, paused and terminal
|
|
767
|
+
builds are left parked, and pending operator commands remain executable.
|
|
768
|
+
Answering that escalation with guidance or a bare `ab answer <slug>` retry grants
|
|
769
|
+
three fresh executions. The guard is derived from durable facts, including on
|
|
770
|
+
supervisor restart, and uses the ordinary watch, dashboard and operator API
|
|
771
|
+
attention paths.
|
|
772
|
+
|
|
740
773
|
For each repeat conflict, Autobuild compares the base merged by the latest
|
|
741
774
|
completed reconcile with a fresh authoritative base snapshot. An advanced base
|
|
742
775
|
proves the attempt lost a race and permits another reconcile regardless of the
|
|
@@ -788,13 +821,14 @@ state eligible.
|
|
|
788
821
|
|---|---|---|---|
|
|
789
822
|
| `source` | — | **required**, nonblank builtin or plugin registration name | Which provider backs all ticket operations. Unknown names report every available builtin and plugin source. |
|
|
790
823
|
| `readyLabels` | — (source-aware) | optional; array of nonempty strings | A ticket must carry **every** listed label to be dispatchable. `[]` = **no label gate**. Absent uses the source default below. |
|
|
791
|
-
| `
|
|
824
|
+
| `url` | selected build service | HTTPS URL (loopback HTTP allowed), no userinfo/query/fragment; hosted only | Ticket service independent of builds; required for local builds. |
|
|
825
|
+
| `readyState` | hosted: `ready` | non-blank string; required for other sources | The one workflow state a ticket must sit in to be dispatchable. Linear matches exactly and case-sensitively; file matches it case-insensitively to a state directory (`ready` → `ready/`). There is no any-state mode. |
|
|
792
826
|
| `readyAssignee` | — | optional; `"me"` or an email; `linear` only | Also require the ticket to be assigned to that Linear user (`me` = owner of `LINEAR_API_KEY`); unassigned tickets are never ready. |
|
|
793
|
-
| `teamKey` | — | required by `linear` and `hosted`, forbidden by `file`, allowed for plugins | The Linear team key (e.g. `"ENG"`) or
|
|
827
|
+
| `teamKey` | — | required by `linear` and `hosted`, forbidden by `file`, allowed for plugins | The Linear team key (e.g. `"ENG"`), the captured prefix for new hosted ticket ids (`ENG-1`), or a plugin configuration field. Hosted numbers are project-local and continue across prefix changes; legacy ids remain aliases and readable inputs are case-insensitive. |
|
|
794
828
|
| `claimedState` | — | optional nonempty string; forbidden by `file`, allowed for plugins | Workflow state `claim()` moves a ticket to when a build starts. |
|
|
795
|
-
| `doneState` | hosted: `"
|
|
829
|
+
| `doneState` | hosted: `"done"` | optional nonblank string | Project completion state and merged-ticket transition; hosted tickets only. |
|
|
796
830
|
| `createState` | — | optional, nonempty string | Default state for new tickets when a create does not name one. Absent = the provider's default (Linear: the team's default, e.g. Backlog; file: Triage). |
|
|
797
|
-
| `triageState` | — | optional, nonempty string | State the dispatcher hands tickets back to for human triage — spec-gate bounces, aborted builds, closed-unmerged PRs. Absent = Linear
|
|
831
|
+
| `triageState` | — | optional, nonempty string | State the dispatcher hands tickets back to for human triage — spec-gate bounces, aborted builds, closed-unmerged PRs. Absent = Linear: Backlog; hosted: triage; file/plugin: Triage. Must name a state the tracker actually has — a Linear team only has "Triage" when its triage feature is enabled. |
|
|
798
832
|
| `proposalState` | — | optional, nonempty string | State observation harvest files approved proposals into. Absent = the resolved `triageState`, which keeps the human grooming gate. Setting it to `readyState` waives that gate: harvested proposals dispatch without being read. Keep it distinct from `triageState` — see below. |
|
|
799
833
|
| `dir` | file: `.autobuild/tickets`; plugin: — | optional nonempty string; forbidden by `linear`, allowed for plugins | Root holding file state directories, or an existing plugin configuration field. |
|
|
800
834
|
|
|
@@ -1214,16 +1248,19 @@ transitions, so an invalid state fails with the
|
|
|
1214
1248
|
source's known states. A missing id fails nonzero with an error naming both the
|
|
1215
1249
|
id and configured source.
|
|
1216
1250
|
|
|
1217
|
-
Human-readable output is the default and must not be parsed for ids.
|
|
1218
|
-
|
|
1251
|
+
Human-readable output is the default and must not be parsed for ids. The CRUD
|
|
1252
|
+
subcommands accept `--json`, which emits one bare JSON value and no prose: a
|
|
1219
1253
|
`Ticket[]` for `list`, and the complete resulting `Ticket` for `create`,
|
|
1220
1254
|
`update`, `block`, `unblock`, `show`, or `move`.
|
|
1221
1255
|
|
|
1222
1256
|
### Ticket assets
|
|
1223
1257
|
|
|
1224
|
-
A ticket asset is a file or folder attached to a ticket
|
|
1225
|
-
|
|
1226
|
-
|
|
1258
|
+
A ticket asset is a file or folder attached to a ticket: a design export,
|
|
1259
|
+
reference screenshots, or a sample payload. Hosted tickets store assets in their
|
|
1260
|
+
ticket service, independently of the build store; file tickets use native portable
|
|
1261
|
+
assets composed with legacy BuildStore attachments. Sources without native assets
|
|
1262
|
+
use the BuildStore. Existing service assets need no migration. Old local hosted-ticket
|
|
1263
|
+
assets are not moved and are not a fallback. Each has a kind and a name.
|
|
1227
1264
|
|
|
1228
1265
|
- `ab ticket attach <id> <kind> <path> [--name <name>] [--json]` stores `<path>`
|
|
1229
1266
|
(default name: its basename). Attaching the same kind and name again makes a
|
|
@@ -1235,7 +1272,10 @@ a sample payload. Each has a kind and a name.
|
|
|
1235
1272
|
- `ab ticket asset get <id> <kind> <name> <dest> [--rev <n>]` downloads the
|
|
1236
1273
|
exact bytes of the latest (or given) revision; `ab ticket asset rm <id> <kind>
|
|
1237
1274
|
<name>` drops it from the current assets.
|
|
1238
|
-
- **Frozen at claim.** A claim pins the current revisions into the build.
|
|
1275
|
+
- **Frozen at claim.** A claim pins the current revisions into the build.
|
|
1276
|
+
Hosted revisions are copied into immutable snapshots in the build store,
|
|
1277
|
+
preserving revision numbers and allowing reads without the ticket service.
|
|
1278
|
+
Old pins without snapshots keep reading their original ticket rows. A
|
|
1239
1279
|
later attach or remove leaves a running build alone; a human answer with
|
|
1240
1280
|
`--revise-spec-from-ticket` pins again. `ab build asset get <slug> <kind>
|
|
1241
1281
|
<name> <dest> [--rev <n>]` downloads what the build froze, also after it ends.
|
|
@@ -2038,3 +2078,43 @@ build phase, which is the criterion for membership (§16.3): `ab-spec` and
|
|
|
2038
2078
|
`ab-tickets` are human/agent-facing pre-build surfaces, `ab-guide` is
|
|
2039
2079
|
read-only reference including the setup handoff guidance, and `ab-operate` is
|
|
2040
2080
|
the manual for the orchestrator role, which any agent may take on.
|
|
2081
|
+
|
|
2082
|
+
## Revision-checked ticket writes
|
|
2083
|
+
|
|
2084
|
+
Every ticket read includes an opaque `revision`. Pass it as `expectedRevision`
|
|
2085
|
+
(on MCP/operator requests) or `--expected-revision <opaque>` on `ab ticket update`,
|
|
2086
|
+
`move`, `block` and `unblock`. A stale write fails without changing anything and
|
|
2087
|
+
reports the current revision. Omission preserves unguarded behavior. Blocker
|
|
2088
|
+
checks cover the entire batch. Dispatcher moves remain unguarded and change the
|
|
2089
|
+
revision. Sources without guarded writes, including Linear, refuse supplied
|
|
2090
|
+
revisions rather than ignoring them.
|
|
2091
|
+
|
|
2092
|
+
`ab ticket edit <id>` opens `$VISUAL`, falling back to `$EDITOR`, using TOML
|
|
2093
|
+
frontmatter and a Markdown body. Only title, body and labels are editable;
|
|
2094
|
+
id, blockers, creation key and reference comments are read-only. It saves only
|
|
2095
|
+
changed fields with the opening revision. Unchanged or formatting-only edits
|
|
2096
|
+
write nothing. On conflict, invalid input or editor failure, the error names an
|
|
2097
|
+
absolute file path containing the edited text. Read the current ticket and
|
|
2098
|
+
reapply the intended changes before retrying; conflicts are never merged.
|
|
2099
|
+
|
|
2100
|
+
File and Linear revisions fingerprint title, body, canonical label/blocker sets
|
|
2101
|
+
and state; reverting identical content restores its fingerprint. File checks
|
|
2102
|
+
re-read immediately before writing, with the existing no-lock race window.
|
|
2103
|
+
Direct file edits and state-directory moves change revisions. File revisions also
|
|
2104
|
+
cover structured comments and portable attachments. Legacy file comments change
|
|
2105
|
+
the body; structured comments preserve it. Separate hosted comments do not change
|
|
2106
|
+
ticket revisions. Hosted revisions
|
|
2107
|
+
are monotonic decimal bigint strings, and checks run under the same row lock
|
|
2108
|
+
and transaction as all requested changes. Hosted reads return a coherent
|
|
2109
|
+
snapshot of fields, blockers and revision.
|
|
2110
|
+
|
|
2111
|
+
Read before writing, and copy the opaque revision verbatim:
|
|
2112
|
+
|
|
2113
|
+
```sh
|
|
2114
|
+
ab ticket show AUT-8 --json
|
|
2115
|
+
ab ticket update AUT-8 --title 'Revised title' --expected-revision '<revision from show>'
|
|
2116
|
+
ab ticket edit AUT-8
|
|
2117
|
+
```
|
|
2118
|
+
|
|
2119
|
+
Use `expectedRevision` on MCP `tickets.update`, `tickets.move`, `tickets.block`
|
|
2120
|
+
and `tickets.unblock` with the revision from `tickets.get` or `tickets.list`.
|
|
@@ -87,6 +87,7 @@ turn runner (a later ticket) is the only producer of turn facts.
|
|
|
87
87
|
| `GET …/harvest/status` | Harvest gate, runs, steps, recovery, and attention projection. |
|
|
88
88
|
| `GET …/builds/{slug}/artifacts/{kind}?rev=N` | Raw bytes (`application/octet-stream`) plus content-disposition and `X-Autobuild-Artifact-*` metadata headers. Omit `rev` for latest. |
|
|
89
89
|
| `GET …/tickets?state=S&label=L` | `{states,tickets,diagnostics,criteria,triageState,readyState}`. The two lifecycle names come from effective repository configuration (including provider fallback). With no `state`, `criteria.state` is `triageState`; repeated `label` parameters narrow that state with AND filters. An explicit `state` selects that exact backend workflow state. |
|
|
90
|
+
| `GET …/tickets/states` | Full state metadata `{name,tickets,about?}[]`, including empty states (`tickets.states` in MCP). |
|
|
90
91
|
| `GET …/tickets/{id}` | `{ticket,blockers,build}`. Blockers include native `exists`/`resolved` status; `build` prefers an active matching repository build, otherwise the most recently updated one. |
|
|
91
92
|
|
|
92
93
|
For artifact reads, an absent `rev` selects the latest revision. A supplied value
|
|
@@ -119,6 +120,7 @@ JSON bodies are strict: unknown fields are rejected.
|
|
|
119
120
|
| `POST …/settings/auto-merge-default/toggle` | Empty body. |
|
|
120
121
|
| `POST …/bulk-control` | `{"action":"pause"\|"resume"}`. The hold fact, intake fact, then eligible build events are written in that order. |
|
|
121
122
|
| `POST …/harvest/control` | `{"action":"toggle-gate"}` or `{"action":"run","run":"…"}`. A run action is bound to that concrete projected run. |
|
|
123
|
+
| `POST …/tickets/states` | `{name,about?}` → `{state,created}`; requires attributed operator identity (`tickets.state.create` in MCP). Hosted duplicate names are refused case-insensitively. |
|
|
122
124
|
| `POST …/tickets` | `{"title":"…","body":"…","labels"?:[],"state"?:"…","blockedBy"?:[]}`; omitted state preserves the backend default. |
|
|
123
125
|
| `PATCH …/tickets/{id}` | Any nonempty subset of `title`, `body`, and complete-replacement `labels`. Body bytes are not normalized. |
|
|
124
126
|
| `POST …/tickets/{id}/move` | `{"state":"backend state name"}`. |
|
|
@@ -284,3 +286,8 @@ even with `--since`; live and backlog matches ignore routine outcomes and
|
|
|
284
286
|
self-healing failures. A merge match uses the watch observation with
|
|
285
287
|
`condition: "attention"`. Explicit `--event` globs remain event-only.
|
|
286
288
|
Watch and wait preserve each other's cursor notification metadata.
|
|
289
|
+
|
|
290
|
+
Hosted ticket tools resolve independent project ticket policy before legacy uploaded
|
|
291
|
+
configuration. Queue, detail, state discovery and mutations work before any build
|
|
292
|
+
or dispatcher run; detail reports a null matching build. Ticket policy publication
|
|
293
|
+
never creates synthetic build or repository history.
|
|
@@ -485,6 +485,16 @@ does not apply. `{repo}` and `{id}` are percent-encoded path segments. Token
|
|
|
485
485
|
authorization requires a credential for that registered project, or owner admin.
|
|
486
486
|
The family is additive, so `REMOTE_STORE_PROTOCOL_VERSION` did not change.
|
|
487
487
|
|
|
488
|
+
Hosted ticket attachments use the ticket service's existing repository-keyed
|
|
489
|
+
asset routes with the ticket connection's URL and credential, independently
|
|
490
|
+
of the build store. Names, revisions, tombstones and service limits are
|
|
491
|
+
unchanged. Existing service attachments need no migration; old local
|
|
492
|
+
attachments are not moved or used as a fallback. A claim or ticket spec re-pin
|
|
493
|
+
copies exact revisions into immutable chunked repository artifacts in the
|
|
494
|
+
destination build store. Build pins reference explicit snapshot manifests;
|
|
495
|
+
build-scoped asset reads need no ticket credentials. Old pins without snapshot
|
|
496
|
+
references keep their existing ticket-row lookup.
|
|
497
|
+
|
|
488
498
|
A build also reads the assets it froze at its claim through its own build-scoped
|
|
489
499
|
routes, authorized against the build's actual project. The
|
|
490
500
|
server reduces the build's events to find the pinned revision, then returns that
|
|
@@ -67,6 +67,16 @@ must match the credential's project; mismatches are 403. Owner admin requests
|
|
|
67
67
|
require this selector and registration is checked before configuration is read.
|
|
68
68
|
Internal operator credentials remain forbidden on machine ticket routes.
|
|
69
69
|
|
|
70
|
+
Hosted ticket attachments use the ticket service's existing repository-keyed
|
|
71
|
+
asset routes with the ticket connection's URL and credential, independently
|
|
72
|
+
of the build store. Names, revisions, tombstones and service limits are
|
|
73
|
+
unchanged. Existing service attachments need no migration; old local
|
|
74
|
+
attachments are not moved or used as a fallback. A claim or ticket spec re-pin
|
|
75
|
+
copies exact revisions into immutable chunked repository artifacts in the
|
|
76
|
+
destination build store. Build pins reference explicit snapshot manifests;
|
|
77
|
+
build-scoped asset reads need no ticket credentials. Old pins without snapshot
|
|
78
|
+
references keep their existing ticket-row lookup.
|
|
79
|
+
|
|
70
80
|
The strict request envelope is:
|
|
71
81
|
|
|
72
82
|
```json
|
|
@@ -82,16 +92,37 @@ The strict request envelope is:
|
|
|
82
92
|
|
|
83
93
|
`teamKey` remains required for older client compatibility. It and the optional
|
|
84
94
|
`claimedState` / `createState` fields are ignored as policy inputs. The service
|
|
85
|
-
loads the project's
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
loads the project's durable independent ticket policy. Project authority publishes
|
|
96
|
+
version 1 policy through `POST /tickets/policy`, using the usual context/input
|
|
97
|
+
envelope. The input contains only `version` and `tickets`: source, teamKey,
|
|
98
|
+
readyState, triageState, claimedState, doneState, createState, proposalState and
|
|
99
|
+
optional readyLabels. URL, secrets, and pipeline configuration are rejected.
|
|
100
|
+
Only project or owner authority can publish. This does not append BuildStore
|
|
101
|
+
history. Registration removal invalidates policy and grants from that registration.
|
|
102
|
+
|
|
103
|
+
For compatibility, projects without independent policy use their latest uploaded
|
|
104
|
+
dispatcher configuration with the old `Backlog`, `Doing`, `Done` defaults.
|
|
105
|
+
New policy defaults are `triage`, `ready`, `doing`, `done`; create/proposal follow
|
|
106
|
+
triage. States are persistent project data, including empty states and optional
|
|
107
|
+
one-line purposes. Configured role names always exist; removed roles remain as
|
|
108
|
+
custom states and keep their tickets. Explicit settings preserve state spelling.
|
|
109
|
+
State lookup prefers an exact match, then a unique case-insensitive match;
|
|
110
|
+
unknown or ambiguous names are refused. Lists order configured triage, ready,
|
|
111
|
+
doing, done first (deduplicated), then other names alphabetically.
|
|
112
|
+
|
|
113
|
+
`[tickets] url` selects the ticket service independently of the build store;
|
|
114
|
+
otherwise the selected remote build store is used. A local build store requires
|
|
115
|
+
an explicit ticket URL. Credentials are resolved for that selected service.
|
|
116
|
+
`ab connect --tickets URL` registers and publishes policy before any dispatcher run.
|
|
89
117
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
118
|
+
`POST /tickets/grants` issues, renews or revokes short-lived, digest-stored ticket
|
|
119
|
+
grants. Project authority issues runner grants scoped to project and execution;
|
|
120
|
+
runners may issue only read-only phase children bound to themselves. Runner grants
|
|
121
|
+
allow read and create; phase grants allow read only. Policy, claim, moves, comments,
|
|
122
|
+
updates, blocker changes, BuildStore, and credential administration are denied.
|
|
123
|
+
AB_TICKET_URL and AB_TICKET_TOKEN carry this authority separately from AB_STORE and
|
|
124
|
+
AB_TOKEN. Revocation, expiry, parent revocation and registration changes invalidate
|
|
125
|
+
grants. Existing combined-service BuildStore delegation remains supported.
|
|
95
126
|
|
|
96
127
|
The service never contacts Linear or relays external trackers. If the project's
|
|
97
128
|
source is Linear, file or another non-hosted source, machine HTTP, operator and
|
|
@@ -111,6 +142,8 @@ The operation names and `input` values are:
|
|
|
111
142
|
|
|
112
143
|
| Operation | Input | Successful response |
|
|
113
144
|
|---|---|---|
|
|
145
|
+
| `states` | `{}` | `{name, tickets, about?}[]`, including empty states |
|
|
146
|
+
| `add-state` | `{name, about?: string}` | `{state: {name,tickets,about?}, created: boolean}` |
|
|
114
147
|
| `list-ready` | `{labels?: string[], state?: string}` | `TicketListing` |
|
|
115
148
|
| `get` | `{id}` | `Ticket \| null` |
|
|
116
149
|
| `claim` | `{id}` | `{claimed: boolean}` |
|
|
@@ -132,6 +165,35 @@ Ticket body strings are opaque. Clients, JSON handling, the service, and the
|
|
|
132
165
|
database backend must not trim or normalize bytes, newlines, Unicode, blank
|
|
133
166
|
lines, or trailing whitespace.
|
|
134
167
|
|
|
168
|
+
## Readable ticket identity
|
|
169
|
+
|
|
170
|
+
New hosted tickets use `<teamKey>-<n>`, for example `ENG-1`. The effective
|
|
171
|
+
project configuration supplies the literal prefix; the request's `teamKey`
|
|
172
|
+
does not choose it. Numbers start at 1 in each project, increase atomically
|
|
173
|
+
across concurrent creates, and are never reused after deletion. Projects sharing
|
|
174
|
+
a team key have independent counters. Changing `teamKey` affects only future
|
|
175
|
+
ids and continues the same numbering; existing ids retain their captured prefix.
|
|
176
|
+
|
|
177
|
+
Every ticket operation, CLI command, MCP tool and blocker input accepts readable
|
|
178
|
+
ids in any letter case. Legacy storage ids (such as `ticket-<uuid>`) remain
|
|
179
|
+
aliases, including references in existing builds, harvest records and assets.
|
|
180
|
+
Current ticket and blocker output uses the canonical readable id. The exception
|
|
181
|
+
is `dependency-states`: each result's `id` echoes the requested id for correlation,
|
|
182
|
+
while its `blockedBy` edges are canonical. Raw historical events and artifacts
|
|
183
|
+
remain unchanged. These rules apply only to hosted tickets, not file or Linear ids.
|
|
184
|
+
|
|
185
|
+
Upgrade the service with its environment-aware migration command before serving
|
|
186
|
+
requests. The transactional migration preserves storage keys and assigns existing
|
|
187
|
+
tickets numbers in `(created_at, id)` order within each project. It first resolves
|
|
188
|
+
legacy team ownership, then reads each populated project's current independent
|
|
189
|
+
ticket policy. Projects without that policy use their selected effective
|
|
190
|
+
configuration through the configured blob backend. Independent-policy projects
|
|
191
|
+
need no uploaded config or blob access. Missing or invalid fallback config,
|
|
192
|
+
missing `teamKey`, or inaccessible required blobs abort the entire migration
|
|
193
|
+
unchanged with a project diagnostic. Restore that config/blob access and rerun;
|
|
194
|
+
an ownership assignment alone cannot supply a readable prefix. Empty projects
|
|
195
|
+
need no config lookup. Re-running a completed migration does not renumber tickets.
|
|
196
|
+
|
|
135
197
|
## Errors and conformance
|
|
136
198
|
|
|
137
199
|
Errors are JSON `{ "error": string, "kind": "validation" | "not-found" |
|
|
@@ -170,3 +232,83 @@ project removal are also checked before parsing or source construction.
|
|
|
170
232
|
|
|
171
233
|
Ticket admission and assets use the authenticated project, including for
|
|
172
234
|
delegated harvest grants.
|
|
235
|
+
|
|
236
|
+
Custom state creation uses `/^[a-z][a-z0-9-]{0,39}$/`; an optional purpose must
|
|
237
|
+
be one nonblank line. A duplicate in any letter case is refused without
|
|
238
|
+
changing purpose. `add-state` requires project or owner machine authority;
|
|
239
|
+
ticket grants and harvest delegations can read states but cannot create them.
|
|
240
|
+
Request context cannot supply lifecycle policy or insert another project's
|
|
241
|
+
states. These operations use the existing wire version and strict envelopes.
|
|
242
|
+
|
|
243
|
+
## Revision-checked ticket writes
|
|
244
|
+
|
|
245
|
+
Every ticket read includes an opaque `revision`. Pass it as `expectedRevision`
|
|
246
|
+
(on MCP/operator requests) or `--expected-revision <opaque>` on `ab ticket update`,
|
|
247
|
+
`move`, `block` and `unblock`. A stale write fails without changing anything and
|
|
248
|
+
reports the current revision. Omission preserves unguarded behavior. Blocker
|
|
249
|
+
checks cover the entire batch. Dispatcher moves remain unguarded and change the
|
|
250
|
+
revision. Sources without guarded writes, including Linear, refuse supplied
|
|
251
|
+
revisions rather than ignoring them.
|
|
252
|
+
|
|
253
|
+
`ab ticket edit <id>` opens `$VISUAL`, falling back to `$EDITOR`, using TOML
|
|
254
|
+
frontmatter and a Markdown body. Only title, body and labels are editable;
|
|
255
|
+
id, blockers, creation key and reference comments are read-only. It saves only
|
|
256
|
+
changed fields with the opening revision. Unchanged or formatting-only edits
|
|
257
|
+
write nothing. On conflict, invalid input or editor failure, the error names an
|
|
258
|
+
absolute file path containing the edited text. Read the current ticket and
|
|
259
|
+
reapply the intended changes before retrying; conflicts are never merged.
|
|
260
|
+
|
|
261
|
+
File and Linear revisions fingerprint title, body, canonical label/blocker sets
|
|
262
|
+
and state; reverting identical content restores its fingerprint. File checks
|
|
263
|
+
re-read immediately before writing, with the existing no-lock race window.
|
|
264
|
+
Direct file edits and state-directory moves change revisions. File revisions also
|
|
265
|
+
cover structured comments and portable attachments. Legacy file comments change
|
|
266
|
+
the body; structured comments preserve it. Separate hosted comments do not change
|
|
267
|
+
ticket revisions. Hosted revisions
|
|
268
|
+
are monotonic decimal bigint strings, and checks run under the same row lock
|
|
269
|
+
and transaction as all requested changes. Hosted reads return a coherent
|
|
270
|
+
snapshot of fields, blockers and revision.
|
|
271
|
+
|
|
272
|
+
### Guarded mutation transport
|
|
273
|
+
|
|
274
|
+
`POST /tickets/mutate-if-revision` accepts the usual authenticated context and
|
|
275
|
+
`input: { id, expectedRevision, mutation }`. Mutation is one of
|
|
276
|
+
`{ kind: "update", patch: { title?, body?, labels? } }`,
|
|
277
|
+
`{ kind: "transition", state }`, or
|
|
278
|
+
`{ kind: "blockers", operation: "block" | "unblock", ids: string[] }`.
|
|
279
|
+
Unknown keys and empty patches are refused. Success returns `{ ok: true }`.
|
|
280
|
+
A revision conflict returns HTTP 409 with `kind: "conflict"`, `error`, `id`,
|
|
281
|
+
`expectedRevision` and `currentRevision`. Delegated credentials retain existing
|
|
282
|
+
permissions: none of the guarded mutation variants grants additional access.
|
|
283
|
+
Ticket responses include `revision` only when the request advertises
|
|
284
|
+
`x-autobuild-ticket-revisions: 1`. The current hosted client sends this header
|
|
285
|
+
on every request and requires revision-bearing responses. Released clients
|
|
286
|
+
through 0.12.x omit the header and retain their strict decoder's original
|
|
287
|
+
response shape on get, list-ready and create. This explicit capability also
|
|
288
|
+
supports new source checkouts that still report version 0.12.0; version alone
|
|
289
|
+
cannot distinguish them from released clients. Unknown capability values do
|
|
290
|
+
not opt in. Clients before 0.11 also omit the existing resolution projections.
|
|
291
|
+
The capability does not change authentication, authorization or the remote
|
|
292
|
+
BuildStore protocol version.
|
|
293
|
+
|
|
294
|
+
## Requested ids for migration
|
|
295
|
+
|
|
296
|
+
Create options accept optional `requestedId: string` alongside `state` and
|
|
297
|
+
`idempotencyKey`. Older clients omit it and responses are unchanged. Unsupported
|
|
298
|
+
sources reject supplied ids; Linear is not an import target. File sources keep
|
|
299
|
+
filesystem-safe ids exactly and never overwrite an occupied id.
|
|
300
|
+
|
|
301
|
+
Hosted sources preserve only literal own-team `<teamKey>-<n>` ids, where `n` is a
|
|
302
|
+
positive canonical decimal within PostgreSQL bigint bounds. For example,
|
|
303
|
+
`AUT-706` in team `AUT` keeps 706; `OTHER-706`, `AUT-01` and `AUT-0` use ordinary
|
|
304
|
+
allocation. Overflow canonical numbers are rejected. A matching creation key is
|
|
305
|
+
adopted before occupancy checks. A kept number belonging to a different ticket
|
|
306
|
+
fails without replacing it. Creation and counter advancement share the existing
|
|
307
|
+
project transaction and counter lock. Later automatic numbers exceed both the
|
|
308
|
+
counter and highest occupied number; importing a lower number never lowers the
|
|
309
|
+
counter. Requested ids require project or owner authority. Ticket grants and
|
|
310
|
+
delegated credentials receive 403 for create options containing `requestedId`,
|
|
311
|
+
before source construction. Their ordinary create grants remain unchanged.
|
|
312
|
+
|
|
313
|
+
See the [migration procedure](ticket-migration.md) for preflight, state mapping,
|
|
314
|
+
resumption and portable file records.
|
|
@@ -398,13 +398,13 @@ the verifier does not fix it.
|
|
|
398
398
|
### Project-scoped hosted tickets
|
|
399
399
|
|
|
400
400
|
Hosted ticket queues belong to the credential's registered repository, even
|
|
401
|
-
when projects share a team key.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
401
|
+
when projects share a team key. Independent project ticket policy owns lifecycle:
|
|
402
|
+
new defaults are triage / ready / doing / done, with create/proposal following
|
|
403
|
+
triage. Explicit names retain their spelling; doneState customizes completion.
|
|
404
|
+
`ab connect --tickets URL` publishes policy before dispatch. Older clients without
|
|
405
|
+
independent policy retain their uploaded dispatcher configuration and legacy defaults.
|
|
406
|
+
Owner admin requests require an explicit registered context.repo; project credentials
|
|
407
|
+
cannot select another project. See [local builds with hosted tickets](#local-builds-with-hosted-tickets).
|
|
408
408
|
|
|
409
409
|
The service never contacts Linear. Projects with external sources (including
|
|
410
410
|
file and Linear) keep using those sources directly through their dispatcher;
|
|
@@ -447,3 +447,29 @@ require the running service's blob backend, access mode, credentials and prefix
|
|
|
447
447
|
(`AB_BLOB_PREFIX`), not just PostgreSQL access. `deploy:build` inherits this
|
|
448
448
|
environment and migration failure stops Next build. Fresh databases, empty v1
|
|
449
449
|
queues and v2 reruns stay SQL-only and do not initialize blob storage.
|
|
450
|
+
|
|
451
|
+
## Local builds with hosted tickets
|
|
452
|
+
|
|
453
|
+
Hosted tickets do not require hosted builds. Set `[tickets] source = "hosted"`
|
|
454
|
+
and `teamKey = "ENG"`, then run `ab connect --tickets https://your-service.example`.
|
|
455
|
+
Approve the project in the browser. This saves a service-scoped credential and
|
|
456
|
+
`[tickets] url` while leaving `[store]` unchanged. Ticket commands work immediately,
|
|
457
|
+
before any dispatcher runs. `ab connect` reports every service and whether it serves
|
|
458
|
+
builds, tickets, or both. Use `ab disconnect --tickets` to revoke ticket access;
|
|
459
|
+
when services differ use `--builds` to select build access. Shared uses share one
|
|
460
|
+
credential, so disconnecting either shared use affects both.
|
|
461
|
+
|
|
462
|
+
Ticket URL selection is `[tickets] url`, then the selected build service (including
|
|
463
|
+
AB_STORE or a supported --store override). With local builds, `[tickets] url` is
|
|
464
|
+
required. Builds, events, artifacts, and harvest journals remain in the local store.
|
|
465
|
+
The service stores ticket records, project ticket policy, and restricted ticket
|
|
466
|
+
grants. Normal project clients synchronize current `[tickets]` policy before use;
|
|
467
|
+
failed synchronization fails the operation. Harvest grants can read and create;
|
|
468
|
+
phase grants can only read. Neither can change policy or access BuildStore routes.
|
|
469
|
+
|
|
470
|
+
New hosted policy defaults to `triage`, `ready`, `doing`, `done`, with creation and
|
|
471
|
+
harvest proposals in `triage`. Explicit state names preserve their spelling.
|
|
472
|
+
To retain earlier names configure `triageState = "Backlog"`,
|
|
473
|
+
`claimedState = "Doing"`, `doneState = "Done"`, and the desired `readyState`.
|
|
474
|
+
Existing ticket rows are never renamed. Older combined-service clients continue
|
|
475
|
+
using the uploaded dispatcher configuration until independent policy is published.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Moving a project between ticket sources
|
|
2
|
+
|
|
3
|
+
Export and import use the ordinary file ticket layout. They operate on tickets
|
|
4
|
+
and current attachments through the ticket source; neither command contacts the
|
|
5
|
+
forge. File and hosted sources support import. Linear supports export only.
|
|
6
|
+
|
|
7
|
+
1. Pause intake and stop other ticket writers for the cutover. Import preflight
|
|
8
|
+
does not reserve numbers against concurrent writers.
|
|
9
|
+
2. With the old `[tickets]` configuration, run `ab ticket export /path/to/export`.
|
|
10
|
+
The destination must be absent or empty. Export reads every state, including
|
|
11
|
+
archived Linear tickets and empty discoverable states, and publishes the
|
|
12
|
+
directory only after all reads succeed. Back up the target tracker too.
|
|
13
|
+
3. Configure the new `[tickets]` source, or use `ab connect --tickets URL` for a
|
|
14
|
+
hosted ticket service. Keep the export directory intact.
|
|
15
|
+
4. Run `ab ticket import /path/to/export`. Inspect its printed ticket and state
|
|
16
|
+
mappings and `/path/to/export/.ab-import-map.json`.
|
|
17
|
+
5. Inspect ticket bodies, labels, blockers, comments and attachments in the new
|
|
18
|
+
source. Confirm lifecycle configuration and the ready gate, then resume intake.
|
|
19
|
+
|
|
20
|
+
Both commands also accept
|
|
21
|
+
`--store <ref>` to reach legacy BuildStore attachments. Keep the old store available
|
|
22
|
+
through export when file or Linear tickets have attachments there.
|
|
23
|
+
|
|
24
|
+
State directory names use lowercase letters, digits and hyphens, start with a
|
|
25
|
+
letter and contain at most forty characters. Export maps `In Progress` to
|
|
26
|
+
`in-progress`, `Backlog` to `backlog`, and `Done` to `done`. It retains each ticket's
|
|
27
|
+
state rather than assigning lifecycle roles. Distinct names that collapse to the
|
|
28
|
+
same directory fail before publication. The hidden `.ab-migration.json` records
|
|
29
|
+
original display names and the state map. Import also accepts ordinary legacy
|
|
30
|
+
file layouts: it reuses exact or unique case-insensitive target states before
|
|
31
|
+
normalizing new names. A seeded `done` satisfies `Done`; absent `In Progress`
|
|
32
|
+
creates `in-progress`. Ambiguous matches and colliding state maps fail preflight.
|
|
33
|
+
|
|
34
|
+
File targets retain source ids. Hosted targets keep canonical own-team ids:
|
|
35
|
+
`AUT-706` imported into team `AUT` stays `AUT-706`. Other ids receive numbers above
|
|
36
|
+
all existing and preserved incoming numbers. Known clashes with unrelated tickets
|
|
37
|
+
are reported together before any target state, ticket, comment or asset write.
|
|
38
|
+
Content similarity and stale mapping files never establish identity.
|
|
39
|
+
|
|
40
|
+
Creation keys establish identity. Exports retain existing keys and assign keys to
|
|
41
|
+
legacy tickets. Importing an ordinary file layout first saves a hidden migration
|
|
42
|
+
identity and keys in that input directory. Preserve this file and the input across
|
|
43
|
+
retries. After interruption, rerun the same command: committed creates are adopted
|
|
44
|
+
by key even if their mapping write failed. Adopted bodies and other fields are not
|
|
45
|
+
overwritten. Missing blockers and comments are replayed, and unchanged attachments
|
|
46
|
+
do not create another revision. Comments are deduplicated by exact body, so two
|
|
47
|
+
identical source comment bodies produce one target comment. Original authors and
|
|
48
|
+
timestamps remain in the export; imported comments use the target's metadata.
|
|
49
|
+
|
|
50
|
+
Portable records use TOML frontmatter between `+++` fences and an unchanged
|
|
51
|
+
Markdown body. Alongside `id`, `title`, `labels`, `blockedBy` and `idempotencyKey`,
|
|
52
|
+
optional `comments` contain `body`, `createdAt` and optional `author`. Optional
|
|
53
|
+
`attachments` contain `kind`, `name`, `layout` (`file` or `tree`) and `entries`:
|
|
54
|
+
`{ type = "dir", path = "empty" }` or
|
|
55
|
+
`{ type = "file", path = "image.png", base64 = "…" }`. Common asset limits and
|
|
56
|
+
path validators apply. Only current attachment contents travel, including binary
|
|
57
|
+
files and empty directories; historical revisions do not. The exported directory
|
|
58
|
+
works directly as a file source, without its migration manifest or original store.
|
|
59
|
+
File source revision history stays in its hidden `.ab-assets` directory.
|
|
60
|
+
|
|
61
|
+
Legacy records without `comments` retain their Markdown comment append behavior;
|
|
62
|
+
export never interprets Markdown headings as structured history. New file tickets
|
|
63
|
+
use structured comments and preserve their body when a comment is added. There is
|
|
64
|
+
no continuous sync, assignee migration, state history or build-history migration.
|
package/skills/tickets/SKILL.md
CHANGED
|
@@ -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`.
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
"build": "ab-hosted-service-build"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@defrex/autobuild-hosted-store-service": "0.
|
|
8
|
-
"@defrex/autobuild": "0.
|
|
7
|
+
"@defrex/autobuild-hosted-store-service": "0.13.0",
|
|
8
|
+
"@defrex/autobuild": "0.13.0",
|
|
9
9
|
"next": "16.1.6",
|
|
10
10
|
"react": "19.2.4",
|
|
11
11
|
"react-dom": "19.2.4"
|