lint/test > skill > rule-line), lands it in the narrowest correct scope (project before global), and logs it to the lesson ledger. "> Lesson - Personal Agentic Workspace
Skip to content

Lesson

The skill that turns a mistake into a gate. Catches a signal that already flows past every day — the user correcting the agent — and converts it into the strongest artifact that will stop the mistake from recurring, in every repo where it applies.

Design intent: ride an existing signal (no new ritual), pay off immediately (the artifact protects the next occurrence), stay narrow by default (project before global), and let recurrence drive promotion up the enforceability ladder. See ~/.claude/lessons/ledger.md for the running record.

When to use

  • The user types /lesson (optionally with a one-line description).
  • The user corrects the agent in a way that would generalize ("you didn't run the tests before saying done", "don't force-push without asking").
  • The user says "remember not to…", "make this a rule", "so it never happens again", or "you keep doing X".
  • At the end of a turn where the agent was visibly corrected, the agent should offer /lesson (soft nudge — the explicit command is the backbone).

When NOT to use

  • One-off, context-specific corrections with no general form ("not that file, the other one") — nothing to enforce.
  • World facts about people/projects/concepts → that's the agent-wiki / PKM, not a lesson.
  • Workflow experiments where you change one variable to measure an effect — track those in your own experiment log, not here.

Procedure

Step 1 — Capture

Extract three things (ask only if not inferable from context):

  • Trigger — what the agent actually did wrong, concretely.
  • Lesson — the generalized rule, stated as an imperative.
  • Root cause — why it happened (missing check, wrong default, no procedure).

Step 2 — Route: pick the FORM (try top-down, stop at first fit)

Bias up the ladder. The test at each rung is "does it fit?", not "is it convenient?".

  1. Hook — the lesson is a deterministic condition on a tool call, shell command, or file path ("never/always X when Y"). Litmus test: could a shell script read the tool input and decide pass/fail with no judgment? If yes → hook. (e.g. "never git push --force", "don't edit generated/".)
  2. Lint rule / test — a code-level invariant in a specific repo that a linter or test can assert.
  3. Skill — a repeatable multi-step procedure that keeps getting reinvented in chat. Delegate authoring to the write-a-skill skill.
  4. Rule-line — judgment/taste that genuinely resists mechanization. The explicit fallback, not the default. Lands in a CLAUDE.md.

Step 3 — Route: pick the SCOPE (default project)

  • Project (default) — anything stack- or repo-specific. Lands in the current repo.
  • Global — only when the lesson is tool/stack-agnostic AND you have positive evidence it applies everywhere (e.g. git safety, secret handling), or it has already bitten in more than one repo. Globalizing is opt-in.

Cross-repo relevance is a trap: a lesson true in repo A is often wrong in repo B. When unsure, choose project. Promotion to global happens later, on evidence, via review mode.

Step 4 — Check the ledger before writing

Read ~/.claude/lessons/ledger.md. If a near-duplicate lesson exists:

  • Increment its hits count instead of adding a duplicate row.
  • If hits reaches 3 and the form is still rule-line, do not just bump — trigger promotion (see Review mode): propose the hook/lint/test that would make it impossible, and build it on approval.

Step 5 — Land the artifact

Form + scope Where it lands
hook · global ~/.claude/settings.json (hooks) + guard script in ~/.claude/hooks/
hook · project <repo>/.claude/settings.json + <repo>/.claude/hooks/
lint/test the repo's lint config or test suite
skill · global author via write-a-skill, install to ~/.claude/skills/<name>/
skill · project <repo>/.claude/skills/<name>/
rule-line · global ~/.claude/CLAUDE.md
rule-line · project <repo>/CLAUDE.md (or <repo>/.claude/CLAUDE.md)

Reuse before authoring: if an existing skill already encodes the lesson, install/enable it rather than writing new (e.g. force-push → the installed git-guardrails-claude-code skill).

Cursor mirror (secondary target): when the user works in Cursor too, mirror the artifact — hook → .cursor/hooks.json; rule-line → a .cursor/rules/*.mdc; skill → a .cursor/skills/<name> copy or symlink of the authored skill.

Step 6 — Append the ledger row

Always append (or update) one row in ~/.claude/lessons/ledger.md:

| date | repo | trigger | lesson | form | scope | artifact | hits |

Step 7 — Report

Print the report template below.

Hook reference (Claude Code)

A PreToolUse hook that exits with code 2 blocks the tool call and feeds its stderr back to the agent as the reason. This is the stable, judgment-free gate.

~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/no-force-push.sh" }
        ]
      }
    ]
  }
}

~/.claude/hooks/no-force-push.sh (chmod +x):

#!/usr/bin/env bash
input=$(cat)
cmd=$(printf '%s' "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))")
if printf '%s' "$cmd" | grep -qE 'git push .*--force'; then
  echo "Blocked: force-push rewrites history. Confirm with the user first." >&2
  exit 2
fi
exit 0

For file-path guards, match on Edit/Write and read tool_input.file_path. Project-scoped hooks use the same shape under <repo>/.claude/settings.json.

Review mode (/lesson review)

Run periodically once the ledger has rows. Two passes:

  • Promote — any rule-line whose hits >= 3 is escalated: propose the concrete hook/lint/test that makes the mistake impossible, build it on approval, and update the row's form/artifact. A lesson that bites three times becomes a gate.
  • Prune — flag hooks that have never fired and rule-lines that never recurred (hits == 1 and old) as deletion candidates, to keep the system from bloating into ignorable noise.

Report template

# Lesson captured

- **Trigger:** <what went wrong>
- **Lesson:** <the imperative>
- **Form:** <hook | lint/test | skill | rule-line>  (chosen because …)
- **Scope:** <project:<repo> | global>
- **Artifact:** <path written / skill installed / hook registered>
- **Ledger:** row appended (hits=N)
- **Next time:** <what now prevents recurrence>

Anti-patterns

  • Defaulting to a rule-line because it's easy — always test the higher rungs first.
  • Globalizing by default — narrow scope is the default; global needs evidence.
  • Capturing one-off corrections with no general form.
  • Adding a duplicate ledger row instead of bumping hits.
  • Writing a new skill/hook when an installed one already covers the lesson.

Compatibility

  • Primary target: Claude Code (~/.claude/). Secondary: Cursor (.cursor/).
  • Read/write inside the workspace, the current repo, and ~/.claude/.
  • No network egress. Destructive edits (settings.json, CLAUDE.md) are shown before writing.