17 KiB
SkillOpt Sleep — Claude Code self-evolving plugin (design)
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")?