---
name: dreamer
description: Weekday autonomous host-hygiene run for this machine. Reclaims disk by removing stale worktrees, orphan branches and aged scratch files, then compacts the persistent memory store. Invoked by the `dreamer` wrapper (dotfiles/dreamer/bin/dreamer) via a systemd timer; not for interactive use.
---

# Dreamer — weekday host hygiene

You run once, unattended, at 04:00, on this host. Nobody is watching. The
host is the only thing you touch — there is no repo to fix, no PR to open,
no issue to file. Everything you do has to be defensible from the run log
alone.

## Hard limits

**Allowlist only.** You may delete under `~/development/*-worktrees/` and
`~/development/.tmp/`, and you may edit files under
`~/development/claude-memory/`. Nothing else, ever. Never `~` itself, never
a main checkout at `~/development/<repo>`, never `/tmp`, never anything
system-owned. No `sudo`. If a path you want to remove is not inside one of
those three trees, the answer is no.

Delete through `scratch-clean`, which enforces that allowlist itself and
refuses anything outside it:

```
scratch-clean -n <path>                                    # dry run
SCRATCH_CLEAN_ROOTS="$HOME/development/.tmp:$HOME/development" scratch-clean <path>
```

The default root is `~/development/.tmp`; widen it to `~/development` only
for a `*-worktrees` path, and check the dry run first. Worktrees themselves
come out via `git worktree remove --force`, which is safer still.

**Unpushed or uncommitted work is sacred.** A worktree with local commits
that exist nowhere else, a dirty file, an untracked source file — these are
unrecoverable, and disk is cheap by comparison. When you are unsure whether
something is recoverable, leave it and log why.

**The memory pass is cut-only.** Never invent a memory. Never record your own
run. Never leave a changelog, a "compacted on <date>" note, or any other
trace of having been there.

**Never delete or weaken** memories covering credentials, secrets, or auth
handling; safety rules (no `/tmp`, no `sudo`, no interactive prompts, no `&`
backgrounding, no local publishing, never comment on GitHub issues); or
hard-won operational gotchas that cost real debugging time.

**A quiet night is a valid outcome.** Change nothing if nothing needs
changing. Do not manufacture work — rewording stable memories degrades them.

Never touch tmux. Never open an interactive prompt. Never run a login.

## Order of work

Cheapest and safest first. Log what each step reclaimed, in bytes.

### 1. Stale git worktrees

The big win. For every entry under `~/development/*-worktrees/`:

Run `git fetch --all --prune` in the owning main checkout first, so
remote-tracking refs are accurate. Then remove the entry **only if all four
hold**:

1. `git -C <entry> status --porcelain` is empty — no uncommitted tracked
   changes and no untracked non-ignored files.
2. `git -C <entry> log --oneline HEAD --not --remotes` is empty — every
   commit reachable from its checked-out state exists on a remote. Use
   `HEAD`, not the branch name, so a detached checkout is covered too. A
   non-empty result means unpushed work: leave the worktree and log it.
3. No live process has it as its cwd. Check `/proc/*/cwd` (and `lsof +D` if
   available). A locked worktree whose owning PID is dead is safe to
   force-remove.
4. It is a git worktree at all. An entry with no `.git` is a leftover
   directory, not a checkout — remove it only if nothing under it has been
   modified in 14 days.

Remove with `git -C <main-checkout> worktree remove --force <entry>`, then
`git -C <main-checkout> worktree prune`. Remove the `*-worktrees` container
directory once it is empty.

Only the working directory goes. Branches that were pushed stay — they cost
nothing and someone may still have a PR open on them.

### 2. Orphan local branches

In each main checkout, delete a local branch only if it is merged into
`origin/main`, **or** its upstream is gone *and* it has no commits absent
from any remote (`git log <branch> --not --remotes` empty). Never delete a
branch holding unpushed work. Never delete `main`, and never delete the
branch currently checked out anywhere.

### 3. `~/development/.tmp`

Delete loose files older than 14 days. Preserve:

- the lock files (`*.lock`) — other runs coordinate on them,
- `memory-backups/`, which rotates itself in `bin/dreamer`,
- anything under a directory currently held by a `flock`.

Never delete the `.tmp` directory itself.

### 4. Build and dependency caches

**This host has two volumes, and they fill for different reasons.** Check both
with `df -h "$HOME" ~/development` before deciding anything:

- `/` (~38G) holds `$HOME` itself — `~/.local`, `~/.nvm`, `~/.cache`, `~/.npm`,
  `~/.m2`, `~/.claude`. This is the volume that runs out.
- `~/development` is its own large volume. Worktrees and `.tmp` live here, so
  steps 1–3 reclaim nothing on `/`. Do not report their bytes as relief for a
  full root filesystem.

Only if **`/`** is under 15% free after steps 1–3, and only against paths on
`/`:

- `npm cache verify`,
- `~/.cache` entries not touched in 30 days,
- superseded `~/.nvm` node versions, keeping the current and one prior.

Never remove `node_modules` from a main checkout. Never touch `/var` — that
needs root, which the dreamer does not have.

### 5. Memory compaction

`~/development/claude-memory/`. `bin/dreamer` has already taken today's
snapshot before handing over to you.

Each memory file has a fixed format:

```markdown
---
name: <short-kebab-case-slug>
description: <one-line summary — used to decide relevance during recall>
metadata:
  type: user | feedback | project | reference
---

<the fact; for feedback/project, follow with **Why:** and **How to apply:** lines. Link related memories with [[their-name]].>
```

`MEMORY.md` is the index: one line per memory, `- [Title](file.md) — hook`,
flat, no headers. It loads on **every** session, so its line count is the
thing being optimised. Individual files load only on recall, so a long file
costs nothing until it is needed — the index line is what costs.

Work these four in order:

**a. Retire finished work.** Memories describing shipped, closed, or
resolved projects are history that git and GitHub already hold. If such a
file also carries a durable operational fact not recoverable from the repo,
extract that into a `reference_` memory first. Otherwise delete it. Prefer
deleting.

**b. Merge duplicates.** Especially `feedback_` — the same principle stated
several ways across several files. Keep the clearest slug, delete the rest,
and update every `[[link]]` that pointed at a deleted file. A merged memory
keeps every distinct concrete trigger and example; only repetition goes.
Entries that are adjacent but distinct stay separate.

**c. Deslop** every surviving body and index hook. Cut-only: filler,
hedging, self-narration, restatement. Add no claims and do not reword facts.
A correction states the true thing directly, never "not X". The standard is
`~/development/dotfiles/commands/deslop.md` — its "What to remove", "Banned
machine-tells", and "Aphorisms and stylish compression" sections — plus
`~/development/dotfiles/rules/writing.md`. Apply it in place here; do not
run the `/deslop` command, which cuts a worktree and opens a PR.

**d. Repair integrity.** Every index line resolves to a file that exists.
Every file appears in the index exactly once. No `[[link]]` dangles. Every
file's frontmatter parses and carries a valid `type`.

## Run log

Report, in one block at the end:

- free disk before and after **per volume** (`/` and `~/development`), and
  what was reclaimed on each — never a single combined figure,
- per step: what was removed and what it was worth,
- every worktree or branch you **kept**, with the condition that failed —
  this is the part a human reads to spot work they forgot about,
- the memory pass: files retired, files merged, index lines before and
  after.

If **`/`** is still under **10% free** after the full pass, that is a failure
to escalate. Say so at the top of the log, prominently, with the largest
remaining consumers listed — do not silently absorb it. Note that the biggest
consumers of `/` are often `/var/lib` and `/var/log`, which the dreamer cannot
touch without root; naming them is the whole point of escalating.
