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::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).
§§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).
Modules§
- adopt
- §6/§13
prime --install CENTERconfig adoption — copy a center’s committedconfig/into this landing, with the remote fetch done by the TRACKER, never core. - change
- §9 deliverable-verb base changes — one
BaseChangeper verb. - checkout
- §12/§13 checkout-lifecycle ops —
bl primeandbl 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). - 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
chronodependency. A hand-rolled civil-from-days (Howard Hinnant’s algorithm) is ~25 lines and exact for any i64, including pre-1970 negatives. - conf
- §4/§9
bl conf— local config CRUD with provenance (bl-c2de). - config
- §4 config VALUES — the
EffectiveConfig, read from the LANDING. - delivery
- §11 delivery / worktree plugin — the DIRECT (local-squash) variant.
- delivery_
fold - §11 fold rigor (bl-a04a): the strict-fold guard and the no-resurrection invariant.
- delivery_
prune - §11/§14 deferred branch cleanup —
prime.postprunes settledwork/<id>branches. - delivery_
reconcile - bl-22dd — sync the checkout that owns the integration branch after a
plumbing
update-refmoved 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). - 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 tobl skill:skillis the full manual (what, and in decreasing quantity how and why);helpis just the “what” — one line per command, so a reader can find the verb and then reach forskillfor the depth.- hooks
- §6 plugin wiring — the
config/plugins.toml[hooks]schedule. - id
- § id generation — the
id_schemeand 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 twoballsbranches. - 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 tocrate::checkout(which wires the difflessprime/sync): these author atasks/<id>.mddiff 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);--jsonnever dispatches. Each walkstasks/on the STORE checkout (§12 — reads run with cwd = the store, never the landing), parses every [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>. - seed
- §1/§12 the seed — the app default-config, copied into a fresh landing.
- substrate
- §12 substrate —
prime’s bootstrap-on-miss, the retiredinit. - 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 injectsnow). - tracker
- The
trackerplugin — 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_branchnames 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).
Functions§
- run
- The §8 dispatch entrypoint: resolve argv to its verb and run it.
prime/sync(§12/§13) wire to the engine viacheckout; the deliverable verbs (§9) viamutate; the read verbs (show/list, §9) viareads— they author no diff and print the store view;install(§6) seals its path-copy onto the landing or store viainstall::run.skillprints the embedded agent guide andhelp(also--help/-h) the terse command directory (help::directory).edgecarries the host inputsmainresolved.