diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1ab84bd..68a2cde 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,6 +31,24 @@ IKE_DATA_FILE=$(mktemp -d)/t.json go run . list
It must be an absolute path whose parent directory exists.
+To build a binary that reports a real version rather than `dev`, stamp it the
+way the release workflow does:
+
+```sh
+go build -ldflags "-X github.com/jonascript/ike/internal/cli.version=$(git describe --tags --always)" -o ike .
+```
+
+The demo GIF in the README is generated from a checked-in script, so it can be
+regenerated when the UI changes rather than re-recorded by hand:
+
+```sh
+brew install vhs # https://github.com/charmbracelet/vhs
+vhs assets/demo.tape
+```
+
+It builds ike from the working tree and seeds a throwaway matrix in a fresh
+`mktemp -d`, so recording never touches your own data file.
+
## How the pieces fit together
Dependency direction is `cli` → `tui`/`mcpserver` → `store` → `task`.
diff --git a/README.md b/README.md
index 5642418..b64df00 100644
--- a/README.md
+++ b/README.md
@@ -13,83 +13,28 @@
----
-
-An Eisenhower matrix task manager, named for the president who popularized the
-method ("Ike" was his campaign-era nickname). One binary, three ways in:
-
-- **TUI** — run `ike` for an interactive 4-quadrant matrix
-- **CLI** — `ike add`, `ike done`, … for quick capture and scripting
-- **MCP** — `ike mcp` serves the Model Context Protocol so AI agents can manage your matrix
-
-All three share a single JSON data file, safe against concurrent writes.
-
-## The matrix
-
-| | Urgent | Not urgent |
-|---|---|---|
-| **Important** | 1 · Do It First | 2 · Schedule It |
-| **Not important** | 3 · Delegate It | 4 · Consider Eliminating It |
-
-Those headings are defaults, not fixed: rename any quadrant with `t` in the TUI
-or `ike label`. The quadrant *numbers* never change, so scripts and the MCP
-tools keep working whatever you call them.
-
-## Spaces
-
-One data file holds several independent matrices, called **spaces** — work and
-personal, say. Each has its own tasks, archive, quadrant headings, ID numbering,
-and undo history, so `ike undo` in one can never reach into another. A fresh
-install has a single space named `default`, and nothing changes until you make
-a second.
-
-```sh
-ike space # list spaces, marking the current one
-ike space new work # create it (does not switch)
-ike space use work # switch; every later command follows
-ike space rename work job
-ike space rm work # refuses a non-empty space without --force
-```
-
-Every command takes `-s/--space NAME` to act on one space just once, without
-switching:
-
-```sh
-ike add "Fix prod bug" -s work -q 1
-ike list -s work
-```
-
-Two concepts worth keeping apart: a **file** is a document and contains spaces;
-a **space** is one matrix inside it. `--file` picks the document, `--space` picks
-the matrix. In the TUI, `s` opens a space picker and `]`/`[` move between spaces.
-
-**Deleting a space cannot be undone.** History lives inside the space, so there
-is no stack left to revert from — which is why `ike space rm` names the counts it
-is about to destroy and needs `--force` if the space still holds anything. The
-previous file contents remain in `tasks.json.bak` until the next change.
-
-## Moving a matrix between machines
+
+
+
-The data file is self-contained and fully portable: nothing in it refers to a
-path or a machine, and timestamps are stored in UTC. Copy it to another computer
-and every space comes with it. The sidecar `.lock` and `.bak` files do not need
-copying.
+---
-To move one space rather than the whole file:
+A task manager that sorts work by urgency against importance, into four
+quadrants — the [method popularized
+by](https://en.wikipedia.org/wiki/Time_management#The_Eisenhower_Method) the
+president whose campaign-era nickname it borrows. What makes it unusual is that
+an agent can work with it in both directions:
-```sh
-ike space export work ~/work-matrix.json # a standalone ike data file
-# copy that one file to the other machine, then:
-ike space import ~/work-matrix.json
-ike space import ~/old.json --as archive-2025
-ike --file ~/work-matrix.json list # or just open it in place
-```
+- **An agent can manage your matrix.** `ike mcp` serves the Model Context
+ Protocol, so Claude Code or any MCP client can capture, complete, and
+ reprioritise tasks while you work. Off until you allow it.
+- **You can hand a task to an agent.** `ike plan 3` drafts a plan you read and
+ argue with; `ike delegate 3` carries it out in the task's own directory and
+ streams what it does. Also off until you allow it — separately.
-An export is an ordinary data file, so `--file` opens it directly. **MCP access
-is always off in an exported file**, whatever it was in the original: agent
-access is a decision about a file on a machine, and an export is made to travel.
-Importing a name that is already in use is an error rather than a merge — use
-`--as` to bring it in under a different name.
+One Go binary, three ways in (TUI, CLI, MCP), over a single JSON file that is
+yours: mode `0600`, on your disk, no account and no server. All three can run at
+once without losing writes.
## Install
@@ -97,38 +42,40 @@ Importing a name that is already in use is an error rather than a merge — use
brew install jonascript/tap/ike
```
-The formula builds from source, so Homebrew fetches Go for the build. Linux and
-macOS, Intel and Apple silicon.
-
Or with Go 1.25+:
```sh
go install github.com/jonascript/ike@latest
```
-Or from a checkout, with a stamped version:
+Linux and macOS, Intel and Apple silicon. Prebuilt archives are attached to
+every [release](https://github.com/jonascript/ike/releases) — they are not
+code-signed, so verify what you download:
```sh
-go build -ldflags "-X github.com/jonascript/ike/internal/cli.version=$(git describe --tags --always)" -o ike .
+shasum -a 256 -c checksums.txt --ignore-missing
+gh attestation verify ike_0.2.0_darwin_arm64.tar.gz --repo jonascript/ike
```
-### Prebuilt archives
+That second command checks ike's published build provenance, which proves the
+archive was built by this repository's release workflow from the tag it claims.
-Every [release](https://github.com/jonascript/ike/releases) attaches archives
-for macOS and Linux on both architectures. They are **not** code-signed, so
-verify what you download — against `checksums.txt`:
+## Quickstart
```sh
-shasum -a 256 -c checksums.txt --ignore-missing
+ike add "Fix prod bug" -q 1 # quadrant 1: urgent and important
+ike add "Write the release notes" # defaults to quadrant 2
+ike # open the matrix
```
-and, if you have the GitHub CLI, against the build provenance ike publishes,
-which proves the archive was built by this repository's release workflow from
-the tag it claims:
+| | Urgent | Not urgent |
+|---|---|---|
+| **Important** | 1 · Do It First | 2 · Schedule It |
+| **Not important** | 3 · Delegate It | 4 · Consider Eliminating It |
-```sh
-gh attestation verify ike_0.1.0_darwin_arm64.tar.gz --repo jonascript/ike
-```
+Those headings are defaults, not fixed — rename any of them with `t` in the TUI
+or `ike label`. The quadrant *numbers* never change, so scripts and the MCP
+tools keep working whatever you call them.
## TUI
@@ -161,11 +108,10 @@ Run `ike` with no arguments.
Changes made by the CLI or MCP server while the TUI is open appear within ~2 seconds.
-A dim `◆ mcp` marker sits in the footer while AI agent access is enabled; see
-[MCP](#mcp). No marker means nothing but you can reach the matrix. After a title,
+A dim `◆ mcp` marker sits in the footer while [agent access](#agents) is
+enabled. No marker means nothing but you can reach the matrix. After a title,
`✎` means the task has a plan attached, `⌁` that it has a conversation you can
-pick back up, and `⣾` that an agent is working on it right now (see
-[Delegating a task](#delegating-a-task)).
+pick back up, and `⣾` that an agent is working on it right now.
## CLI
@@ -181,290 +127,73 @@ ike archive # completed tasks, newest first
ike restore 3 # un-archive task 3, back to its quadrant
ike undo # revert the last change, from any frontend
ike redo # re-apply the last undone change
-ike label # show the four quadrant headings
-ike label 1 "Firefighting" # rename a quadrant
-ike label 1 --reset # restore its default name
+ike label 1 "Firefighting" # rename a quadrant (--reset restores the default)
-ike plan 3 # draft a plan for task 3: see "Delegating a task"
+ike plan 3 # draft a plan for task 3
ike delegate 3 # hand task 3 to an agent
ike agent status # whether ike may run an agent
-ike space # spaces: see "Spaces" below
+ike space # list spaces
ike list -s work # act on one space just this once
ike --file /path/to.json list # act on a different data file
```
-## Renaming the quadrants
+## Agents
-The default headings are *Do It First*, *Schedule It*, *Delegate It*, and
-*Consider Eliminating It*. Rename any of them with `t` in the TUI or `ike label`
-— up to 40 characters. Custom names are stored per data file and shared by all
-three frontends; a rename is undoable like any other change.
-
-Only the display name changes. Quadrant numbers 1–4 and their urgent/important
-meanings are fixed, so `ike add -q 1`, the matrix layout, and the MCP tools are
-unaffected by whatever you call them. Clearing a name (empty input, or
-`--reset`) drops the override and restores the default, which also means a
-future release's defaults reach you rather than being frozen at rename time.
-
-## Undo and redo
-
-Every change records a snapshot, so `ike undo` (or `u` in the TUI) reverts the
-last one — including a delete, and including changes made from a different
-frontend. Run it repeatedly to walk further back; the last 20 changes are kept
-in the data file, so history survives restarts. Undo does not recycle task IDs.
-History belongs to the space it was made in, so `ike undo` never reverts a change
-made in a different one.
-
-`ike redo` (or `U` / `ctrl+r`) re-applies what you just undid, and is itself
-undoable. **Any new change discards the redo history** — that includes a change
-made from another frontend, so an MCP agent writing while your TUI is open will
-clear a redo you were holding. The alternative is replaying a snapshot from a
-branch you've diverged from, which would silently clobber the newer change. The
-TUI only shows the redo hint while a redo is actually available.
-
-## MCP
-
-**Agent access is off by default.** ike will not serve your tasks over MCP
-until you say so, and registering ike with an MCP client is not enough on its
-own — `ike mcp` refuses to start while access is off:
+Both directions are off by default, and they are separate decisions: letting an
+agent edit your task list and letting ike start a process that edits your
+*files* have very different blast radii.
```sh
-ike mcp enable # allow AI agents to read and manage this matrix
-ike mcp disable # revoke it, including for a session already connected
-ike mcp status # show the current setting and which data file it applies to
+ike mcp enable # let an MCP client read and manage this matrix
+ike agent enable # let ike run an agent on a task's working directory
```
-The setting is remembered in the data file, so it survives restarts and
-upgrades, and it is scoped to that **file** — which means every space in it.
-Enabling access for one space enables it for all of them; to keep a matrix out
-of reach, put it in a separate file (see [Spaces](#spaces)) or pin the server to
-one space with `ike -s work mcp`. Access never travels: a space you export
-always lands with it switched off. It is not
-part of undo history — no sequence of `ike undo` can re-open access you closed.
-While access is on, the TUI shows a `◆ mcp` marker in its footer, and `?`
-explains the setting either way.
-
-`ike mcp disable` takes effect immediately, including on a client that is
-already connected: the setting is re-checked on every read and every write, so
-the next thing an agent tries fails. It does not wait for the session to end.
-
-> **What this is, and isn't.** The gate is a **consent mechanism, not a security
-> boundary.** It controls whether ike's MCP server will serve your matrix. It
-> cannot restrain an agent that already has shell access on your machine — such
-> an agent can read `~/.local/share/ike/tasks.json` directly, run `ike list`, or
-> simply run `ike mcp enable` itself. Treat it as "I have decided to let my
-> agent manage my tasks", not as a sandbox.
-
-Once enabled, `ike mcp` runs an MCP server on stdio with tools `list_tasks`,
-`add_task`, `complete_task`, `move_task`, `reorder_task`, `update_task`,
-`delete_task`, `list_archive`, `restore_task`, `undo`, `redo`, `list_quadrants`,
-`set_quadrant_label`, and `list_spaces`.
-
-Every tool takes an optional `space` argument and defaults to whichever space
-you are on, so an agent can work in one matrix while you work in another. It is
-read-only about spaces: no tool can create, rename, delete, or switch one, since
-switching would change what your own TUI and a bare `ike list` show. Launching
-the server as `ike -s work mcp` pins it to that space — a request naming another
-is refused, and `list_spaces` then reports only the one it was launched for.
-
-Register with Claude Code:
+**Serving your matrix over MCP.** `ike mcp` runs a stdio server with tools for
+listing, adding, completing, moving, reordering, archiving, and undoing —
+scoped to one space if you launch it that way. `ike mcp disable` takes effect
+immediately, even on a client already connected.
```sh
ike mcp enable
claude mcp add ike -- ike mcp
```
-Or in any MCP client config:
+**Handing a task over.** `ike plan 3` asks an agent to explore the task's
+directory and draft a plan, which is attached to the task for you to read and
+edit — that run is read-only and needs no permission. `ike delegate 3` then
+carries the plan out and streams what it does. `-i` on either hands you the
+terminal for a real Claude Code session instead, resuming the same conversation
+each time you come back to the task.
-```json
-{ "mcpServers": { "ike": { "command": "ike", "args": ["mcp"] } } }
-```
-
-## Delegating a task
+A delegated run never completes the task. You read what it did and decide.
-MCP lets an agent manage your matrix. This is the other direction: handing a
-task *to* an agent. It runs the [Claude Code](https://claude.com/claude-code)
-CLI — your own installation, so your authentication, settings, and per-project
-`CLAUDE.md` all apply.
+→ **[docs/agents.md](docs/agents.md)** covers the consent gates, what a
+delegated run is actually allowed to do (with measurements — the permission
+modes are not a safety ladder), how ike picks an effort level per run, and where
+plans are stored.
-There are two steps, and you can stop after the first.
+## Spaces and data
-**Draft a plan.** `ike plan 3` (or `P` in the TUI) asks an agent to explore the
-task's working directory and write a plan, which is then attached to the task.
-This is read-only — it runs in Claude Code's plan mode and cannot change
-anything — so it needs no permission.
+One file holds several independent matrices, called **spaces** — work and
+personal, say. Each has its own tasks, archive, headings, ID numbering, and undo
+history, so `ike undo` in one can never reach into another.
```sh
-ike plan 3 # draft one, streaming as it works
-ike plan 3 --show # print it
-ike plan 3 --edit # open it in $EDITOR
-ike plan 3 --from-file notes.md # attach one you wrote yourself
-ike plan 3 --clear # remove it
+ike space new work
+ike space use work # every later command follows
+ike add "Fix prod bug" -s work # or act on one space just once
```
-The plan is yours. Read it, argue with it, edit it — then either do the work
-yourself, or hand it on.
-
-**Or talk it through.** `ike plan 3 -i` (or `c` in the TUI) hands the terminal to
-a real Claude Code session, opened in the task's directory and already briefed on
-it. Exit the session and you're back in ike exactly where you were.
-
-The conversation belongs to the task. Run it again next week and you resume the
-same session — full history, nothing re-explained:
-
-```sh
-ike plan 3 -i # first time: a new conversation, briefed on the task
-ike plan 3 -i # later: picks up where you left off
-ike plan 3 -i --new-session # start over deliberately
-```
-
-When you agree on a plan, the agent writes it to a path ike gave it, and ike
-attaches it to the task as you come back — so `ike plan 3 --show` reflects what
-you actually decided together. A conversation that ends without a plan attaches
-nothing, which is most of them.
-
-`⌁` marks a task with a conversation waiting to be picked up.
-
-**Hand it on.** `ike delegate 3` (or `D`) runs an agent that carries the task
-out, following the attached plan if there is one.
-
-```sh
-ike agent enable # required before any delegated run
-ike delegate 3 # follow the attached plan, streaming
-ike delegate 3 -i # supervise it in a real session instead
-ike delegate 3 --plan-first # draft a plan, then carry it out
-ike delegate 3 --dir ~/dev/thing # set the working directory
-ike delegate 3 --model opus # choose a model
-ike delegate 3 --effort max # choose how hard it works
-```
-
-`-i` works here too, and resumes the same per-task conversation: the agent does
-the work while you watch and answer its questions, rather than reporting back
-afterwards. Being present doesn't remove the gate — `ike agent enable` is still
-required, because it's still ike starting an agent that edits your files.
-
-**Delegation is off by default**, separately from MCP access. Letting an agent
-edit your task list and letting ike start a process that edits your *files* are
-different decisions, so agreeing to one is not agreeing to the other. Like the
-MCP gate, the setting is per data file, survives restarts, is never carried into
-an export, and is not part of undo history — no sequence of `ike undo` can
-re-open it.
-
-**A delegated run never completes the task** — read what it did and decide.
-
-### How hard the agent works
-
-Effort controls how deeply the agent thinks, how many tools it reaches for, and
-how much it says on the way. It usually matters more than the model: a run's wall
-clock is dominated by how many turns it takes, and effort is what moves that.
-
-ike picks a level per run rather than using one setting for everything, because
-the two things it delegates are not the same shape of work — and it prints what
-it chose, and why, in the run header:
-
-| run | effort | why |
-|---|---|---|
-| `ike plan 3` | `high` | drafting a plan — the thinking *is* the product |
-| `ike delegate 3` with a plan attached | `medium` | following an attached plan; the approach is already decided and reviewed |
-| `ike delegate 3` with no plan | `high` | no plan to follow, so it has to work the approach out as well as do it |
-
-```
-$ ike delegate 3
-delegating 3 Fix the flaky reorder test
- in /Users/you/dev/thing
- · effort medium — following an attached plan
-```
-
-`--effort low|medium|high|xhigh|max` overrides it, on both `ike plan` and
-`ike delegate`, and the header then just states the level. A mistyped level fails
-before any process starts. Attaching a plan is therefore also the cheapest way to
-make a delegated run cheaper: the run stops paying to re-derive decisions the
-plan already records.
-
-The TUI has no flag for this and always uses the recommendation, shown in the
-same line at the top of the run view.
-
-### What a delegated run is allowed to do
-
-Runs are unattended, so nothing can prompt you part-way through. The default is
-`--permission-mode auto`, which lets the agent change files *and run commands* in
-the working directory.
-
-Do not read the mode names as a safety ladder — they are not. Measured against a
-real run, asking an agent to `rm` a file:
-
-| `--permission-mode` | result |
-|---|---|
-| `manual` | denied — the file survived |
-| `acceptEdits` | allowed — the file was deleted |
-| `auto` (default) | allowed — the file was deleted |
-| `bypassPermissions` | allowed, by definition |
-
-So `acceptEdits` is **not** a middle setting that withholds the shell.
-`manual` is the one mode that meaningfully restrains a delegated run: with nobody
-to ask, it denies anything needing approval, and the denials come back in the
-transcript. Use it when you want the agent to work and then stop at the first
-thing you would have wanted to be asked about.
-
-```sh
-ike delegate 3 --permission-mode manual
-```
+Everything lives in `~/.local/share/ike/tasks.json` (mode `0600`, in a `0700`
+directory). Writes go through a lock file and an atomic rename with the previous
+contents kept as `.bak`, so three frontends can run at once and an interrupted
+write costs one change rather than the matrix. The file is self-contained and
+portable — copy it to another machine and every space comes with it.
-One trap worth knowing if you test this yourself: commands the harness
-classifies as safe — `echo`, `ls` — run under *every* mode including `manual`,
-so a harmless command shows no difference between any of them.
-
-The practical control is therefore the gate — whether ike starts an agent at all
-— rather than the mode it starts it in.
-
-Each task remembers a **working directory**. The first run stores the one you
-give it, or the directory you ran from, so `cd ~/dev/thing && ike delegate 3`
-does the obvious thing and later runs go back to the same project wherever you
-start them.
-
-In the TUI a run takes over the screen and streams. `esc` detaches and the run
-keeps going — the task shows `⣾` in the matrix and `D` on it reattaches. `ctrl+c`
-in the run view stops the run. Quitting ike stops it too: the agent is a child
-process, and ike will not leave one running with nothing reading it.
-
-> **The same caveat as MCP.** The gate is a consent mechanism, not a security
-> boundary. It decides whether *ike* starts an agent; it does nothing about what
-> that agent then does, which is governed by Claude Code's own permissions. And
-> anyone who can run commands as you can run `claude` directly.
-
-Plans are stored beside the data file, one file per task, in
-`tasks.json.plans//.md` — not inside `tasks.json`, so a few KB of
-markdown per task is not copied into every undo snapshot. Deleting a task leaves
-its plan, so undoing the delete brings both back; `ike plan --prune` sweeps the
-ones left behind. Note that plans do **not** travel with `ike space export` yet.
-
-Delegation needs `claude` on your `PATH`; `ike agent status` says whether it
-found it. Set `IKE_AGENT_CMD` to point at a different binary or a wrapper.
-
-## Data
-
-Tasks live in `$XDG_DATA_HOME/ike/tasks.json` (default
-`~/.local/share/ike/tasks.json`), created mode `0600` in a `0700` directory —
-your matrix is not readable by other users on the machine.
-
-Writes are serialized through a sidecar lock file and land via an atomic
-rename, so the TUI, CLI, and MCP server can run at the same time without losing
-updates. Each write is flushed to disk before the rename, and the previous
-contents are kept as `tasks.json.bak`, so an interrupted write costs at most
-one change rather than the whole matrix. If the file is ever unreadable, ike
-refuses to overwrite it rather than starting fresh over the top.
-
-Override the location with `--file` or `IKE_DATA_FILE` — highest precedence
-first: `--file`, then `IKE_DATA_FILE`, then the default above. Either must be an
-**absolute** path whose parent directory already exists (a relative path would resolve against
-whatever directory the process happened to start in — not something you can
-predict when an MCP client launches ike for you).
-
-One caveat on the concurrency guarantee: it relies on advisory `flock`, which is
-unreliable on NFS and some FUSE mounts — so pointing the data file at a Dropbox,
-iCloud, or network-mounted folder and writing from two machines at once is not
-covered. A single machine writing to a synced folder is fine.
+→ **[docs/data.md](docs/data.md)** covers spaces in full, exporting and
+importing a single matrix, the file's durability guarantees and their one
+caveat (`flock` on network mounts), undo/redo semantics, and renaming quadrants.
## Platform support
diff --git a/assets/demo-seed.sh b/assets/demo-seed.sh
new file mode 100644
index 0000000..992f5ef
--- /dev/null
+++ b/assets/demo-seed.sh
@@ -0,0 +1,47 @@
+# Seeds a throwaway matrix for the demo recording. Meant to be sourced, not run:
+# it exports PATH and IKE_DATA_FILE into the calling shell, which is what lets
+# demo.tape drive a real `ike` rather than a mock.
+#
+# The data file is a fresh mktemp directory every time, so recording a demo can
+# never touch the matrix you actually use. Everything else in here is chosen to
+# look like a real week of work on this repo.
+
+set -e
+
+demo_dir="$(mktemp -d)"
+demo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+# Built from the working tree, so the GIF always shows the code it ships with.
+go build -o "$demo_dir/ike" "$demo_root"
+
+export PATH="$demo_dir:$PATH"
+export IKE_DATA_FILE="$demo_dir/demo.json"
+
+ike add "Fix the flaky reorder test" -q 1 >/dev/null
+ike add "Reply to the security report" -q 1 >/dev/null
+ike add "Write the v0.3 release notes" -q 2 >/dev/null
+ike add "Add plan export to space export" -q 2 >/dev/null
+ike add "Bump the Homebrew formula" -q 3 >/dev/null
+ike add "Triage this week's new issues" -q 3 >/dev/null
+ike add "Hand-tune the logo SVG again" -q 4 >/dev/null
+
+# One task carries a plan, so the ✎ mark and the plan view have something real
+# to show without the demo having to run (and pay for) an agent.
+cat > "$demo_dir/plan.md" <<'PLAN'
+## Goal
+
+Make reorder deterministic under -race.
+
+## Steps
+
+1. Reproduce with `go test -run TestReorder -race -count=50`
+2. Renormalise ranks inside the flock, not after it
+3. Add a regression test that interleaves two writers
+
+## Checking it worked
+
+The loop above passes 50/50, and `ike list` order is unchanged.
+PLAN
+ike plan 1 --from-file "$demo_dir/plan.md" >/dev/null
+
+set +e
diff --git a/assets/demo.gif b/assets/demo.gif
new file mode 100644
index 0000000..107c903
Binary files /dev/null and b/assets/demo.gif differ
diff --git a/assets/demo.tape b/assets/demo.tape
new file mode 100644
index 0000000..7193645
--- /dev/null
+++ b/assets/demo.tape
@@ -0,0 +1,75 @@
+# Demo recording for the README, driven by VHS (https://github.com/charmbracelet/vhs).
+#
+# brew install vhs
+# vhs assets/demo.tape
+#
+# Checked in so the demo can be regenerated when the UI changes, rather than
+# being a hand-recorded artifact nobody can reproduce. It drives the real binary
+# built from the working tree against a throwaway data file — see demo-seed.sh.
+
+Output assets/demo.gif
+
+Set Shell bash
+Set FontSize 15
+Set Width 1180
+Set Height 700
+Set Padding 24
+Set Theme "Catppuccin Mocha"
+Set TypingSpeed 55ms
+
+# Build and seed off camera.
+Hide
+Type "source assets/demo-seed.sh"
+Enter
+Sleep 8s
+Type "clear"
+Enter
+Sleep 1s
+Show
+
+# Capture from the shell, the way most tasks actually arrive.
+Type "ike add 'Post the v0.2 announcement' -q 2"
+Sleep 700ms
+Enter
+Sleep 1800ms
+
+# Then open the matrix.
+Type "ike"
+Sleep 500ms
+Enter
+Sleep 2500ms
+
+# Move around quadrant 1.
+Type "j"
+Sleep 900ms
+Type "k"
+Sleep 1200ms
+
+# The task carries a plan — open it.
+Type "p"
+Sleep 3000ms
+Escape
+Sleep 1200ms
+
+# Capture straight into the matrix.
+Type "a"
+Sleep 600ms
+Type "Reply to the packaging thread"
+Sleep 700ms
+Enter
+Sleep 1800ms
+
+# Reclassify it: not important, just urgent.
+Type "m"
+Sleep 900ms
+Type "3"
+Sleep 2000ms
+
+# Undo history spans every frontend and survives restarts, and says what it
+# undid rather than just moving something back.
+Type "u"
+Sleep 3000ms
+
+# Ends on the matrix rather than back at a bare prompt, so the loop reads as a
+# tool rather than as a session that finished.
+Sleep 500ms
diff --git a/docs/agents.md b/docs/agents.md
new file mode 100644
index 0000000..3dfeb1f
--- /dev/null
+++ b/docs/agents.md
@@ -0,0 +1,237 @@
+# Agents
+
+ike works with agents in both directions: an agent can manage your matrix, and
+you can hand a task *to* an agent. They are separate features with separate
+permissions, and both are off until you say otherwise.
+
+- [MCP: letting an agent manage your matrix](#mcp)
+- [Delegating a task to an agent](#delegating-a-task)
+
+---
+
+## MCP
+
+**Agent access is off by default.** ike will not serve your tasks over MCP
+until you say so, and registering ike with an MCP client is not enough on its
+own — `ike mcp` refuses to start while access is off:
+
+```sh
+ike mcp enable # allow AI agents to read and manage this matrix
+ike mcp disable # revoke it, including for a session already connected
+ike mcp status # show the current setting and which data file it applies to
+```
+
+The setting is remembered in the data file, so it survives restarts and
+upgrades, and it is scoped to that **file** — which means every space in it.
+Enabling access for one space enables it for all of them; to keep a matrix out
+of reach, put it in a separate file (see [Spaces](data.md#spaces)) or pin the
+server to one space with `ike -s work mcp`. Access never travels: a space you
+export always lands with it switched off. It is not part of undo history — no
+sequence of `ike undo` can re-open access you closed. While access is on, the
+TUI shows a `◆ mcp` marker in its footer, and `?` explains the setting either
+way.
+
+`ike mcp disable` takes effect immediately, including on a client that is
+already connected: the setting is re-checked on every read and every write, so
+the next thing an agent tries fails. It does not wait for the session to end.
+
+> **What this is, and isn't.** The gate is a **consent mechanism, not a security
+> boundary.** It controls whether ike's MCP server will serve your matrix. It
+> cannot restrain an agent that already has shell access on your machine — such
+> an agent can read `~/.local/share/ike/tasks.json` directly, run `ike list`, or
+> simply run `ike mcp enable` itself. Treat it as "I have decided to let my
+> agent manage my tasks", not as a sandbox.
+
+Once enabled, `ike mcp` runs an MCP server on stdio with tools `list_tasks`,
+`add_task`, `complete_task`, `move_task`, `reorder_task`, `update_task`,
+`delete_task`, `list_archive`, `restore_task`, `undo`, `redo`, `list_quadrants`,
+`set_quadrant_label`, and `list_spaces`.
+
+Every tool takes an optional `space` argument and defaults to whichever space
+you are on, so an agent can work in one matrix while you work in another. It is
+read-only about spaces: no tool can create, rename, delete, or switch one, since
+switching would change what your own TUI and a bare `ike list` show. Launching
+the server as `ike -s work mcp` pins it to that space — a request naming another
+is refused, and `list_spaces` then reports only the one it was launched for.
+
+Register with Claude Code:
+
+```sh
+ike mcp enable
+claude mcp add ike -- ike mcp
+```
+
+Or in any MCP client config:
+
+```json
+{ "mcpServers": { "ike": { "command": "ike", "args": ["mcp"] } } }
+```
+
+---
+
+## Delegating a task
+
+MCP lets an agent manage your matrix. This is the other direction: handing a
+task *to* an agent. It runs the [Claude Code](https://claude.com/claude-code)
+CLI — your own installation, so your authentication, settings, and per-project
+`CLAUDE.md` all apply.
+
+There are two steps, and you can stop after the first.
+
+### Draft a plan
+
+`ike plan 3` (or `P` in the TUI) asks an agent to explore the task's working
+directory and write a plan, which is then attached to the task. This is
+read-only — it runs in Claude Code's plan mode and cannot change anything — so
+it needs no permission.
+
+```sh
+ike plan 3 # draft one, streaming as it works
+ike plan 3 --show # print it
+ike plan 3 --edit # open it in $EDITOR
+ike plan 3 --from-file notes.md # attach one you wrote yourself
+ike plan 3 --clear # remove it
+```
+
+The plan is yours. Read it, argue with it, edit it — then either do the work
+yourself, or hand it on.
+
+### Or talk it through
+
+`ike plan 3 -i` (or `c` in the TUI) hands the terminal to a real Claude Code
+session, opened in the task's directory and already briefed on it. Exit the
+session and you're back in ike exactly where you were.
+
+The conversation belongs to the task. Run it again next week and you resume the
+same session — full history, nothing re-explained:
+
+```sh
+ike plan 3 -i # first time: a new conversation, briefed on the task
+ike plan 3 -i # later: picks up where you left off
+ike plan 3 -i --new-session # start over deliberately
+```
+
+When you agree on a plan, the agent writes it to a path ike gave it, and ike
+attaches it to the task as you come back — so `ike plan 3 --show` reflects what
+you actually decided together. A conversation that ends without a plan attaches
+nothing, which is most of them.
+
+`⌁` marks a task with a conversation waiting to be picked up.
+
+### Hand it on
+
+`ike delegate 3` (or `D`) runs an agent that carries the task out, following the
+attached plan if there is one.
+
+```sh
+ike agent enable # required before any delegated run
+ike delegate 3 # follow the attached plan, streaming
+ike delegate 3 -i # supervise it in a real session instead
+ike delegate 3 --plan-first # draft a plan, then carry it out
+ike delegate 3 --dir ~/dev/thing # set the working directory
+ike delegate 3 --model opus # choose a model
+ike delegate 3 --effort max # choose how hard it works
+```
+
+`-i` works here too, and resumes the same per-task conversation: the agent does
+the work while you watch and answer its questions, rather than reporting back
+afterwards. Being present doesn't remove the gate — `ike agent enable` is still
+required, because it's still ike starting an agent that edits your files.
+
+**Delegation is off by default**, separately from MCP access. Letting an agent
+edit your task list and letting ike start a process that edits your *files* are
+different decisions, so agreeing to one is not agreeing to the other. Like the
+MCP gate, the setting is per data file, survives restarts, is never carried into
+an export, and is not part of undo history — no sequence of `ike undo` can
+re-open it.
+
+**A delegated run never completes the task** — read what it did and decide.
+
+### How hard the agent works
+
+Effort controls how deeply the agent thinks, how many tools it reaches for, and
+how much it says on the way. It usually matters more than the model: a run's wall
+clock is dominated by how many turns it takes, and effort is what moves that.
+
+ike picks a level per run rather than using one setting for everything, because
+the two things it delegates are not the same shape of work — and it prints what
+it chose, and why, in the run header:
+
+| run | effort | why |
+|---|---|---|
+| `ike plan 3` | `high` | drafting a plan — the thinking *is* the product |
+| `ike delegate 3` with a plan attached | `medium` | following an attached plan; the approach is already decided and reviewed |
+| `ike delegate 3` with no plan | `high` | no plan to follow, so it has to work the approach out as well as do it |
+
+```
+$ ike delegate 3
+delegating 3 Fix the flaky reorder test
+ in /Users/you/dev/thing
+ · effort medium — following an attached plan
+```
+
+`--effort low|medium|high|xhigh|max` overrides it, on both `ike plan` and
+`ike delegate`, and the header then just states the level. A mistyped level fails
+before any process starts. Attaching a plan is therefore also the cheapest way to
+make a delegated run cheaper: the run stops paying to re-derive decisions the
+plan already records.
+
+The TUI has no flag for this and always uses the recommendation, shown in the
+same line at the top of the run view.
+
+### What a delegated run is allowed to do
+
+Runs are unattended, so nothing can prompt you part-way through. The default is
+`--permission-mode auto`, which lets the agent change files *and run commands* in
+the working directory.
+
+Do not read the mode names as a safety ladder — they are not. Measured against a
+real run, asking an agent to `rm` a file:
+
+| `--permission-mode` | result |
+|---|---|
+| `manual` | denied — the file survived |
+| `acceptEdits` | allowed — the file was deleted |
+| `auto` (default) | allowed — the file was deleted |
+| `bypassPermissions` | allowed, by definition |
+
+So `acceptEdits` is **not** a middle setting that withholds the shell.
+`manual` is the one mode that meaningfully restrains a delegated run: with nobody
+to ask, it denies anything needing approval, and the denials come back in the
+transcript. Use it when you want the agent to work and then stop at the first
+thing you would have wanted to be asked about.
+
+```sh
+ike delegate 3 --permission-mode manual
+```
+
+One trap worth knowing if you test this yourself: commands the harness
+classifies as safe — `echo`, `ls` — run under *every* mode including `manual`,
+so a harmless command shows no difference between any of them.
+
+The practical control is therefore the gate — whether ike starts an agent at all
+— rather than the mode it starts it in.
+
+Each task remembers a **working directory**. The first run stores the one you
+give it, or the directory you ran from, so `cd ~/dev/thing && ike delegate 3`
+does the obvious thing and later runs go back to the same project wherever you
+start them.
+
+In the TUI a run takes over the screen and streams. `esc` detaches and the run
+keeps going — the task shows `⣾` in the matrix and `D` on it reattaches. `ctrl+c`
+in the run view stops the run. Quitting ike stops it too: the agent is a child
+process, and ike will not leave one running with nothing reading it.
+
+> **The same caveat as MCP.** The gate is a consent mechanism, not a security
+> boundary. It decides whether *ike* starts an agent; it does nothing about what
+> that agent then does, which is governed by Claude Code's own permissions. And
+> anyone who can run commands as you can run `claude` directly.
+
+Plans are stored beside the data file, one file per task, in
+`tasks.json.plans//.md` — not inside `tasks.json`, so a few KB of
+markdown per task is not copied into every undo snapshot. Deleting a task leaves
+its plan, so undoing the delete brings both back; `ike plan --prune` sweeps the
+ones left behind. Note that plans do **not** travel with `ike space export` yet.
+
+Delegation needs `claude` on your `PATH`; `ike agent status` says whether it
+found it. Set `IKE_AGENT_CMD` to point at a different binary or a wrapper.
diff --git a/docs/data.md b/docs/data.md
new file mode 100644
index 0000000..4306dcb
--- /dev/null
+++ b/docs/data.md
@@ -0,0 +1,124 @@
+# Spaces, files, and your data
+
+Everything ike knows lives in one JSON file you own. This page covers how that
+file is organised, where it lives, how it survives being written to by three
+frontends at once, and how to move it around.
+
+- [Spaces](#spaces)
+- [Moving a matrix between machines](#moving-a-matrix-between-machines)
+- [Where your data lives](#where-your-data-lives)
+- [Undo and redo](#undo-and-redo)
+- [Renaming the quadrants](#renaming-the-quadrants)
+
+---
+
+## Spaces
+
+One data file holds several independent matrices, called **spaces** — work and
+personal, say. Each has its own tasks, archive, quadrant headings, ID numbering,
+and undo history, so `ike undo` in one can never reach into another. A fresh
+install has a single space named `default`, and nothing changes until you make
+a second.
+
+```sh
+ike space # list spaces, marking the current one
+ike space new work # create it (does not switch)
+ike space use work # switch; every later command follows
+ike space rename work job
+ike space rm work # refuses a non-empty space without --force
+```
+
+Every command takes `-s/--space NAME` to act on one space just once, without
+switching:
+
+```sh
+ike add "Fix prod bug" -s work -q 1
+ike list -s work
+```
+
+Two concepts worth keeping apart: a **file** is a document and contains spaces;
+a **space** is one matrix inside it. `--file` picks the document, `--space` picks
+the matrix. In the TUI, `s` opens a space picker and `]`/`[` move between spaces.
+
+**Deleting a space cannot be undone.** History lives inside the space, so there
+is no stack left to revert from — which is why `ike space rm` names the counts it
+is about to destroy and needs `--force` if the space still holds anything. The
+previous file contents remain in `tasks.json.bak` until the next change.
+
+## Moving a matrix between machines
+
+The data file is self-contained and fully portable: nothing in it refers to a
+path or a machine, and timestamps are stored in UTC. Copy it to another computer
+and every space comes with it. The sidecar `.lock` and `.bak` files do not need
+copying.
+
+To move one space rather than the whole file:
+
+```sh
+ike space export work ~/work-matrix.json # a standalone ike data file
+# copy that one file to the other machine, then:
+ike space import ~/work-matrix.json
+ike space import ~/old.json --as archive-2025
+ike --file ~/work-matrix.json list # or just open it in place
+```
+
+An export is an ordinary data file, so `--file` opens it directly. **MCP access
+is always off in an exported file**, whatever it was in the original: agent
+access is a decision about a file on a machine, and an export is made to travel.
+Importing a name that is already in use is an error rather than a merge — use
+`--as` to bring it in under a different name.
+
+Note that attached plans do **not** yet travel with `ike space export`.
+
+## Where your data lives
+
+Tasks live in `$XDG_DATA_HOME/ike/tasks.json` (default
+`~/.local/share/ike/tasks.json`), created mode `0600` in a `0700` directory —
+your matrix is not readable by other users on the machine.
+
+Writes are serialized through a sidecar lock file and land via an atomic
+rename, so the TUI, CLI, and MCP server can run at the same time without losing
+updates. Each write is flushed to disk before the rename, and the previous
+contents are kept as `tasks.json.bak`, so an interrupted write costs at most
+one change rather than the whole matrix. If the file is ever unreadable, ike
+refuses to overwrite it rather than starting fresh over the top.
+
+Override the location with `--file` or `IKE_DATA_FILE` — highest precedence
+first: `--file`, then `IKE_DATA_FILE`, then the default above. Either must be an
+**absolute** path whose parent directory already exists (a relative path would
+resolve against whatever directory the process happened to start in — not
+something you can predict when an MCP client launches ike for you).
+
+One caveat on the concurrency guarantee: it relies on advisory `flock`, which is
+unreliable on NFS and some FUSE mounts — so pointing the data file at a Dropbox,
+iCloud, or network-mounted folder and writing from two machines at once is not
+covered. A single machine writing to a synced folder is fine.
+
+## Undo and redo
+
+Every change records a snapshot, so `ike undo` (or `u` in the TUI) reverts the
+last one — including a delete, and including changes made from a different
+frontend. Run it repeatedly to walk further back; the last 20 changes are kept
+in the data file, so history survives restarts. Undo does not recycle task IDs.
+History belongs to the space it was made in, so `ike undo` never reverts a change
+made in a different one.
+
+`ike redo` (or `U` / `ctrl+r`) re-applies what you just undid, and is itself
+undoable. **Any new change discards the redo history** — that includes a change
+made from another frontend, so an MCP agent writing while your TUI is open will
+clear a redo you were holding. The alternative is replaying a snapshot from a
+branch you've diverged from, which would silently clobber the newer change. The
+TUI only shows the redo hint while a redo is actually available.
+
+## Renaming the quadrants
+
+The default headings are *Do It First*, *Schedule It*, *Delegate It*, and
+*Consider Eliminating It*. Rename any of them with `t` in the TUI or `ike label`
+— up to 40 characters. Custom names are stored per data file and shared by all
+three frontends; a rename is undoable like any other change.
+
+Only the display name changes. Quadrant numbers 1–4 and their urgent/important
+meanings are fixed, so `ike add -q 1`, the matrix layout, and the MCP tools are
+unaffected by whatever you call them. Clearing a name (empty input, or
+`--reset`) drops the override and restores the default, which also means a
+future release's defaults reach you rather than being frozen at rename time.