@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
package/README.md CHANGED
@@ -24,7 +24,7 @@ needs an authenticated [`gh` CLI](https://cli.github.com) or an exported
24
24
  <!-- release-install:start -->
25
25
 
26
26
  ```sh
27
- bun add -g @defrex/autobuild@0.11.0
27
+ bun add -g @defrex/autobuild@0.13.0
28
28
  ```
29
29
 
30
30
  <!-- release-install:end -->
@@ -174,3 +174,5 @@ routine escalations, leaving you only the real product calls.
174
174
  ## License
175
175
 
176
176
  [Apache License 2.0](LICENSE).
177
+
178
+ Deploy your own store and dashboard with the [hosted service setup](docs/hosted-service.md).
package/SPEC.md CHANGED
@@ -432,6 +432,13 @@ body file leaves the pinned assets unchanged. The one case this cannot cover is
432
432
  a record an older dispatcher created with an empty stream: recovery can only
433
433
  sample the ticket's current assets for it.
434
434
 
435
+ For hosted tickets, exact sampled revisions are copied into immutable repository
436
+ artifacts in the destination build store before pins are published. Pins carry
437
+ explicit manifest references; bounded binary chunks preserve files and empty
438
+ directories without contacting the ticket service during build reads. Original
439
+ ticket revision numbers are preserved. Old pins without snapshot references
440
+ continue reading their existing ticket rows.
441
+
435
442
  ## 7. The build store
436
443
 
437
444
  The v1 repo-bloat and no-observability problems share one root cause: the
@@ -581,9 +588,8 @@ registry already exposes bounded repository reads). Each source's cursor
581
588
  advances only through a recorded trigger naming that source, so a crash
582
589
  between scan and start cannot skip or duplicate a wake, and other builds' —
583
590
  and the journal's — matching events re-trigger on later ticks. Sessions with
584
- empty wake settings are never woken. The default wake settings span both
585
- halves of the attention set: the build events `ab watch` filters on by
586
- default plus the repository-journal events `ab watch --repository` follows.
591
+ empty wake settings are never woken. The legacy hosted wake settings retain their event defaults independently of
592
+ the actionable CLI watch projection.
587
593
 
588
594
  **Crash recovery.** A turn invocation that dies without an outcome leaves the
589
595
  session `running` with a silent open stream; the tick's reaper fails such a
@@ -619,15 +625,23 @@ resume or turn — no second turn, no concurrent turns, no subagents.
619
625
 
620
626
  A **ticket asset** is a named, versioned bundle of files attached to a ticket —
621
627
  a design export, a reference screenshot, a sample payload — that shapes a change
622
- but belongs neither in the repository (where it goes stale once the change
623
- ships) nor in the ticket tool (which build agents cannot reach). It is keyed by
628
+ but belongs outside the repository, where it would go stale once the change ships. It is keyed by
624
629
  `(repo, ticketId, kind, name)` and works identically whichever ticket source is
625
630
  configured. It is a fourth resource family beside builds, repository journals,
626
631
  and sessions, but a deliberately thin one: no event log, because nothing here
627
632
  needs reduction, and no retention pruning, because assets live as long as the
628
633
  ticket's other durable data and never expire on their own.
629
634
 
630
- Each put creates a new 0-based **revision**; earlier revisions stay retrievable.
635
+ Hosted ticket assets live in the ticket service's repository-keyed storage,
636
+ regardless of where builds run or which BuildStore is selected. File tickets use
637
+ native portable storage composed with legacy BuildStore attachments; Linear
638
+ tickets retain BuildStore storage. Existing service assets need no migration;
639
+ old local hosted-ticket attachments are not moved or used as a fallback.
640
+
641
+ Each put creates a new increasing **revision**; earlier revisions stay retrievable.
642
+ Adapters normally start at 0. Native file assets reserve revisions from
643
+ 1,000,000,000 upward so explicit reads of old BuildStore revisions remain
644
+ unambiguous when native and legacy assets are composed.
631
645
  An asset is a `file` (exactly one file) or a `tree` (a folder: its root is
632
646
  implicit, so a tree with zero entries is a valid empty folder). The manifest is
633
647
  a list of entries — `file` entries with size and content address, and `dir`
@@ -701,7 +715,8 @@ conformance suite):
701
715
  hosted HTTP service, selected by an `http(s)://` reference. Kernels,
702
716
  sandboxes, terminal dashboards, and later human-facing pages share this
703
717
  service tier; it owns token authorization, protocol write validation,
704
- artifact policy, and exact package/protocol version-skew enforcement. The
718
+ artifact policy, release-line compatibility, and independent protocol
719
+ enforcement. The
705
720
  later human routes share its deployment so a browser or second service never
706
721
  receives the machine signing secret. Artifacts remain content-by-value. The
707
722
  documented [remote store protocol](docs/remote-store-protocol.md) is the
@@ -710,6 +725,24 @@ conformance suite):
710
725
  client, is the conformance bar. Autobuild does not load in-process BuildStore
711
726
  plugins. Git worktrees and default file tickets necessarily remain local.
712
727
 
728
+ A service accepts stable clients from its own `major.minor` release line and
729
+ the two preceding minor lines in the same major. A major starts a new family:
730
+ X.0 accepts only X.0, X.1 accepts X.0/X.1, and X.2 onward accepts three lines.
731
+ Patch versions are interchangeable in both directions and must never change
732
+ the protocol. Upgrade the service first, then projects independently within
733
+ its window. Supported release lines retain their wire handlers and known
734
+ protocol identities; an unknown protocol is refused independently of version.
735
+ Malformed, missing, and prerelease identities are refused with HTTP 409.
736
+ A newer client is told to upgrade the service; a client below the window is
737
+ told to upgrade the project's Autobuild. Diagnostics name the service version
738
+ and supported range.
739
+
740
+ Unauthenticated `GET /health` publishes `ok`, `autobuildVersion`,
741
+ `protocolVersion`, and `supportedClientRange: { minVersion, maxVersionExclusive }`.
742
+ The range is inclusive at the minimum and exclusive at the maximum; a 0.11.x
743
+ service publishes `>=0.9.0 <0.12.0`. Health needs no credentials, version
744
+ headers, or available backend.
745
+
713
746
  `subscribe` is specced in the interface; the v2.0 implementation is polling
714
747
  `getEvents(since)`. True push comes later.
715
748
 
@@ -734,6 +767,43 @@ longer grows with the number of past dispatcher invocations. The journal
734
767
  itself stays append-only: full replay remains the normative truth, and
735
768
  `ab watch` still sees every recorded fact.
736
769
 
770
+ #### Hosted connected projects
771
+
772
+ The product's hosted service keeps a durable PostgreSQL project registry,
773
+ independent of BuildStore repository journals and ticket data. Entries record
774
+ normalized repository origin, addition timestamp and adding identity. An
775
+ append-only journal records additions/removals; latest sequence determines
776
+ membership. Removal never deletes build, event, artifact or ticket history;
777
+ re-registration restores access and records fresh addition metadata.
778
+
779
+ Database-authorized owners administer it without redeploying through
780
+ `ab-hosted-store projects list`, `projects add REPOSITORY --user IDENTITY`, and
781
+ `projects remove REPOSITORY --user IDENTITY`. Attribution is declared by the
782
+ administrator; ordinary store/project tokens cannot administer registration.
783
+ All allowlisted signed-in users own every registered project (single-owner
784
+ model). Per-project memberships and machine credential boundaries are deferred.
785
+
786
+ Dashboard discovery, MCP `repositories.list` and every repository tool call,
787
+ and all operator API routes for every operator credential consult current
788
+ membership. Unregistered operator paths return not-found before execution.
789
+ Registry failures fail closed and transient initialization can retry.
790
+ The dashboard selection lives at `/?repo=<URL-encoded normalized origin>`;
791
+ missing/invalid selections canonicalize to the first active entry, and an
792
+ empty registry issues no project data/control requests.
793
+
794
+ The hosted dispatcher queries the registry each authorized invocation when
795
+ `AB_DISPATCHER_REPOSITORIES` is unset. An explicit list is intersected with
796
+ current registration in its configured order. Empty registration is a
797
+ successful zero-project tick. Removal stops later invocations, without
798
+ cancelling already-started work or changing machine-route token authority.
799
+
800
+ `AB_WEB_REPOSITORIES` is deprecated: the first initialization with a nonempty
801
+ CSV imports absent projects once per database as `system:AB_WEB_REPOSITORIES`.
802
+ The import and durable marker share one transaction and mutation lock across
803
+ processes. Absence does not consume the marker. Restarts or later variable
804
+ edits cannot resurrect removals. Deployments migrate the pinned schema before
805
+ starting services; runtime initialization never runs DDL.
806
+
737
807
  ### 7.3 Persistence granularity
738
808
 
739
809
  Required phase outputs persist at **phase/round boundaries**; a killed phase
@@ -781,8 +851,27 @@ Four base-selection invariants make this safe with Git:
781
851
 
782
852
  ### 7.5 What the PR gets
783
853
 
784
- A summary comment — verdict history, verification results, links into the
785
- store. The full audit trail is queryable, not committed to the branch.
854
+ One marked summary comment contains verdict history, verification results,
855
+ and links into the store. Built-in forges discover its per-build marker on
856
+ every refresh and edit it, recreating it only after confirmed absence. The
857
+ full audit trail is queryable, not committed to the branch.
858
+
859
+ Summary publication is best effort, once per completed phase occurrence,
860
+ after sessions end; attaching artifacts performs no summary write. Durable
861
+ claim-before-write facts consume the occurrence budget even across crashes.
862
+ Rate limits stop that occurrence and persist a cooldown (provider headers or
863
+ at least one minute) before a later phase may catch up. Failures never fail
864
+ artifact designation or phase completion. Bodies are capped at 65,536 UTF-16
865
+ code units. Oversized projections are deposited as a non-attached `pr-summary`
866
+ artifact; a compact comment provides the exact pinned download command.
867
+ Current-API plugins retain `commentOnPr` and receive the same bounded calls;
868
+ legacy plugins may append instead of editing.
869
+
870
+ Local runners publish under their build lease. Remote guests record durable
871
+ summary boundary requests and yield; only the credentialed dispatcher settles
872
+ those requests, after guest execution ends and under exclusive ownership,
873
+ before replacement execution or workspace cleanup. Attempts and request
874
+ dispositions survive restart and do not alter pipeline routing.
786
875
 
787
876
  Any agent session can explicitly designate an exact deposited artifact as a PR
788
877
  attachment through `ab artifact put <kind> <file> --attach`. Designation is a
@@ -802,8 +891,8 @@ dispatcher reclaims them after the build reaches a terminal outcome, while
802
891
  store artifacts remain under the store's retention policy. Upload/validation/timeout failures record follow-up
803
892
  observations and preserve the complete text projection; they never fail
804
893
  verification or block finalize. Agents receive no forge credentials. A
805
- designation after the PR exists publishes a new complete summary, so finalize
806
- post-steps and post-reconcile verifiers need no custom phase.
894
+ designation after the PR exists is included at the completed phase boundary,
895
+ so finalize post-steps and post-reconcile verifiers need no custom phase.
807
896
 
808
897
  ### 7.6 Streams (the third primitive)
809
898
 
@@ -925,7 +1014,7 @@ AB_STORE # store URL or local path
925
1014
  AB_BUILD # build slug
926
1015
  AB_PHASE # current phase (+ round)
927
1016
  AB_SESSION # session id
928
- AB_TOKEN # transports the scoped handle's authority to a remote store
1017
+ AB_TOKEN # signed credential for this exact phase session (remote store)
929
1018
  ```
930
1019
 
931
1020
  A harvest session instead carries `AB_REPO`, `AB_HARVEST`, and an `AB_PHASE`
@@ -933,14 +1022,18 @@ of `synthesize@N` or `review@N`. The AgentRunner invocation argument is
933
1022
  opaque: a build skill receives its slug; a harvest skill receives its run id.
934
1023
  The CLI resolves identity from ambient auth. Every build process holds an
935
1024
  interface-enforced build-scoped Store handle. At the phase CLI opening boundary,
936
- a remote handle forwards the runner-minted token unchanged, while a local handle
937
- is wrapped with authority over exactly `AB_BUILD` or `AB_REPO`. Build and
1025
+ both local and remote handles are wrapped with authority over exactly
1026
+ `AB_BUILD` or `AB_REPO`. Before launch, the dispatcher exchanges its project credential with the service
1027
+ for a build or prepared harvest-execution grant. After recording a session start,
1028
+ the runner exchanges that grant for an exact phase-session grant. The remote
1029
+ handle forwards only this delegated credential; the service enforces its project,
1030
+ resource and session boundaries on the wire. Build and
938
1031
  repository resources cannot cross, and naming another resource to a Store
939
1032
  operation cannot widen either handle. Event-bearing writes attributed to an
940
1033
  agent must name the ambient `AB_SESSION`; non-agent writes remain allowed so the
941
1034
  CLI can perform its existing trusted kernel plumbing, including finalize.
942
- Operator and kernel processes with no agent identity markers retain unscoped
943
- Store access. Three read-only forms are also valid inside a phase: `ab build
1035
+ Operator and kernel processes with no agent identity markers retain interface-unscoped
1036
+ Store handles; remote authorization still limits access to the credential project. Three read-only forms are also valid inside a phase: `ab build
944
1037
  status` and `ab artifact download` may read only `AB_BUILD`, while `ab builds`
945
1038
  is denied because collection access cannot represent one build. A complete
946
1039
  Harvest identity has repository scope and cannot use those build/admin reads.
@@ -949,10 +1042,60 @@ If any build or Harvest identity marker is present, the corresponding
949
1042
  partial, empty, malformed, shared-only, or mixed identity fails before Store
950
1043
  access rather than falling back to operator authority. `AB_STORE` and
951
1044
  `AB_TOKEN` alone remain operator selection and credentials, not identity.
952
- Remote tokens transport the corresponding resource and session authority over
953
- the wire; build-process resource scope is already enforced by the Store
954
- interface. Least privilege comes from the Store and runner, not prompt
955
- instructions.
1045
+ Dispatcher machine authority is an opaque, revocable credential for exactly one
1046
+ registered project. The service keeps only a digest of its random secret and
1047
+ credential metadata (creator, creation, last use, expiry and revocation), and
1048
+ checks both credential status and project registration on each request. It
1049
+ checks actual build/session/stream ownership and filters collections server-side.
1050
+ Explicit project mismatch is 403; missing and foreign indirect IDs share 404.
1051
+ Remote project build creation allocates a fresh slug and branch; the returned
1052
+ record is authoritative. Local adapters retain exact-slug behavior.
1053
+
1054
+ Owners issue/list/revoke credentials with deployment-local `ab-hosted-store
1055
+ credentials` commands. Retired deployment-wide and repository-less operator
1056
+ tokens are rejected with migration guidance. An explicit versioned owner-only
1057
+ break-glass admin is the sole machine cross-project exception. Internal signed
1058
+ owner delegation is short-lived, repository-bound and operator-only. The hosted
1059
+ dispatcher issues stored credentials with immutable dispatcher purpose (raw
1060
+ store and tickets only) per invocation and keeps them valid through configured
1061
+ expiry. Already issued delegated grants do not depend on that parent expiry.
1062
+ Owner-issued project credentials attribute operator writes to their recorded
1063
+ creator. Agents never receive the dispatcher's project credential. Delegated
1064
+ reads retain access to same-project repository state; build mutations target only
1065
+ that build, and harvest mutations target only the prepared run and execution,
1066
+ its artifacts and streams, with heartbeat authority for its adopted lease holder.
1067
+ Phase grants also enforce agent session attribution. They cannot create builds or
1068
+ operator sessions, administer repositories, invoke operator routes, or delegate.
1069
+ A runner can delegate only to a recorded session within its own authority.
1070
+
1071
+ Delegated grants have no fixed wall-clock expiry. The service checks signed
1072
+ bindings against authoritative durable lifecycle facts on every request and
1073
+ fails closed if those reads fail. Active, paused and escalated builds may outlive
1074
+ a dispatcher restart or a week-long parent credential. Build termination,
1075
+ harvest execution/run termination, or the phase's own session end retires the
1076
+ corresponding grant after a 60-second evidence grace window. A durable ending
1077
+ permanently closes that grant; recovered harvest executions use fresh durable
1078
+ authorizations and fresh grants. Project removal denies all its grants immediately.
1079
+ Local-store projects require no exchange.
1080
+
1081
+ Hosted ticket operations are gated by grant kind: build-runner and build-phase
1082
+ grants have no access; harvest-phase grants can only list-ready, get and read
1083
+ dependency-states; harvest-runner grants can also create proposals. All other
1084
+ ticket mutations require project or admin authority. Refusals name the grant
1085
+ kind and operation.
1086
+
1087
+ Ticket admission uses the credential project. Hosted tickets, comments, blockers, creation-key
1088
+ deduplication and assets are partitioned by registered repository, never team.
1089
+ Lifecycle comes from independent project ticket policy (triage, ready, doing, done
1090
+ by default; configurable doneState). Legacy projects without independent policy
1091
+ use their uploaded configuration with Backlog / Doing / Done defaults. Owner admin ticket requests explicitly select a registered
1092
+ repository. The service has no Linear ticket backend: external tracker and
1093
+ file sources remain dispatcher-side, and service ticket surfaces refuse those
1094
+ projects with actionable source feedback. Legacy team queues migrate atomically
1095
+ to their sole configured claimant; conflicts stop unchanged for an owner
1096
+ assignment and rerun. See the hosted service migration procedure.
1097
+ See the remote store, remote ticket and operator protocol references for wire
1098
+ and revocation details. Least privilege comes from code, not prompt instructions.
956
1099
 
957
1100
  Every phase and harvest turn also receives a runner-controlled `PATH` prefix
958
1101
  containing a private `ab` launcher from the same Autobuild distribution that
@@ -1145,9 +1288,10 @@ implementer: ab context (findings.json now materialized) → …
1145
1288
 
1146
1289
  Human/pre-build ticket grooming uses one configured-source namespace
1147
1290
  (`ab ticket create|update|block|unblock|list|show|move`, plus the ticket-asset
1148
- forms below). These commands are
1291
+ forms below). Migration adds `ab ticket export <dir>` and `ab ticket import <dir>`
1292
+ (see [Ticket source migration](#ticket-source-migration)). These commands are
1149
1293
  sessionless, source-agnostic, and never available as a mid-build spec
1150
- mutation path. Every subcommand accepts `--json` and then writes exactly one
1294
+ mutation path. The CRUD subcommands accept `--json` and then writes exactly one
1151
1295
  bare value: `list` emits `Ticket[]`; every other form emits the complete
1152
1296
  created, read, or post-mutation `Ticket`. Human-readable confirmations are not
1153
1297
  a data format and callers must not parse ids from them.
@@ -1162,8 +1306,10 @@ creates tickets directly in its ready state must therefore create a dependency
1162
1306
  chain in dependency order, carrying each predecessor id into the next create,
1163
1307
  before a later ticket can be claimed.
1164
1308
 
1165
- **Ticket assets** (§7.1) attach files to a ticket without touching the ticket
1166
- source's storage:
1309
+ **Ticket assets** (§7.1) attach files to a ticket. Hosted tickets use their
1310
+ ticket service storage independently of the selected build store; file tickets use
1311
+ native storage with legacy fallback, and sources without native assets use the
1312
+ selected BuildStore:
1167
1313
 
1168
1314
  - `ab ticket attach <id> <kind> <path> [--name <name>] [--store <ref>] [--json]`
1169
1315
  stores a file or a whole folder (its relative tree preserved) as an asset.
@@ -1219,6 +1365,17 @@ be narrowed to the ambient build — and `--repo` constrains every call to one
1219
1365
  repository. Mutating tools attribute their writes to the sessionless operator
1220
1366
  identity, exactly as the other operator commands do.
1221
1367
 
1368
+ ### 8.10 Hosted project connection
1369
+
1370
+ `ab connect <service-url>` requests owner browser approval for the normalized
1371
+ origin, registers the project, and saves a project credential outside the
1372
+ checkout. `ab connect` reports its status; `ab disconnect` revokes and removes
1373
+ only that credential, retaining project data. Store precedence is `--store`,
1374
+ nonblank `AB_STORE`, `[store] url`, then local state. `AB_TOKEN` overrides saved
1375
+ credentials. Configured services without credentials fail closed. Connection
1376
+ commands require a network origin and are unavailable in phase sessions.
1377
+
1378
+
1222
1379
  ## 9. AgentRunner
1223
1380
 
1224
1381
  Session-based, because review loops need memory:
@@ -1619,6 +1776,19 @@ event-level mechanics live in the repository catalog and reducer tests):
1619
1776
 
1620
1777
  ## 13. Ticket source policy
1621
1778
 
1779
+ **Hosted tickets are project-scoped.** A project credential selects one registered
1780
+ repository; an owner selects a registered repository explicitly. Team and state
1781
+ fields from clients convey no authority or lifecycle policy. Tickets, comments,
1782
+ blockers, creation-key deduplication and assets are isolated by repository.
1783
+ Independent project ticket policy supplies hosted lifecycle names: triage, ready,
1784
+ doing, done (`doneState`), and create/proposal equal to triage by default. Hosted
1785
+ readyState defaults to ready; other sources require it. Legacy clients without
1786
+ independent policy use uploaded dispatcher config and its old defaults. Configured roles may overlap.
1787
+ The service has no Linear backend and never relays external ticket sources.
1788
+ Projects using Linear, files or plugins keep their direct dispatcher connection;
1789
+ service ticket surfaces refuse with project/source feedback instead of empty data.
1790
+
1791
+
1622
1792
  The TicketSource **initiates and receives projections; it is never consulted
1623
1793
  mid-build and never used as artifact storage.** Dispatch reads the ticket
1624
1794
  (including the spec) as part of initiation — at claim time, or on a later tick
@@ -1629,14 +1799,16 @@ This keeps the abstraction honest: a file-based TicketSource with nowhere to
1629
1799
  put blobs must be fully workable.
1630
1800
 
1631
1801
  **Ticket assets follow the same rule.** The contents of a ticket asset (§7.1)
1632
- live in the BuildStore and the ticket source never stores them. Attaching,
1802
+ live in the ticket service for hosted tickets, independently of build storage;
1803
+ file sources keep portable bundles and source-local history, while sources without
1804
+ native assets keep them in the BuildStore. Attaching,
1633
1805
  replacing, or removing an asset instead leaves a note on the ticket through the
1634
1806
  existing `comment()` port — the outward projection every source, including the
1635
1807
  plugin surface, already supports. The note names the asset, its revision, file
1636
1808
  count, and size, and gives the `ab ticket asset get` command that fetches it, so
1637
1809
  a person reading the ticket in Linear, the hosted service, or a local file knows
1638
1810
  the asset exists. The note is a projection outward only; nothing reads it back,
1639
- and no TicketSource port or plugin-protocol change is needed.
1811
+ and existing sources and plugins need no asset capability.
1640
1812
 
1641
1813
  **Claim lifecycle.** Claim acquires nonterminal work: it returns `true` and
1642
1814
  moves a claimable ticket to the source's claimed state. It returns `false` for
@@ -1871,7 +2043,8 @@ with illustrative members:
1871
2043
  | Spec | `spec.imported`, `spec.authored`, `spec.revised` (optionally carrying `assets`, a re-pin of the ticket's assets, §6.3) |
1872
2044
  | Sessions | `session.started`; `session.ended` with transcript ref and usage (ordinary completion — the analysis corpus), or `session.ended {outcome: reclaimed, reclaimedBy: {instance, resumedFromSeq}}` (explicit transcriptless takeover) |
1873
2045
  | Plan/code loops | `plan.started` … `plan-review.verdict`; `implement.started` … `code-review.verdict` |
1874
- | Verify/finalize | `verify.started {step, attempt, feedback?}`, `verify.completed {step, outcome}`, `finalize.completed {pr}`, `finalize.step-completed {step, ok, headSha?}` |
2046
+ | Verify/finalize | `verify.started {step, attempt, feedback?}`, `verify.completed {step, outcome, skipCause?}`, `verify.reconcile-policy-captured {reconcileSeq, steps}`, `finalize.completed {pr}`, `finalize.step-started {step}`, `finalize.step-completed {step, ok, headSha?}` |
2047
+ | PR summary | Kernel-authored `pr-summary.requested {occurrence, boundary}`, `pr-summary.attempted {occurrence, boundary, pr}`, `pr-summary.result {attempt, outcome, eligibleAfter?}`, `pr-summary.settled {request, disposition}`; sequence identities correlate durable starts, completed boundaries, attempts, and handoffs (§7.5) |
1875
2048
  | PR attachments | `pr-attachment.designated`, `pr-attachment.hosted`, `pr-attachment.reclaimed`, `pr-attachment.reclaim-failed` |
1876
2049
  | Post-PR [D1] | `pr.merged`, `pr.conflicted`, `reconcile.progress-checked`, `reconcile.started`, `reconcile.completed` |
1877
2050
  | Cross-cutting | `observation.recorded`, `escalation.raised`, `phase.failed` |
@@ -1975,8 +2148,18 @@ ordinary dispatcher tick provisions the workspace, atomically imports the spec,
1975
2148
  posts the ticket notice once, and launches the runner. Claim-time auto-merge
1976
2149
  attribution retained by `build.created` is materialized as the ordinary
1977
2150
  human-authored request before launch. A failed boundary appends `dispatch.failed
1978
- {stage,attempt,error}` and stays queued for another tick. A discard request is
1979
- honored only while the build remains queued; one that races with runner
2151
+ {stage,attempt,error}` and stays queued for another tick until its budget is
2152
+ exhausted. Dispatch failures share a `[policy].maxDispatchAttempts` budget
2153
+ (default 3) across
2154
+ create, workspace, spec, comment, and launch stages. Exhaustion raises one
2155
+ `dispatch-failure-limit` policy escalation naming the latest stage and error,
2156
+ and parks dispatch until answered. Both bare `ab answer <slug>` retries and
2157
+ text guidance grant a fresh budget; abort does not. Confirmed runner attachment
2158
+ clears the streak. Recovery-provider workspace failures belong exclusively to
2159
+ the infrastructure budget. Exhaustion is repaired from durable facts on restart
2160
+ before further dispatch work.
2161
+
2162
+ A discard request is honored only while the build remains queued; one that races with runner
1980
2163
  attachment is inert and cannot tear down live work. A human discard waits out
1981
2164
  any unexpired execution lease, then releases a partial workspace and stale
1982
2165
  lease, returns the ticket to Ready, and appends `build.completed {outcome:
@@ -2068,6 +2251,21 @@ adapter never re-cuts it from a newer base (§7.4). `ab context` rehydrates
2068
2251
  scratch from the store into a fresh session. Uncommitted round-2 work is lost by
2069
2252
  design (§7.3 — phase boundaries are the resume points).
2070
2253
 
2254
+ The dispatcher also applies a fixed, non-configurable limit of three consecutive
2255
+ executions that complete normally with exit code zero and no durable build
2256
+ progress. Execution start/end, runner attachment and workspace lifecycle
2257
+ bookkeeping do not count as progress. Pipeline, session, spec, publication/PR,
2258
+ observation and acknowledged operator facts reset the streak; infrastructure,
2259
+ budget and provider failures do not consume the allowance and retain their own
2260
+ retry limits. Before a fourth actionable launch, the dispatcher raises one
2261
+ setup-targeted `execution-no-progress` policy escalation naming the count and
2262
+ the next action it expected the runner to take. Waiting, paused and terminal
2263
+ builds are left parked, and pending operator commands remain executable.
2264
+ Answering that escalation with guidance or a bare `ab answer <slug>` retry grants
2265
+ three fresh executions. The guard is derived from durable facts, including on
2266
+ supervisor restart, and uses the ordinary watch, dashboard and operator API
2267
+ attention paths.
2268
+
2071
2269
  Two liveness rules complete the picture. Within one dispatcher process,
2072
2270
  build-runner launches are single-flighted by slug through supervised process
2073
2271
  liveness — execution completion means the complete environment has been reaped,
@@ -2182,7 +2380,8 @@ fresh from `origin`, and records the resolved SHA on `reconcile.started` — kno
2182
2380
  used (§15.3). The agent merges that base into the branch guided by the spec,
2183
2381
  plan, and implement-notes, with the explicit charge to regress against
2184
2382
  neither; the resolution lands as a merge commit, and because reconciliation
2185
- changed code, **`verify:*` re-runs in full**. A resolution the agent judges
2383
+ changed code, **`verify:*` starts a fresh cycle**, subject to the captured
2384
+ post-reconcile policy (default: re-run all applicable steps). A resolution the agent judges
2186
2385
  risky — semantic conflicts, spec-relevant choices — escalates rather than
2187
2386
  guesses. Reconcile skips `code-review` by default (escalation covers the
2188
2387
  judgment cases; policy can force it).
@@ -2314,6 +2513,7 @@ sessionBudgetSeconds = 7200 # optional role override
2314
2513
  sessionBudgetSeconds = 3600 # non-infinite default for build agent sessions
2315
2514
  stallRounds = 3
2316
2515
  maxVerifyAttempts = 3
2516
+ maxDispatchAttempts = 3
2317
2517
  maxSetupAttempts = 3
2318
2518
  maxInfrastructureAttempts = 3
2319
2519
  maxReconcileAttempts = 3
@@ -2386,9 +2586,12 @@ artifact deposited on reload changes only those sections. In-flight
2386
2586
  environments are never re-provisioned; a fresh build from the new base is the
2387
2587
  path for a changed provisioning definition.
2388
2588
 
2389
- Two configurable narrowing mechanisms govern which verify steps run, both
2589
+ Three configurable narrowing mechanisms govern which verify steps run, all
2390
2590
  resolving to the ordinary `skipped` outcome so exclusions stay queryable:
2391
2591
 
2592
+ - **Post-reconcile policy.** The cycle snapshot resolves repository and step
2593
+ `afterReconcile` settings before any plan or path exclusions, including for
2594
+ `always` steps. It applies only after a completed reconcile.
2392
2595
  - **Plan selection.** An approved plan may declare the complete subset of
2393
2596
  optional verify steps warranted by the spec, in strict front matter
2394
2597
  validated at deposit. The selection paired with the approving review
@@ -2402,6 +2605,30 @@ resolving to the ordinary `skipped` outcome so exclusions stay queryable:
2402
2605
  match skips without launching anything; a Git failure is infrastructure and
2403
2606
  fails closed, never a synthetic skip.
2404
2607
 
2608
+
2609
+ #### Post-reconcile verification policy
2610
+
2611
+ Post-reconcile verification defaults to enabled: `[verify].afterReconcile = true`.
2612
+ Set it to `false` to retain pre-reconcile verification and skip all steps after
2613
+ reconciliation. An optional boolean `[verify.<step>].afterReconcile` overrides
2614
+ the repository setting in either direction; overrides require a configured step.
2615
+ For example, set `afterReconcile = false` in `[verify]` and
2616
+ `afterReconcile = true` in `[verify.types]` to keep only that step re-running.
2617
+
2618
+ The kernel freezes the resolved ordered policy in
2619
+ `verify.reconcile-policy-captured` at the next engine action after reconcile
2620
+ completion. Live configuration changes apply at that boundary; a cycle already
2621
+ started keeps its policy through recovery. Historical cycles already started
2622
+ without a captured policy retain the rerun-all behavior. Initial verification
2623
+ and verification after subsequent code review always use ordinary applicability.
2624
+ Policy exclusion precedes plan selection and paths, including `always = true`.
2625
+ Enabled steps still follow plan selection and path rules. Excluded steps record
2626
+ `verify.completed` with outcome `skipped`, `skipCause = "post-reconcile-policy"`,
2627
+ and a reason naming the setting. Dashboards and PR summaries call this
2628
+ "skipped by post-reconcile policy"; it is never a pass. Forge-side required
2629
+ checks and branch protections still gate merge. For excluded steps, merged code
2630
+ is not re-verified, so breakage that only exists in the combination can land.
2631
+
2405
2632
  ### 16.3 Skill installation: vendored, namespaced, editable [D11]
2406
2633
 
2407
2634
  This project ships 11 canonical default skills for the fixed phases, the
@@ -2483,6 +2710,15 @@ install failure is fatal and does not merge against the wrong defaults.
2483
2710
  `--no-self-update` always selects merge-only
2484
2711
  behavior. An exact version may be older than the installed version.
2485
2712
 
2713
+ Before installation or project writes, upgrades targeting a hosted store probe
2714
+ its public health range. An incompatible target produces a directional warning
2715
+ (service first for a newer target, project upgrade for a stale target) and
2716
+ continues. A legacy health response has an exact-version contract; different
2717
+ targets warn that the service needs the compatibility-window release. Failed,
2718
+ timed-out, or malformed probes warn that connectivity could not be verified
2719
+ and may fail after upgrade, then continue. Merge-only and replacement-process
2720
+ handoffs check the distribution actually applied before skill writes.
2721
+
2486
2722
  Upgrade records its successful work in one local commit on the target's current
2487
2723
  HEAD by default. The exact ownership boundary is the reported skills' canonical
2488
2724
  `.agents` trees, pristine records, and `.claude` discovery paths, plus
@@ -2581,3 +2817,71 @@ or installs releases in the foreground or background.
2581
2817
  enforcement and will colocate later session-authenticated human routes.
2582
2818
  Artifacts remain content-by-value for now; direct-to-blob references are a
2583
2819
  possible later protocol revision, not a second current deployment path.
2820
+
2821
+ ## Revision-checked ticket writes
2822
+
2823
+ Every ticket read includes an opaque `revision`. Pass it as `expectedRevision`
2824
+ (on MCP/operator requests) or `--expected-revision <opaque>` on `ab ticket update`,
2825
+ `move`, `block` and `unblock`. A stale write fails without changing anything and
2826
+ reports the current revision. Omission preserves unguarded behavior. Blocker
2827
+ checks cover the entire batch. Dispatcher moves remain unguarded and change the
2828
+ revision. Sources without guarded writes, including Linear, refuse supplied
2829
+ revisions rather than ignoring them.
2830
+
2831
+ `ab ticket edit <id>` opens `$VISUAL`, falling back to `$EDITOR`, using TOML
2832
+ frontmatter and a Markdown body. Only title, body and labels are editable;
2833
+ id, blockers, creation key and reference comments are read-only. It saves only
2834
+ changed fields with the opening revision. Unchanged or formatting-only edits
2835
+ write nothing. On conflict, invalid input or editor failure, the error names an
2836
+ absolute file path containing the edited text. Read the current ticket and
2837
+ reapply the intended changes before retrying; conflicts are never merged.
2838
+
2839
+ File and Linear revisions fingerprint title, body, canonical label/blocker sets
2840
+ and state; reverting identical content restores its fingerprint. File checks
2841
+ re-read immediately before writing, with the existing no-lock race window.
2842
+ Direct file edits and state-directory moves change revisions. File revisions also
2843
+ cover structured comments and portable attachments. Legacy file comments change
2844
+ the body; structured comments preserve it. Separate hosted comments do not change
2845
+ ticket revisions. Hosted revisions
2846
+ are monotonic decimal bigint strings, and checks run under the same row lock
2847
+ and transaction as all requested changes. Hosted reads return a coherent
2848
+ snapshot of fields, blockers and revision.
2849
+
2850
+ ### Independent hosted ticket service
2851
+
2852
+ `[tickets] url` selects hosted ticket transport independently of BuildStore selection;
2853
+ without it, tickets use the selected remote build store. Local builds require an
2854
+ explicit ticket URL. Project registration and ticket policy publication through
2855
+ `ab connect --tickets URL` work before dispatch. Policy is a durable per-registration
2856
+ record, outside BuildStore events/artifacts; legacy uploaded configuration remains
2857
+ a compatibility fallback. Hosted lifecycle defaults are triage/ready/doing/done.
2858
+ Independent harvest uses revocable, expiring, service-bound ticket grants with
2859
+ read/create runner authority and read-only child authority. Build state and harvest
2860
+ journals remain exclusively in the selected BuildStore.
2861
+
2862
+ ### Ticket source migration
2863
+
2864
+ `ab ticket export <dir>` enumerates all states through TicketSource and writes a
2865
+ portable file tracker atomically to an absent or empty destination.
2866
+ `ab ticket import <dir>` preflights the entire tracker and target inventory, then
2867
+ creates or adopts tickets, maps blockers, deduplicates comments by exact body and
2868
+ restores current attachments. Both run sessionless without forge access.
2869
+
2870
+ TicketCreateOptions includes optional `requestedId`. Unsupported sources reject
2871
+ it. File keeps safe ids exactly; hosted preserves canonical own-team positive
2872
+ numbers and advances its counter above every occupied number. Creation-key
2873
+ adoption precedes collision checks. Known kept-number clashes stop import before
2874
+ any target mutation. Stable migration identities and atomic mapping files make
2875
+ interrupted imports resumable without content-based adoption.
2876
+
2877
+ File frontmatter additionally supports optional structured `comments` (body,
2878
+ createdAt, optional author) and `attachments` (kind, name, layout, entries with
2879
+ file base64 bytes or directory paths). Legacy absent fields retain old behavior;
2880
+ new creates use structured comments. Native file assets retain historical
2881
+ revisions in hidden source-local metadata and compose with legacy BuildStore
2882
+ assets. Build claims and spec re-pins freeze native assets into durable snapshots.
2883
+
2884
+ State normalization and the complete operator procedure are documented in
2885
+ [Ticket migration](docs/ticket-migration.md). The same reference is delivered in
2886
+ the installed guide. Export keeps lifecycle membership without inferring roles;
2887
+ import reuses unique case-insensitive states and creates normalized missing ones.