18 KiB
SkillOpt Sleep — Claude Code self-evolving plugin (design)
Historical design proposal. This document records the June 2026 design target and includes planned controls that are not part of the current nightly CLI. It is not an installation or configuration reference. For implemented behavior, flags, defaults, and data boundaries, use
docs/sleep/README.md,docs/reference/cli.md, andplugins/README.md.
Status: approved-for-build (autonomous offline session, 2026-06-07)
Author: generated for Yifan Yang, executed autonomously while user is asleep
Branch: feat/claude-code-sleep-plugin (worktree my_repo/SkillOpt-sleep)
1. One-paragraph summary
skillopt-sleep is a Claude Code plugin that gives a user's local Claude
agent a nightly sleep cycle. While the user is offline, it (1) harvests
the day's real Claude Code session transcripts from ~/.claude, (2) mines
them into discrete task records with checkable outcomes, (3) replays /
"dreams" those tasks offline using the user's own API budget, and (4) runs
the SkillOpt optimizer loop (reflect → bounded edit → held-out gate) to
consolidate short-term experience into long-term memory (CLAUDE.md) and
skills (SKILL.md). Only changes that pass a validation gate are kept, and
every change is written to a review staging area the user approves before it
touches live config — mirroring Claude Dream's "input store is never modified"
safety contract. The result: an agent that measurably gets better at this
user's recurring work, every night, with zero model-weight training.
2. Why this is the right synthesis of the three ingredients
| Ingredient | What we take from it | Where it lives in this design |
|---|---|---|
| SkillOpt (your paper/code) | Skill = trainable text state; bounded add/delete/replace edits under a textual learning rate; held-out validation gate; rejected-edit buffer; epoch-wise slow/meta update. | The consolidate stage is a single SkillOpt epoch, reusing skillopt.optimizer.* and skillopt.evaluation.gate. |
| Claude Dreams | Async offline job: read a memory store + 1–100 session transcripts → emit a new, separate reorganized memory store (dedup / merge / resolve contradictions / surface insights). Input never mutated; output reviewed then adopted or discarded. | The harvest + consolidate-memory stages and the staging/adopt safety model are modeled directly on Dreams. |
| Agent Sleep paper (2605.26099) | Agents need periodic offline consolidation: short-term experience buffer → synthetic replay/self-generated data → self-update; "sleep" turns episodes into durable competence. | The whole nightly schedule, the replay step, and the short-term→long-term framing. |
The key novel claim this enables for the project (and a future paper section): SkillOpt's validation-gated bounded-edit optimizer is the missing "safe update rule" for Dream-style memory consolidation. Dreams reorganize memory but don't prove the reorganization helps; the Sleep paper consolidates but assumes weight updates. SkillOpt-Sleep consolidates text (memory + skills) and gates each change on replayed task performance, so nightly evolution is both weight-free and regression-protected.
3. Goals / non-goals
Goals
- A working Claude Code plugin: scheduled (nightly/cron) and user-triggered (
/sleep). - Look back over the user's real past prompts & trajectories from local
~/.clauderecords. - Offline "dream training": re-run mined tasks (mock-env or fresh retry) on the user's budget.
- Continuous evolution of memory (
CLAUDE.md) and skills (SKILL.md) via the SkillOpt gate. - A reproducible experiment that answers: does the nightly loop actually improve a held-out score?
- Safety: never silently overwrite user config; stage → user approves → adopt.
Non-goals (now)
- Codex version (explicitly deferred by user; architecture keeps it pluggable).
- Anthropic managed Dreams API integration (we emulate Dreams locally; managed API is a future backend).
- Model fine-tuning / weight updates (out of scope by design — text-only).
- Fully unattended auto-adopt by default (opt-in; default is review-gated).
4. The local data we read (verified on this machine)
- Prompt history:
~/.claude/history.jsonl— one JSON/line:{display, pastedContents, timestamp, project}. The cross-session list of every prompt the user typed, with project path + epoch-ms timestamp. - Full transcripts:
~/.claude/projects/<path-slug>/<sessionId>.jsonl— one record/line. Recordtype∈ {user,assistant,mode,permission-mode,attachment,file-history-snapshot,last-prompt,…}. User/assistant records carrymessage(role+content blocks), pluscwd,gitBranch,timestamp,sessionId,version,userType. ~215k transcripts present on this box. - Deployment targets we may evolve:
- Project memory:
<project>/CLAUDE.md(and~/.claude/CLAUDE.mdglobal). - User skills:
~/.claude/skills/<name>/SKILL.md(frontmatter:name,description, optionalallowed-tools,argument-hint). - Plugin skills under
~/.claude/plugins/....
- Project memory:
Everything stays on-disk and local; the only network calls are the LLM optimizer/replay calls the user already pays for.
5. Architecture
5.1 The nightly Sleep Cycle (stages)
┌────────────────────────── SLEEP CYCLE (one "night") ──────────────────────────┐
│ │
trigger → │ 1.HARVEST 2.MINE 3.REPLAY 4.CONSOLIDATE 5.STAGE │ → wake report
(cron or │ read ~/.claude scan sessions re-run tasks SkillOpt epoch: write to │
/sleep) │ transcripts → → task records offline (mock or reflect→edit→ .skillopt-│
│ + history w/ outcomes & fresh retry) under GATE on held-out sleep/ │
│ checkable refs current skill/mem replay split staging/ │
│ ↓ │
│ 6.ADOPT (opt-in / user-approved) │
└────────────────────────────────────────────────────────────────────────────────┘
1. Harvest (harvest.py)
Read history.jsonl + per-project transcript JSONLs for a time window
(default: since last sleep, fallback last 24–72h). Group by project (cwd /
project). Emit normalized SessionDigest objects: ordered user prompts,
assistant final texts, tool-call summary, files touched (from
file-history-snapshot), git branch, errors seen, and user-feedback signals
(e.g. "still broken", "that's wrong", "perfect", re-asks of the same thing).
2. Mine (mine.py)
Turn digests into TaskRecords — the unit the optimizer trains on. A task is a
self-contained intent (the user's request) plus an outcome label and, where
possible, a checkable reference:
- Explicit success/failure from feedback signals ("works now" after N retries → the early attempts are failures, the fix is the success exemplar).
- Self-consistency check: re-derivable answers (math, lookups) get a reference; open-ended ones get an LLM-judge rubric instead.
- Each TaskRecord:
{id, project, intent, context_excerpt, attempted_solution, outcome ∈ {success,fail,mixed}, reference_kind ∈ {exact, rubric, none}, reference, tags}. Mining is itself an LLM call (the miner), prompt-tunable, with a deterministic regex/heuristic fallback for offline/no-key runs.
3. Replay / "Dream" (replay.py)
For mined tasks, re-run the intent offline under the current skill+memory
to get a fresh trajectory & score. Two modes:
mock(default, safe): reconstruct a sandboxed prompt from the task's captured context (no live repo mutation, no network side effects) and run the target model. Deterministic, cheap, safe to run unattended.fresh(opt-in): actually re-attempt in a throwaway git worktree of the project. Higher fidelity, heavier, never touches the user's working tree. Scoring: exact-match / substring forexactrefs; LLM-judge (0–1) forrubricrefs; this yields thehard/softscores SkillOpt already expects.
4. Consolidate (consolidate.py) — this is one SkillOpt epoch
Reuse the existing optimizer pieces rather than reinventing:
reflect: partition replayed tasks into failure/success minibatches → propose add/delete/replace edits to skill and a parallel proposer for memory (CLAUDE.md). (Memory consolidation also does Dream-style dedup/merge/contradiction-resolution over existingCLAUDE.mdlines.)aggregate+rank_and_selectunder an edit budget (textual learning rate).apply_patch_with_report→ candidate skill / candidate memory.- GATE (
skillopt.evaluation.gate.evaluate_gate): replay a held-out slice of tasks with the candidate; accept only if it strictly beats current. Rejected edits go to the rejected-edit buffer (negative feedback) exactly as in the paper. - A slow/meta pass across nights (not just within one night) carries durable, cross-session lessons — the literal "short-term experience → long-term knowledge" of the Sleep paper. Per-night state persists in
~/.skillopt-sleep/state.json.
5. Stage (staging/)
Write proposed_CLAUDE.md, proposed_SKILL.md, a unified diff, and a
sleep_report.md (what changed, why, gate deltas, token cost) into
<project>/.skillopt-sleep/staging/<date>/. Nothing live is modified.
6. Adopt
/sleep adopt (or auto_adopt: true in config for power users) copies staged
files over the live CLAUDE.md / SKILL.md, after a git-style backup. This
is the only stage that mutates user-facing config, and it is explicit by default
— the Dreams "review the output, then adopt or discard" contract.
5.2 Components & boundaries (each independently testable)
skillopt/sleep/
__init__.py
types.py # SessionDigest, TaskRecord, ReplayResult, SleepConfig, SleepReport (dataclasses)
harvest.py # ~/.claude transcripts + history.jsonl -> list[SessionDigest]
mine.py # list[SessionDigest] -> list[TaskRecord] (LLM miner + heuristic fallback)
replay.py # TaskRecord + skill + memory -> ReplayResult (hard/soft) (mock | fresh)
consolidate.py # ReplayResults -> candidate skill+memory -> GATE -> accepted artifacts
memory.py # CLAUDE.md read/merge/dedup/diff (Dream-style) + protected-region markers
state.py # ~/.skillopt-sleep/state.json: last_sleep, night counter, slow/meta memory
staging.py # write/adopt staging dir, backups
cli.py # `python -m skillopt.sleep {run|status|adopt|harvest|dry-run}`
config.py # SleepConfig load/merge (defaults + ~/.skillopt-sleep/config.yaml)
optimizer_backend.py # thin: route reflect/judge to a chosen backend; mock backend for tests
skillopt-sleep-plugin/ # the Claude Code plugin surface
.claude-plugin/plugin.json
commands/sleep.md # /sleep [run|status|adopt|dry-run]
commands/sleep-status.md
skills/skillopt-sleep/SKILL.md # so Claude knows how to drive the engine
hooks/hooks.json # optional: schedule + on-session-end harvest
scripts/* # shims that call `python -m skillopt.sleep ...`
Reuse, don't fork: consolidate.py calls into existing
skillopt.optimizer.clip.rank_and_select, skillopt.gradient.aggregate.merge_patches,
skillopt.optimizer.skill.apply_patch_with_report, and
skillopt.evaluation.gate.evaluate_gate. The sleep layer is an EnvAdapter-shaped
shim over the user's own life, not a new optimizer.
5.3 Data flow (one task, end to end)
history.jsonl + <session>.jsonl
└─harvest→ SessionDigest{prompts, finals, tools, feedback}
└─mine→ TaskRecord{intent, attempted, outcome, reference}
└─replay(current skill+mem)→ ReplayResult{hard, soft, trajectory}
└─reflect→ edits(skill), edits(memory)
└─rank/clip(edit_budget)→ candidate
└─GATE(replay held-out)→ accept? → staging/ → (adopt) live CLAUDE.md/SKILL.md
6. Scheduling & triggering
- Cron/scheduled: documented
crontabline + an optional Claude Code hook; default0 3 * * *(3am local; pick an off-:00 minute in practice). The engine is a plain CLI so it works under cron, systemd-timer, or the Claude Code scheduler. - User-triggered:
/sleep run(full cycle),/sleep dry-run(harvest+mine+replay, no edits),/sleep status,/sleep adopt. - On-session-end harvest (optional hook): cheaply append the just-finished session to the night's buffer so the 3am run has fresh data without a full rescan.
7. Safety model (hard requirements)
- Never mutate live
CLAUDE.md/SKILL.mdexcept via explicitadopt(or opt-inauto_adopt). Default = staged + reviewed (Dreams contract). - Backups: every adopt snapshots the prior file to
staging/<date>/backup/. - Read-only harvest: transcripts are read, never written.
freshreplay runs only in throwaway worktrees, never the user's checkout; norm -rf, no force-push, network off unlessreplay.network: true.- Budget cap:
max_tokens_per_night+max_tasks_per_night; stop early when hit, log what was skipped (no silent truncation). - Secret hygiene: redact obvious secrets from digests before they enter prompts (reuse
_redact_*ideas from trainer). - PII/scope: only harvest projects on an allowlist (default: the project the plugin is invoked in) or
projects: allopt-in.
8. Validation experiment — "does it actually improve?"
A self-contained, deterministic-by-default experiment lives in
skillopt/sleep/experiments/ and is the acceptance test for the whole idea.
Setup: a synthetic "user persona" (e.g. researcher who keeps asking for
arXiv-id extraction in a fixed format, or programmer who keeps mis-formatting
git commit messages). We ship 12–20 tiny tasks with exact checkable
references, split into replay (train) and holdout (test).
Procedure:
- Score the holdout with an empty skill+memory →
baseline. - Run
Nsleep nights (each: replay train slice → reflect → gated edit). - Score holdout with the evolved skill+memory →
after. - Report
after − baseline, accept/reject counts, edit count, tokens.
Two backends:
mock(default, no API key, fully deterministic): a scripted optimizer that proposes the known-good rule on failure and a scripted judge. Proves the plumbing (harvest→mine→replay→gate→adopt) monotonically improves the score and the gate blocks regressions. This is the CI-able acceptance test.anthropic(opt-in, usesANTHROPIC_API_KEY): the real optimizer/judge, to demonstrate genuine lift on the persona tasks.
Success criteria:
- Mock:
after > baseline, gate rejects an injected harmful edit, adopt+backup works, re-run is reproducible. (Hard gate in CI.) - Anthropic (when run):
after ≥ baselineon holdout with ≥1 accepted, human-readable edit; documented in the wake-up report.
9. Personas (the user's framing) → concrete recurring-task families
- Programmer: commit-message conventions, repo-specific build/test commands, "always run X before Y", framework gotchas → consolidated into project
CLAUDE.md+ arepo-workflowskill. - Researcher: citation/format preferences, experiment-logging habits, paper-section style, dataset-path memory →
research-prefsskill + memory. - Finance/analyst: report formatting, recurring data-pull recipes, terminology →
report-styleskill + memory. The engine is domain-agnostic; the persona only changes which tasks get mined.
10. Phased delivery
- Phase 0 — scaffold + types + harvest (read-only, no API). Provable on this box's real
~/.claude. - Phase 1 — mine + replay(mock) + consolidate + gate + staging, with the mock optimizer backend and the deterministic experiment green. (primary deliverable of the offline session)
- Phase 2 — plugin surface (
/sleep, skill, hooks, plugin.json) wired to the CLI. - Phase 3 — real Anthropic backend for miner/reflect/judge +
freshreplay in worktrees. - Phase 4 — slow/meta cross-night memory, adopt automation, multi-project, polish + docs.
This session targets Phase 0 + Phase 1 fully, Phase 2 scaffolded, and the deterministic experiment passing, all committed (not pushed) for review.
11. Open questions for the user (answer when awake)
- Adopt policy: keep default review-gated, or do you want
auto_adoptfor your own machine? - Scope: harvest only the invoked project, or all projects in
~/.claude/projects? - Real-API demo: want me to spend live
ANTHROPIC_API_KEYbudget on the persona demo, or keep everything mock until you say go? - Skill target: evolve a new dedicated
skillopt-sleep-managed skill, or also edit your existing hand-written skills in~/.claude/skills? - Paper: should this become a section/figure in the SkillOpt arXiv (Dream+Sleep framing as "deployment-time continual skill optimization")?