looop 0.11.0

A tiny, portable, Kubernetes-shaped control loop for your work
looop — a tiny, portable, Kubernetes-shaped control loop for your work.

This single binary IS the whole program: principles, rules, and defaults all
live inside it. Install one binary and run it — no README, no helper dir.

THE IDEA
  Arrive in the morning, look at the state of the world, make the single most
  important move, repeat. Now a machine does that. It is a reconciliation loop,
  exactly like Kubernetes — with one deliberate twist (see RULE 1):

    Kubernetes            looop
    ----------            -----
    desired state (YAML)  goals/*.md         how things should be (inert files)
    etcd                  the data dir       PLAYBOOK + goals + journal = memory
    informers / watch     sensors/*.sh       scripts that print the world as JSON
    reconcile loop        the pulse (looop) observe -> diff -> one move per tick
    scheduler policy      PLAYBOOK.md        priorities + guardrails, in prose
    Job / Pod             worker session     a real agent doing hands-on work
    Pod sandbox           box workspace      a git worktree per session

THE THREE NOUNS
  PLAYBOOK.md   judgment in prose: priorities, guardrails, rules. Humans AND the
                AI grow it. It is data (lives with goals + journal).
  goals/*.md    the desired state. One file per goal: a 'goal:' frontmatter line
                + free-form dated notes. The AI creates/updates/archives them;
                they are evaluated every tick but NEVER executed.
  session       an agent actually working — born only for hands-on work, runs in
                a box workspace under babysit, gone when finished.

ONE BEAT (a tick)
  1. run sensors/*.sh -> snapshot the world as JSON (fresh every tick)
  2. if nothing changed since last tick -> do nothing (cheap, no AI call)
     ("changed" = PLAYBOOK + goals + each sensor's .signal + session transitions.
      NOTE: editing a sensor SCRIPT does not itself wake the loop — only the
      reading it emits does. PLAYBOOK/goals edits wake the loop immediately.)
  3. else hand PLAYBOOK + goals + sensor readings + sessions to the AI, which
     EMITS exactly ONE typed action (to .decision.json); looop executes it and
     appends ONE line to journal.md
  4. the AI process is disposable — all memory is the files in the data dir

RULES (the invariants that keep this safe and legible)
  1. ONE TICK = ONE MOVE — enforced. The AI only EMITS one typed action; looop is
     the SOLE executor, so a tick does at most one move and leaves one journal
     line no matter how the model misbehaves ("do nothing" = the noop action).
  2. THE PULSE IS UNBREAKABLE SHELL, JUDGMENT IS THE AI, MEMORY IS FILES. This
     program never "thinks"; it only gathers context and runs the AI once.
  3. NEVER DO IRREVERSIBLE THINGS AUTOMATICALLY (merge, public comments, closing
     issues, deleting data). Prepare fully, then a worker session raises a
     flag (looop flag) and waits for a human. Encode this in the PLAYBOOK.
  4. BE INQUISITIVE. When a worker lacks context it asks (flag + wait) rather
     than guessing. Asking is cheaper than a wrong irreversible move.
  5. ADD STRUCTURE LATER. Only harden a drift into a PLAYBOOK rule or a check
     once it actually hurts. Never build machinery in advance.
  6. LEVEL-TRIGGERED. Each tick re-observes from scratch; the loop keeps no
     "what I was waiting for" state. Re-running after days off is just a tick.
  7. SINGLE-WRITER POLICY FILES. The pulse (the tick AI) is the sole writer of
     PLAYBOOK.md, goals/ and sensors/. Workers write only to claims/, reports/
     and their own code sandbox — never racing the pulse on policy files. The
     lone exception is a human-gated meta session (setup / playbook grooming),
     which shows a diff and waits for approval (RULE 3) before touching policy.

DRIVING A SESSION (a session is a live terminal, not a black box)
  A worker runs under a real PTY, so you can both WATCH it and TALK to it from
  outside — the same primitives a human or a script would use:
    observe   looop shot <id>        the current screen
              looop log <id> -f      follow its output (tail -f)
              looop status           pulse + all sessions at a glance
    drive     looop send <id> TEXT   type into its stdin
              looop key  <id> KEY    named keys (Enter, C-c, Up, …)
              looop expect <id> RE   block until a regex appears (scriptable)
              looop wait[-idle] <id> block until exit / until output settles
    manage    looop resize / restart / attach / detach
  This makes a session driveable by hand, by a shell script (expect-style), or by
  the pulse itself as one move. Human-gated decisions still go through ⚑flags
  (RULE 3/4) — driving is for steering and automation, not for answering a flag.

CODE / CONFIG / DATA are cleanly separated (all overridable by env)
  EXEC    this one binary                          the program (portable)
  CONFIG  ${XDG_CONFIG_HOME:-~/.config}/looop.json  one file: runner wiring
          └ override with $LOOOP_CONFIG            (seeded inline if absent)
            optional knobs: interval/busy_interval/active_interval (cadence),
            session_ttl (corpse retention), max_daily_usd (skip ticks once the
            day's metered spend hits the cap — a billing circuit breaker).
  DATA    ${XDG_STATE_HOME:-~/.local/state}/looop/  the file-based memory
          └ override with $LOOOP_DATA_DIR
            PLAYBOOK.md  goals/  journal.md  sensors/*.sh   <- the durable memory
            snapshots/ prompts/ runs/ .lock .last-tick-hash <- scratch
            sessions/<id>/  <- worker + pulse sessions (babysit state, per
                               profile; no $BABYSIT_DIR, no shared ~/.babysit).
                               System scratch: auto-reaped after a retention
                               window ($LOOOP_SESSION_TTL / session_ttl, def 3d).
                               Deliverables go to reports/, NOT here.
            (runs/<id>/{prompt.md,output.log} = a replayable archive per beat)

BOOTSTRAP
  A fresh data dir is seeded once with a starter PLAYBOOK + goals/setup.md +
  goals/playbook-daily.md + sensors/today.sh, all embedded in this binary. The
  starter PLAYBOOK's top priority is an interactive setup session (goals/setup.md)
  that interviews you and rewrites the seed into your real config. Setup is just
  a goal; the program only seeds.

DEPENDENCIES
  Just the configured agent CLI (claude or pi). Session management (babysit) is
  LINKED IN as a library and runs in-process — no babysit binary is required.
  The pulse provisions NO workspaces; a worker that touches code makes its own
  sandbox (box if available, else git worktree), so box/git are worker concerns,
  not pulse prerequisites. (gh and other tools are used only by sensor scripts /
  worker agents — looop never requires them.)