knowledge-architecture
Knowledge management for Claude Code projects. Use when: creating skills or agents, deciding where to put new knowledge (skill vs CLAUDE.md vs reference-data vs memory vs agent), routing information, CLAUDE.md hygiene, editing/modifying CLAUDE.md or skills or agents or memory files, extracting content from bloated files, or any discussion about knowledge architecture, documentation structure, or SOP placement. MUST be invoked BEFORE proposing any edits to CLAUDE.md, skills, agents, or memory - no exceptions.
SKILL.md
| Name | knowledge-architecture |
| Description | Knowledge management for Claude Code projects. Use when: creating skills or agents, deciding where to put new knowledge (skill vs CLAUDE.md vs reference-data vs memory vs agent), routing information, CLAUDE.md hygiene, editing/modifying CLAUDE.md or skills or agents or memory files, extracting content from bloated files, or any discussion about knowledge architecture, documentation structure, or SOP placement. MUST be invoked BEFORE proposing any edits to CLAUDE.md, skills, agents, or memory - no exceptions. |
name: knowledge-architecture description: Knowledge management for Claude Code projects. Use when: creating skills or agents, deciding where to put new knowledge (skill vs CLAUDE.md vs reference-data vs memory vs agent), routing information, CLAUDE.md hygiene, editing/modifying CLAUDE.md or skills or agents or memory files, extracting content from bloated files, or any discussion about knowledge architecture, documentation structure, or SOP placement. MUST be invoked BEFORE proposing any edits to CLAUDE.md, skills, agents, or memory - no exceptions. user-invocable: false
Claude Code Knowledge Management
Part 1: Where Does Knowledge Go?
Evaluate in order. First match wins.
0. BEHAVIORAL FIX FOR EXISTING AGENT/SKILL (agent outputs bad data, skill misses a check):
โ Update the agent/skill definition directly โ it owns its own behavior rules
โ SSoT: don't scatter fixes across CLAUDE.md, memory, or caller prompts
โ Poka-yoke: fix at the source so ALL callers benefit, not just the one who hit the bug
1. TABULAR/REGISTRY DATA (URLs, IDs, contacts, rosters):
โ Used by ONE skill: .claude/skills/{skill}/references/*.md
โ Used repo-wide: reference-data/*.md
2. SITUATIONAL PERSONAL DATA (address, ID, rarely-needed info):
โ Create pointer registry (reference-data/personal-data-sources.md)
โ Registry contains POINTERS to sources (Keep notes, sheets), not data
โ CLAUDE.md has one-liner: "check registry for personal info"
โ PA looks up dynamically when needed, doesn't cache in context
โ Pattern: reference-data/{type}-sources.md with table of pointers
3. NEEDS ISOLATED CONTEXT (heavy processing, browser automation):
โ Propose agent to user, await approval
โ If rejected: create skill instead
4. DOMAIN KNOWLEDGE (procedures, workflows, explanations):
โ .claude/skills/{name}/SKILL.md
โ This is the default for most content
5. SESSION-LEARNED LESSON (mistake pattern, environment quirk, workaround):
โ Auto memory: ~/.claude/projects/{project}/memory/MEMORY.md (or topic files in same dir)
โ MEMORY.md is always loaded into system prompt โ keep under 200 lines
โ Use for: lessons that DON'T belong to any skill/agent but affect behavior across sessions
โ Do NOT use for: behavioral fixes to existing skills/agents (rule 0), procedures (rule 4), or data (rule 1)
โ Anti-pattern: duplicating a rule that already exists in a skill/hook/CLAUDE.md โ memory is NOT for reinforcement of existing rules
6. ONE-LINER CROSS-CUTTING RULE:
โ CLAUDE.md (only if truly 1-2 lines)
7. UNCLEAR:
โ Ask user
Pointer Registry Pattern (for situational data): When user provides info that PA needs occasionally but shouldn't bloat every session:
- Ask: "Should I add this to a source registry for future lookup?"
- Create/update
reference-data/{domain}-sources.md - Store POINTER (source location), not actual data
- Add one-liner to CLAUDE.md: "For X, check reference-data/{domain}-sources.md"
- PA dynamically looks up only when needed
Part 2: CLAUDE.md Hygiene
CLAUDE.md bloats fast. Rules:
- One-liners only - >2-3 lines = extract to skill
- Pointers not content - "See skill X" not the procedure
- No procedures - All procedures โ skills
- Skills are default - When in doubt, create skill
- Minimal edits - Modify existing or remove violations OK; adding = max 1 line
- Agents need approval - Never auto-create
Part 3: Skill Structure
.claude/skills/{name}/
โโโ SKILL.md โ Required (exact filename)
โ โโโ YAML frontmatter โ Required: name + description
โ โโโ Markdown body โ Instructions
โโโ references/ โ Docs loaded on demand
โโโ scripts/ โ Executable code
โโโ assets/ โ Templates, files for output
Critical:
- Path must be
.claude/skills/{name}/SKILL.md - Frontmatter
descriptiondetermines when skill triggers - be comprehensive - Body only loads AFTER skill triggers
.claude/must NOT be gitignored
Part 4: Frontmatter
Official reference: https://code.claude.com/docs/en/skills
Standard template (use for ALL skills):
---
name: skill-name
description: What it does AND when to use it. This is critical - Claude uses it to decide when to apply the skill.
user-invocable: false
---
Complete field reference
| Field | Required | Default | Description |
|---|---|---|---|
name | No | directory name | Display name. Lowercase, numbers, hyphens only (max 64 chars). |
description | Recommended | first paragraph of body | WHAT + WHEN. Claude uses this to decide when to apply the skill. |
user-invocable | Yes (our standard) | true | Set to false on ALL skills. Our skills are background knowledge, not user-invocable commands. |
disable-model-invocation | No | false | Set true to prevent Claude from auto-loading. Use for side-effect workflows user must trigger manually. |
allowed-tools | No | conversation permissions | Tools Claude can use without per-use approval when skill is active. String or array. |
model | No | inherits | Model override (sonnet, opus, haiku). |
context | No | inline | Set to fork to run in a forked subagent context (isolated context window). |
agent | No | general-purpose | Which subagent type when context: fork is set. Built-in: Explore, Plan, general-purpose, or custom from .claude/agents/. |
argument-hint | No | none | Hint for autocomplete, e.g. [issue-number]. |
hooks | No | none | Hooks scoped to this skill's lifecycle. |
Invocation matrix (how fields interact):
| Frontmatter | User can /invoke | Claude auto-loads | Context loading |
|---|---|---|---|
| (default) | Yes | Yes | Description always in context, body loads on invoke |
disable-model-invocation: true | Yes | No | Description NOT in context |
user-invocable: false | No | Yes | Description always in context, body loads on invoke |
String substitutions available in skill body:
$ARGUMENTSโ all arguments passed on invocation$ARGUMENTS[N]or$Nโ specific argument by 0-based index${CLAUDE_SESSION_ID}โ current session ID!`command`โ dynamic context injection (shell command runs before skill content is sent)
Our standard: user-invocable: false
ALL skills MUST include user-invocable: false. Our skills are model-invoked background knowledge (Claude decides when to load them based on description). Only slash commands in .claude/commands/ are user-invocable. Omitting this field defaults to true, which pollutes the / menu with non-actionable entries.
When to propose context: fork
context: fork runs the skill in an isolated subagent context. The skill body becomes the subagent's task prompt โ it won't have conversation history.
Propose context: fork when:
- Skill involves heavy browser automation or screenshot processing
- Skill processes large data (parsing spreadsheets, logs, transcripts)
- Skill runs multi-step workflows that could bloat parent context
- Skill is frequently delegated to Task tool subagents anyway
- Skill has explicit task instructions (not just guidelines/conventions)
Do NOT use when:
- Skill is lightweight (lookup, simple procedure, reference)
- Skill needs to read/write parent conversation state
- Skill is a quick decision tree or routing logic
- Skill contains conventions/guidelines without an actionable task (subagent gets guidelines but no task = useless)
Workflow: When creating/editing a skill and context: fork seems beneficial, propose to user: "This skill involves [heavy processing/browser automation/etc.] โ recommend adding context: fork so it runs in an isolated context. Approve?" Do NOT add silently. If proposing fork, also suggest an appropriate agent: value.
Troubleshooting Misbehaving Skills
If a skill is not triggering, triggers too often, or behaves unexpectedly, fetch the troubleshooting section from https://code.claude.com/docs/en/skills for current diagnostic steps. Key issues:
| Symptom | Likely Cause | Fix |
|---|---|---|
| Skill not triggering | Description missing keywords users naturally say | Rephrase description with concrete trigger phrases |
| Skill triggers too often | Description too broad | Narrow description; consider disable-model-invocation: true |
| Claude doesn't see all skills | Too many skill descriptions exceed character budget (default 15k) | Run /context to check; set SLASH_COMMAND_TOOL_CHAR_BUDGET env var to increase |
| Fork skill returns empty | Skill has guidelines but no actionable task | context: fork needs explicit task instructions, not just conventions |
Always fetch the live URL โ troubleshooting guidance may be updated upstream.
Part 5: Writing Guidelines
- Imperative form - "Create documents" not "This skill creates"
- Description is trigger - Include all contexts when skill should activate
- Don't repeat - Info in SKILL.md OR references, not both
- Concise - Claude is smart; only add context it lacks
- Compliance reporting - After creating/editing a skill, just say "โ Compliant with knowledge-architecture" โ don't enumerate every rule that was followed
- Deterministic output format - When a skill or SOP produces user-facing output (reports, tables, status updates), define the EXACT format in the skill body. Include:
- A template/example showing the structure
- Emoji legends for visual scanning (e.g., ๐ป = code, ๐ = docs, โ = done, โ ๏ธ = attention)
- Column names, row ordering, section order
- This ensures identical formatting across sessions โ user builds muscle memory for scanning output. Ad-hoc formatting = cognitive overhead every time.
Part 5b: Reference Doc Types & Three-Level Hierarchy
references/ files serve two distinct purposes. Never mix them in one file.
| Type | Purpose | SSoT rule |
|---|---|---|
| Operational | Registry data, design rationale for runtime choices | Supplements the skill; skill body is SSoT for behavior |
| Re-engineering | Change impact, contracts, extension checklists for modifying the framework | Must NOT re-describe what skills already define โ only meta-guidance for maintainers |
If a re-engineering guide drifts from the skills, the skills win. The guide is a convenience layer, never a source of truth for behavior.
Three Levels of Context Engineering
When skills form a framework (e.g., autonomous issue dispatch with 5+ interconnected skills):
Level 1: Operational skills (SKILL.md) โ SSoT for behavior
Level 2: Re-engineering guides (references/) โ how to safely modify Level 1; must not duplicate it
Level 3: This skill (knowledge-architecture) โ governs how Levels 1 and 2 are structured
When creating a re-engineering guide: It must add value beyond reading the skills sequentially โ change impact analysis, interface contracts, extension checklists. Content that's already true by reading the skills doesn't belong in the guide.
Part 6: Size Management
| Component | Guideline | Loaded |
|---|---|---|
| SKILL.md body | ~500 lines | When skill triggers |
| references/ | Unlimited | On demand |
| scripts/ | Unlimited | Executed, not loaded |
If >500 lines: keep core workflow in SKILL.md, move details to references/.
Part 7: Global vs Local
| Location | Scope |
|---|---|
~/.claude/skills/ | All projects |
.claude/skills/ (repo) | This project only |
Part 8: Post-Creation
- Frontmatter has
name:,description:, anduser-invocable: false - Description explains WHAT + WHEN comprehensively
- Located at
.claude/skills/{name}/SKILL.md - Add to
.claude/settings.json:"Skill(name)"inpermissions.allow - Register in CLAUDE.md skills index (local skills only)
- Evaluate whether
context: forkis appropriate โ if yes, propose to user - ๐งช Evaluate whether a reminder hook is appropriate (see Part 12) โ if Claude repeatedly forgets to follow this skill and it maps to a matchable action, create a PostToolUse hook and log it on the tracking issue
Part 9: No Subagent Delegation for Context Engineering
NEVER delegate context engineering changes to subagents (developer, general-purpose, etc.). This includes edits to CLAUDE.md, skills, agents, and memory files.
Why: Context engineering changes encode the owner's intent and philosophy from the current conversation. Subagents lack conversation history and can only work from the prompt given โ nuances discussed with the owner are silently lost. A broken SOP degrades all future sessions, making this higher-stakes than application code.
Do it yourself, even when it's slow. PreToolUse hooks that block SKILL.md edits require retries โ that's annoying but the cost is mechanical (extra tool calls), not intellectual. The alternative (subagent) risks semantic drift.
Exception: None. If the edit volume is too high for one session, split across sessions rather than delegate.
Part 10: Agents (Require Approval)
Never auto-create agents. Protocol:
- Identify candidate (needs isolated context, heavy processing)
- Propose: "This could be an agent because [X]. Create agent?"
- User approves โ
.claude/agents/*.md - User rejects โ Create skill instead
Part 11: Search Both Locations
When searching for agents or skills, ALWAYS check both:
| Type | Local (project) | Global (user home) |
|---|---|---|
| Skills | .claude/skills/ | ~/.claude/skills/ |
| Agents | .claude/agents/ | ~/.claude/agents/ |
Never assume "not found" after checking only one location.
Part 12: Auto-Fix Rule
When this skill is invoked to review an edit that already happened (post-hoc), do NOT ask the user whether to fix violations. Fix them immediately, then report:
- What was violated
- What was fixed
- What was learned
Asking "want me to fix this?" after reading a skill that clearly states the rules wastes the user's time. The rules are unambiguous โ just apply them.
Pre-Edit Root Cause Check
๐จ Before editing any skill, ask: "Did I actually invoke/follow this skill during the task that revealed the gap?"
- If NO โ the edit alone won't prevent recurrence. The root cause is that the skill wasn't loaded, not that it was incomplete. After making the edit, also evaluate:
- Why wasn't the skill triggered? (description too narrow? no hook? wrong trigger phrases?)
- What would have caught it? (add hook per Part 12? broaden description? add trigger to CLAUDE.md skill index?)
- Implement the prevention โ don't just fix content and move on
- If YES โ the skill was loaded but insufficient. Content edit is the correct fix.
Part 13: Skill Reminder Hooks (๐งช Experimental)
Tracking issue: https://github.com/<GITHUB_USER>/PersonalAssistant-ClaudeCode/issues/23 Status: Evaluating until 2026-02-02. When implementing this pattern, comment on the issue with where you applied it (repo, skill name, hook matcher) so we can track all implementations and roll back if needed.
Problem
Claude frequently forgets to load relevant skills before/during actions. CLAUDE.md instructions are suggestions; hooks are deterministic guarantees.
Pattern: PostToolUse Reminder Hook
A PostToolUse hook fires after every tool call matching a pattern. By exiting with code 2 and writing to stderr, the message is fed directly to Claude as feedback (not just shown to the user). This forces Claude to acknowledge and follow the skill.
How It Works
Claude edits file โ PostToolUse hook fires โ script checks file type โ
If match: exit 2 + stderr reminder โ Claude receives feedback, loads skill
If no match: exit 0 โ silent, no overhead
Implementation Steps
When creating or modifying a skill that Claude frequently forgets to follow:
- Create the hook script in the skill's
scripts/folder:
#!/usr/bin/env python3
"""PostToolUse reminder hook for {skill-name}."""
import json, sys
try:
data = json.load(sys.stdin)
except (json.JSONDecodeError, EOFError):
sys.exit(0) # exit 0 = silent pass-through (no input, not our concern)
file_path = data.get("tool_input", {}).get("file_path", "")
# Adjust condition to match the skill's domain
if not file_path.lower().endswith(".md"):
sys.exit(0) # exit 0 = not a match, pass silently
content = (
"You modified a file relevant to {skill-name}. "
"Follow the {skill-name} skill (~/.claude/skills/{name}/SKILL.md)."
)
# ASCII-only border (Unicode crashes on Windows cp1252)
border = "=" * 70
msg = f"\n+{border}\n| HOOK OUTPUT\n+{border}\n\n{content}\n\n+{border}\n"
print(msg, file=sys.stderr)
sys.exit(2) # exit 2 = feedback to Claude (MUST be 2, not 0)
- Register the hook in
~/.claude/settings.json(global) or.claude/settings.json(project):
๐จ Cross-platform compatibility: Use python -c with os.path.expanduser('~') to resolve the home directory. $HOME doesn't expand on Windows, and hardcoded paths aren't portable. Only $CLAUDE_PROJECT_DIR is reliably injected by Claude Code (for project-scoped hooks only).
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [{
"type": "command",
"command": "python -c \"import os;exec(open(os.path.expanduser('~/.claude/skills/{name}/scripts/postToolUse-reminder.py')).read())\"",
"timeout": 5
}]
}
]
}
}
- Comment on the tracking issue (https://github.com/<GITHUB_USER>/PersonalAssistant-ClaudeCode/issues/23) with:
- Skill name and path
- Hook matcher used
- File type / condition that triggers the reminder
Hook Event Reference
| Event | When | Use For |
|---|---|---|
PostToolUse | After tool succeeds | Remind after file edits (most common) |
PreToolUse | Before tool runs | Block/warn before dangerous actions |
Stop | Claude finishes responding | Verify all rules were followed |
SubagentStop | Subagent finishes | Validate subagent output quality |
SessionStart | Session begins | Inject context (schedule, state) |
Matchers: Tool name patterns โ Edit|Write|MultiEdit, Bash, mcp__gmail__send_email, * (all), etc.
Exit codes: 0 = silent, 2 = feedback to Claude (stderr), other = non-blocking error.
๐จ Hook stdin input varies by event type:
| Event | stdin fields | How to get transcript data |
|---|---|---|
PreToolUse | tool_name, tool_input | N/A |
PostToolUse | tool_name, tool_input, tool_output | N/A |
Stop | session_id, transcript_path, cwd, stop_hook_active | Parse JSONL file at transcript_path |
Stop hooks do NOT receive transcript_summary or tools_used. Must read and parse transcript_path (JSONL, one JSON object per line, entries have type: "assistant" with content blocks containing tool_use and text).
Official docs: https://code.claude.com/docs/en/hooks
Pattern: PreToolUse Blocking Hook
A PreToolUse hook fires before tool execution. Exit code 2 blocks the tool call entirely and feeds the stderr message to Claude. Use this to permanently blacklist dangerous/faulty tool variants.
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__dangerous__tool_name",
"hooks": [{
"type": "command",
"command": "echo 'BLOCKED: Use mcp__safe__tool_name instead. Reason: [why].' && exit 2",
"timeout": 5
}]
}
]
}
}
When to use blocking hooks:
- Two MCP tools overlap in function but one is broken/unsafe
- A tool silently fails (no error, wrong behavior) โ worse than crashing
- The correct alternative is known and deterministic
When NOT to use:
- Tool works but needs guardrails โ use reminder hook instead
- Tool is the only option โ fix the tool, don't block it
Pattern: Poka-Yoke Blocking Hook (Block + Bypass Flag)
A poka-yoke (mistake-proofing) hook blocks a dangerous action by default but provides a bypass flag that Claude can use after verifying the action is genuinely correct. This creates a two-step flow:
- Claude attempts the action โ hook blocks it with rejection reason + bypass instructions
- Claude evaluates: is there a better approach (API, ORM, etc.)?
- Yes โ use the proper approach (hook achieved its goal)
- No (direct action is genuinely required) โ retry with bypass flag in the command
Key design principles:
- Block is the default โ unsafe action never runs without conscious acknowledgment
- Bypass flag is embedded in the command โ Claude must modify the command to include it, forcing deliberate intent
- Rejection message teaches โ explains WHY it's blocked and WHAT the preferred alternative is
- No user interaction needed โ Claude self-corrects without bothering the user
Implementation:
# In the hook script:
BYPASS_FLAG = "--confirmed-reason"
# Check for bypass flag in command
if BYPASS_FLAG in command:
sys.exit(0) # Allow through
# Block with instructions
content = (
"BLOCKED: [dangerous action] detected.\n"
"PREFERRED: Use [safe alternative] instead.\n"
f"IF [safe alternative] is unavailable: re-run with {BYPASS_FLAG} flag.\n"
"Example: command /* {BYPASS_FLAG} */ args..."
)
print(content, file=sys.stderr)
sys.exit(2)
When to use poka-yoke (vs hard block vs reminder):
| Pattern | When | Claude can proceed? |
|---|---|---|
| Hard block | Action is NEVER correct (e.g., drizzle-kit push) | No โ must use alternative |
| Poka-yoke | Action is usually wrong but sometimes correct (e.g., raw SQL when API unavailable) | Yes โ with bypass flag |
| Reminder | Action is correct but needs guardrails (e.g., check skill before editing) | Yes โ always proceeds |
Existing implementations:
| Hook | Bypass Flag | Blocks | Preferred Alternative |
|---|---|---|---|
preToolUse-block-raw-sql-writes.py | --sql-write-confirmed | psql UPDATE/INSERT/DELETE/ALTER/DROP/TRUNCATE | Admin API endpoints |
Testing Hooks (Subprocess Verification)
๐จ Hook changes in settings.json only apply to NEW Claude Code sessions. The current session uses the settings loaded at startup.
To verify a hook works without restarting your session:
# Safe test โ spawns a fresh CC instance that loads new settings
# Use --print flag to run non-interactively (prints output, exits)
claude --print "Try calling mcp__google-workspace__gmail_send to <YOUR_EMAIL> with subject 'Hook verification test' and body 'This should be blocked by PreToolUse hook.'" --allowedTools "mcp__google-workspace__gmail_send,mcp__google-workspace__gmail_*"
Safety rules for subprocess hook testing:
- ONLY test blocking hooks โ never test reminder hooks this way (they don't block, so the action executes)
- ONLY use self-targeted test data โ send to user's own email, use test subjects
- NEVER test hooks that involve destructive actions โ if the hook fails, the action runs
- Expected result: The subprocess should report the hook blocked the tool. If the tool executes, the hook is misconfigured.
- If test fails (tool executes): Check matcher pattern, exit code (must be 2), and that settings.json is valid JSON
Hook Pitfalls (MANDATORY checklist)
Before deploying any hook script:
- Exit codes โ
sys.exit(0)= silent pass,sys.exit(2)= feedback to Claude. The FINAL exit must be2for the hook to actually work. Using0makes the hook a silent no-op. - ASCII only in .py source โ
exec(open(...).read())on Windows uses cp1252 encoding. Unicode box-drawing (โโโโ โฃโโ) and emojis crash the script silently before any code executes. Use ASCII borders only:=,+,|. - QA in fresh instance โ After creating or modifying any hook, test in a fresh
claude --printinstance (see "Testing Hooks" section). Current session caches settings at startup. Never consider a hook change done without end-to-end verification. Test ALL tool variants in the matcher โ if matcher covers 5 tools, test all 5, not just one.
When to Add a Reminder Hook
Add a hook when ALL of these are true:
- Claude repeatedly forgets to follow the skill (proven pattern, not hypothetical)
- The skill applies to a matchable action (file type, tool name, MCP call)
- The skill is corrective/preventive (rules to enforce, not just reference info)
Do NOT add hooks for:
- Informational skills (lookup, reference data)
- Skills that already trigger reliably via description
- One-off workflows the user explicitly invokes
Existing Implementations
| Skill | Matcher | Condition | Script |
|---|---|---|---|
| knowledge-architecture (reminder) | Edit|Write|MultiEdit | *.md files OR files inside .claude/skills//.claude/agents/ dirs (PostToolUse) | ~/.claude/skills/knowledge-architecture/scripts/postToolUse-reminder.py |
| knowledge-architecture (blocker) | Edit|Write|MultiEdit | BLOCKS CLAUDE.MD and SKILL.MD (PreToolUse) | ~/.claude/skills/knowledge-architecture/scripts/preToolUse-block-knowledge-files.py |
| message-drafting | mcp__google-workspace__gmail_send|..gmail_createDraft|..gmail_sendDraft|mcp__gmail__send_email|..draft_email | Any gmail send/draft call | .claude/skills/message-drafting/scripts/preToolUse-reminder.py |
| email-verification | Bash|mcp__google-workspace__gmail_send|..gmail_createDraft|..gmail_sendDraft|mcp__gmail__send_email|..draft_email | Bash with mail.google.com/mailto: + all gmail send/draft | .claude/skills/message-drafting/scripts/preToolUse-email-verification.py |
| gmail-send-blocker | mcp__google-workspace__gmail_send|mcp__google-workspace__gmail_sendDraft | DENY LIST โ tools don't exist in current MCP but denied as insurance. Real protection: gmail-agent definition enforces mcp__gmail__send_email + from param | deny list in ~/.claude/settings.json |
| db-safety (prod bypass) | Write | BLOCKS writing .ts/.js with prod DB hostname + raw DB client | ~/.claude/skills/db-safety/scripts/preToolUse-block-prod-bypass.py |
| db-safety (raw SQL writes) | Bash | POKA-YOKE โ blocks psql write ops, bypass: --sql-write-confirmed | ~/.claude/skills/db-safety/scripts/preToolUse-block-raw-sql-writes.py |
Part 16: Skill Inheritance (OOP Pattern)
Skills can inherit from a base skill, like OOP class inheritance. The child inherits all principles from the base and declares only its additions and overrides.
Pattern:
base-skill (defines shared principles)
โโโ child-skill-A (inherits + overrides item naming)
โโโ child-skill-B (inherits + adds domain sections)
โโโ [future children]
How to declare inheritance:
In the child skill's output format section, add:
> **Inherits:** {base-skill-name}
**Overrides:**
- {base section}: {what changes}
**Additions:**
- {new section not in base}
Rules:
- Child MUST NOT duplicate base content โ reference it, don't copy it
- If child doesn't mention a base principle, it applies as-is (implicit inheritance)
- Overrides MUST be explicit โ state what you're replacing and why
- Base skill is the SSoT โ if child contradicts base without declaring an override, base wins
- When loading a child skill, Claude SHOULD also load the base to understand the full contract
Current inheritance tree:
report-format (base)
โโโ audit-common (overrides: C1/W3/I5 IDs; adds: registry logging, drift protocol)
โโโ task-triage (overrides: adds confidence %, incident registry; adds: platform-specific sections)
Common Mistakes
| Mistake | Fix |
|---|---|
Flat file skills/foo.md | Move to .claude/skills/foo/SKILL.md |
| Missing frontmatter | Add name:, description:, and user-invocable: false |
Missing user-invocable: false | Add it โ skills are never user-invocable |
| Vague description | Describe WHAT + WHEN comprehensively |
| Body >500 lines | Move details to references/ |
| Procedure in CLAUDE.md | Extract to skill |
| Multi-paragraph in CLAUDE.md | Extract to skill |
| Searched only local for agent/skill | Check both local .claude/ AND global ~/.claude/ |
| Asked user "should I fix?" after skill showed clear violation | Just fix it and report what was done |
| Created reminder hook without logging on tracking issue | Comment on https://github.com/<GITHUB_USER>/PersonalAssistant-ClaudeCode/issues/23 with skill name, matcher, and condition |
| Added hook for skill that triggers reliably already | Only add hooks for skills Claude repeatedly forgets โ don't over-hook |
Part 14: Stop Hook Self-Documentation (๐งช Experimental)
Status: Experimental. Only verified by code review, not real-world usage. Evaluate before relying on this pattern.
Problem
When Stop hooks fire for edge cases (temp scripts, non-project files), Claude often wastes user attention by explaining why the hook is irrelevant. The user already knows โ they don't need commentary.
Solution: Self-Explanatory Hook Messages
Stop hooks should include instructions for Claude on how to handle the feedback:
msg = (
f"VIOLATION โ {details}\n\n"
"---\n"
"[STOP HOOK] This is a safety reminder, not a blocker. If obviously not applicable "
"(e.g., temp scripts, non-project files, user explicitly waived), ignore silently "
"without comment โ user already knows."
)
print(msg, file=sys.stderr)
sys.exit(2)
Hook Writing Guidelines for Stop Hooks
-
Add temp path detection โ Exit silently (code 0) if all edited files are in temp directories:
TEMP_PATH_PATTERNS = ("/temp/", "\\temp\\", "/tmp/", "\\tmp\\", "n:\\temp\\") all_files_in_temp = all( any(pat in fp.lower() for pat in TEMP_PATH_PATTERNS) for fp in edited_file_paths ) if all_files_in_temp: sys.exit(0) -
End message with
[STOP HOOK]instruction โ Tells Claude:- This is a reminder, not a blocker
- When to ignore silently (temp scripts, non-project, user waived)
- Don't comment when ignoring โ user already knows
-
Fire-once guard โ Prevent infinite loops if Claude's response triggers another stop:
marker_file = os.path.join(tempfile.gettempdir(), f"hook-fired-{session_hash}") if os.path.exists(marker_file): sys.exit(0)
When Claude Receives Stop Hook Feedback
Claude MUST follow the [STOP HOOK] instruction at the end:
- If applicable: Address the feedback, follow the SOP
- If not applicable: Ignore silently, no comment, continue with task
Anti-pattern: Saying "Hook feedback irrelevant because..." โ this wastes user attention. Just ignore and continue.
Part 15: Post-Action Verification ("Trust but Verify")
Principle: MCP/API success response โ actual success. Side-effect operations MUST include independent verification.
The Problem
MCP tools and APIs return success in cases where the action silently failed:
- Gmail says "sent" but email stuck in drafts or had wrong recipient
- Sheets says "updated" but wrong cell modified or value coerced (number โ string)
- Slack API returns 200 but reaction not applied (wrong channel, missing permissions)
- Deployment exits 0 but process crashed on startup
- Database write succeeds but constraint silently truncated data
Subagents are especially vulnerable โ they report "done" to the parent based on tool response alone, with no way to retroactively verify.
Rule
Every agent spec and skill that performs side-effect operations MUST include a verification step after each critical action. The verification must use an independent read/query โ not the write response.
Verification Patterns
| Operation | Verification | Example |
|---|---|---|
| Send email | Read sent folder, confirm recipient + subject | search_emails("in:sent subject:X to:Y newer_than:1m") |
| Modify spreadsheet | Read back modified cells, compare values | get_sheet_data(range) after update_cells |
| Slack message | Fetch channel history, confirm message exists | conversations.history after chat.postMessage |
| Slack reaction | Fetch message reactions, confirm emoji present | reactions.get after reactions.add |
| File write | Read file back, verify content/size | Read after Write for critical files |
| API mutation | Follow-up GET to confirm state change | GET /resource/:id after PUT /resource/:id |
| Database write | SELECT to verify row exists with expected values | Query after INSERT/UPDATE |
| Deployment | Health check endpoint or process list | curl /health or docker ps after deploy |
| Git push | Verify remote ref matches local | git ls-remote after git push |
Where to Encode Verification Steps
| Location | How |
|---|---|
Agent specs (agents/*.md) | Section: "Post-Action Verification" listing verify-after patterns per operation type the agent performs |
Skills (skills/*/SKILL.md) | Inline after each side-effect step: "Verify: [what to check]" |
| CLAUDE.md delegation rules | When mandating delegation ("always use X agent"), note: "Agent verifies actions per knowledge-architecture Part 15" |
Writing Verification Steps
Good verification: Independent observation that confirms the intended outcome.
Step 3: Send the email via mcp__gmail__send_email
Step 4: VERIFY โ Search sent folder for subject "X" to "Y" within last 2 minutes.
If not found: retry send. If found but wrong content: flag to user.
Bad verification: Trusting the tool response.
Step 3: Send the email via mcp__gmail__send_email
Step 4: If no error returned, report success. โ WRONG: silent failures return no error
Scope โ What Needs Verification
Not every operation needs it. Apply to side effects that are hard to undo or whose failure has consequences:
| Needs Verification | Doesn't Need |
|---|---|
| Sending emails/messages | Reading files |
| Modifying shared data (sheets, DB) | Searching/querying |
| Deploying code | Local file edits (editor shows result) |
| Applying permissions/settings | Listing/enumerating |
| Financial/billing operations | Formatting/transforming data |
| Slack reactions used as state tracking | Informational API calls |
Anti-Patterns
| Anti-Pattern | Why It Fails |
|---|---|
| "MCP returned success, reporting done" | MCP success = HTTP 200, not business logic success |
| "No error thrown, must have worked" | Many APIs return 200 with error in body or silently no-op |
| "Verified by checking the tool response fields" | Response is the CLAIM, not independent evidence |
| "Will verify in the next step anyway" | Next step may depend on this step and cascade-fail silently |