longrun

Memory and coordination for Claude Code sessions. A project keeps its own state on disk, written and kept in order by the agents themselves, so every session sees what the project knows and who else is working on it. From there: work goes to the session that already has the context, waiting for an event costs no tokens and cannot be slept through, and one session can coordinate the rest and reach you when only you can decide. The course shows each of the four, step by step, on a toy project shop. If you do not need the internals, part 0 is enough.

1. Shared documents on disk

One .longrun/ per project with the notes every session sees, plus own notes per session. Hooks bring both back after every compaction, /clear and resume, and show what other sessions changed on every turn. Parts 3, 5, 6.

2. Delegation: messages and tasks

A message or a task for another session arrives there as a user turn: through the socket if it is running, through the inbox on its next turn if it is stopped. Sessions are named as you see them in the sidebar. Part 7.

3. Event-driven: waiting that never misses the event

A watch is a check a timer runs every five minutes with no model. When the condition holds, the session wakes with the text you gave. No polling loop, no tokens spent waiting, no fixed sleep that ends too late or too early. Part 7.

4. One session coordinates the rest - and reaches you

One session keeps the board for the already open sessions: hands out tasks, unblocks, names a peer that is stuck, and puts a dialog in front of every window when only you can decide. Not autonomy: one session toward one checkable condition is what /goal is for. Part 8.

Part 0

Quick start: install and just work

The skill assumes the human works in sessions as usual, while notes, digests and message delivery are handled by the hooks and by the model following its instructions. Below is everything you do by hand.

  1. Install once. One line, no checkout needed - the installer fetches the sources itself:
    curl -fsSL https://krllx.github.io/longrun/install.sh | bash
    It puts in the skill, 12 hooks in ~/.claude/settings.json, the CLI ~/.local/bin/longrun, the longrun MCP server, and the timer that runs the checks and the watcher every five minutes. It asks about one thing only - desktop notifications, the next step. From a clone it is the same install.sh; --uninstall reverses it.
  2. Notifications, if you want them. install.sh asks whether to set them up, and this step is what the answer decides. longrun never depends on them - a halt and a fired watch reach the session through its socket or inbox either way - so declining changes nothing else (--no-notify answers in advance, brew install terminal-notifier turns them on later). On macOS the installer runs brew install terminal-notifier when it is missing (Homebrew is the prerequisite; without it it says so and moves on) and sends one test notification; macOS may ask once whether to allow notifications from terminal-notifier - allow it. Nothing built into macOS can replace that step: osascript -e 'display notification' posts on behalf of Script Editor, which holds no notification permission, so the system files it, draws nothing and exits 0 (25 of 25 that way on macOS 15; Script Editor gains no permission even after being launched). The banner then carries terminal-notifier's own name and icon: macOS takes both from the bundle that posted it, and there is no way to override them per notification. On Linux it is notify-send (libnotify), which most desktops already have. Check either way with longrun notify --test: it sends a probe, reads back whether the system really drew it, and prints which session a click opens. One session you must not miss - longrun important on (or next, or a number): its turn ends then always notify, whatever notify_turn_end is set to and whichever window is in front, which is what the focus check cannot do for you while you are typing in another Claude session.
  3. Make the folder a project. A project for longrun is the folder you open in Claude Code. Not necessarily a repository root: it can be a folder with several repositories, a subdirectory of a monorepo or an empty headquarters folder. Run longrun init there once: in a terminal or right in the Claude Code session, the agent runs it for you if asked. A .longrun/ directory appears; when the folder is inside a repository, add it to the ignore file (or longrun init --external, which puts the directory under ~/.claude/longrun/). From every worktree of the same project: longrun link <path to the project>. Check: longrun where.
  4. Tune it. In the session tell the agent: "set up longrun" (or /longrun onboard). It runs longrun onboard, tells you briefly what the skill does and asks about the main settings: the autocompact window, the stuck thresholds, note sizes, notifications. It applies the answers itself with longrun config set. One exception: a hook cannot set the autocompact window, so the agent asks you to type /autocompact 300k (or the value you chose) in Claude Code. 50 thousand tokens before that threshold a hook asks the session to write its notes before compaction. The keys in detail (see part 9 below).
  5. Work. Nothing to call. At start the session gets a digest (shared notes, own notes, who else is working), after compaction everything comes back by itself, other sessions' changes arrive on the next turn. The agent writes notes about dead ends and decisions on its own. You can ask in words: "write it down", "what have we tried", "session status", "hand it to the other session", "what is left", "tell me when the PR merges".
  6. Several sessions and a common goal. In one of them: "take the orchestrator role, the goal is ...". It creates a task board and hands out work; the other sessions receive tasks as turns, nothing to learn. "Stop everyone" and "go on" are also words, in the orchestrator session (see part 8 below).
Diagnostics in one command: longrun where (which project and session), longrun status (what each session does), longrun watch status (is the timer alive), longrun notify --test (do notifications reach the screen), longrun config (effective settings).
Part 1

Why

A project's real state - what was tried and failed, what was decided, what waits on whom - lives inside one conversation and dies with it. The next session, tomorrow's or the one in the other window, starts from nothing and does not know the others exist. So the project keeps that state on disk and the agents maintain it. Compaction is the sharpest case of the same problem, not the problem itself: the history is squeezed into a summary, and the next compaction squeezes the summary.

Problem 1: the state dies with the conversationDead ends, decisions and hard-won facts exist only in this window's history. Compaction is where it shows first: the summary drops "what we tried and why it failed", and half an hour later the agent proposes the fix it already reverted.
Problem 2: several sessionsEach lives in its own window. One opened a PR, the other does not know and says "the PR is not created yet". A dead end walked by one is walked again by the other.
Problem 3: waiting"Tell me when the PR merges" turns into a polling loop that burns tokens on every wake-up, or into a fixed sleep that ends long after the event, or before it.
Problem 4: many sessions, one goalFive sessions on one project, and only the human knows what is done, what is stuck and what is next. Every hand-over goes through them.
Cure 1: documents on diskA note on disk does not degrade, and the agents decide what goes in it. The hooks bring the notes back after every compaction, /clear and resume, archive the summaries verbatim, and tell the summariser itself what to keep.
Cure 2: messages and tasksOne .longrun/ per project: shared notes every session sees, a list of sessions with their status, messages and tasks delivered as user turns.
Cure 3: watchesa timer checks the condition every five minutes with no model and wakes the session when it holds. Reliable, reactive, free.
Cure 4: the orchestratorOne session keeps the board, hands out tasks, names the peer that is stuck, and asks the human through a dialog only when it must.

The picture below is about problem 1. Top row: what the model holds in its context, left to right in time: first the full history, after compaction only the summary, and the agent reading that summary. Bottom row: the file on disk where the agent wrote one note at the very beginning. Dotted arrows: the note enters the context at start and again after compaction.

The model context over time 1. history: tried X, it failed, reverted, took Y... 2. summary: "did Y" 3. agent: "let's try X!" The session's notes file on disk (longrun) [s1] dead: X fails: it needs a real Redis, use REDIS_URL=fake:// one note: [s1] its number (s = own, n = shared), dead = the "dead end" tag, then the fact itself in one line
The summary loses the reasons. A line on disk costs 26 tokens and comes back after every compaction, so at step 3 the agent sees that X was already tried.

What the skill is made of

Mechanics: hooks, CLI, a timer

Twelve Claude Code hooks (see part 4 below), the longrun command and a timer (a launchd agent on macOS, a systemd user timer or cron on Linux). The mechanics work without the model:

  • print the digest at session start and after every compaction;
  • archive every compaction summary verbatim;
  • write failed commands into the journal;
  • take a HANDOFF snapshot before compaction: the user's last requests and the edited files, so that the session continues from the same place afterwards;
  • deliver messages between sessions;
  • run deferred checks with no model: a PR merged, a URL answers, a file appeared;
  • watch the sessions for the orchestrator, the session that drives the project to a goal (see part 8 below).

The model's instructions: SKILL.md

What the model does because it was asked to: writes a one-line note when it hits a dead end, makes a decision or learns a fact about the environment; calls recall when something is missing from the context; hands work to the session that has more context. What to write (see part 5 below).

Part 2

Three entities, nothing else

Project

The shared memory of a group of sessions: the .longrun/ directory that longrun init creates in the project folder (or outside the tree with --external). Inside:

  • shared notes [n1], [n2]: one line = one fact, seen by every session;
  • the inbox: messages for sessions that are not running now;
  • the orchestrator's task board;
  • the archive of compaction summaries and snapshots.

More (see part 3 below).

Session

One Claude Code conversation, in the app one row in the sidebar. Its own:

  • own notes [s1]: the same, but seen only by this session;
  • a journal: the timeline, milestones, failures, compactions;
  • meta: counters and the last status.

They live under ~/.claude/longrun/, not in the repository.

Directory

The folder where the session started: the root, a worktree, a subdirectory. Stores nothing. Only says which project the session belongs to.

The word "project" means only the .longrun/ directory: not a project in a monorepo and not the Claude Code transcripts folder. A worktree is attached to a project with longrun link; it has no notes of its own. A session belongs to one project. On resume the app gives the session a new CLI id; longrun follows the priorCliSessionIds chain, and the own notes continue.

Part 3

Files on disk

Every file came from a specific problem:

  • notes.md of a session: problem 1. This session's dead ends and decisions that must survive compaction.
  • NOTES.md of the project: problem 2. Facts any session needs: a PR number, a decision, a quirk of the environment.
  • journal.md: a timeline without tokens, written by the mechanics alone: start, end, failed commands, compactions, sends. It shows what a session did without reading the transcript.
  • archive/compact/ and archive/precompact/: every compaction summary verbatim and the snapshot before it (HANDOFF). The first is found by recall, the second comes back in the digest after compaction.
  • inbox/: a message for a session that is not running now waits for its next turn.
  • meta.json: counters, the last answer, the running tool. The SESSIONS rows and the flags for the orchestrator are built from it.
  • board.json, stale.json, watch/, halt.json: the orchestrator's board, the "stopped being true" marks, deferred checks, stop for everyone. Parts 7 and 8.

Everything shared lives in the project. Everything own lives outside the repository tree and outside synced folders, because it is rewritten on every tool call. Click a file.

Who writes it, when it is created, what is inside.
FileWho writesWhen it is removed
NOTES.md of the projectthe agent: add, rm, replace, stale, pruneentries older than 14 days go to the archive (except pin); rm moves the line to archive/notes.md
notes.md of a sessionthe agent: add --own, rm, replace, prunewith a session silent for 7 days (into archive/sessions/)
journal.mdhooksthe tail beyond 200 lines goes to the archive
meta.jsonhooks on every eventwith the session
archive/compact/*.mdthe PostCompact hookolder than 30 days or more than 40 of them
inbox/*.mdsend, watch, board, askon delivery into .archive/, from there after 30 days
watch/w*.jsonwatch add, the timer ticka week after it fired
Part 4

Hooks: where the mechanics live

Claude Code calls an external command on every session event. longrun installs twelve such commands, all of them one python script. A hook is a separate process per event, so after an install the new code works in every session at once. Click a hook in the loop.

The loop of one session

Start, then turns: the human's request, tool calls, the answer. When the context is full, compaction and SessionStart again. Hooks on every node.
The only things that reach the model's context from the hooks: the digest on SessionStart, the shared-notes delta and messages on UserPromptSubmit, a rare reminder to write a note, a context warning and a refusal under halt. Everything else is written to files silently.
Part 5

Notes: what to write and where

One line, in English, up to 400 characters (the same fact in another language costs about 1.5x more tokens, and the budgets are hard). One test: would one command, Read or grep get this back? If yes, do not write it.

TagWhatWhere, usually
deadan approach that failed, and whyown; shared if another session could hit the same dead end
decisiona choice and its reasonshared if it concerns others
facta fact about the environment that cost effortshared
ctxtask framing from the humanown
pina fact with no expiry: PR number, branch, hostshared
doca pointer to a file too long for one lineshared

A note goes to the project's shared notes by default; --own keeps one inside this conversation. Too long for one line? Then it is a file: longrun doc add research/plan.md "the rollout plan and what is open" leaves one pointer line in the shared notes, and every session opens the file only when it needs it. Progress ("pushed", "tests green") is not written anywhere: the hooks already keep that in the journal. What outlives the task (who the user is, how they work) goes to Claude Code's auto memory, not here.

Check yourself: where does this go?

Part 6, a scenario

One session survives compaction

Project shop, session A. The "Next" and "Back" buttons or the arrow keys. Left: who talks to whom at this step; right: the command and what it prints; below: what changed on disk.

human, dialogsessionproject .longrunown of a sessiontimerglobal
What to remember: dead ends and decisions are written right away, anything too long for a line stays a file with a pointer to it, after compaction everything comes back by itself, a lost detail is found by recall.
Part 7, two scenarios

Two sessions: shared memory

Session B opens in the same folder.

Hand work over and wait for an event

A message to another session arrives there as a user turn. Waiting for an event costs zero tokens: the timer runs the check.

A cmd check runs from the timer, whose environment is nearly empty: absolute paths only, no Touch ID, ssh or aliases. First longrun watch test -- cmd '...'.
Part 8, a scenario

The orchestrator: one session coordinates the others

A layer on top of notes and messages. One session holds the goal and the board, the others learn three commands: board take, board done, board block. Everything the orchestrator needs lives in state files; it does not read transcripts. This is coordination across several windows plus a line to you - not autonomous driving: one session toward one condition an evaluator can check is what Claude Code's own /goal does.

Who may do what

The watcher and the orchestrator only propose. Nothing here kills a running tool - a window that is stuck is yours to stop, with Esc. Stopping everyone (halt) and lifting the stop (resume) happen only on the human's word in this conversation, never on another session's request.

Dialog or board

The dialog in front of every window is for what cannot wait: a confirmation, an access, an action only the human can take. Questions about the goal and priorities go to the board through board block; the orchestrator sorts them out.

Part 9

Settings

Two levels: the global file ~/.claude/longrun/config.json and the project file .longrun/config.json; the project value overrides the global one. No need to edit JSON by hand:

longrun config                                  # effective values and where each comes from: default / global / project
longrun config set stuck_tool_min 60 --global   # for every project
longrun config set notes_max_bytes 8000         # for this project
longrun config unset notes_max_bytes            # back to the default

There are about forty keys (the full list is in docs/REFERENCE.md, section 6). Right after the install these are worth a look:

KeyDefaultWhen and how to change
notes_max_bytes, session_notes_max_bytes5000, 3000Budgets of shared and own notes. They are read at every start, so bigger is not better. A project with four or more sessions where the digest keeps asking for PRUNE: 8000 and 4000.
notes_max_age_days14Older entries go to the archive (except pin). A project with rare sessions: 30.
nudge_tools, nudge_turns40, 8After how many calls or turns without a note to remind (and only if there were edits or failures). Annoying: 80 and 16.
ctx_warn_before50000How many tokens before the auto-compact window to ask for notes. Works together with /autocompact 300k in Claude Code; 50k is enough.
stuck_tool_min, stuck_wait_min30, 10The watcher's two thresholds for the orchestrator: a tool that is still running, an unanswered permission prompt (minutes). 40-minute builds are normal for you: stuck_tool_min 60.
ask_wait_sec, ask_expire_min90, 360How many seconds the dialog waits for an inline answer; after how many minutes it closes by itself. Usually leave as is.
watch_ttl_days7The default lifetime of a check (global only). Waiting for a release for weeks: 30, or --for 30d on that check.
autocompact_window0Where Claude Code compacts, as set with /autocompact N; accepts 300k, 1M. 0 = follow Claude Code. A hook cannot run /autocompact, so this is the intent: onboard compares it with the Claude Code setting and asks you to type the command. Recommended for 1M-window models: 300k.
pr_toolautoHow pr-merged checks ask for the PR status: gh (GitHub CLI) or arc (Arcadia). auto looks at the folder the watch was registered from.
watch_timerautoWhat runs the watch tick: launchd (macOS), systemd (a user timer), cron, none. auto picks whatever this machine has.
notify_turn_endoffA banner when a session finishes a turn: off, unfocused (only while the Claude window is not in front), always. Global only. For one session you must not miss there is longrun important on, which beats both gates.
The easiest way is not to memorise keys but to tell the agent "set up longrun": longrun onboard prints it a brief with the current value and meaning of every key, it asks you one at a time and applies the answers. The minimal set: the autocompact window 300k (type /autocompact 300k yourself), stuck_tool_min 60 if your builds are long, notify_turn_end unfocused if you want to be told when a session is done. Touch the rest when the digest or the watcher starts getting in the way.
Part 10

Cheat sheet and limits

# install
curl -fsSL https://krllx.github.io/longrun/install.sh | bash
                        # skill, 12 hooks, CLI, MCP server, the 5-minute timer, notifications
longrun watch install   # the timer alone (the installer already did it)
longrun notify --test   # optional: does a notification reach the screen
longrun init [--external]  # in the project folder
longrun link <project>  # from every worktree
longrun where           # which project, which session
# notes
longrun add [--own] -t TAG "..."
longrun doc add path "what is in it" | doc ls
longrun rm s3 n12 | replace n12 "..."
longrun stale n12 "why" | mute n12
longrun notes | prune [--auto] [--shared]
longrun recall term
# sessions
longrun status
longrun send [--list] [--resume] WHO "text"
longrun important on|next|N|off [--to WHO]
longrun watch add --to WHO --then "..." -- pr-merged ID
longrun watch ls | test -- CHECK | sessions
# orchestrator
longrun orchestrate start --goal "..."
longrun board | board add|assign|take|done|block T7
longrun board add --fact "..." --task T7 | board ls --facts | board ack F1
longrun halt "why" | resume
longrun ask "question" --options "Yes,No"
# settings
longrun onboard [done]
longrun config
longrun config set KEY VALUE [--global]
longrun config unset KEY [--global]

What the mechanics guarantee and what depends on the model

Guaranteed by the mechanicsDepends on the model and its instructions
the archive of every compaction summary, the journal of failures, the HANDOFF snapshot, notes returning into the context, message delivery, the shared-notes delta, tool refusal under halt, watch checksthat a note is written and written to the point, that recall runs before re-exploring, that a message from a peer is not taken for the human's word

Limits

  • The digest is at most 9000 bytes. Evicted in order: watches, the journal tail, the orchestrator's duties, the session list, the board, HANDOFF. Notes give way last, and by shedding their oldest non-pin entries one at a time, never by cutting the newest ones off the end.
  • Resume in the app is linked through priorCliSessionIds; /clear in the CLI through a heuristic: "same folder, an end with reason clear no more than three minutes ago".
  • The socket and transcript formats are undocumented: on a Claude Code version change send into a running session breaks first, the inbox queue keeps working.
  • Under the macOS timer python may have no access to ~/Documents (TCC). Checks do not depend on it; a message is then placed into the session directory under ~/.claude.
  • Subagents do not get the digest. /rewind does not roll notes back.

Details: README.md, docs/REFERENCE.md, docs/ORCHESTRATOR.md in the repository, and longrun help.