Add the tracker layer (tickets, deps, states, API), real agent-spawn adapter, and JS workflow authoring - #2
Merged
Conversation
…al workers
Addresses the review of the first PR ("reference impl of the dispatcher/
execution layer, not the tracker layer") — the pieces needed to serve as a
Plane replacement rather than just a per-task engine:
- Ticket entity + tasks.json registry (§1.5): the #N / #N.x tree, needs
cross-task dependencies (acyclic by construction), locked + versioned
local registry with atomic writes
- Cross-ticket DAG promotion: blockedBy -> promote on done; unmet needs sit
in backlog, promotion staffs autoStaff tickets in the background, missed
promotions are re-derived on recovery
- The eight-value state model (§1.1): backlog/todo/design/design_review/
in_progress/in_review/done/cancelled, projected live from the run record
via a per-ticket state_map (INT map shipped as a preset), with park
reasons surfaced on the ticket
- HTTP API (node:http, dependency-free): file/board/status, staff, nudge,
gate verdicts, pause/resume/cancel, events/journal reads; 400/404/409
error mapping; owns the engine tick cadence
- CLI is no longer read-only: serve boots the full stack (tracker + git
worktrees + process workers); board/file/ticket/staff/nudge/gate/pause/
resume/cancel drive the API
- Real agent-spawn adapter: spawns an actual worker process per seat in its
git worktree, writes the stage manifest + brief into .beckett/, speaks a
line-JSON driver protocol (ready handshake, turns, file changes,
checkpoints, done-signal), nudges over stdin, abort = WIP commit +
SIGTERM, silent exits synthesize finished and ride the alarm/retry ladder
- CI: GitHub Actions (typecheck, tests, simulated deployments)
- 26 new tests (151 total): tracker semantics, promotion, projection,
registry versioning, real-child-process end to end (incl. under the
tracker), full API lifecycle over the wire
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3DUAdiNuM62Q6TVAyj7rv
Workflows can now be authored with JavaScript instead of (or alongside)
JSON, so the shape is computed for the task at hand rather than one rigid
structure for every ticket:
- FlowBuilder: fluent flow().worker().gate().fanout().join().budget()
.build() API; build() runs the full linter so an unbounded or malformed
graph never leaves the builder
- Flow scripts: a .js/.mjs/.cjs file default-exports (ctx) => flow, where
ctx carries the ticket (title/body/criteria), the builder, and the
presets; supports async scripts, wrapped returns ({flow, stateMap,
hooks}), module-level exports, and CJS interop; results are linted and
frozen exactly like hand-written specs
- Hooks: an onEvent export acts as a scripted concierge with operator
authority (nudge, pause/resume with grants, gate verdicts, cancel, file
follow-up tickets); runs serialised off the event path; errors are
journalled, never fatal; reloaded from the stored script path on recovery
- Wiring: Ticket.flowScript + filing via the tracker, HTTP API and CLI
(--flow-script); examples/flows/adaptive.flow.mjs demonstrates per-task
shapes (hotfix -> one-pass, "areas:" -> computed fanout arms, criteria
count -> effort) plus an auto-extend-once park policy
- 15 new tests (166 total): builder composition/refusals, script loading
(async, cjs, error paths), per-task shape divergence through the tracker,
hook policies (auto-approve gate, auto-grant on park, follow-up filing,
error journalling), and hook reload across recovery
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3DUAdiNuM62Q6TVAyj7rv
frgmt0
marked this pull request as ready for review
July 13, 2026 14:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1, addressing the review feedback point by point: #1 shipped "a reference impl of the dispatcher/execution layer, not the tracker layer" — no ticket entity, no cross-task deps, no todo/review/done state model, no server or API (read-only CLI), an unimplemented agent-spawn adapter, and no CI. This PR adds all of it, so the repo can serve as the Plane replacement, not just the per-task engine — plus JS-scripted workflow authoring.
What's new
tasks.jsonregistry (§1.5) — the#N / #N.xtree with one-level children,needscross-task dependencies (acyclic by construction: needs may only reference existing tickets), and a local, locked + versioned registry (advisory lock, version counter, atomic tmp+rename writes).blockedBy → promote on done: tickets with unmet needs sit inbacklogburning zero tokens; when the last need completes they promote totodoand (ifautoStaff) open their run in the background. Missed promotions are re-derived on recovery.backlog / todo / design / design_review / in_progress / in_review / done / cancelled, projected live from the run record on every event, via a per-ticketstate_map(node → column, the doc's "each board carries a state_map"); node-kind defaults otherwise (gates → in_review, parks a human owns → in_review with the reason on the ticket). The INT design flow's map ships as a preset.node:http) — file/board/status, staff, nudge, human-gate verdicts, pause/resume (with grants), cancel, plus event-log and journal reads; 400/404/409 error mapping; the server owns the engine tick cadence.serveboots the full stack (tracker + git worktrees + process workers);board / file / ticket / staff / nudge / gate / pause / resume / canceldrive the API..beckett/, and speaks a line-JSON driver protocol (readiness handshake with real observed branch/sha, turns, file changes, checkpoints, done-signal; nudges over stdin; abort = WIP commit + SIGTERM; a silent process exit synthesizesfinishedand rides the engine's alarm/retry ladder).flow().worker().gate().fanout().join().budget().build()builder, plus flow scripts (.js/.mjs/.cjsdefault-exporting(ctx) => flow) that compute the shape for the task at hand (seeexamples/flows/adaptive.flow.mjs: hotfix → one-pass, "areas:" in the body → computed fanout arms, criteria count → effort). Scripts may also export hooks — a scripted concierge with operator authority (auto-approve gates, auto-grant a visit on park, file follow-up tickets), run off the event path, reloaded from the stored script path on recovery, errors journalled never fatal. Everything a script returns goes through the full linter, so scripting controls shape and management without loosening the bounded-execution guarantees.Testing (166 tests, all green — 41 new)
autoStaff:falseneeds, filing-time lint refusal, registry versioning, recovery with missed promotions.coerceWorkerEvent).Also smoke-tested the assembled stack manually:
run-of-show serveover a fresh repo,filevia CLI → auto-staffed → real worker process committed its output in the task worktree → ticketdoneon the board.Not done here: npm publishing (needs registry credentials and a go-ahead).
🤖 Generated with Claude Code
https://claude.ai/code/session_01A3DUAdiNuM62Q6TVAyj7rv