A self-improving harness for Claude Code. It watches your coding sessions across opted-in projects, extracts lessons learned from what actually happened, and turns recurring lessons into concrete improvement proposals for your skills and settings — which you approve or reject. Over time, Claude gets better at working with you, and the harness measures whether each change actually helped.
Think of it as a feedback loop with four stages:
- Collect — when a session ends in a linked project, the transcript is digested and a small model extracts lesson candidates. Every candidate must cite a verbatim quote from the session as evidence; opinions without an observed outcome are dropped.
- Curate — an autonomous curation pass merges candidates into a deduplicated corpus of lessons, then drafts skill revisions or new-skill proposals when the numbers justify them.
- Review — nothing touches your live skills or settings without your
explicit approval in
/hub-review. Rejections are captured as feedback lessons, so the harness also learns what not to propose. - Measure — every injection, success, failure, and skill friction is logged as a timestamped event. Applied changes get a measurement window and a verdict (improved / regressed / inconclusive) before further changes to the same skill are allowed.
| Term | Meaning |
|---|---|
| Lesson | One markdown file capturing a single reusable fact ("when X, do Y"), with counters for how often it was injected, helped, or harmed. |
| Skill | A reusable instruction file (SKILL.md) that Claude Code loads. The hub manages the skills under its own skills/ directory. |
| Friction | Evidence that a skill misbehaved: a user correction, an error, an override, or a miss (the skill should have fired but did not). |
| Proposal | A drafted change (skill revision, new skill, deprecation, or settings patch) waiting under proposals/ for human review. |
| Reflector | The cheap, single-turn model call that turns a session digest into lesson candidates and scores. |
| Curator | The autonomous consolidation run (/hub-consolidate auto) — the only writer of the lesson corpus. |
SessionEnd hook (opt-in projects only)
└─ capture.sh ── nohup ──▶ extract.sh
│ ├─ digest: text + [skill-invoked:] + [tool-error:] markers (≤10)
│ ├─ INJECTED LESSONS block → evidence-gated helped/harmed scoring
│ ├─ reflector (haiku, 1 turn) → inbox candidates + typed frictions
│ ├─ events: helped/harmed (with project) / skill-used / skill-friction
│ └─ auto-consolidate.sh … inbox ≥ 5 & cooldown → spawn ─────────┐
SessionStart hook ─ inject.sh │
├─ lesson digest (≤15 lessons AND ≤3,200 bytes) + pending-work notes │
└─ daily catch-up: first session of the day spawns a run (weekly:deep) ┤
▼
claude -p "/hub-consolidate auto|deep" (autonomous, propose-only)
lock → sweep → fold counters (skill-stats fold-plan deltas)
→ ADD/UPDATE/MERGE/NOOP lessons
→ revision/promotion drafts (skill-stats decisions, scope-locked,
revise-first routing, linted) → build index → commit
→ macOS notification (delta + daily cap)
▼
Human: /hub-review — approve/reject each proposal
apply + symlink + applied.log baseline · record matured verdicts
rejections → feedback lessons
/hub-status: pipeline health, friction trends, context budget
| Command | What it does |
|---|---|
/hub-link / /hub-unlink |
Opt the current project in / out of collection. Nothing is collected by default. |
/hub-review |
The human gate. Review pending proposals one by one, approve or reject. This is the only routine command you need — run it when a notification arrives. |
/hub-status |
Health report: autonomous-loop state, friction trends, corpus stats, context budget, kill switches. |
/hub-consolidate |
Run curation manually (it normally runs itself; use this when you don't want to wait). |
/retro |
Manual in-session reflection — a backup for when the session-end hook can't fire. |
/hub-import |
One-time migration of existing auto-memory files into the pipeline. |
You never need to poll. Two channels surface pending work automatically:
- macOS notification after each autonomous curation run that produced proposals (delta-based, with a daily cap so repeats don't nag you).
- Session-start note — every session in a linked project shows
NOTE: N proposal(s) awaiting /hub-reviewin its injected digest.
Every skill's description is loaded into every session, so an ever-growing skill inventory would burn context window and dilute trigger precision. The harness is therefore biased toward strengthening existing skills, and several deterministic guards enforce that bias:
- Numbers come from a tool, not the model.
bin/skill-stats.shcomputes friction windows, promotion eligibility, drift, and fold deltas, and prints a decision column (TRIGGER-*,PROMOTE-ELIGIBLE,SKIP-open-verdict). The curator consumes rulings; it never recounts raw event logs. - Scope lock. A skill revision may change how the skill executes, but
never when it fires or what it may touch:
bin/skill-lint.sh --revisionfails unlessname,description,allowed-tools, anddisable-model-invocationare byte-identical to the live skill. The one exception is a trigger-fix revision driven bymissfrictions, which may change the description (and only that). Any other scope change must be made by the human at/hub-review. - Misses get their own clock. A skill that never fires produces no usage events, so miss frictions are counted against recent sessions instead of recent uses — chronically missing skills can still be repaired.
- Revise-first routing. When a lesson qualifies for promotion, the
curator first checks (via
bin/skill-overlap.sh) whether an existing skill could host the rule as an enrichment revision. A brand-new skill is allowed only when no existing skill fits, and its proposal must include an overlap analysis of the three nearest skills. - Skill budget (one-in-one-out). At
HUB_SKILL_BUDGET(default 10) hub-managed skills, new-skill proposals fail lint unless a deprecation proposal is bundled. - Garbage collection. The weekly deep pass flags promotion-born skills
with zero uses in
SKILL_GC_DAYS(default 28) and drafts deprecation proposals — applied, like everything else, only through/hub-review. - Context budget. Injection is capped at 15 lessons and 3,200 bytes
(~800 tokens);
skill-stats.sh budgetmonitors total description bytes across all installed skills and warns when they grow past the threshold.
Events are single-line, timestamped, and written only by
bin/score-event.sh (arguments are whitespace-sanitized so fields never
shift):
<ISO8601Z> injected <session> <id,id,...>
<ISO8601Z> helped|harmed <lesson-id> <session> <project>
<ISO8601Z> skill-used <skill> <session>
<ISO8601Z> skill-friction <skill> <correction|error|override|miss> <session>
When a proposal is applied, /hub-review records a baseline line in
state/applied.log. While that entry has no verdict, further revisions of
the same skill are blocked (SKIP-open-verdict) to keep the measurement
clean. Once the skill has ≥10 post-apply uses or 30 days have passed,
/hub-status reports a verdict — improved, regressed, or inconclusive
(the default; no causal claims) — and /hub-review records it, unblocking
the skill. A regression produces a revert proposal, never an automatic
revert.
- Propose-only autonomy: during autonomous runs, a PreToolUse guard
denies writes outside
lessons/,proposals/, andstate/, and the curator's shell access is limited to a fixed set of validating helper scripts (no rawgit/rm). - Kill switches:
touch state/DISABLEDstops all automation (delete the file to resume);state/FROZEN— written automatically when drift monitoring detects three consecutive worsening weeks — stops proposal generation only. Only a human removes either. - Human gate: skills, settings, and agents change only through
/hub-reviewapproval, and every apply commit is a one-command rollback (git revert <sha>).
- Only the consolidate run writes
lessons/active/(single writer); the index is regenerated only bybin/build-index.sh. - Delta updates only — one lesson per file, never bulk rewrites.
- Events are append-only; folding archives raw lines to
state/events-archive.log, never destroys them. - The user-global
~/.claude/CLAUDE.mdis never touched. lessons/doubles as a standalone Obsidian vault (.obsidian/is gitignored).
bin/install-hooks.sh # record a baseline + merge hooks into ~/.claude/settings.json (with backup)
bin/sync.sh # symlink skills/ and agents/ into ~/.claude
# then, inside each project you want collected:
# /hub-linkEmergency stop: touch state/DISABLED (delete to resume).