A fix found in a Claude Code session is lost when the session ends, unless something writes it down. Have Claude do the writing: procedures go to skills in ~/.claude/skills/, facts go to an Obsidian vault. The trigger is one UserPromptSubmit hook in ~/.claude/settings.json, as the Claudeception README sets it up:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/claudeception-activator.sh"
}
]
}
]
}
}
The script is a single cat heredoc. The hook injects what it prints into every prompt, so Claude reads the checklist on every turn. Claudeception is blader's work. I installed it and wired the hook.
Note: This hook was retired on 28 April 2026 because it auto-created skills from session gotchas. Gotchas now go into a one-line patterns file first and become a skill only when they recur at least twice.
Put procedures in SKILL.md files and facts in vault notes
A procedure is how to debug X or what to do when Y breaks. It goes in a SKILL.md under ~/.claude/skills/. A fact is a configuration or a decision, such as "the sales order saved search uses Amount, not Amount (Transaction Total)". It goes in the vault and gets edited when it changes.
Claude loads a skill on its own when the skill's description matches the problem, so that line lists the symptoms. The top of my LazyLibrarian skill, with the section bodies cut:
---
name: lazylibrarian-config-system
description: Fix LazyLibrarian config.ini values being silently ignored, dropped on restart, or causing unexpected behavior. Use when (1) a config value you set in config.ini has no effect after restart, (2) a [SECTION] you added to config.ini disappears on restart, ...
---
# LazyLibrarian Config System Gotchas
## Problem
...
## Context / Trigger Conditions
- You edit `config.ini` manually but the change has no effect after restart
- A config section you added (e.g., `[TORRENT]`) disappears after container restart
...
## Root Causes and Solutions
### 1. ConfigBool Requires Integer Values (`1`/`0`), NOT `True`/`False`
### 2. Config.ini is Rewritten on ANY Shutdown
...
## Verification
...
Heading 1 is wrong. ConfigBool loads with configparser.getboolean(), which accepts True, as the LazyLibrarian post shows from the source. The values that load as 0 are non-integers in ConfigInt fields.
Most extracted skills follow the template that ships with Claudeception. Its headings are Problem, Context / Trigger Conditions, Solution, Verification, Example and Notes.
The activator asks three questions after every request
The part of the heredoc that does the work, as Claude sees it:
...
EVALUATION PROTOCOL (NON-NEGOTIABLE):
1. COMPLETE the user's request first
2. EVALUATE: Ask yourself:
- Did this require non-obvious investigation or debugging?
- Was the solution something that would help in future similar situations?
- Did I discover something not immediately obvious from documentation?
3. IF YES to any question above:
ACTIVATE: Use Skill(claudeception) NOW to extract the knowledge
4. IF NO to all questions:
SKIP: No skill extraction needed
...
On a yes, the claudeception skill runs its quality checks: reusable, non-trivial, verified. It searches the existing skills for a duplicate, then writes the new one from the template. Its README says the hook gets higher activation rates than leaving the skill to description matching. Before it was retired, my copy skipped slash commands and acknowledgements under 40 characters.
Write facts back through a manifest
The update-codex skill covers the facts: an IP address that changed, a config decision, an architecture choice. It lists every change in a manifest before making any, then edits surgically. Frontmatter gets an updated date and new wiki-links get checked. In February it waited for approval after the manifest. Today it shows the manifest and proceeds:
Codex Update Manifest
─────────────────────
UPDATE 03_Resources/tools/vaultctl.md — MCP 18→14 tools
CREATE feedback_jq_in_hooks.md + MEMORY pointer — new rule
SKIP debug noise — transient
─────────────────────
Keep frontmatter flat
vaultctl reads frontmatter one level deep. Its meta get reads frontmatter[key]. Its --type and --status filters compare top-level fields. Its date normalisation only touches top-level values. A project note:
---
type: project
created: 2026-01-15
updated: 2026-02-18
status: active
tags: [netsuite, revops]
summary: Multi-tab analytics dashboard inside NetSuite
area_link: "[[02_Areas/work/my-company]]"
---
Every note gets YAML frontmatter, enforced by the 13 templates and read by the 7 dashboards. The folders are Tiago Forte's PARA (Projects, Areas, Resources, Archive), plus an inbox for notes I have not filed yet. Each note holds one concept and links related notes with [[note-name]].
00_Inbox/ : capture now, triage later
01_Projects/ : time-bound initiatives
02_Areas/ : ongoing domains (work, personal, learning)
03_Resources/ : tools, concepts, people, companies
04_Archive/ : completed/abandoned work
05_Journal/ : daily, weekly and monthly notes
_dashboards/ : 7 Dataview-powered query hubs
_templates/ : 13 Templater templates
_ai/ : AI context, handoffs and logs
dashboard-home shows recent notes, the active project count, upcoming deadlines and quick capture links. dashboard-weekly-review is a checklist for the weekly review, with Dataview queries showing the week's activity.
Read the vault through a CLI, not MCP
vaultctl replaced my Obsidian MCP server with commands that read the vault from disk and exit:
# Search notes by content, type, or status
vaultctl search "tempo plans" --type project --status active
# Rename a tag across the vault (dry run; add --yes to apply)
vaultctl tags rename status/active status/in-progress
# Check vault health
vaultctl health --check stale --days 90
# Read and update frontmatter
vaultctl meta set 01_Projects/vaultctl.md status=completed
The CLI is on npm as vaultctl. Set the vault path with vaultctl config set vault <path>, and see the config post for why an export in .zshrc is not enough. In February a 3am job on the Mac Mini checked the vault for broken wiki-links, stale notes, missing frontmatter and orphaned files. A simplified version of the job is in the Gemini CLI post. vaultctl health has a check for each of those four.
Removing the server takes more than clearing ~/.mcp.json (the removal steps). block-obsidian-mcp.sh, a PreToolUse hook with the matcher mcp__obsidian__*, blocks any call to the old server's tools.
Block destructive Bash commands with a PreToolUse hook
Claude runs in bypassPermissions mode, so the safety net is a hook. It is pre-tool-use.sh, on PreToolUse with the matcher Bash. In February it blocked five operations: force-push to main, rm -rf on source directories, DROP DATABASE, an Unraid server shutdown and docker system prune -a.