App-karr
view release on metacpan or search on metacpan
ex/README.md view on Meta::CPAN
### Helper refs
Not every shared state is a card. `karr set-refs` and `karr get-refs` put
arbitrary payloads into refs outside of `refs/karr/*`:
```bash
karr set-refs superpowers/spec/1234.md draft ready
karr set-refs superpowers/spec/1234.md < design.md # mehrzeilig über stdin
karr get-refs superpowers/spec/1234.md
```
The arguments after the ref are joined with a space, so they're a single-line
payload. A document comes via stdin: without a content argument, `karr set-refs
REF < file` stores the file verbatim, and `karr get-refs REF > file` returns it
unchanged. Good for planning blobs, generated specs, agent scratch state,
workflow metadata you want synced through Git without modeling it as a card.
Protected namespaces are locked: branches, tags, remotes, stash, `refs/karr/*`
and `refs/karr-local/*` (where `karr pick` keeps its process-local locks).
`refs/karr-foundation/chain/*`, `.../log/*` and `.../questions/*` are
**read-only** for `set-refs` â those are written by `karr-foundation` with
schema and compare-and-swap â while `get-refs` reads them freely. That's exactly
how you look at a step, a run log or a question.
### Skills
The distribution ships a `karr` skill, installable locally in the repository or
globally in the home directory:
```bash
karr skill install
karr skill install --agent claude-code
karr skill install --agent codex --global --force
karr skill check --global
karr skill update
```
Targets are `claude-code`, `codex` and `cursor`.
### Characters inside, octets only at the edge
A rule that has no command line but affects every output: everything between the
CLI entry point and the Git ref blob is a Perl **character** string.
`App::karr::Encoding` owns every crossing â `@ARGV`, STDOUT/STDERR, ref reading
and writing, YAML, JSON â and nothing else may encode or decode directly. Boards
written before this rule are detected via `refs/karr/meta/encoding` and repaired
on read; `karr repair` migrates them permanently. The reason this is worth
mentioning at all: a double-encoded UTF-8 in a card title is the bug nobody
notices until the board has been broken for months.
---
## 4. `karr-foundation` â the coordinator
A board is half the battle. `karr-foundation` is the other half and the reason
why `karr` isn't just a file-based kanban.
It's a single-shot, idempotent companion binary. It watches **many**
repositories, decides per board whether there's work there, and lets the
configured agent command run until the board stops moving. Cron, a systemd
timer or a `while` loop point at it; every tick is complete in itself.
And it has two operating modes, each useful on its own:
| If you want | you call | you get |
|---|---|---|
| a picture of every board | `karr-foundation --status` | status counters, in-progress/blocked cards, lock, cooldown, agent state, open questions â read-only, an agent is never started |
| agents to work the boards | `karr-foundation` | one agent per repository, per the `.karr` file in it. If there is no `.karr` anywhere, it prints the overview instead |
**Agent execution is opt-in.** That isn't reticence but the security design: a
default that suddenly starts four agents on an operator's laptop would be a
surprise, and the surprise would land on a machine, not in a review.
### What a tick does
In order â and the order is the interesting part. (The hub commands `ask`,
`answer` and `chain` come before it and never discover a board: a question is
fleet state in the hub and has nothing to do with which repositories this
machine drains.)
1. **Discover repositories.** From `dirs:` (explicit list) and `scan:` (direct
children of a directory that have a `.karr` file or are themselves a karr
board). A repository reachable both ways is processed **once**, not twice â
deduplicated over the canonical filesystem path, because the same thing can
appear as a string, with a trailing slash, or as a symlink and its target.
If nothing is found at all, that's a runtime error with exit code `1`.
2. **`--status`?** Then print the overview and be done.
3. **Is an agent even running anywhere?** Per board it's resolved once whether
it is disabled and which command (and which named agent) would run. If the
answer is "none" everywhere, `karr-foundation` prints the overview â with a
line saying why, and how to turn it on.
4. **Pull the fleet namespace.** `refs/karr-foundation/*` from the hub, and
before anything reads it, so this tick's limits are the fleet's current ones
and not the ones this machine happened to fetch last. An ordinary tick writes
**nothing** back there.
5. **Install signal handling.** `SIGTERM`/`SIGINT`/`SIGHUP` take every running
agent along: first `TERM`, then `KILL` to the **process group** of the
agent. Without it the agent would remain behind as an orphan at `init`, while
the `.karr.lock` names a dead PID â and the next cron tick would read the
dead PID as free and start a second agent on the same board.
6. **Work the boards**, serially or concurrently (see `concurrent:` below).
Per repository, in exactly this order:
1. Is there a board here at all? (`.karr` file or `refs/karr/config`)
2. **Is the board disabled?** â first, before everything else.
3. Resolve the agent command. None? Skipped.
4. Is the `.karr.lock` held? Then someone is already working on it.
5. Is the board in cooldown?
6. Is the named agent currently down?
7. Pull `refs/karr/*` â and then check the disabling **again**, because the
pull may have just brought in the flag from another machine.
8. Is there reason to run? (`--force`, or the board has moved since the last
tick, or there are workable cards, or `on_idle: always-run`)
9. Take the lock (`flock(2)`), drain, run `on_drained`, release the lock.
10. Advance cooldown and agent availability, write `.karr.state`.
### Where the pieces live â and which ones travel
This table is the most important of the section because it makes the design
decision visible:
( run in 1.054 second using v1.01-cache-2.11-cpan-ad66724bd6a )