Hand Claude Code a goal and walk away. Aperture runs a budget-aware work loop that stops itself before it burns your usage limit, keeps all of its state on disk so the context window never bottlenecks, dispatches the heavy lifting to subagents, resumes across usage windows, and gates completion with an adversarial review council. In the morning you get a clean report — and an honest one.
It is a skill (a Markdown playbook + a few small shell scripts), not a service. Nothing runs in the cloud; nothing phones home.
The cake is a lie. The budget isn't.
Long unattended runs fail in two boring ways:
- They blow the usage limit. Claude Code subscriptions have a rolling 5-hour window and a 7-day window. An eager agent will happily burn the whole thing at 2am and leave you locked out for the rest of the day.
- They drown the context window. A naive loop re-reads its own logs and state every iteration — quadratic cost — and the re-read content lingers in context, re-billed every turn until it compacts and loses the thread.
Aperture is built around fixing both:
- A brake that watches your real subscription usage and winds the run down at a configurable threshold (default 70% of whichever window hits first), then schedules a resume for when the window resets.
- A thin orchestrator + on-disk state + subagent labor design so the agent's context grows linearly and slowly, and a mid-run compaction or restart loses nothing.
- Self-stopping at a usage threshold — soft brake at
brake_at(70%), hard backstop hook athard_at(82%) that blocks new heavy work. Reads the real 5h/7d signal; falls back conservatively if it can't. - Disk-externalized state — every run lives in
.aperture/runs/<run-id>/(STATE.jsoncursor, append-onlyJOURNAL.md,HANDOFF.md,MORNING-REPORT.md,lessons.md). Context loss becomes a non-event: the agent re-hydrates from disk. - Subagent labor — heavy I/O (reading sources, running tests, research) happens in subagents that return token-capped digests. The giant context dies with the subagent.
- Resume across windows — at wind-down it computes the reset time and schedules a wake-up.
- Adversarial COUNCIL — when the objective is declared done, a panel of opposed roles (Red Team, QA, Objective Owner, Architect, End-User rep, + domain roles) tries to refute that it's actually finished. A neutral Judge issues the verdict; unmet gaps become new tasks.
- A real preflight — before going unattended it probes the usage signal, checks permissions, and
flags missing logins/tokens/endpoints as
NEEDS-HUMANso 3am holds no surprises. - Sentinel-gated brake — the hook only acts during an Aperture run. Your normal interactive sessions are never throttled.
- Spec-driven — uses GitHub Spec Kit if present, otherwise a built-in lightweight plan/tasks, scaled to the task.
stateDiagram-v2
[*] --> INTAKE
INTAKE --> PLAN: objective + mode + hours
PLAN --> PREFLIGHT: plan / tasks (scaled)
PREFLIGHT --> GO: checklist PASS / NEEDS-HUMAN
GO --> WORK: explicit "go"
WORK --> WORK: task -> subagent -> checkpoint -> check brake
WORK --> WINDDOWN: budget>=70% | objective done | block
WINDDOWN --> COUNCIL: if objective declared done
WINDDOWN --> SLEEP: if work remains + budget spent
COUNCIL --> WORK: verdict = not met + budget remains
COUNCIL --> [*]: verdict = met
SLEEP --> RESUME: wake-up @ reset + buffer
RESUME --> WORK: re-hydrate from disk + re-probe
The core idea: the main session is a thin orchestrator. It holds a tiny in-context ledger and writes state to disk; it only reads disk on a cold start or resume. All heavy work is delegated to subagents that return ≤~150-token digests. So the orchestrator's context stays flat while arbitrarily much work happens around it — and adding new capabilities doesn't inflate the per-turn cost.
The brake works by a small chain:
statusLine JSON ──► statusline-writer.sh ──► ~/.claude/aperture/usage-cache.json
│
usage-probe.sh ◄───────────┘ (normalizes to signal_pct)
│
soft brake: The Core reads it each loop iteration ──► wind down at 70%
hard brake: brake-hook.sh (PreToolUse) blocks heavy work at 82% [sentinel-gated]
- Claude Code (CLI, desktop, or IDE).
- A Claude Pro / Max subscription for the usage signal. On API-key / Bedrock / Vertex setups the signal isn't exposed, so the brake falls back to a conservative estimate (still safe, just blunt).
python3/python(preferred JSON engine for the scripts) orjqas fallback, plus bash. Tested on macOS (bash 3.2, BSDdate) and Windows/Git Bash; should work on Linux. On corporate Windows, Device Guard / WDAC may block unsignedjq.exe— the scripts use Python in that case.- Optional: GitHub Spec Kit for richer planning artifacts.
git clone https://github.com/alvarocortesdev/aperture-loop-skill.git
cd aperture-loop-skill
./install.shinstall.sh copies the skill to ~/.claude/skills/aperture/, installs the runtime scripts to
~/.claude/aperture/, and wires the statusLine + a sentinel-gated PreToolUse hook into
~/.claude/settings.json (with a backup). If you already have a custom statusLine it won't be
clobbered — pass --force-statusline to replace it, or --no-wire to install without touching settings
and wire it yourself.
One-time check. After your first message in a Claude Code session, run
bash ~/.claude/aperture/usage-probe.sh discover. It prints the real fields of your statusLine JSON. If you don't see something likerate_limits.five_hour.used_percentage, your plan/version uses different names — adjust the field paths instatusline-writer.sh+usage-probe.shand re-runinstall.sh. (The field names are the one thing that can vary between versions; everything else is robust to it.)
In Claude Code:
/aperture build out the CSV import feature and its tests
Aperture will:
- Intake — ask for the objective, constraints, deliverable format, mode (
silent= fully unattended,reachable= away but contactable), and a soft hours estimate. - Plan — produce a plan/tasks scaled to the work.
- Preflight — probe the usage signal, check permissions, surface anything that needs you now.
- Wait for your explicit "go."
- Work — loop autonomously, checkpointing to disk, stopping at the brake.
- Wind down — write the handoff + morning report, schedule a resume, then sleep.
- Resume — pick up from disk when the window reopens; convene the COUNCIL when the objective is met.
You can interrupt at any time — state is always on disk.
- Thresholds —
brake_at(default 70) andhard_at(default 82) live in each run'sSTATE.jsonunderbudget. The hard-brake hook readshard_atlive. - Autonomy ceiling —
STATE.autonomy(default: non-destructive + push to feature branches + open PRs + deploy to staging). The defaults never push to the default branch and never spend money; anything beyond the ceiling is parked for you. Tune it per run. - Output location — runs go to
./.aperture/runs/by default; override with theAPERTURE_OUTenvironment variable. - Notification — Aperture is channel-agnostic. In
reachablemode it asks The Core to notify you; wire whatever channel you like (a CLI, a webhook, a messaging integration).
- The brake hook is gated by a sentinel (
~/.claude/aperture/RUN-ACTIVE). No sentinel → the hook is a no-op. It is created at "go" and removed when the run ends, so interactive sessions are never throttled. - The hard brake only ever blocks new heavy work and tells the agent to write its handoff — it never destroys anything.
- Default autonomy is conservative: no pushes to
main/master, no paid operations, no irreversible destructive ops without a human. - Secrets are never written into journals, digests, or reports.
Review skills/aperture/references/aperture-directives.md — that's the full set of laws the agent runs
under.
.aperture/runs/<run-id>/
STATE.json # small cursor (current state, overwritten)
JOURNAL.md # append-only telegraphic event log
BRIEF.md # the intake
PREFLIGHT.md # the go/no-go checklist
HANDOFF.md # self-contained resume contract
lessons.md # what it learned (read on resume)
MORNING-REPORT.md # your human-readable deliverable
- The usage signal depends on Claude Code exposing rate-limit data to the statusLine; field names may differ by version (see the one-time check above).
- In-session resume assumes the machine stays awake and networked. The on-disk handoff makes a fresh- session resume possible too, but a fully cloud-resumed flow isn't included out of the box.
- This is a methodology skill: its quality depends on the model driving it. It encodes good defaults and guardrails; it is not a guarantee of correctness. The COUNCIL exists precisely because autonomous work needs an adversarial check.
Issues and PRs welcome. The architecture is designed to extend cheaply: keep SKILL.md a thin
dispatcher, put detail behind references/, and grow the schemas additively. See §0.1 ("scaling
contract") in the directives.
MIT.
Named after Aperture Science. Built as a personal tool and opened up in case it's useful to you. No testing tracks were harmed.