Skip to main content

Crate balls

Crate balls 

Source
Expand description

balls — a git-native task tracker (greenfield rewrite, spec bl-2e26).

This is the next major version of balls, built fresh under epic bl-72a8. The previous implementation is deleted from the working tree (recoverable from git history); the system-installed bl keeps tracking this work until cutover, so main does not need to build a functional bl during the rewrite — do not make install from this tree until the rewrite lands.

§§0 — what balls is

State rides TWO branches of a git repo (§2): the balls/config landing holds config/, the tasks_branch store holds tasks/; persistence is git, local-first. Base balls is the smallest possible thing — it commits config to the landing and task-file changes to the store. Everything that touches the world beyond it (a remote, the project’s code) is a plugin.

§§8 — the op lifecycle is the spine

Every verb is the same shape: balls authors a base change, an ordered plugin chain acts on it, balls SEALS it (commit + integrate, atomically), and plugins react. The seal is the pre/post boundary. op names the verb-agnostic phase shape; git is the anvil seal (change worktree, commit + ff-integrate, un-seal); lifecycle is the lifecycle::Engine that runs the shape and unwinds it in reverse on any abort (§14). change implements the verb diff (lifecycle::BaseChange) for each §9 deliverable verb (create/claim/unclaim/update/close); the plugin chain (lifecycle::Plugins) is filled by plugin::Subprocess over the §7 wire (wire). run dispatches the checkout-lifecycle verbs (prime/sync, §12/§13) to the engine via checkout, the deliverable verbs via mutate, import — the §16 write inverse of the bedrock read — via import::run, and install — the op that seals to the LANDING (§6/§8) — via install::run; every verb is wired.

§§12/§13 — readiness & synchronization

checkout is bl prime (idempotent orchestrator: bootstrap-on-miss via substrate — the balls/config landing + the tasks_branch store — then the prime chain) and bl sync (the synchronization primitive: run the sync chain against the store). Core stays local-only — it reads config from the landing and reads/writes tasks on the store; the tracker plugin the chain runs is the one component that talks to a remote (§0). edge is the host inputs main resolves at the boundary.

§§6/§7 — the plugin contract

Plugins are subprocesses, invoked uniformly (<bin> <op> <phase>) with the §7 payload on stdin and no return channel — they mutate the change worktree, never print state back. plugin is the dispatch (env, recursion guard, stderr-to-logs, protocol self-describe); wire is the payload shape. install is the §6 bl install capability transfer: a pure path-copy of a committed path between two branches whose SHAPE decides the semantics (folder = mirror with deletions propagating, file/glob = additive union), never touching siblings or the gitignored bin/, then resolving + validating a local binary against its protocol self-describe before binding it. tracker is the one shipped remote-talker (a separate binary): it reads the §7 wire and does the §12/§13 git acts — sync (fetch + ff-only), push on post, and prime (adopt/found, stealth no-op) — and nothing local touches a remote without it.

§§3/§10 — task files & the blocker model

task is the schema and its derived predicates (status/ready/ closeable); taskfile is the shared tasks/<id>.md IO (read/write, exists as the §10 resolver, the front-door reciprocal add_blocker). Enforcement is CORE (§10): enforce guards claim on task::Task::ready and close on task::Task::closeable (called from change at stage), so a blocker actually blocks without a plugin — its meaning is enforced where the op is authored.

§§11 — the delivery / worktree plugin

The first shipped plugin: a SIBLING binary (bl-delivery) that owns the work/<id> code worktree of the PROJECT repo end to end — materialize on claim, deliver (direct local-squash) + tear down on close. delivery is the kind-blind, stateless-across-ops policy (the hook→act matrix + the derived delivery_path::worktree_path); delivery_repo is its real git seam. It lives in-repo as a default capability + reference impl, dispatched subprocess-uniform like any third party (§6).

§§11.1 — attempts: the same delivery, a source that is not a ball

attempt reaches that one delivery law without manufacturing a ball (bl-4eac): a private attempt/<handle> source ref, index and worktree forked from an exact target commit, delivered through the SAME delivery_message::deliver_to the ball path uses and returning the identities that delivery already computed (delivery::Delivered). It is policy-blind — balls owns refs, worktrees, delivery and safe cleanup, and holds no notion of candidate, winner or outcome. There is no bl verb for it; bl close is the N = 1 ball attempt, and a linking host reaches the N > 1 alternatives through the crate. See docs/design/bl-4eac-attempt-capability.md.

§the speculative merge queue — verdict cache (design bl-24e7)

speculate is tree-keyed gate memoization (bl-1263): the pre-commit gate’s verdict is a pure function of the worktree TREE and the GATE fingerprint (toolchain + gate scripts), so scripts/pre-commit consults a per-(tree, gate) record under the bl-speculate §1 territory and skips a re-execution of a known pass, recording fresh passes for whoever folds to the same tree next. The bl-speculate SIBLING binary is the env-reading edge; it is called by the hook, not dispatched by bl. Speculative builders (bl-d0c2) warm the same records ahead of the merge queue. speculate_queue (bl-5c5f) is that queue: an annotated merging/<id> tag on the sealed work/<id> tip is membership, position (taggerdate) and seal (tag target vs live tip) in one ref — order is a query, eviction is re-tagging at the bottom, and reads never mutate. speculate_run (bl-d0c2) is the speculator pass that joins them: sweep the unsealed, then chain candidates head-first with speculate_candidate (merge-tree trees wrapped in UNREFERENCED commits — git gc food, nothing to leak), consulting the cache before spending a gate; strict order means every prefix under a build already holds a PASS, so the depth-risk of building on a future eviction is zero at build time, and a conflict or FAIL ends the buildable chain. Gates run under nice in a detached build worktree, removed before the pass returns; the close-time gate on a cache miss runs unniced, so the real merge path always preempts. Remote builders (bl-6312) need no protocol of their own: the store file IS the wire format, so a runner executes the stock gate and ships its store dir home, and speculate::import adopts each file after validating the key — see .github/workflows/speculate.yml. See docs/design/bl-24e7-speculative-merge-queue.md.

§§4 — config values, read from the landing

config is the §4 EffectiveConfig: the landing’s config/balls.toml overlaid by the XDG user config, with built-in defaults beneath — no trail, config lives on the landing alone (§12).

§§1/§2 — the layout substrate

encoding, layout, and registry answer where balls’ state lives and how it is named: percent-encoded (never hashed) paths under the XDG dirs, and the local config/plugins/bin/<name> binary binding (gitignored absolute symlinks) that resolves the committed config/plugins.toml hooks schedule to this machine. Pure path arithmetic plus the registry’s filesystem ops — no git, no env reads (the binary edge supplies those), no bootstrap (that is prime’s job).

Re-exports§

pub use dispatch::run;

Modules§

adopt
§6/§13 prime --install CENTER config adoption — copy a center’s committed config/ into this landing, with the remote fetch done by the TRACKER, never core.
attempt
§11.1 — the ATTEMPT: a delivery source that is not a ball (bl-4eac).
brief
The landing’s brief — config/PRIME.md, printed verbatim by every prime (bl-c84f).
change
§9 deliverable-verb base changes — one BaseChange per verb.
checkout
§12/§13 checkout-lifecycle ops — bl prime and bl sync, wired to the engine. These author no ball-file diff, so they run the DIFFLESS shape (§8 “skip steps 1/3/5”): no change worktree, no seal — the configured plugin chain runs against the STORE checkout directly (crate::lifecycle::Engine).
chore
bl-chore — the guarded close-gate mint at claim (design bl-3df3).
civil
Unix-time → ISO-8601 rendering — the SOLE place storage’s i64 seconds (§3) become a human date (§9). Storage and transit are ALWAYS unix-time; only this display layer converts, so the rest of balls needs no date library and no chrono dependency. A hand-rolled civil-from-days (Howard Hinnant’s algorithm) is ~25 lines and exact for any i64, including pre-1970 negatives.
clock
The op instant T — one clock read per op, three consumers (§8, bl-8b98).
conf
§4/§9 bl conf — local config CRUD with provenance (bl-c2de).
config
§4 config VALUES — the EffectiveConfig, read from the LANDING.
converge
§12/§15 prime’s rename convergence (bl-18bf piece 1) — the one MUTATION prime performs to close version skew.
delivery
§11 delivery / worktree plugin — the DIRECT (local-squash) variant.
delivery_bin
The delivery plugin’s process boundary as a LIBRARY entrypoint.
delivery_fold
§11 reconciliation rigor: the ancestry precondition (bl-a1a4), the half-merge guard and the no-resurrection invariant (bl-a04a).
delivery_message
The §11 delivery commit MESSAGE — carrying the author’s rich work/<id> context into the squash, not just the ball title (bl-b9a6).
delivery_path
§11 delivery path/string derivations — the pure (binding, id) → worktree path / branch / subject / marker arithmetic, split out of crate::delivery so the policy module holds only the hook matrix. No git, no IO beyond path math; balls prints the same paths from the same formulas (no return channel).
delivery_precondition
§11 delivery PRECONDITION (bl-4a88) — the one predicate, two surfacings.
delivery_prune
§11/§14 deferred branch cleanup — prime.post prunes settled work/<id> branches and, beside that prune (bl-c117, piece 3 of docs/design/bl-18bf-prime-convergence.md), REPORTS the debris an unsettled one leaves when its worktree directory is gone.
delivery_reconcile
bl-22dd — sync the checkout that owns the integration branch after a plumbing update-ref moved it underneath them.
delivery_repo
§11 delivery plugin — the real project-repo git seam (Project).
delivery_standing
§11/§14 delivery standing — where work/<id> sits relative to this incarnation’s delivery (the converge-on-retry predicate, bl-430e/bl-c231).
delivery_wire
The §7 wire slice the delivery plugin reads off stdin. balls only ever serializes the wire (crate::wire); the plugin owns the matching deserialize for the slice it needs, split out of crate::delivery so the policy module holds only behaviour.
dispatch
§8 dispatch — argv → verb → run, and the pre-verb help/skill affordances.
edge
The binary edge’s resolved inputs — env read once, in main, then handed in.
encoding
§1 percent-encoding — the one naming primitive.
enforce
§10 blocker enforcement — CORE, not a plugin.
git
§8 anvil git plumbing — the change worktree and the SEAL.
help
bl help — the minimal command DIRECTORY. The terse companion to the fuller bl --skill guide: --skill carries the depth (what, and in decreasing quantity how and why), help is just the “what” — one line per command, so a reader can find the verb and then reach for bl <command> --skill for its full usage.
hooks
§6 plugin wiring — the config/plugins.toml [hooks] schedule.
id
§ id generation — the id_scheme and the one shipped (random) generator.
import
§16 bl import — the write inverse of the bedrock read (show --json).
install
§6 bl install — copy a committed path between two balls branches.
layout
§1 XDG layout — where balls’ host-side state lives, as pure path arithmetic.
lifecycle
§8 op lifecycle + §14 rollback — the verb-agnostic engine.
log
§1/§4/§6 the unified per-clone op log — one JSON-lines sink per clone.
message
§5 commit-message protocol.
mutate
§9 deliverable-verb dispatch — create/claim/unclaim/update/close, wired to the §8 engine. The MUTATING counterpart to crate::checkout (which wires the diffless prime/sync): these author a tasks/<id>.md diff and SEAL it, so they run the full Author → Pre → Seal → Post → Teardown shape against a change worktree off the STORE anvil (crate::lifecycle::Engine::seal).
op
§8 op lifecycle — the named phases of the verb-agnostic shape.
plugin
§6 plugin contract & dispatch — subprocess-uniform.
reads
§9 read verbs — show, list. Diffless ops (§8 “skip steps 1/3/5”): they author no ball-file diff and seal nothing; the printed output IS the whole contribution. Every read’s human render may also FOLD IN a §6 read-op plugin dispatch ([readop] — show’s delivery worktree line, §11); --json never dispatches. Each walks tasks/ on the STORE checkout (§12 — reads run with cwd = the store, never the landing), parses every crate::task::Task, and renders the §3 derived status ladder plus the §10 ready/closeable predicates two ways: a human view (status glyphs, ANSI colour) and --json (the supported machine contract).
registry
§6 local binary binding — config/plugins/bin/<name>.
renames
Renamed first-party plugins (§6/§15): the closed, static map of a retired plugin name to its current one.
seed
§1/§12 the seed — the app default-config, copied into a fresh landing.
skill
bl --skill / bl skill (the top-level operating guide) and bl <cmd> --skill (one command’s full usage, into which the per-command --help is folded). The agent documentation, embedded so it works from a bare cargo install with no repo checkout to read from.
speculate
The verdict cache — tree-keyed gate memoization (bl-1263, design docs/design/bl-24e7-speculative-merge-queue.md).
speculate_candidate
Candidate arithmetic for the speculator (bl-d0c2, design docs/design/bl-24e7-speculative-merge-queue.md) — the git acts that make a queue prefix buildable without creating anything that can leak.
speculate_queue
The merging queue — order as a query over git tags (bl-5c5f, design docs/design/bl-24e7-speculative-merge-queue.md).
speculate_run
The speculator pass (bl-d0c2, design docs/design/bl-24e7-speculative-merge-queue.md): one idempotent sweep of the merge queue that pre-pays gates so closes land as cache hits.
substrate
§12 substrate — prime’s bootstrap-on-miss, the retired init.
task
§3 task schema — the one node type.
taskfile
Task-file IO on a worktree dir — the primitives every base change shares, so tasks/<id>.md’s path arithmetic and read/write live in ONE place (§3). Pure filesystem ops: no git (the anvil owns that), no clock (the verb layer injects now).
tracker
The tracker plugin — balls’ one remote-talker (§0/§12/§13).
verb
§9 verbs and their §8 op class.
wire
§7 plugin wire payloads — what balls hands a plugin on stdin.

Constants§

DEFAULT_TASKS_BRANCH
The default STORE branch — holds tasks/ (§2). Unlike the landing this is the one indirection: config.tasks_branch names it and may point elsewhere (§4). Default-two (a DISTINCT ref) is simplest and fewest code paths (§0/§2).
LANDING_BRANCH
The LANDING branch — path-derived, single-owner, holds config/ (§2). It is never named by config (you read config FROM it, so it cannot name where it lives — §4); the one fixed point a fresh checkout bootstraps against (§12).