@defrex/autobuild 0.9.0 → 0.10.1
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 +97 -113
- package/SPEC.md +17 -4
- package/docs/architecture.md +109 -0
- package/docs/configuration.md +26 -7
- package/docs/remote-store-protocol.md +35 -0
- package/docs/website.md +4 -0
- package/package.json +6 -4
- package/packages/core/src/cli/dashboard/facts.ts +215 -0
- package/packages/core/src/cli/dashboard/frame-parse.ts +223 -0
- package/packages/core/src/cli/dashboard/model.ts +85 -147
- package/packages/core/src/cli/dashboard/poll.ts +7 -2
- package/packages/core/src/cli/dispatch-process.ts +1 -13
- package/packages/core/src/cli/dispatch.ts +80 -37
- package/packages/core/src/cli/help.ts +15 -1
- package/packages/core/src/cli/init-validation.ts +18 -0
- package/packages/core/src/cli/init.ts +3 -2
- package/packages/core/src/cli/installation.ts +50 -8
- package/packages/core/src/cli/self-update.ts +77 -2
- package/packages/core/src/cli/status.ts +1 -1
- package/packages/core/src/cli/ticket.ts +146 -1
- package/packages/core/src/cli/wait.ts +10 -2
- package/packages/core/src/cli/watch.ts +10 -2
- package/packages/core/src/config/live.ts +1 -0
- package/packages/core/src/config/pipeline-source.ts +17 -5
- package/packages/core/src/config/schema.ts +21 -0
- package/packages/core/src/integration/harness.ts +113 -1
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +1369 -0
- package/packages/core/src/kernel/auto-merge-default.ts +43 -9
- package/packages/core/src/kernel/auto-merge.ts +99 -22
- package/packages/core/src/kernel/dispatch-settings.ts +42 -17
- package/packages/core/src/kernel/dispatch-status.ts +23 -0
- package/packages/core/src/kernel/engine.ts +9 -438
- package/packages/core/src/kernel/generators/build-log.ts +259 -0
- package/packages/core/src/kernel/generators/repository-journals.ts +206 -0
- package/packages/core/src/kernel/generators/session-log.ts +72 -0
- package/packages/core/src/kernel/harvest.ts +62 -18
- package/packages/core/src/kernel/incremental-contract.ts +75 -0
- package/packages/core/src/kernel/incremental-fixtures.ts +211 -0
- package/packages/core/src/kernel/incremental.ts +53 -0
- package/packages/core/src/kernel/log-index.ts +620 -0
- package/packages/core/src/kernel/pr-attachments.ts +164 -46
- package/packages/core/src/kernel/reducer-registry.ts +164 -0
- package/packages/core/src/kernel/reducer.ts +216 -74
- package/packages/core/src/operator/query.ts +68 -28
- package/packages/core/src/operator/read-view.ts +98 -0
- package/packages/core/src/orchestrator/prompt.ts +3 -1
- package/packages/core/src/ports/tickets/fake.ts +5 -1
- package/packages/core/src/ports/tickets/file.ts +169 -48
- package/packages/core/src/ports/tickets/linear.ts +33 -1
- package/packages/core/src/ports/tickets/operations.ts +1 -1
- package/packages/core/src/ports/tickets/remote.ts +8 -1
- package/packages/core/src/ports/types.ts +32 -1
- package/packages/core/src/processes/build-execution-state.ts +31 -15
- package/packages/core/src/processes/build-runner.ts +184 -90
- package/packages/core/src/processes/dispatcher-selectors.ts +123 -0
- package/packages/core/src/processes/dispatcher.ts +270 -176
- package/packages/core/src/processes/execution-settlement.ts +72 -39
- package/packages/core/src/processes/harvest-execution-state.ts +41 -29
- package/packages/core/src/processes/infrastructure-failure-budget.ts +31 -18
- package/packages/core/src/processes/orchestrator-tick.ts +44 -3
- package/packages/core/src/processes/publication-state.ts +164 -52
- package/packages/core/src/processes/repo-view.ts +1436 -0
- package/packages/core/src/processes/sandbox-state.ts +26 -2
- package/packages/core/src/processes/tick-state.ts +33 -0
- package/packages/core/src/store/adapter.ts +10 -1
- package/packages/core/src/store/build-scope.ts +26 -0
- package/packages/core/src/store/contract.ts +337 -0
- package/packages/core/src/store/digest.ts +71 -18
- package/packages/core/src/store/local/schema.ts +17 -0
- package/packages/core/src/store/local/store.ts +111 -4
- package/packages/core/src/store/memory.ts +68 -1
- package/packages/core/src/store/phase-session.ts +23 -0
- package/packages/core/src/store/remote/client.ts +50 -1
- package/packages/core/src/store/remote/index.ts +2 -0
- package/packages/core/src/store/remote/protocol.ts +15 -0
- package/packages/core/src/store/repo-state-events.ts +72 -7
- package/packages/core/src/store/session-handle.ts +30 -0
- package/packages/core/src/store/session-reducer.ts +52 -11
- package/packages/core/src/store/snapshots.ts +88 -0
- package/packages/core/src/store/ticket-assets.ts +41 -8
- package/packages/core/src/store/types.ts +38 -0
- package/skills/guide/SKILL.md +38 -21
- package/skills/guide/references/remote-store-protocol.md +35 -0
- package/skills/operate/SKILL.md +146 -19
- package/skills/tickets/SKILL.md +57 -5
package/README.md
CHANGED
|
@@ -2,72 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
# Autobuild
|
|
4
4
|
|
|
5
|
-
**Tickets in,
|
|
5
|
+
**Tickets in, Product out.**
|
|
6
6
|
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Autobuild runs coding agents through a fixed pipeline that plans, implements,
|
|
10
|
+
reviews, and verifies every ticket before it merges. You groom tickets in and
|
|
11
|
+
review pull requests out; agents do the work, and deterministic code keeps
|
|
12
|
+
them honest.
|
|
12
13
|
|
|
13
14
|

|
|
15
16
|
|
|
16
|
-
## Groomed ticket in, reviewed PR out
|
|
17
|
-
|
|
18
|
-
Running several coding agents by hand doesn't scale well. Every session wants
|
|
19
|
-
prompts, permissions, and attention, and your focus becomes the ceiling.
|
|
20
|
-
With Autobuild, each build runs the whole loop headlessly and escalates only
|
|
21
|
-
when it is truly blocked. With attention off the critical path, throughput
|
|
22
|
-
is only limited by how fast you can write good tickets.
|
|
23
|
-
|
|
24
|
-
Headless is safe because the pipeline is deterministic code, not model
|
|
25
|
-
judgment. State lives in a typed, append-only event log, phase transitions
|
|
26
|
-
are owned by tested code, and every build leaves a queryable paper trail. Your
|
|
27
|
-
merge gates are never bypassed: a PR lands when your required checks and your
|
|
28
|
-
consent say it lands.
|
|
29
|
-
|
|
30
|
-
The loop also feeds itself. While building, agents record observations —
|
|
31
|
-
latent bugs, worthwhile refactors, follow-ups they noticed but rightly left
|
|
32
|
-
alone. A harvester distills them into proposed tickets and files them for
|
|
33
|
-
triage; approve one and it runs the same loop.
|
|
34
|
-
|
|
35
|
-
Every seam is an adapter: ticket sources (the hosted service with database or
|
|
36
|
-
Linear backend, direct Linear, local files, or a configured plugin), agent runtimes (Claude, Codex, Pi, or a configured plugin), the forge
|
|
37
|
-
(GitHub via `gh`, credential-free local Git, or a configured plugin), workspaces,
|
|
38
|
-
and the build store all sit behind
|
|
39
|
-
narrow interfaces. Trusted Bun plugins declared in `autobuild.toml` can register
|
|
40
|
-
third-party ticket, runtime, workspace, and forge adapters against the versioned
|
|
41
|
-
`@defrex/autobuild/plugin-sdk` surface. Repositories may also opt into the shipped
|
|
42
|
-
`vercel-sandbox` workspace provider: the complete build runs in an isolated
|
|
43
|
-
Vercel VM while the local supervisor retains credentialed branch/PR publication.
|
|
44
|
-
The default remains a local Git worktree. See the [workspace configuration and
|
|
45
|
-
security contract](docs/configuration.md#vercel-sandbox).
|
|
46
|
-
Teams that need one durable store across hosts
|
|
47
|
-
can deploy the optional [hosted service](packages/hosted-store-service/README.md),
|
|
48
|
-
which also serves a GitHub-authenticated browser dashboard, can own ticket
|
|
49
|
-
persistence and external tracker credentials, and can run the dispatcher itself on a schedule (see [the hosted dispatcher](docs/hosted-dispatcher.md)). Repository identity across the
|
|
50
|
-
Store, dispatcher, query commands, and that dashboard is the repository's
|
|
51
|
-
normalized origin URL (e.g. `https://github.com/defrex/autobuild`), never a
|
|
52
|
-
checkout path; pre-origin records are not migrated. The web UI uses a server-managed
|
|
53
|
-
session; automation clients can use the versioned [operator API](docs/operator-api.md)
|
|
54
|
-
for the same controls and projections without exposing service credentials, and
|
|
55
|
-
external MCP agents connect through the [hosted MCP server](docs/mcp.md).
|
|
56
|
-
|
|
57
17
|
## Quickstart
|
|
58
18
|
|
|
59
19
|
You need [Bun](https://bun.sh), `git`, and the local prerequisites for the
|
|
60
|
-
agent runtime you select: Claude Code, Codex CLI, or Pi.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
account or remote.
|
|
64
|
-
Init suggests only runtimes whose executable and/or provider authentication is
|
|
65
|
-
usable on your machine.
|
|
20
|
+
agent runtime you select: Claude Code, Codex CLI, or Pi. The GitHub forge also
|
|
21
|
+
needs an authenticated [`gh` CLI](https://cli.github.com) or an exported
|
|
22
|
+
`GITHUB_TOKEN`/`GH_TOKEN`; the local-git forge needs no account or remote.
|
|
66
23
|
|
|
67
24
|
<!-- release-install:start -->
|
|
68
25
|
|
|
69
26
|
```sh
|
|
70
|
-
bun add -g @defrex/autobuild@0.
|
|
27
|
+
bun add -g @defrex/autobuild@0.10.1
|
|
71
28
|
```
|
|
72
29
|
|
|
73
30
|
<!-- release-install:end -->
|
|
@@ -78,44 +35,36 @@ Then, from the repository you want built:
|
|
|
78
35
|
ab init
|
|
79
36
|
```
|
|
80
37
|
|
|
81
|
-
|
|
82
|
-
repository-aware setup to a coding agent.
|
|
83
|
-
[
|
|
84
|
-
|
|
85
|
-
environment without creating build history:
|
|
86
|
-
|
|
87
|
-
```sh
|
|
88
|
-
ab init --validate
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
See the [configuration reference](docs/configuration.md) for every field. Only
|
|
92
|
-
a clean readiness result should be followed by:
|
|
38
|
+
This vendors the editable `ab-*` skills, writes a stack-neutral config, and
|
|
39
|
+
hands repository-aware setup to a coding agent. The
|
|
40
|
+
[configuration reference](docs/configuration.md) covers every field. Then
|
|
41
|
+
start the dispatcher and its live dashboard:
|
|
93
42
|
|
|
94
43
|
```sh
|
|
95
44
|
ab dispatch
|
|
96
45
|
```
|
|
97
46
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
In your selected coding agent, invoke the ticket-grooming skill:
|
|
47
|
+
Then groom your first ticket in your coding agent:
|
|
101
48
|
|
|
102
49
|
```text
|
|
103
|
-
/ab-spec
|
|
50
|
+
/ab-spec add a retry budget
|
|
104
51
|
```
|
|
105
52
|
|
|
106
|
-
The agent asks the questions needed to produce a
|
|
107
|
-
|
|
108
|
-
largely becomes your input workflow.
|
|
53
|
+
The agent asks the questions needed to produce a buildable ticket and files it
|
|
54
|
+
in your tracker. From then on, that skill is how work goes in.
|
|
109
55
|
|
|
110
56
|
## How it works
|
|
111
57
|
|
|
112
|
-
Every
|
|
58
|
+
### Every ticket runs the pipeline
|
|
113
59
|
|
|
114
60
|
```text
|
|
115
61
|
spec → plan ⇄ plan-review → implement ⇄ code-review → verify:* → finalize
|
|
116
62
|
epilogue: (pr.conflicted → reconcile → verify:*)* → merged or closed
|
|
117
63
|
```
|
|
118
64
|
|
|
65
|
+
Each ticket moves through deterministic phases inside its own build-runner.
|
|
66
|
+
Every phase is a fresh agent session, with artifacts knitting them together.
|
|
67
|
+
|
|
119
68
|
1. **spec** — the dispatcher claims a ready ticket, establishes the final
|
|
120
69
|
spec, and cuts a branch. The spec is the contract for everything after.
|
|
121
70
|
2. **plan ⇄ plan-review** — a planner writes an implementation plan; an
|
|
@@ -125,54 +74,88 @@ spec → plan ⇄ plan-review → implement ⇄ code-review → verify:* → fin
|
|
|
125
74
|
you instead of looping forever.
|
|
126
75
|
4. **verify:\*** — your verification steps, in the order you declare them:
|
|
127
76
|
shell commands judged by exit code, or agent verifiers that return a
|
|
128
|
-
verdict.
|
|
129
|
-
5. **finalize** — the PR opens with an agent-written description and
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
commits selected files locally; the runner extends the open PR branch with
|
|
133
|
-
a regular push. A no-op adds no commit, and a publication failure becomes a
|
|
134
|
-
follow-up observation rather than failing the green build. Agent verifiers
|
|
135
|
-
attach an exact screenshot, trace, or other artifact with
|
|
136
|
-
`ab artifact put <kind> <file> --attach`; the PR always gets a pinned
|
|
137
|
-
retrieval command, and configured public image hosting can also render
|
|
138
|
-
images inline.
|
|
77
|
+
verdict and can attach evidence such as screenshots.
|
|
78
|
+
5. **finalize** — the PR opens with an agent-written description and the
|
|
79
|
+
attached evidence, then any post-PR steps you've configured (changelogs,
|
|
80
|
+
release notes) run.
|
|
139
81
|
6. **epilogue** — the dispatcher watches the open PR. Conflicts route back
|
|
140
82
|
through reconcile and re-verify; the build ends `merged` or `closed`.
|
|
141
83
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
84
|
+
### A dispatcher spawns builds
|
|
85
|
+
|
|
86
|
+
The dispatcher claims ready tickets and starts a build-runner for each.
|
|
87
|
+
Runners are isolated, and all state is logged. Agents never decide what phase
|
|
88
|
+
comes next, and outcomes are never inferred from what a model printed: every
|
|
89
|
+
phase reports through a typed CLI into the event log, and tested code decides
|
|
90
|
+
the transition. That log is the build. Kill the process at any point and the
|
|
91
|
+
dispatcher resumes from durable state.
|
|
149
92
|
|
|
150
93
|
The pipeline grammar is fixed on purpose; `verify:*` and `finalize:*` are the
|
|
151
|
-
extension points, declared per
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
94
|
+
extension points, declared per repository in `autobuild.toml`. For the seams
|
|
95
|
+
and the reasoning behind them, see [`docs/architecture.md`](docs/architecture.md)
|
|
96
|
+
and [`SPEC.md`](SPEC.md).
|
|
97
|
+
|
|
98
|
+
## All local, all remote, or anywhere between
|
|
99
|
+
|
|
100
|
+
Every seam is an adapter you can customize for your project. The build
|
|
101
|
+
pipeline stays the same.
|
|
102
|
+
|
|
103
|
+
| Seam | Decides | Adapters |
|
|
104
|
+
| ---------- | --------------------- | ------------------------------------------ |
|
|
105
|
+
| tickets | where work comes from | local files, Linear, hosted, plugin |
|
|
106
|
+
| dispatcher | what starts builds | local process, scheduled (hosted) |
|
|
107
|
+
| workspace | where a build runs | git worktree, Vercel Sandbox, plugin |
|
|
108
|
+
| runtime | who does the thinking | Claude Code, Codex, Pi, plugin |
|
|
109
|
+
| forge | where code lands | local git, GitHub, plugin |
|
|
110
|
+
| store | where state lives | SQLite, Postgres, any HTTP store |
|
|
111
|
+
| operators | how you watch | terminal, web UI, MCP |
|
|
112
|
+
|
|
113
|
+
Plugins are trusted Bun modules declared in `autobuild.toml` against the
|
|
114
|
+
versioned `@defrex/autobuild/plugin-sdk`. Teams that want one durable store
|
|
115
|
+
across hosts can deploy the optional
|
|
116
|
+
[hosted service](packages/hosted-store-service/README.md), which adds a
|
|
117
|
+
browser dashboard, a [scheduled dispatcher](docs/hosted-dispatcher.md), an
|
|
118
|
+
[operator API](docs/operator-api.md), and an [MCP server](docs/mcp.md).
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Built for throughput, not latency
|
|
122
|
+
|
|
123
|
+
One build may take longer than a chat with an agent. The trade is far more
|
|
124
|
+
changes landing every day, with much greater reliability.
|
|
125
|
+
|
|
126
|
+
- **File it and walk away.** Nothing in the pipeline waits on you. You don't
|
|
127
|
+
approve tool calls, paste errors back, or watch a terminal. A build
|
|
128
|
+
escalates to you only when it is truly blocked.
|
|
129
|
+
- **As many builds as you can groom.** Builds run side by side, each in its
|
|
130
|
+
own workspace, up to the limit you set. Your attention stops being the
|
|
131
|
+
ceiling; how fast you can write good tickets becomes the limit.
|
|
132
|
+
|
|
133
|
+
Headless is safe because the pipeline is deterministic code, not model
|
|
134
|
+
judgment. State lives in a typed, append-only event log, phase transitions
|
|
135
|
+
are owned by tested code, and every build leaves a queryable paper trail. Your
|
|
136
|
+
merge gates are never bypassed: a PR lands when your required checks and your
|
|
137
|
+
consent say it lands.
|
|
138
|
+
|
|
139
|
+
## Let the queue fill itself
|
|
140
|
+
|
|
141
|
+
Once tickets are the interface, anything that can write a ticket can put work
|
|
142
|
+
in front of you.
|
|
143
|
+
|
|
144
|
+
Builds feed the queue themselves. An implementer that spots a latent bug, a
|
|
145
|
+
worthwhile refactor, or a missing follow-up outside its spec records an
|
|
146
|
+
observation (`ab observe`) and moves on: the insight is kept, and scope creep
|
|
147
|
+
stays out of the PR. Once enough pile up, the dispatcher distills them into
|
|
148
|
+
proposed tickets, deduplicated against work already filed, and by default
|
|
149
|
+
files them into triage. You groom them like any ticket you wrote yourself.
|
|
150
|
+
Agents propose; humans dispatch.
|
|
151
|
+
|
|
152
|
+
Support threads, meeting notes, and error telemetry can feed it the same way.
|
|
153
|
+
An agent with the `ab` CLI or the MCP server can file tickets and answer
|
|
154
|
+
routine escalations, leaving you only the real product calls.
|
|
173
155
|
|
|
174
156
|
## Learn more
|
|
175
157
|
|
|
158
|
+
- [autobuild.run](https://www.autobuild.run) — the project site.
|
|
176
159
|
- [`docs/spec-standard.md`](docs/spec-standard.md) — what makes a ticket
|
|
177
160
|
buildable: the standard every dispatched spec must meet.
|
|
178
161
|
- [`docs/configuration.md`](docs/configuration.md) — the complete strict
|
|
@@ -181,7 +164,8 @@ propose; humans dispatch.
|
|
|
181
164
|
configuration, as a worked example of the config surface.
|
|
182
165
|
- [`docs/architecture.md`](docs/architecture.md) — how the design maps to the
|
|
183
166
|
codebase: kernel, ports, processes, and stores.
|
|
184
|
-
- [`packages/postgres-store/README.md`](packages/postgres-store/README.md) —
|
|
167
|
+
- [`packages/postgres-store/README.md`](packages/postgres-store/README.md) —
|
|
168
|
+
serverless PostgreSQL persistence with S3-compatible or Vercel Blob storage.
|
|
185
169
|
- [`docs/remote-store-protocol.md`](docs/remote-store-protocol.md) — the
|
|
186
170
|
complete HTTP server contract and BuildStore conformance instructions.
|
|
187
171
|
- [`SPEC.md`](SPEC.md) — the source of truth for the design and its
|
package/SPEC.md
CHANGED
|
@@ -241,6 +241,16 @@ same BuildStore. Consequences, by design:
|
|
|
241
241
|
|
|
242
242
|
- **State is a reduction of events.** Any state snapshot is a cache, never
|
|
243
243
|
the source of truth. Resumability falls out.
|
|
244
|
+
- **Reducer snapshots are a cache the log regenerates, never an authority.**
|
|
245
|
+
The store may keep, per scope (one build log or the repository journal) and
|
|
246
|
+
reducer, that reducer's serialized state as of a cursor, keyed by scope,
|
|
247
|
+
reducer, reducer version, and cursor, so a process with no hot memory loads
|
|
248
|
+
it, reads only the newer events, and advances. A missing snapshot, one
|
|
249
|
+
written by a different reducer version, or one ahead of the log is ignored
|
|
250
|
+
and the state is rebuilt by full replay with identical results; stored
|
|
251
|
+
cursors never decrease; deleting every snapshot changes no observable
|
|
252
|
+
output. A store without snapshot storage behaves as if every snapshot were
|
|
253
|
+
missing.
|
|
244
254
|
- **The UI layer is a subscriber** plus a command channel back. TUI, web —
|
|
245
255
|
same adapter pattern. The shipped interactive terminal owns a supervised
|
|
246
256
|
kernel child, and the BuildStore is their only changing-state boundary.
|
|
@@ -491,8 +501,11 @@ stream read's ([docs/remote-store-protocol.md](docs/remote-store-protocol.md),
|
|
|
491
501
|
|
|
492
502
|
#### 7.1.2 The embedded orchestrator (turns)
|
|
493
503
|
|
|
494
|
-
The orchestrator is the agent that files tickets,
|
|
495
|
-
them, and discusses direction
|
|
504
|
+
The orchestrator is a role, not a deployment: the agent that files tickets,
|
|
505
|
+
watches builds, unblocks them, and discusses direction. Any agent can take it
|
|
506
|
+
on — a coding-agent session at the repository root using the `ab` CLI or
|
|
507
|
+
`ab mcp` needs no initialization — and the canonical `ab-operate` skill is its
|
|
508
|
+
manual in either setting. In the hosted service it is played by a stateless
|
|
496
509
|
turn runner over the operator tool registry, not by an agent living in a
|
|
497
510
|
sandbox (parity with MCP; no credential plumbing) and not by an external
|
|
498
511
|
workflow engine (durability comes from the store; the tick pattern already
|
|
@@ -2413,8 +2426,8 @@ installs into a repo:
|
|
|
2413
2426
|
non-agent-invocable: they are invoked explicitly by the runner or a human,
|
|
2414
2427
|
never auto-triggered by a model pattern-matching a description — a model
|
|
2415
2428
|
must not start a pipeline phase by accident. The model-invocable exceptions
|
|
2416
|
-
(`ab-spec`, `ab-tickets`, `ab-guide`) are exactly the
|
|
2417
|
-
**drive no phase**; membership is decided by that criterion, not taste.
|
|
2429
|
+
(`ab-spec`, `ab-tickets`, `ab-guide`, `ab-operate`) are exactly the four
|
|
2430
|
+
skills that **drive no phase**; membership is decided by that criterion, not taste.
|
|
2418
2431
|
|
|
2419
2432
|
When `.agents/skills` and `.claude/skills` resolve to the same filesystem
|
|
2420
2433
|
entry, that repository-level alias already satisfies harness discovery. Init
|
package/docs/architecture.md
CHANGED
|
@@ -131,6 +131,115 @@ session's status, open turn, pending approval, and wake cursors from its own
|
|
|
131
131
|
closed catalog. No decision anywhere
|
|
132
132
|
consults a snapshot in place of the append-only log.
|
|
133
133
|
|
|
134
|
+
**Incremental reducers.** Every pure projection of an event array is a fold plus
|
|
135
|
+
a final derivation, defined once with `defineReducer` in
|
|
136
|
+
`packages/core/src/kernel/incremental.ts`: `initial()` builds an empty
|
|
137
|
+
accumulator, `advance(acc, events)` folds in only the events newer than the
|
|
138
|
+
accumulator, and `finish(acc)` derives the public state. The whole-array
|
|
139
|
+
functions (`reduceBuild`, `reduceHarvest`, `reduceSession`, and the rest) are
|
|
140
|
+
wrappers over the same fold, so results cannot diverge, and a property test
|
|
141
|
+
checks every split point of fixture and generated logs. The accumulator is plain
|
|
142
|
+
JSON, so it survives a serialize-and-parse round trip. `advance` never mutates
|
|
143
|
+
its input: it clones, then folds, so a state derived earlier is never disturbed
|
|
144
|
+
by a later advance. Each reducer publishes a `version`; bump it whenever the
|
|
145
|
+
accumulator shape or the fold semantics change, because a cached accumulator is
|
|
146
|
+
only valid for the version that wrote it. `kernel/reducer-registry.ts` lists
|
|
147
|
+
every reducer with its version, and its snapshot test fails when an accumulator
|
|
148
|
+
shape changes without a bump. Most callers still read whole logs (the terminal
|
|
149
|
+
dashboard poll advances a carried build accumulator by per-build deltas; the
|
|
150
|
+
dispatcher view below restores from stored accumulators).
|
|
151
|
+
|
|
152
|
+
**Reducer snapshots.** The `BuildStore` port can keep, per scope (one build log
|
|
153
|
+
or the repository journal) and registered reducer, that reducer's accumulator as
|
|
154
|
+
of a cursor: `getReducerSnapshot(scope, reducer, version)` and
|
|
155
|
+
`putReducerSnapshot(scope, reducer, {version, cursor, state})`
|
|
156
|
+
(`packages/core/src/store/snapshots.ts`). The log stays the only authority. The
|
|
157
|
+
store enforces the invariants: the stored cursor never decreases (a write whose
|
|
158
|
+
cursor does not advance is a no-op, so concurrent writers cannot undo each
|
|
159
|
+
other), a row of another reducer version reads as absent and is replaced once a
|
|
160
|
+
writer advances past it, and a cursor beyond the log's tail is refused and never
|
|
161
|
+
returned. Every consumer treats a missing, mismatched, ahead-of-log, malformed
|
|
162
|
+
or unreadable snapshot as a miss and replays the log, with identical results;
|
|
163
|
+
deleting all snapshots changes no observable output (the integration harness
|
|
164
|
+
audits this for every scenario at cleanup). Memory, SQLite, Postgres and the
|
|
165
|
+
remote client implement it, and the hosted store service serves it on
|
|
166
|
+
`GET|PUT /builds|repos/{id}/snapshots/{reducer}`
|
|
167
|
+
(`docs/remote-store-protocol.md`). SQLite creates the `reducer_snapshots` table
|
|
168
|
+
at open. Postgres keeps it outside the versioned schema: `reducer_snapshots` is
|
|
169
|
+
created by the idempotent `SNAPSHOT_SCHEMA_DDL` in `migratePostgres`, with no
|
|
170
|
+
marker, version or checksum change, so a deployment at the current schema keeps
|
|
171
|
+
serving across the upgrade, a new client against a database that lacks the table
|
|
172
|
+
reads every snapshot as missing and replays (SQLSTATE `42P01` is never an
|
|
173
|
+
error), and an older client never asks. Run `bun run postgres:migrate` once to
|
|
174
|
+
gain the table; until then the dispatcher and every other reader replay in full.
|
|
175
|
+
|
|
176
|
+
**Dispatcher repository view.** A dispatcher process builds its view of the
|
|
177
|
+
repository once and refreshes it by deltas
|
|
178
|
+
(`packages/core/src/processes/repo-view.ts`). `RepoViewStore` decorates the
|
|
179
|
+
`BuildStore` port: the first `refresh()` reads the journal's high-water mark
|
|
180
|
+
(`getRepoHighWater`), the bounded journal subset, the build listing and digests,
|
|
181
|
+
and the full log of every build that still has work (the same predicate the tick
|
|
182
|
+
loops use); every later `refresh()` reads only events newer than its cursors —
|
|
183
|
+
one journal delta, one delta per held build, plus the listing and digest reads
|
|
184
|
+
that discover new builds. Every tick stage reads and writes through the view, so
|
|
185
|
+
an event a stage appends is folded in at once and later stages in the same tick
|
|
186
|
+
see it with no re-read; a foreign append interleaved with ours leaves a seq gap
|
|
187
|
+
that marks the log dirty, a lost compare-and-set does the same, and any delta
|
|
188
|
+
that is not contiguous from its cursor discards the log and re-reads it in full,
|
|
189
|
+
so no event is skipped. Every state transition of one source (a build, the
|
|
190
|
+
journal) runs on that source's single promise chain — snapshot and delta reads,
|
|
191
|
+
window opening and widening, the fold after an own append, a lost
|
|
192
|
+
compare-and-set's invalidation — and readers queue on it too, so overlapping
|
|
193
|
+
readers join the repair in flight instead of observing half-applied state.
|
|
194
|
+
Decisions that deliberately re-read at the last moment
|
|
195
|
+
(`refreshBuild`) take an explicit delta. Cursor-bearing readers such as the
|
|
196
|
+
orchestrator wake pass get per-source windows that hold only events above the
|
|
197
|
+
lowest session cursor. A `--once` invocation builds the view cold and reads each
|
|
198
|
+
log once; a long-running dispatcher pays that replay at its first tick and not
|
|
199
|
+
again.
|
|
200
|
+
|
|
201
|
+
The view restores from reducer snapshots on cold start. The journal comes from
|
|
202
|
+
the `journalView` reducer (the journal pruned on arrival: durable types plus the
|
|
203
|
+
tail from the latest `dispatcher.run-started`); each work build comes from one
|
|
204
|
+
`work` snapshot row — the `build`, `logIndex`, `openExecution`,
|
|
205
|
+
`openBuildWorkspace`, `publicationState`, `lastExecutionOutcome`, `buildDigest`,
|
|
206
|
+
`dashboardFacts` and `currentDeferralObservation` accumulators at a single
|
|
207
|
+
cursor. One row is one atomic write on every adapter, so a reader in another
|
|
208
|
+
process sees the old bundle or the new, never a mix; the bundle version is
|
|
209
|
+
derived from the component reducer versions, so a stale bundle misses and the
|
|
210
|
+
build is replayed once. The view
|
|
211
|
+
reads only the events newer than that cursor and holds, for a snapshot-backed
|
|
212
|
+
build, just that delta plus the reduced state of everything before it, so a tick
|
|
213
|
+
with no new events reads zero event rows (apart from the discovery listing and
|
|
214
|
+
digests) and one with M new events reads exactly M. Tick stages that run for every
|
|
215
|
+
build each tick take `buildFacts(slug)` (state, log index, open execution, open
|
|
216
|
+
workspace, publication view) instead of the event array, and read raw history
|
|
217
|
+
only on the branches that act on it. `persistSnapshots()` writes the advanced
|
|
218
|
+
state back at the end of each tick and on shutdown — best effort, throttled in a
|
|
219
|
+
resident dispatcher, always run by a cold single-tick process — and never writes
|
|
220
|
+
a cursor that was not read from the log.
|
|
221
|
+
|
|
222
|
+
**Operator reads.** The web dashboard, the repository and Harvest status
|
|
223
|
+
routes, the operator build listings, and the `ab watch`/`ab wait` discovery
|
|
224
|
+
passes derive from that same view instead of replaying the log
|
|
225
|
+
(`packages/core/src/operator/read-view.ts`). A dashboard row renders from the
|
|
226
|
+
`BuildState` plus the `dashboardFacts` reducer
|
|
227
|
+
(`packages/core/src/cli/dashboard/facts.ts`: queued-row dispatch text, session
|
|
228
|
+
brackets, phase intervals, pause time, and the per-round loop-output seqs) and
|
|
229
|
+
the auto-merge deferral ledger; `projectBuild(…, events)` is the same projection
|
|
230
|
+
over the whole-array reduction, so the snapshot and replay paths cannot differ.
|
|
231
|
+
Within one serving process, passes are single-flight per repository: requests
|
|
232
|
+
arriving while a pass runs join the one trailing pass, which restores from the
|
|
233
|
+
snapshot the previous pass persisted. A poll at idle therefore reads no event
|
|
234
|
+
rows beyond snapshot lookups and build discovery, and a poll after M new events
|
|
235
|
+
reads those M rows once, however many tabs or watchers the process serves.
|
|
236
|
+
Serving processes do not coordinate: with P of them, each may read the same M
|
|
237
|
+
rows once, so duplicate reads are bounded by P and never by history, tabs, or
|
|
238
|
+
watchers. A settled build listed under `--all` is restored from the snapshot the
|
|
239
|
+
dispatcher left at settlement, or read once and then persisted. The first
|
|
240
|
+
operator poll after upgrading replays each work build once, because the stored
|
|
241
|
+
bundle layout changed.
|
|
242
|
+
|
|
134
243
|
**Session Store authority.** `packages/core/src/cli/binary.ts` validates a complete build or
|
|
135
244
|
Harvest ambient tuple before opening its phase Store through
|
|
136
245
|
`packages/core/src/cli/store-opening.ts`. `packages/core/src/cli/env.ts` also classifies optional ambient
|
package/docs/configuration.md
CHANGED
|
@@ -202,7 +202,7 @@ These fields hot-reload:
|
|
|
202
202
|
| `[workspace]` provider and config (including `provisioning`) | Next launch of a build whose OWN branch carries it. A base-branch reload never re-provisions an in-flight build. |
|
|
203
203
|
| `[roles]` | Next agent invocation for that role. Runtime, model, args, alternates, and the session budget are captured together and delivered live. |
|
|
204
204
|
| `[policy]` | Next policy, convergence, or agent-session boundary, delivered live. A running session keeps its captured budget. |
|
|
205
|
-
| `tickets.readyLabels`, `tickets.readyState` | Next ready-ticket scan. |
|
|
205
|
+
| `tickets.readyLabels`, `tickets.readyState`, `tickets.readyAssignee` | Next ready-ticket scan. |
|
|
206
206
|
| `tickets.triageState`, `tickets.proposalState` | Next dispatcher handback or harvest filing boundary. |
|
|
207
207
|
|
|
208
208
|
Adapter selectors and factory inputs are constructed once and require a
|
|
@@ -1167,6 +1167,7 @@ state. Within a present table, `source` and `readyState` are required.
|
|
|
1167
1167
|
| `source` | — | required nonblank builtin or plugin registration name | Select the ticket adapter after configured plugins load. |
|
|
1168
1168
|
| `readyLabels` | source-aware | optional array of nonempty strings; `[]` allowed | Require every listed label in addition to the state gate. |
|
|
1169
1169
|
| `readyState` | — | required nonblank string | The one workflow state eligible for dispatch. |
|
|
1170
|
+
| `readyAssignee` | — | optional; `"me"` or an email address; Linear only (rejected for file, hosted, and plugin sources) | Require the ticket to be assigned to one Linear user, in addition to the state and label gates. |
|
|
1170
1171
|
| `teamKey` | — | required for Linear and hosted; forbidden for file; optional for plugins | Backend team key such as `"ENG"`, or an existing plugin config field. |
|
|
1171
1172
|
| `claimedState` | `"In Progress"` for direct Linear; hosted backend default otherwise | optional nonempty string; forbidden for file; allowed for plugins | Workflow state entered when a ticket is claimed. |
|
|
1172
1173
|
| `createState` | provider default | optional nonempty string | Default state for newly created tickets when `ab ticket create` omits `--state`. |
|
|
@@ -1174,13 +1175,33 @@ state. Within a present table, `source` and `readyState` are required.
|
|
|
1174
1175
|
| `proposalState` | the resolved `triageState` | optional nonempty string | State harvest files its synthesized proposals into. Setting it to `readyState` waives the human grooming gate, not dependency eligibility. |
|
|
1175
1176
|
| `dir` | file: selected local state root's `tickets/`; plugin: omitted | optional nonempty path; forbidden for Linear; allowed for plugins | Root containing file-source state directories, or an existing plugin config field. Relative file paths resolve from the repository. |
|
|
1176
1177
|
|
|
1178
|
+
`readyAssignee = "me"` resolves to the Linear user who owns `LINEAR_API_KEY`,
|
|
1179
|
+
so one committed value makes each person's local dispatcher take only the work
|
|
1180
|
+
assigned to them; an email names a specific user, for a shared or bot key. The
|
|
1181
|
+
gate is conjunctive with `readyState` and `readyLabels`, and a ticket with no
|
|
1182
|
+
assignee is never ready while it is set. It applies to every ready surface
|
|
1183
|
+
(dispatch, the queued count, and `ab ticket list` without filters); blockers
|
|
1184
|
+
still prevent claim. It is empty or blank, or neither `"me"` nor an email,
|
|
1185
|
+
fails validation. `ab init --validate` prints the user the gate resolves to and
|
|
1186
|
+
fails when an email matches no workspace user. The field reloads without a
|
|
1187
|
+
restart.
|
|
1188
|
+
|
|
1177
1189
|
When `readyLabels` is absent, direct Linear uses `["autobuild"]`; hosted, file,
|
|
1178
1190
|
and plugin sources use `[]`, meaning no host-imposed label gate. An explicit value always
|
|
1179
1191
|
wins. A nonempty list is
|
|
1180
1192
|
conjunctive: every configured label must be present. `readyState` remains
|
|
1181
1193
|
mandatory regardless of labels. Linear compares state and label names exactly
|
|
1182
|
-
and case-sensitively. The file source
|
|
1183
|
-
|
|
1194
|
+
and case-sensitively. The file source matches state names case-insensitively against the directories
|
|
1195
|
+
under the tracker root: the four lifecycle directories (`triage/`, `ready/`,
|
|
1196
|
+
`doing/`, `done/`, always present) plus any directory the repository adds, such
|
|
1197
|
+
as `icebox/`. A name matching no directory is an error listing those that
|
|
1198
|
+
exist, so creating a state stays an explicit act: `ab ticket state create <name>
|
|
1199
|
+
[--about <text>]` makes one, and `ab ticket states` lists every state with its
|
|
1200
|
+
roles, ticket count, and purpose. A purpose is stored as a one-line `.about` file
|
|
1201
|
+
inside the state directory, so it travels with a git-tracked `dir` and is never
|
|
1202
|
+
read as a ticket. `createState`, `readyState`,
|
|
1203
|
+
`triageState`, and `proposalState` may each name any existing directory;
|
|
1204
|
+
`ab ticket list` with no filter still lists only `readyState`.
|
|
1184
1205
|
|
|
1185
1206
|
Source-specific validation is strict:
|
|
1186
1207
|
|
|
@@ -1551,10 +1572,8 @@ pre-attempt index and reports the original Git failure; merged worktree files
|
|
|
1551
1572
|
remain in place and the merge's exit status is unchanged. Upgrade never pushes
|
|
1552
1573
|
or rewrites history.
|
|
1553
1574
|
|
|
1554
|
-
Autobuild now installs 12 skills; only `ab-spec`, `ab-tickets`,
|
|
1555
|
-
are model-invocable.
|
|
1556
|
-
manual — also carries `disable-model-invocation: true`; it is read by the
|
|
1557
|
-
hosted turn runner, never invoked by a model in a build session.) The setup reference is an ordinary support file in the
|
|
1575
|
+
Autobuild now installs 12 skills; only `ab-spec`, `ab-tickets`, `ab-guide`,
|
|
1576
|
+
and `ab-operate` are model-invocable. The setup reference is an ordinary support file in the
|
|
1558
1577
|
editable/pristine `ab-guide` tree and participates in the same three-way
|
|
1559
1578
|
upgrade merge as every other vendored file.
|
|
1560
1579
|
|
|
@@ -297,6 +297,8 @@ it to exist. An unknown build returns `404 not-found`.
|
|
|
297
297
|
| `appendIfCurrent` | `POST /builds/{slug}/events/conditional` | `{"expectedSeq": nonnegative integer, "event": event write}` | appended: `201` + build event envelope; stream advanced: `200 null` |
|
|
298
298
|
| `getEvents` | `GET /builds/{slug}/events?since={n}&wait={s}` | optional `since` query value, parsed below; absence defaults to `0`. Optional `wait` in whole seconds, parsed per the event-wait rules below | `200` + envelopes whose `seq` is strictly greater than parsed `since`, in increasing sequence order |
|
|
299
299
|
| `appendWithArtifacts` | `POST /builds/{slug}/deposits` | atomic deposit request | `201` + `{event, artifacts}` |
|
|
300
|
+
| `getReducerSnapshot` | `GET /builds/{slug}/snapshots/{reducer}?version={n}` | required `version` query value, a positive integer; `reducer` is a registry name (`^[A-Za-z][A-Za-z0-9._-]{0,63}$`) | `200` + `{"version", "cursor", "state"}` or `200 null` when absent, written by another reducer version, or ahead of the build log's tail; an unknown build is `404`, which the shipped client also maps to `null` |
|
|
301
|
+
| `putReducerSnapshot` | `PUT /builds/{slug}/snapshots/{reducer}` | `{"version": int >= 1, "cursor": int >= 0, "state": JSON}`, at most 16 MiB (`413` above) | `200 {"written": boolean}`; the stored cursor never decreases, so a write at an equal or lower cursor (or an equal cursor at an equal or lower version) and one past the log's tail answer `written: false` |
|
|
300
302
|
| `putArtifact` | `POST /builds/{slug}/artifacts` | artifact input | `201` + build artifact metadata |
|
|
301
303
|
| `getArtifact` | `GET /builds/{slug}/artifacts?kind={kind}&rev={n}` | `kind` is required and nonempty; optional `rev` is parsed below | `200` + artifact read; an absent `rev` parameter selects the latest revision; a missing kind/revision is `200 null` |
|
|
302
304
|
| `listArtifacts` | `GET /builds/{slug}/artifact-list?kind={kind}` | optional `kind`; absence means all kinds | `200` + metadata ordered by kind and then increasing revision |
|
|
@@ -336,6 +338,32 @@ part of this protocol. Retention is enforced server-side inside the store at
|
|
|
336
338
|
deposit time (bounded pruning of dispatcher run/config artifact revisions),
|
|
337
339
|
not through any endpoint.
|
|
338
340
|
|
|
341
|
+
### Reducer snapshots
|
|
342
|
+
|
|
343
|
+
A reducer snapshot is one registered reducer's serialized state as of a log
|
|
344
|
+
cursor, kept per scope (one build log or one repository journal) and reducer
|
|
345
|
+
name. It is a cache the log regenerates, never an authority: a process with no
|
|
346
|
+
hot memory loads it, reads only the events newer than its cursor, advances the
|
|
347
|
+
reducer, and writes the advanced state back, and deleting every snapshot changes
|
|
348
|
+
no observable output. The routes above and in section 4 are additive and carry
|
|
349
|
+
no protocol-version bump. The store, not the client, enforces the invariants:
|
|
350
|
+
|
|
351
|
+
- the stored cursor never decreases, whatever the versions of the competing
|
|
352
|
+
writers; a write succeeds only when no row exists, when its cursor is above the
|
|
353
|
+
stored one, or when its cursor equals the stored one and its version is higher
|
|
354
|
+
(an upgraded reducer replacing the old shape at an unchanged tail);
|
|
355
|
+
- a read answers `null` for a row whose version differs from the requested one,
|
|
356
|
+
and for one whose cursor is beyond the scope's log tail; a write beyond the
|
|
357
|
+
tail is refused;
|
|
358
|
+
- an unknown build or repository answers `null` (read) or `written: false`
|
|
359
|
+
(write) and never creates the scope.
|
|
360
|
+
|
|
361
|
+
A client treats `404` and `405` from either route as "no snapshot", so a client
|
|
362
|
+
newer than its store replays the log with no error, and a client older than its
|
|
363
|
+
store never calls the routes. Authentication and transport failures still
|
|
364
|
+
surface normally. A build token may touch only its own build's snapshots and a
|
|
365
|
+
repository token only its own repository's, exactly as for events.
|
|
366
|
+
|
|
339
367
|
### The event-read bounded wait
|
|
340
368
|
|
|
341
369
|
Both event reads (`getEvents` and, symmetrically, the `getRepoEvents` read of
|
|
@@ -382,6 +410,8 @@ without a journal record, or a repository without builds.
|
|
|
382
410
|
| `appendRepo` | `POST /repos/{repo}/events` | event write | `201` + repository event envelope |
|
|
383
411
|
| `getRepoEvents` | `GET /repos/{repo}/events?since={n}&wait={s}` | optional `since` query value, parsed as in section 3; absence defaults to `0`. Optional `wait` in whole seconds, per the event-wait rules of section 3 | `200` + envelopes with `seq >` parsed `since`, in increasing sequence order |
|
|
384
412
|
| `getRepoStateEvents` | `GET /repos/{repo}/state-events` | none | `200` + the bounded repository-journal subset (AUT-489): every durable event type (`harvest.*`, `orchestrator.sandbox.*`, the three dispatcher setting types) plus, when the journal has one, the tail from the latest `dispatcher.run-started`, in increasing sequence order; served inside the repository-existence gate like `GET events`, so an unknown repository is `404` |
|
|
413
|
+
| `getReducerSnapshot` (repo scope) | `GET /repos/{repo}/snapshots/{reducer}?version={n}` | required `version` query value, a positive integer; `reducer` is a registry name (`^[A-Za-z][A-Za-z0-9._-]{0,63}$`) | `200` + `{"version", "cursor", "state"}` or `200 null` when absent, written by another reducer version, ahead of the journal's tail, or the repository has no journal record; an unknown repository is `404`, which the shipped client also maps to `null` |
|
|
414
|
+
| `putReducerSnapshot` (repo scope) | `PUT /repos/{repo}/snapshots/{reducer}` | `{"version": int >= 1, "cursor": int >= 0, "state": JSON}`, at most 16 MiB (`413` above) | `200 {"written": boolean}`; the stored cursor never decreases, so a write at an equal or lower cursor (or an equal cursor at an equal or lower version) and one past the journal's tail answer `written: false` |
|
|
385
415
|
| `appendRepoWithArtifacts` | `POST /repos/{repo}/deposits` | atomic deposit request | `201` + `{event, artifacts}` using repository shapes |
|
|
386
416
|
| `putRepoArtifact` | `POST /repos/{repo}/artifacts` | artifact input | `201` + repository artifact metadata |
|
|
387
417
|
| `getRepoArtifact` | `GET /repos/{repo}/artifacts?kind={kind}&rev={n}` | required nonempty `kind`; optional `rev` is parsed as in section 3 | `200` + artifact read; latest only when the `rev` parameter is absent; missing kind/revision is `200 null` |
|
|
@@ -834,6 +864,11 @@ resource access; an explicit resource whose id is `"*"` is not admin.
|
|
|
834
864
|
| repo | no | no | no | exact id only | yes | exact id only | exact id only | exact repo only | no |
|
|
835
865
|
| session | no | no | no | no | no | no | no | no | exact id only |
|
|
836
866
|
|
|
867
|
+
The snapshot routes (`/builds/{slug}/snapshots/{reducer}` and
|
|
868
|
+
`/repos/{repo}/snapshots/{reducer}`) follow the one-matching-build and
|
|
869
|
+
one-matching-repo columns: a build token reads and writes only its own build's
|
|
870
|
+
snapshots, a repo token only its own repository's.
|
|
871
|
+
|
|
837
872
|
A valid token used for the wrong resource receives `403 auth`. Resource scope
|
|
838
873
|
gates all operations, including reads, artifact operations, and leases. A
|
|
839
874
|
session resource token's authority is exactly its one session — it cannot
|
package/docs/website.md
CHANGED
|
@@ -5,6 +5,10 @@ The marketing site is one static page in `packages/website`, reproduced from the
|
|
|
5
5
|
- `bun run website:dev` serves it on port 3300 (`PORT` overrides), rebuilding on every request.
|
|
6
6
|
- `bun run website:build` writes `packages/website/dist/` (`index.html`, `site.css`, `site.js`).
|
|
7
7
|
|
|
8
|
+
The hero is the real `ab dispatch` dashboard: `packages/website/src/hero-frame.txt` holds the exact colored lines the terminal dashboard painted for the scripted `website-hero` capture, and the page renders them as preformatted HTML with each terminal color mapped to its design token. `bun run capture:website-hero` regenerates the file from the capture, and `bun run check` fails when it is stale.
|
|
9
|
+
|
|
10
|
+
Shared links preview through Open Graph and Twitter card tags in the page head, pointing at `og.png`, a 1200×630 card drawn from the same frame in the site's own styles. `bun run capture:website-og` screenshots it with a local Chromium into `packages/website/src/og.png`; rerun it after changing the headline, the hero frame, or the styles, and look at the result, since font rasterization differs between machines and nothing checks the bytes. The canonical origin every absolute URL is built from is `SITE_URL` in `packages/website/src/constants.ts`.
|
|
11
|
+
|
|
8
12
|
`dist/` is plain static files: any static host can serve it with no server runtime, credentials, or environment variables. On Vercel, set the root directory to `packages/website`, the build command to `bun run build`, and the output directory to `dist`.
|
|
9
13
|
|
|
10
14
|
The page loads one webfont from Google Fonts and makes no other third-party requests.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defrex/autobuild",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Agent-driven software delivery from groomed ticket to merged PR",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -117,9 +117,11 @@
|
|
|
117
117
|
"website:dev": "bun packages/website/src/dev.ts",
|
|
118
118
|
"website:build": "bun packages/website/src/build.ts",
|
|
119
119
|
"capture:readme-headline": "bun tools/readme-headline.ts",
|
|
120
|
+
"capture:website-hero": "bun tools/website-hero.ts",
|
|
121
|
+
"capture:website-og": "bun tools/website-og.ts",
|
|
120
122
|
"release": "bun tools/release.ts",
|
|
121
123
|
"postgres:migrate": "bun packages/postgres-store/src/bin.ts migrate",
|
|
122
|
-
"check": "bun tools/biome-check.ts && bun tools/product-name-check.ts && bun tools/docs-asset-check.ts && bun tools/skill-docs-asset-check.ts && bun tools/readme-headline.ts --check && bun tools/workspace-manifest-check.ts && bun tools/package-boundary-check.ts && bun tools/bin-mode-check.ts && bun tools/publish-contents-check.ts && bun tools/publish-imports-check.ts",
|
|
124
|
+
"check": "bun tools/biome-check.ts && bun tools/product-name-check.ts && bun tools/docs-asset-check.ts && bun tools/skill-docs-asset-check.ts && bun tools/readme-headline.ts --check && bun tools/website-hero.ts --check && bun tools/workspace-manifest-check.ts && bun tools/package-boundary-check.ts && bun tools/bin-mode-check.ts && bun tools/publish-contents-check.ts && bun tools/publish-imports-check.ts",
|
|
123
125
|
"format:staged": "git-format-staged --formatter './node_modules/.bin/biome format --stdin-file-path {}' '*.js' '*.jsx' '*.ts' '*.tsx' '*.json' '*.jsonc' '*.css' '*.graphql' '*.gql'",
|
|
124
126
|
"prepare": "husky",
|
|
125
127
|
"test": "bun test packages/*/src tools bin packages/hosted-store-service/app",
|
|
@@ -137,8 +139,8 @@
|
|
|
137
139
|
},
|
|
138
140
|
"devDependencies": {
|
|
139
141
|
"@biomejs/biome": "2.5.5",
|
|
140
|
-
"@defrex/autobuild-postgres-store": "0.
|
|
141
|
-
"@defrex/autobuild-vercel-sandbox": "0.
|
|
142
|
+
"@defrex/autobuild-postgres-store": "0.9.0",
|
|
143
|
+
"@defrex/autobuild-vercel-sandbox": "0.9.0",
|
|
142
144
|
"@resvg/resvg-js": "2.6.2",
|
|
143
145
|
"@types/bun": "^1.3.14",
|
|
144
146
|
"@types/node": "^24.0.0",
|