Design notes
Why clu is built the way it is — local-first, single file, pure Go.
A few load-bearing choices shape how clu behaves. They're worth knowing because they explain what clu is good at — and what it deliberately leaves out.
Local-first, one file
Everything for a project lives in a single SQLite database at
.clu/data.sqlite — no server, no daemon, no account, no network. To share a
tracker, copy the file; to throw it away, delete it. Every other choice follows
from this one. See the storage layout.
Pure Go, no CGo
clu uses the pure-Go SQLite driver (modernc.org/sqlite), so the binary is a
single static executable: nothing to compile against, no system libraries to
install. go install and you're done.
One identity
There's a single identity flag, -a / --agent <name>. It's both the lane
filter (which work do I pull from?) and the actor recorded in the audit log (who
did the thing?). clu doesn't separate "users" from "agents" — on a local tool
there's no second actor to track. See multi-agent.
In-place, append-only migrations
The schema upgrades itself on first run, gated by SQLite's user_version.
Migrations are append-only and forward-only, so opening an older database with a
newer clu just works — it never rewrites or discards your data.
Atomic by construction
The one operation where races matter — claim — is a single SQL statement
(UPDATE … RETURNING with a subquery), so two agents claiming at the same time
always get different issues. Cancel-cascade and cycle checks use recursive SQL.
See status semantics.
Contributing
clu is open source. Code conventions, the migration list, and the test setup live in the repository — start there if you want to hack on it.