diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index 612c8627..191b1342 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -11,7 +11,7 @@
{
"name": "ecc",
"source": "./",
- "description": "The most comprehensive Claude Code plugin — 56 agents, 213 skills, 72 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
+ "description": "The most comprehensive Claude Code plugin — 56 agents, 217 skills, 72 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
"version": "2.0.0-rc.1",
"author": {
"name": "Affaan Mustafa",
diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index 7969aca0..a39d5c24 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "ecc",
"version": "2.0.0-rc.1",
- "description": "Battle-tested Claude Code plugin for engineering teams — 56 agents, 213 skills, 72 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
+ "description": "Battle-tested Claude Code plugin for engineering teams — 56 agents, 217 skills, 72 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
"author": {
"name": "Affaan Mustafa",
"url": "https://x.com/affaanmustafa"
diff --git a/AGENTS.md b/AGENTS.md
index 196c3f90..738700a0 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,6 +1,6 @@
# Everything Claude Code (ECC) — Agent Instructions
-This is a **production-ready AI coding plugin** providing 56 specialized agents, 213 skills, 72 commands, and automated hook workflows for software development.
+This is a **production-ready AI coding plugin** providing 56 specialized agents, 217 skills, 72 commands, and automated hook workflows for software development.
**Version:** 2.0.0-rc.1
@@ -148,7 +148,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
```
agents/ — 56 specialized subagents
-skills/ — 213 workflow skills and domain knowledge
+skills/ — 217 workflow skills and domain knowledge
commands/ — 72 slash commands
hooks/ — Trigger-based automations
rules/ — Always-follow guidelines (common + per-language)
diff --git a/README.md b/README.md
index cf6bc714..baa6efad 100644
--- a/README.md
+++ b/README.md
@@ -358,7 +358,7 @@ If you stacked methods, clean up in this order:
/plugin list ecc@ecc
```
-**That's it!** You now have access to 56 agents, 213 skills, and 72 legacy command shims.
+**That's it!** You now have access to 56 agents, 217 skills, and 72 legacy command shims.
### Dashboard GUI
@@ -1362,7 +1362,7 @@ The configuration is automatically detected from `.opencode/opencode.json`.
|---------|-------------|----------|--------|
| Agents | PASS: 56 agents | PASS: 12 agents | **Claude Code leads** |
| Commands | PASS: 72 commands | PASS: 35 commands | **Claude Code leads** |
-| Skills | PASS: 213 skills | PASS: 37 skills | **Claude Code leads** |
+| Skills | PASS: 217 skills | PASS: 37 skills | **Claude Code leads** |
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
@@ -1467,7 +1467,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
|---------|------------|------------|-----------|----------|
| **Agents** | 56 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
| **Commands** | 72 | Shared | Instruction-based | 35 |
-| **Skills** | 213 | Shared | 10 (native format) | 37 |
+| **Skills** | 217 | Shared | 10 (native format) | 37 |
| **Hook Events** | 8 types | 15 types | None yet | 11 types |
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks |
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions |
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 0504057a..32846330 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -160,7 +160,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
/plugin list ecc@ecc
```
-**完成!** 你现在可以使用 56 个代理、213 个技能和 72 个命令。
+**完成!** 你现在可以使用 56 个代理、217 个技能和 72 个命令。
### multi-* 命令需要额外配置
diff --git a/agent.yaml b/agent.yaml
index a21c3391..80fa07d6 100644
--- a/agent.yaml
+++ b/agent.yaml
@@ -9,10 +9,12 @@ model:
fallback:
- claude-sonnet-4-6
skills:
+ - agent-architecture-audit
- agent-eval
- agent-harness-construction
- agent-payment-x402
- agentic-engineering
+ - agentic-os
- ai-first-engineering
- ai-regression-testing
- android-clean-architecture
@@ -61,6 +63,7 @@ skills:
- e2e-testing
- energy-procurement
- enterprise-agent-ops
+ - error-handling
- eval-harness
- exa-search
- fal-ai-media
@@ -96,6 +99,7 @@ skills:
- logistics-exception-management
- market-research
- mcp-server-patterns
+ - motion-ui
- nanoclaw-repl
- nextjs-turbopack
- nutrient-document-processing
diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md
index 9f3f4895..415086ee 100644
--- a/docs/zh-CN/AGENTS.md
+++ b/docs/zh-CN/AGENTS.md
@@ -1,6 +1,6 @@
# Everything Claude Code (ECC) — 智能体指令
-这是一个**生产就绪的 AI 编码插件**,提供 56 个专业代理、213 项技能、72 条命令以及自动化钩子工作流,用于软件开发。
+这是一个**生产就绪的 AI 编码插件**,提供 56 个专业代理、217 项技能、72 条命令以及自动化钩子工作流,用于软件开发。
**版本:** 2.0.0-rc.1
@@ -147,7 +147,7 @@
```
agents/ — 56 个专业子代理
-skills/ — 213 个工作流技能和领域知识
+skills/ — 217 个工作流技能和领域知识
commands/ — 72 个斜杠命令
hooks/ — 基于触发的自动化
rules/ — 始终遵循的指导方针(通用 + 每种语言)
diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md
index 61cb39e1..e5d77e33 100644
--- a/docs/zh-CN/README.md
+++ b/docs/zh-CN/README.md
@@ -224,7 +224,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
/plugin list ecc@ecc
```
-**搞定!** 你现在可以使用 56 个智能体、213 项技能和 72 个命令了。
+**搞定!** 你现在可以使用 56 个智能体、217 项技能和 72 个命令了。
***
@@ -1134,7 +1134,7 @@ opencode
|---------|-------------|----------|--------|
| 智能体 | PASS: 56 个 | PASS: 12 个 | **Claude Code 领先** |
| 命令 | PASS: 72 个 | PASS: 35 个 | **Claude Code 领先** |
-| 技能 | PASS: 213 项 | PASS: 37 项 | **Claude Code 领先** |
+| 技能 | PASS: 217 项 | PASS: 37 项 | **Claude Code 领先** |
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
@@ -1242,7 +1242,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|---------|------------|------------|-----------|----------|
| **智能体** | 56 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
| **命令** | 72 | 共享 | 基于指令 | 35 |
-| **技能** | 213 | 共享 | 10 (原生格式) | 37 |
+| **技能** | 217 | 共享 | 10 (原生格式) | 37 |
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
diff --git a/manifests/install-modules.json b/manifests/install-modules.json
index 627a27ef..c6c3477c 100644
--- a/manifests/install-modules.json
+++ b/manifests/install-modules.json
@@ -139,6 +139,7 @@
"skills/fastapi-patterns",
"skills/frontend-patterns",
"skills/frontend-slides",
+ "skills/motion-ui",
"skills/golang-patterns",
"skills/golang-testing",
"skills/java-coding-standards",
@@ -229,6 +230,7 @@
"skills/continuous-learning-v2",
"skills/council",
"skills/e2e-testing",
+ "skills/error-handling",
"skills/eval-harness",
"skills/hookify-rules",
"skills/iterative-retrieval",
@@ -513,8 +515,10 @@
"kind": "skills",
"description": "Agentic engineering, autonomous loops, agent harness construction, and LLM pipeline optimization skills.",
"paths": [
+ "skills/agent-architecture-audit",
"skills/agent-harness-construction",
"skills/agentic-engineering",
+ "skills/agentic-os",
"skills/ai-first-engineering",
"skills/autonomous-loops",
"skills/blueprint",
diff --git a/package.json b/package.json
index 7f4aff76..a56b27be 100644
--- a/package.json
+++ b/package.json
@@ -90,10 +90,12 @@
"scripts/status.js",
"scripts/work-items.js",
"scripts/uninstall.js",
+ "skills/agent-architecture-audit/",
"skills/agent-harness-construction/",
"skills/agent-introspection-debugging/",
"skills/agent-sort/",
"skills/agentic-engineering/",
+ "skills/agentic-os/",
"skills/ai-first-engineering/",
"skills/ai-regression-testing/",
"skills/android-clean-architecture/",
@@ -147,6 +149,7 @@
"skills/email-ops/",
"skills/energy-procurement/",
"skills/enterprise-agent-ops/",
+ "skills/error-handling/",
"skills/eval-harness/",
"skills/evm-token-decimals/",
"skills/exa-search/",
@@ -193,6 +196,7 @@
"skills/mcp-server-patterns/",
"skills/messages-ops/",
"skills/mle-workflow/",
+ "skills/motion-ui/",
"skills/mysql-patterns/",
"skills/nanoclaw-repl/",
"skills/nestjs-patterns/",
diff --git a/skills/agent-architecture-audit/SKILL.md b/skills/agent-architecture-audit/SKILL.md
new file mode 100644
index 00000000..75d33e9b
--- /dev/null
+++ b/skills/agent-architecture-audit/SKILL.md
@@ -0,0 +1,256 @@
+---
+name: agent-architecture-audit
+description: Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature.
+origin: oh-my-agent-check
+tools: Read, Write, Edit, Bash, Grep, Glob
+---
+
+# Agent Architecture Audit
+
+A diagnostic workflow for agent systems that hide failures behind wrapper layers, stale memory, retry loops, or transport/rendering mutations.
+
+## When to Activate
+
+**MANDATORY for:**
+- Releasing any agent or LLM-powered application to production
+- Shipping features with tool calling, memory, or multi-step workflows
+- Agent behavior degrades after adding wrapper layers
+- User reports "the agent is getting worse" or "tools are flaky"
+- Same model works in playground but breaks inside your wrapper
+- Debugging agent behavior for more than 15 minutes without finding root cause
+
+**Especially critical when:**
+- You've added new prompt layers, tool definitions, or memory systems
+- Different agents in your system behave inconsistently
+- The model was fine yesterday but is hallucinating today
+- You suspect hidden repair/retry loops silently mutating responses
+
+**Do not use for:**
+- General code debugging — use `agent-introspection-debugging`
+- Code review — use language-specific reviewer agents
+- Security scanning — use `security-review` or `security-review/scan`
+- Agent performance benchmarking — use `agent-eval`
+- Writing new features — use the appropriate workflow skill
+
+## The 12-Layer Stack
+
+Every agent system has these layers. Any of them can corrupt the answer:
+
+| # | Layer | What Goes Wrong |
+|---|-------|----------------|
+| 1 | System prompt | Conflicting instructions, instruction bloat |
+| 2 | Session history | Stale context injection from previous turns |
+| 3 | Long-term memory | Pollution across sessions, old topics in new conversations |
+| 4 | Distillation | Compressed artifacts re-entering as pseudo-facts |
+| 5 | Active recall | Redundant re-summary layers wasting context |
+| 6 | Tool selection | Wrong tool routing, model skips required tools |
+| 7 | Tool execution | Hallucinated execution — claims to call but doesn't |
+| 8 | Tool interpretation | Misread or ignored tool output |
+| 9 | Answer shaping | Format corruption in final response |
+| 10 | Platform rendering | Transport-layer mutation (UI, API, CLI mutates valid answers) |
+| 11 | Hidden repair loops | Silent fallback/retry agents running second LLM pass |
+| 12 | Persistence | Expired state or cached artifacts reused as live evidence |
+
+## Common Failure Patterns
+
+### 1. Wrapper Regression
+
+The base model produces correct answers, but the wrapper layers make it worse.
+
+**Symptoms:**
+- Model works fine in playground or direct API call, breaks in your agent
+- Added a new prompt layer, existing behavior degraded
+- Agent sounds confident but is confidently wrong
+- "It was working before the last update"
+
+### 2. Memory Contamination
+
+Old topics leak into new conversations through history, memory retrieval, or distillation.
+
+**Symptoms:**
+- Agent brings up unrelated past topics
+- User corrections don't stick (old memory overwrites new)
+- Same-session artifacts re-enter as pseudo-facts
+- Memory grows without bound, degrading response quality over time
+
+### 3. Tool Discipline Failure
+
+Tools are declared in the prompt but not enforced in code. The model skips them or hallucinates execution.
+
+**Symptoms:**
+- "Must use tool X" in prompt, but model answers without calling it
+- Tool results look correct but were never actually executed
+- Different tools fight over the same responsibility
+- Model uses tool when it shouldn't, or skips it when it must
+
+### 4. Rendering/Transport Corruption
+
+The agent's internal answer is correct, but the platform layer mutates it during delivery.
+
+**Symptoms:**
+- Logs show correct answer, user sees broken output
+- Markdown rendering, JSON parsing, or streaming fragments corrupt valid responses
+- Hidden fallback agent quietly replaces the answer before delivery
+- Output differs between terminal and UI
+
+### 5. Hidden Agent Layers
+
+Silent repair, retry, summarization, or recall agents run without explicit contracts.
+
+**Symptoms:**
+- Output changes between internal generation and user delivery
+- "Auto-fix" loops run a second LLM pass the user doesn't know about
+- Multiple agents modify the same output without coordination
+- Answers get "smoothed" or "corrected" by invisible layers
+
+## Audit Workflow
+
+### Phase 1: Scope
+
+Define what you're auditing:
+
+- **Target system** — what agent application?
+- **Entrypoints** — how do users interact with it?
+- **Model stack** — which LLM(s) and providers?
+- **Symptoms** — what does the user report?
+- **Time window** — when did it start?
+- **Layers to audit** — which of the 12 layers apply?
+
+### Phase 2: Evidence Collection
+
+Gather evidence from the codebase:
+
+- **Source code** — agent loop, tool router, memory admission, prompt assembly
+- **Logs** — historical session traces, tool call records
+- **Config** — prompt templates, tool schemas, provider settings
+- **Memory files** — SOPs, knowledge bases, session archives
+
+Use `rg` to search for anti-patterns:
+
+```bash
+# Tool requirements expressed only in prompt text (not code)
+rg "must.*tool|必须.*工具|required.*call" --type md
+
+# Tool execution without validation
+rg "tool_call|toolCall|tool_use" --type py --type ts
+
+# Hidden LLM calls outside main agent loop
+rg "completion|chat\.create|messages\.create|llm\.invoke"
+
+# Memory admission without user-correction priority
+rg "memory.*admit|long.*term.*update|persist.*memory" --type py --type ts
+
+# Fallback loops that run additional LLM calls
+rg "fallback|retry.*llm|repair.*prompt|re-?prompt" --type py --type ts
+
+# Silent output mutation
+rg "mutate|rewrite.*response|transform.*output|shap" --type py --type ts
+```
+
+### Phase 3: Failure Mapping
+
+For each finding, document:
+
+- **Symptom** — what the user sees
+- **Mechanism** — how the wrapper causes it
+- **Source layer** — which of the 12 layers
+- **Root cause** — the deepest cause
+- **Evidence** — file:line or log:row reference
+- **Confidence** — 0.0 to 1.0
+
+### Phase 4: Fix Strategy
+
+Default fix order (code-first, not prompt-first):
+
+1. **Code-gate tool requirements** — enforce in code, not just prompt text
+2. **Remove or narrow hidden repair agents** — make fallback explicit with contracts
+3. **Reduce context duplication** — same info through prompt + history + memory + distillation
+4. **Tighten memory admission** — user corrections > agent assertions
+5. **Tighten distillation triggers** — don't compress what shouldn't be compressed
+6. **Reduce rendering mutation** — pass-through, don't transform
+7. **Convert to typed JSON envelopes** — structured internal flow, not freeform prose
+
+## Severity Model
+
+| Level | Meaning | Action |
+|-------|---------|--------|
+| `critical` | Agent can confidently produce wrong operational behavior | Fix before next release |
+| `high` | Agent frequently degrades correctness or stability | Fix this sprint |
+| `medium` | Correctness usually survives but output is fragile or wasteful | Plan for next cycle |
+| `low` | Mostly cosmetic or maintainability issues | Backlog |
+
+## Output Format
+
+Present findings to the user in this order:
+
+1. **Severity-ranked findings** (most critical first)
+2. **Architecture diagnosis** (which layer corrupted what, and why)
+3. **Ordered fix plan** (code-first, not prompt-first)
+
+Do not lead with compliments or summaries. If the system is broken, say so directly.
+
+## Quick Diagnostic Questions
+
+When auditing an agent system, answer these:
+
+| # | Question | If Yes → |
+|---|----------|----------|
+| 1 | Can the model skip a required tool and still answer? | Tool not code-gated |
+| 2 | Does old conversation content appear in new turns? | Memory contamination |
+| 3 | Is the same info in system prompt AND memory AND history? | Context duplication |
+| 4 | Does the platform run a second LLM pass before delivery? | Hidden repair loop |
+| 5 | Does the output differ between internal generation and user delivery? | Rendering corruption |
+| 6 | Are "must use tool X" rules only in prompt text? | Tool discipline failure |
+| 7 | Can the agent's own monologue become persistent memory? | Memory poisoning |
+
+## Anti-Patterns to Avoid
+
+- Avoid blaming the model before falsifying wrapper-layer regressions.
+- Avoid blaming memory without showing the contamination path.
+- Do not let a clean current state erase a dirty historical incident.
+- Do not treat markdown prose as a trustworthy internal protocol.
+- Do not accept "must use tool" in prompt text when code never enforces it.
+- Keep findings direct, evidence-backed, and severity-ranked.
+
+## Report Schema
+
+Audits should produce structured reports following this shape:
+
+```json
+{
+ "schema_version": "ecc.agent-architecture-audit.report.v1",
+ "executive_verdict": {
+ "overall_health": "high_risk",
+ "primary_failure_mode": "string",
+ "most_urgent_fix": "string"
+ },
+ "scope": {
+ "target_name": "string",
+ "model_stack": ["string"],
+ "layers_to_audit": ["string"]
+ },
+ "findings": [
+ {
+ "severity": "critical|high|medium|low",
+ "title": "string",
+ "mechanism": "string",
+ "source_layer": "string",
+ "root_cause": "string",
+ "evidence_refs": ["file:line"],
+ "confidence": 0.0,
+ "recommended_fix": "string"
+ }
+ ],
+ "ordered_fix_plan": [
+ { "order": 1, "goal": "string", "why_now": "string", "expected_effect": "string" }
+ ]
+}
+```
+
+## Related Skills
+
+- `agent-introspection-debugging` — Debug agent runtime failures (loops, timeouts, state errors)
+- `agent-eval` — Benchmark agent performance head-to-head
+- `security-review` — Security audit for code and configuration
+- `autonomous-agent-harness` — Set up autonomous agent operations
+- `agent-harness-construction` — Build agent harnesses from scratch
diff --git a/skills/agentic-os/SKILL.md b/skills/agentic-os/SKILL.md
new file mode 100644
index 00000000..d8a870de
--- /dev/null
+++ b/skills/agentic-os/SKILL.md
@@ -0,0 +1,387 @@
+---
+name: agentic-os
+description: Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases.
+origin: ECC
+---
+
+# Agentic OS
+
+Treat Claude Code as a persistent runtime / operating system rather than a chat session. This skill codifies the architecture used by production agentic setups: a kernel config that routes tasks to specialist agents, persistent file-based memory, scheduled automation, and a JSON/markdown data layer.
+
+## When to Activate
+
+- Building a multi-agent workflow inside Claude Code
+- Setting up persistent Claude Code automation that survives session restarts
+- Creating a "personal OS" or "agentic OS" for recurring tasks
+- User says "agentic OS", "personal OS", "multi-agent", "agent coordinator", "persistent agent"
+- Structuring long-running projects where context must survive across sessions
+
+## Architecture Overview
+
+The Agentic OS has four layers. Each layer is a directory in your project root.
+
+```
+project-root/
+├── CLAUDE.md # Kernel: identity, routing rules, agent registry
+├── agents/ # Specialist agent definitions (markdown prompts)
+├── .claude/commands/ # Slash commands: user-facing CLI
+├── scripts/ # Daemon scripts: scheduled or event-driven tasks
+└── data/ # State: JSON/markdown filesystem, no external DB
+```
+
+### Layer Responsibilities
+
+| Layer | Purpose | Persistence |
+|---|---|---|
+| Kernel (`CLAUDE.md`) | Identity, routing, model policies, agent registry | Git-tracked |
+| Agents (`agents/`) | Specialist identities with scoped tools and memory | Git-tracked |
+| Commands (`.claude/commands/`) | User-facing slash commands (`/daily-sync`, `/outreach`) | Git-tracked |
+| Scripts (`scripts/`) | Python/JS daemons triggered by cron or webhooks | Git-tracked |
+| State (`data/`) | Append-only logs, project state, decision records | Git-ignored or tracked |
+
+## The Kernel
+
+`CLAUDE.md` is the kernel. It acts as the COO / orchestrator. Claude reads it at session start and uses it to route work.
+
+### Kernel Structure
+
+```markdown
+# CLAUDE.md - Agentic OS Kernel
+
+## Identity
+You are the COO of [project-name]. You route tasks to specialist agents.
+You never write code directly. You delegate to the right agent and synthesize results.
+
+## Agent Registry
+
+| Agent | Role | Trigger |
+|---|---|---|
+| @dev | Code, architecture, debugging | User says "build", "fix", "refactor" |
+| @writer | Documentation, content, emails | User says "write", "draft", "blog" |
+| @researcher | Research, analysis, fact-checking | User says "research", "analyze", "compare" |
+| @ops | DevOps, deployment, infrastructure | User says "deploy", "CI", "server" |
+
+## Routing Rules
+1. Parse the user request for intent keywords
+2. Match to the Agent Registry trigger column
+3. Load the corresponding agent file from `agents/.md`
+4. Hand off execution with full context
+5. Synthesize and present the result back to the user
+
+## Model Policies
+- Default model: use the repository or harness default.
+- @dev tasks: prefer a higher-reasoning model for complex architecture.
+- @researcher tasks: use the configured research-capable model and approved search tools.
+- Cost ceiling: warn before exceeding the project's configured spend threshold.
+```
+
+### Key Principle
+
+The kernel should be **small and declarative**. Routing logic lives in plain markdown tables, not code. This makes the system inspectable and editable without debugging.
+
+## Specialist Agents
+
+Each agent is a standalone markdown file in `agents/`. Claude loads the relevant agent file when routing a task.
+
+### Agent Definition Format
+
+```markdown
+# @dev - Software Engineer
+
+## Identity
+You are a senior software engineer. You write clean, tested, production-grade code.
+You prefer simple solutions. You ask clarifying questions when requirements are ambiguous.
+
+## Memory Scope
+- Read `data/projects/.md` for context
+- Read `data/decisions/` for architectural decisions
+- Append execution logs to `data/logs/-@dev.md`
+
+## Tool Access
+- Full filesystem access within project root
+- Git operations (status, diff, commit, branch)
+- Test runner access
+- MCP servers as configured in `.claude/mcp.json`
+
+## Constraints
+- Always write tests for new features
+- Never commit directly to `main`; use feature branches
+- Prefer editing existing files over creating new ones
+- Keep functions under 50 lines when possible
+```
+
+### Multi-Agent Collaboration Pattern
+
+When a task spans multiple agents, the kernel runs them sequentially or in parallel:
+
+```
+User: "Build a landing page and write the launch blog post"
+
+Kernel routing:
+1. @dev - "Build a landing page with [requirements]"
+2. @writer - "Write a launch blog post for [product] using the landing page copy"
+3. Kernel synthesizes both outputs into a unified response
+```
+
+For parallel execution, use Claude Code's background task capability or shell scripts that invoke Claude Code with specific agent contexts.
+
+## Commands and Daily Workflows
+
+Slash commands are markdown files in `.claude/commands/`. They define reusable workflows.
+
+### Command Structure
+
+```markdown
+# /daily-sync
+
+Run the morning briefing:
+
+1. Read `data/logs/last-sync.md` for context
+2. Check project status: `git status`, pending PRs, CI health
+3. Review `data/inbox/` for new tasks or decisions needed
+4. Generate a summary of blockers, priorities, and next actions
+5. Append the briefing to `data/logs/daily/.md`
+```
+
+### Standard Command Set
+
+| Command | Purpose |
+|---|---|
+| `/daily-sync` | Morning briefing: status, blockers, priorities |
+| `/outreach` | Run outreach workflow (email, LinkedIn, etc.) |
+| `/research ` | Deep research with citation tracking |
+| `/apply-jobs` | Tailor resume + cover letter for a target role |
+| `/analytics` | Pull metrics from Stripe, GitHub, or custom sources |
+| `/interview-prep` | Generate flashcards or mock interview questions |
+| `/decision ` | Log a decision with pros/cons and chosen path |
+
+### Activating Commands
+
+Place command files in `.claude/commands/.md`. Claude Code auto-discovers them. Users invoke them with `/`.
+
+## Persistent Memory
+
+Memory is file-based. No vector DB, no Redis, no PostgreSQL. JSON and markdown files in `data/` are the database.
+
+### Memory Directory Structure
+
+```
+data/
+├── daily-logs/ # Append-only daily activity logs
+├── projects/ # Per-project context files
+├── decisions/ # Architectural and business decisions (ADR format)
+├── inbox/ # New tasks or ideas awaiting triage
+├── contacts/ # People, companies, relationship notes
+└── templates/ # Reusable prompts and formats
+```
+
+### Daily Log Format
+
+```markdown
+# 2026-04-22 - Daily Log
+
+## Sessions
+- 09:00 - Session 1: Refactored auth module (@dev)
+- 11:30 - Session 2: Drafted investor update (@writer)
+
+## Decisions
+- Switched from JWT to session cookies (see `data/decisions/2026-04-22-auth.md`)
+
+## Blockers
+- Waiting on API key from vendor (follow up 2026-04-24)
+
+## Next Actions
+- [ ] Merge auth refactor PR
+- [ ] Send investor update for review
+```
+
+### Auto-Reflection Pattern
+
+At the end of each session, the kernel appends a reflection:
+
+```markdown
+## Reflection - Session 3
+- What worked: Parallel agent execution saved 20 minutes
+- What didn't: @researcher hit a paywalled source, need better source ranking
+- What to change: Add `source-tier` field to research notes (A/B/C credibility)
+```
+
+This creates a feedback loop that improves the system over time without code changes.
+
+## Scheduled Automation
+
+Agentic OS tasks run on a schedule using external cron, not Claude Code's built-in cron (which dies when the session ends).
+
+### macOS: LaunchAgent
+
+```xml
+
+
+
+
+
+ Label
+ com.agentic.daily-sync
+ ProgramArguments
+
+ /claude
+ --cwd
+ /path/to/project
+ --command
+ /daily-sync
+
+ StartCalendarInterval
+
+ Hour
+ 8
+ Minute
+ 0
+
+ StandardOutPath
+ /tmp/agentic-daily-sync.log
+
+
+```
+
+### Linux: systemd Timer
+
+```ini
+# ~/.config/systemd/user/agentic-daily-sync.service
+[Unit]
+Description=Agentic OS Daily Sync
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/claude --cwd /path/to/project --command /daily-sync
+```
+
+```ini
+# ~/.config/systemd/user/agentic-daily-sync.timer
+[Unit]
+Description=Run daily sync every morning
+
+[Timer]
+OnCalendar=*-*-* 8:00:00
+Persistent=true
+
+[Install]
+WantedBy=timers.target
+```
+
+### Cross-Platform: pm2
+
+```bash
+# ecosystem.config.js
+module.exports = {
+ apps: [{
+ name: 'agentic-daily-sync',
+ script: 'claude',
+ args: '--cwd /path/to/project --command /daily-sync',
+ cron_restart: '0 8 * * *',
+ autorestart: false
+ }]
+};
+```
+
+## Data Layer
+
+The data layer is your filesystem. Use JSON for structured data and markdown for narrative content.
+
+### JSON for Structured State
+
+```json
+// data/projects/website-v2.json
+{
+ "name": "Website v2",
+ "status": "in-progress",
+ "milestone": "beta-launch",
+ "agents_involved": ["@dev", "@writer"],
+ "files": {
+ "spec": "docs/website-v2-spec.md",
+ "design": "designs/website-v2.fig"
+ },
+ "metrics": {
+ "commits": 47,
+ "last_session": "2026-04-22T11:30:00Z"
+ }
+}
+```
+
+### Markdown for Narrative
+
+Use markdown for anything a human reads: decisions, logs, research notes, contact records.
+
+### Schema Evolution
+
+Never rename existing fields. Add new fields and mark old ones deprecated:
+
+```json
+{
+ "name": "Website v2",
+ "status": "in-progress",
+ "milestone": "beta-launch",
+ "_deprecated_priority": "high",
+ "priority_v2": { "level": "high", "rationale": "Blocks investor demo" }
+}
+```
+
+This keeps historical data readable without migration scripts.
+
+## Anti-Patterns
+
+### Monolithic Single Agent
+
+```markdown
+# BAD - One agent does everything
+You are a full-stack developer, writer, researcher, and DevOps engineer.
+```
+
+Split into specialist agents. The kernel handles routing.
+
+### Stateless Sessions
+
+```markdown
+# BAD - No memory between sessions
+Starting fresh every time Claude Code opens.
+```
+
+Always read `data/` at session start and write back at session end.
+
+### Hardcoded Credentials
+
+```markdown
+# BAD - API keys in agent files or CLAUDE.md
+Your OpenAI API key is sk-xxxxxxxx
+```
+
+Use environment variables or a `.env` file loaded by scripts. Agents reference `process.env.API_KEY`.
+
+### External Database for Simple State
+
+```markdown
+# BAD - PostgreSQL for a solo user's agentic OS
+```
+
+Use JSON/markdown files until you have multiple concurrent users or GBs of data.
+
+### Over-Engineered Routing
+
+```markdown
+# BAD - Routing logic in code instead of markdown tables
+if (intent.includes('deploy')) { agent = opsAgent; }
+```
+
+Keep routing declarative in `CLAUDE.md` markdown tables. It is inspectable, editable, and debuggable.
+
+## Best Practices
+
+- [ ] `CLAUDE.md` is under 200 lines and fits in context window
+- [ ] Each agent file is under 100 lines and focused on one domain
+- [ ] `data/` is git-ignored for sensitive logs, git-tracked for decisions and specs
+- [ ] Commands use imperative names: `/daily-sync`, not `/run-daily-sync`
+- [ ] Logs are append-only; never edit past daily logs
+- [ ] Every agent has a `Memory Scope` section defining what files it reads
+- [ ] Reflections are written at the end of every session
+- [ ] Scheduled tasks use external cron (LaunchAgent, systemd, pm2), not Claude Code's session cron
+- [ ] Cost tracking: log API spend per session in `data/logs/-costs.json`
+- [ ] One project = one Agentic OS. Do not share a single `CLAUDE.md` across unrelated projects.
diff --git a/skills/error-handling/SKILL.md b/skills/error-handling/SKILL.md
new file mode 100644
index 00000000..111f18cf
--- /dev/null
+++ b/skills/error-handling/SKILL.md
@@ -0,0 +1,376 @@
+---
+name: error-handling
+description: Patterns for robust error handling across TypeScript, Python, and Go. Covers typed errors, error boundaries, retries, circuit breakers, and user-facing error messages.
+origin: ECC
+---
+
+# Error Handling Patterns
+
+Consistent, robust error handling patterns for production applications.
+
+## When to Activate
+
+- Designing error types or exception hierarchies for a new module or service
+- Adding retry logic or circuit breakers for unreliable external dependencies
+- Reviewing API endpoints for missing error handling
+- Implementing user-facing error messages and feedback
+- Debugging cascading failures or silent error swallowing
+
+## Core Principles
+
+1. **Fail fast and loudly** — surface errors at the boundary where they occur; don't bury them
+2. **Typed errors over string messages** — errors are first-class values with structure
+3. **User messages ≠ developer messages** — show friendly text to users, log full context server-side
+4. **Never swallow errors silently** — every `catch` block must either handle, re-throw, or log
+5. **Errors are part of your API contract** — document every error code a client may receive
+
+## TypeScript / JavaScript
+
+### Typed Error Classes
+
+```typescript
+// Define an error hierarchy for your domain
+export class AppError extends Error {
+ constructor(
+ message: string,
+ public readonly code: string,
+ public readonly statusCode: number = 500,
+ public readonly details?: unknown,
+ ) {
+ super(message)
+ this.name = this.constructor.name
+ // Maintain correct prototype chain in transpiled ES5 JavaScript.
+ // Required for `instanceof` checks (e.g., `error instanceof NotFoundError`)
+ // to work correctly when extending the built-in Error class.
+ Object.setPrototypeOf(this, new.target.prototype)
+ }
+}
+
+export class NotFoundError extends AppError {
+ constructor(resource: string, id: string) {
+ super(`${resource} not found: ${id}`, 'NOT_FOUND', 404)
+ }
+}
+
+export class ValidationError extends AppError {
+ constructor(message: string, details: { field: string; message: string }[]) {
+ super(message, 'VALIDATION_ERROR', 422, details)
+ }
+}
+
+export class UnauthorizedError extends AppError {
+ constructor(reason = 'Authentication required') {
+ super(reason, 'UNAUTHORIZED', 401)
+ }
+}
+
+export class RateLimitError extends AppError {
+ constructor(public readonly retryAfterMs: number) {
+ super('Rate limit exceeded', 'RATE_LIMITED', 429)
+ }
+}
+```
+
+### Result Pattern (no-throw style)
+
+For operations where failure is expected and common (parsing, external calls):
+
+```typescript
+type Result =
+ | { ok: true; value: T }
+ | { ok: false; error: E }
+
+function ok(value: T): Result {
+ return { ok: true, value }
+}
+
+function err(error: E): Result {
+ return { ok: false, error }
+}
+
+// Usage
+async function fetchUser(id: string): Promise> {
+ try {
+ const user = await db.users.findUnique({ where: { id } })
+ if (!user) return err(new NotFoundError('User', id))
+ return ok(user)
+ } catch (e) {
+ return err(new AppError('Database error', 'DB_ERROR'))
+ }
+}
+
+const result = await fetchUser('abc-123')
+if (!result.ok) {
+ // TypeScript knows result.error here
+ logger.error('Failed to fetch user', { error: result.error })
+ return
+}
+// TypeScript knows result.value here
+console.log(result.value.email)
+```
+
+### API Error Handler (Next.js / Express)
+
+```typescript
+import { NextRequest, NextResponse } from 'next/server'
+
+function handleApiError(error: unknown): NextResponse {
+ // Known application error
+ if (error instanceof AppError) {
+ return NextResponse.json(
+ {
+ error: {
+ code: error.code,
+ message: error.message,
+ ...(error.details ? { details: error.details } : {}),
+ },
+ },
+ { status: error.statusCode },
+ )
+ }
+
+ // Zod validation error
+ if (error instanceof z.ZodError) {
+ return NextResponse.json(
+ {
+ error: {
+ code: 'VALIDATION_ERROR',
+ message: 'Request validation failed',
+ details: error.issues.map(i => ({
+ field: i.path.join('.'),
+ message: i.message,
+ })),
+ },
+ },
+ { status: 422 },
+ )
+ }
+
+ // Unexpected error — log details, return generic message
+ console.error('Unexpected error:', error)
+ return NextResponse.json(
+ { error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },
+ { status: 500 },
+ )
+}
+
+export async function POST(req: NextRequest) {
+ try {
+ // ... handler logic
+ } catch (error) {
+ return handleApiError(error)
+ }
+}
+```
+
+### React Error Boundary
+
+```typescript
+import { Component, ErrorInfo, ReactNode } from 'react'
+
+interface Props {
+ fallback: ReactNode
+ onError?: (error: Error, info: ErrorInfo) => void
+ children: ReactNode
+}
+
+interface State {
+ hasError: boolean
+ error: Error | null
+}
+
+export class ErrorBoundary extends Component {
+ state: State = { hasError: false, error: null }
+
+ static getDerivedStateFromError(error: Error): State {
+ return { hasError: true, error }
+ }
+
+ componentDidCatch(error: Error, info: ErrorInfo) {
+ this.props.onError?.(error, info)
+ console.error('Unhandled React error:', error, info)
+ }
+
+ render() {
+ if (this.state.hasError) return this.props.fallback
+ return this.props.children
+ }
+}
+
+// Usage
+Something went wrong. Please refresh.
}>
+
+
+```
+
+## Python
+
+### Custom Exception Hierarchy
+
+```python
+class AppError(Exception):
+ """Base application error."""
+ def __init__(self, message: str, code: str, status_code: int = 500):
+ super().__init__(message)
+ self.code = code
+ self.status_code = status_code
+
+class NotFoundError(AppError):
+ def __init__(self, resource: str, id: str):
+ super().__init__(f"{resource} not found: {id}", "NOT_FOUND", 404)
+
+class ValidationError(AppError):
+ def __init__(self, message: str, details: list[dict] | None = None):
+ super().__init__(message, "VALIDATION_ERROR", 422)
+ self.details = details or []
+```
+
+### FastAPI Global Exception Handler
+
+```python
+from fastapi import FastAPI, Request
+from fastapi.responses import JSONResponse
+
+app = FastAPI()
+
+@app.exception_handler(AppError)
+async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
+ return JSONResponse(
+ status_code=exc.status_code,
+ content={"error": {"code": exc.code, "message": str(exc)}},
+ )
+
+@app.exception_handler(Exception)
+async def generic_error_handler(request: Request, exc: Exception) -> JSONResponse:
+ # Log full details, return generic message
+ logger.exception("Unexpected error", exc_info=exc)
+ return JSONResponse(
+ status_code=500,
+ content={"error": {"code": "INTERNAL_ERROR", "message": "An unexpected error occurred"}},
+ )
+```
+
+## Go
+
+### Sentinel Errors and Error Wrapping
+
+```go
+package domain
+
+import "errors"
+
+// Sentinel errors for type-checking
+var (
+ ErrNotFound = errors.New("not found")
+ ErrUnauthorized = errors.New("unauthorized")
+ ErrConflict = errors.New("conflict")
+)
+
+// Wrap errors with context — never lose the original
+func (r *UserRepository) FindByID(ctx context.Context, id string) (*User, error) {
+ user, err := r.db.QueryRow(ctx, "SELECT * FROM users WHERE id = $1", id)
+ if errors.Is(err, sql.ErrNoRows) {
+ return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
+ }
+ if err != nil {
+ return nil, fmt.Errorf("querying user %s: %w", id, err)
+ }
+ return user, nil
+}
+
+// At the handler level, unwrap to determine response
+func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
+ user, err := h.service.GetUser(r.Context(), chi.URLParam(r, "id"))
+ if err != nil {
+ switch {
+ case errors.Is(err, domain.ErrNotFound):
+ writeError(w, http.StatusNotFound, "not_found", err.Error())
+ case errors.Is(err, domain.ErrUnauthorized):
+ writeError(w, http.StatusForbidden, "forbidden", "Access denied")
+ default:
+ slog.Error("unexpected error", "err", err)
+ writeError(w, http.StatusInternalServerError, "internal_error", "An unexpected error occurred")
+ }
+ return
+ }
+ writeJSON(w, http.StatusOK, user)
+}
+```
+
+## Retry with Exponential Backoff
+
+```typescript
+interface RetryOptions {
+ maxAttempts?: number
+ baseDelayMs?: number
+ maxDelayMs?: number
+ retryIf?: (error: unknown) => boolean
+}
+
+async function withRetry(
+ fn: () => Promise,
+ options: RetryOptions = {},
+): Promise {
+ const {
+ maxAttempts = 3,
+ baseDelayMs = 500,
+ maxDelayMs = 10_000,
+ retryIf = () => true,
+ } = options
+
+ let lastError: unknown
+
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
+ try {
+ return await fn()
+ } catch (error) {
+ lastError = error
+ if (attempt === maxAttempts || !retryIf(error)) throw error
+
+ const jitter = Math.random() * baseDelayMs
+ const delay = Math.min(baseDelayMs * 2 ** (attempt - 1) + jitter, maxDelayMs)
+ await new Promise(resolve => setTimeout(resolve, delay))
+ }
+ }
+
+ throw lastError
+}
+
+// Usage: retry transient network errors, not 4xx
+const data = await withRetry(() => fetch('/api/data').then(r => r.json()), {
+ maxAttempts: 3,
+ retryIf: (error) => !(error instanceof AppError && error.statusCode < 500),
+})
+```
+
+## User-Facing Error Messages
+
+Map error codes to human-readable messages. Keep technical details out of user-visible text.
+
+```typescript
+const USER_ERROR_MESSAGES: Record = {
+ NOT_FOUND: 'The requested item could not be found.',
+ UNAUTHORIZED: 'Please sign in to continue.',
+ FORBIDDEN: "You don't have permission to do that.",
+ VALIDATION_ERROR: 'Please check your input and try again.',
+ RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
+ INTERNAL_ERROR: 'Something went wrong on our end. Please try again later.',
+}
+
+export function getUserMessage(code: string): string {
+ return USER_ERROR_MESSAGES[code] ?? USER_ERROR_MESSAGES.INTERNAL_ERROR
+}
+```
+
+## Error Handling Checklist
+
+Before merging any code that touches error handling:
+
+- [ ] Every `catch` block handles, re-throws, or logs — no silent swallowing
+- [ ] API errors follow the standard envelope `{ error: { code, message } }`
+- [ ] User-facing messages contain no stack traces or internal details
+- [ ] Full error context is logged server-side
+- [ ] Custom error classes extend a base `AppError` with a `code` field
+- [ ] Async functions surface errors to callers — no fire-and-forget without fallback
+- [ ] Retry logic only retries retriable errors (not 4xx client errors)
+- [ ] React components are wrapped in `ErrorBoundary` for rendering errors
diff --git a/skills/motion-ui/SKILL.md b/skills/motion-ui/SKILL.md
new file mode 100644
index 00000000..7b9bf0c5
--- /dev/null
+++ b/skills/motion-ui/SKILL.md
@@ -0,0 +1,575 @@
+---
+name: motion-ui
+description: "Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns."
+origin: ECC
+---
+
+# Motion System v4.2
+
+Production-ready UI motion system for React / Next.js.
+
+Focused on **performance, accessibility, and usability** — not decoration.
+
+## When to Use
+
+Use this motion system when motion:
+
+* Guides attention (e.g., onboarding, key actions)
+* Communicates state (loading, success, error, transitions)
+* Preserves spatial continuity (layout changes, navigation)
+
+### Appropriate Scenarios
+
+* Interactive components (buttons, modals, menus)
+* State transitions (loading → loaded, open → closed)
+* Navigation and layout continuity (shared elements, crossfade)
+
+### Considerations
+
+* **Accessibility**: Always support reduced motion
+* **Device adaptation**: Adjust for low-end devices
+* **Performance trade-offs**: Prefer responsiveness over visual smoothness
+
+### Avoid Using Motion When
+
+* It is purely decorative
+* It reduces usability or clarity
+* It impacts performance negatively
+
+---
+
+## How It Works
+
+### Core Principle
+
+Motion must:
+
+* Guide attention
+* Communicate state
+* Preserve spatial continuity
+
+If it does none → remove it.
+
+---
+
+### Installation
+
+```bash
+npm install motion
+```
+
+---
+
+### Version
+
+* `motion/react` - default for current Motion for React projects (package: `motion`)
+* `framer-motion` - legacy import path for projects that still depend on Framer Motion
+
+**Do not mix.** Mixing causes conflicting internal schedulers and broken `AnimatePresence` contexts — components from one package will not coordinate exit animations with components from the other.
+
+To check which version your project uses:
+
+```bash
+cat package.json | grep -E '"motion"|"framer-motion"'
+```
+
+Always import from one source consistently:
+
+```ts
+// Correct (modern)
+import { motion, AnimatePresence } from "motion/react"
+
+// Correct (legacy)
+import { motion, AnimatePresence } from "framer-motion"
+
+// Never mix both in the same project
+```
+
+---
+
+### Motion Tokens
+
+```ts
+// motionTokens.ts
+export const motionTokens = {
+ duration: {
+ fast: 0.18,
+ normal: 0.35,
+ slow: 0.6
+ },
+ // Use these as the `ease` value inside a `transition` object:
+ // transition={{ duration: motionTokens.duration.normal, ease: motionTokens.easing.smooth }}
+ easing: {
+ smooth: [0.22, 1, 0.36, 1] as [number, number, number, number],
+ sharp: [0.4, 0, 0.2, 1] as [number, number, number, number]
+ },
+ distance: {
+ sm: 8,
+ md: 16,
+ lg: 24
+ }
+}
+```
+
+Usage example:
+
+```tsx
+import { motionTokens } from "@/lib/motionTokens"
+
+
+```
+
+---
+
+### Performance Rules
+
+**Safe**
+
+* transform
+* opacity
+
+**Avoid**
+
+* width / height
+* top / left
+
+Rule: responsiveness > smoothness
+
+---
+
+### Device Adaptation
+
+The heuristic combines CPU core count **and** available memory for a more reliable signal. `deviceMemory` is available on Chrome/Android; the fallback covers Safari and Firefox.
+
+```ts
+const isLowEnd =
+ typeof navigator !== "undefined" && (
+ // Low memory (Chrome/Android only; undefined elsewhere → treat as capable)
+ (navigator.deviceMemory !== undefined && navigator.deviceMemory <= 2) ||
+ // Few cores AND no memory API (covers Safari/Firefox on weak hardware)
+ (navigator.deviceMemory === undefined && navigator.hardwareConcurrency <= 4)
+ )
+
+const duration = isLowEnd ? 0.2 : 0.4
+```
+
+---
+
+### Accessibility
+
+#### JS (useReducedMotion)
+
+```tsx
+import { motion, useReducedMotion } from "motion/react"
+
+export function FadeIn() {
+ const reduce = useReducedMotion()
+
+ return (
+
+ )
+}
+```
+
+#### CSS
+
+```css
+@media (prefers-reduced-motion: reduce) {
+ .motion-safe-transition {
+ transition: opacity 0.2s;
+ }
+
+ .motion-reduce-transform {
+ transform: none !important;
+ }
+}
+```
+
+#### Tailwind
+
+```html
+
+```
+
+---
+
+### Architecture & Patterns
+
+#### Core Patterns
+
+| Scenario | Pattern |
+|---|---|
+| Hover feedback | `whileHover` |
+| Tap / press feedback | `whileTap` |
+| Reveal on scroll | `whileInView` |
+| Scroll-linked value | `useScroll` + `useTransform` |
+| Conditional mount/unmount | `AnimatePresence` |
+| Small layout shifts (single element, < ~300px change) | `layout` prop |
+| Large layout shifts or full-page reflows | Avoid `layout`; use CSS transitions or page-level routing instead |
+| Complex, imperative sequences | `useAnimate` |
+
+> **Why avoid `layout` on large containers?** Framer's layout animation uses `transform` to reconcile positions, but on elements that span the full viewport or trigger deep reflow, the measurement cost causes visible jank and CLS. Prefer CSS Grid/Flexbox transitions or coordinate with `layoutId` on specific child elements only.
+
+#### Layout & Transitions
+
+* Shared element transitions → `layoutId` (must be unique per mounted instance)
+* Enter / exit transitions → `AnimatePresence` (see `mode` guidance below)
+
+#### AnimatePresence `mode`
+
+Always specify `mode` explicitly — the default (`"sync"`) runs enter and exit simultaneously, which causes visual overlap in most UI patterns.
+
+| `mode` | When to use |
+|---|---|
+| `"wait"` | Exit completes before enter starts. Use for **modals, toasts, page transitions**. |
+| `"sync"` (default) | Enter and exit overlap. Use only when overlap is intentional (e.g., crossfade carousels). |
+| `"popLayout"` | Exiting element is popped out of flow immediately; remaining items animate to fill. Use for **lists, tabs, dismissible cards**. |
+
+```tsx
+// Modal — always use "wait"
+
+ {open && }
+
+
+// Dismissible list item — use "popLayout"
+
+ {items.map(item => )}
+
+```
+
+---
+
+### Advanced Patterns (Concepts)
+
+* Parallax (scroll-linked transforms)
+* Scroll storytelling (sticky sections)
+* 3D tilt (pointer-based transforms)
+* Crossfade (shared `layoutId`)
+* Progressive reveal (clip-path)
+* Skeleton loading (looped opacity)
+* Micro-interactions (hover/tap feedback)
+* Spring system (physics-based motion)
+
+---
+
+### Modal Essentials
+
+* Focus trap
+* Escape close
+* Scroll lock
+* ARIA roles
+* Use `AnimatePresence mode="wait"` so exit animation completes before the next modal enters
+
+#### Full Example
+
+```tsx
+import React, { useEffect, useRef, useState } from "react"
+import { motion, AnimatePresence } from "motion/react"
+
+function useFocusTrap(ref: React.RefObject, active: boolean) {
+ useEffect(() => {
+ if (!active || !ref.current) return
+ const el = ref.current
+ const focusable = el.querySelectorAll(
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
+ )
+ const first = focusable[0]
+ const last = focusable[focusable.length - 1]
+
+ function handleKey(e: KeyboardEvent) {
+ if (e.key !== "Tab") return
+ if (e.shiftKey && document.activeElement === first) {
+ e.preventDefault()
+ last?.focus()
+ } else if (!e.shiftKey && document.activeElement === last) {
+ e.preventDefault()
+ first?.focus()
+ }
+ }
+
+ el.addEventListener("keydown", handleKey)
+ first?.focus()
+ return () => el.removeEventListener("keydown", handleKey)
+ }, [active, ref])
+}
+
+function useScrollLock(active: boolean) {
+ useEffect(() => {
+ if (!active) return
+ const prev = document.body.style.overflow
+ document.body.style.overflow = "hidden"
+ return () => { document.body.style.overflow = prev }
+ }, [active])
+}
+
+function Modal({ open, closeModal }: { open: boolean; closeModal: () => void }) {
+ const ref = useRef(null)
+
+ useFocusTrap(ref, open)
+ useScrollLock(open)
+
+ useEffect(() => {
+ function onKey(e: KeyboardEvent) {
+ if (e.key === "Escape") closeModal()
+ }
+ if (open) window.addEventListener("keydown", onKey)
+ return () => window.removeEventListener("keydown", onKey)
+ }, [open, closeModal])
+
+ return (
+ // mode="wait" ensures exit animation finishes before any new modal enters
+
+ {open && (
+
+
+ Dialog Title
+
+
+
+ )}
+
+ )
+}
+
+export function Example() {
+ const [open, setOpen] = useState(false)
+
+ return (
+ <>
+
+ setOpen(false)} />
+ >
+ )
+}
+```
+
+---
+
+### SSR Safety
+
+* Match initial states between server and client renders
+* Avoid implicit animation origins (always set `initial` explicitly)
+* Wrap motion components in `"use client"` in Next.js App Router
+
+---
+
+### Debugging
+
+Check:
+
+* Wrong import (mixing `motion/react` and `framer-motion`)
+* Missing `"use client"` directive in Next.js App Router
+* Missing `key` prop on `AnimatePresence` children
+* Hydration mismatch (initial state differs between SSR and client)
+* `layout` prop misuse on large containers causing reflow jank
+* State-driven animation not triggering (check dependency arrays)
+
+---
+
+### QA
+
+* No CLS
+* Keyboard works
+* Focus trapped in modals
+* ARIA roles correct (`role="dialog"`, `aria-modal="true"`)
+* Reduced motion respected (`useReducedMotion` + CSS media query)
+* No hydration warnings in Next.js
+* Animations stop cleanly on unmount (no memory leaks)
+* `AnimatePresence mode` set explicitly on all usage sites
+
+---
+
+### Anti-Patterns
+
+* Animating layout properties (`width`, `height`, `top`, `left`)
+* Infinite animations without purpose (always ask: what state does this communicate?)
+* Over-staggering lists (keep `staggerChildren` ≤ 0.1s; beyond that it feels slow)
+* Ignoring reduced motion preferences
+* Using `layout` on large or full-viewport containers
+* Omitting `mode` on `AnimatePresence` (default `"sync"` causes visual overlap)
+* Using motion purely for decoration
+
+---
+
+### Philosophy
+
+Motion is interaction design.
+
+---
+
+### Final Rule
+
+> If motion does not improve UX → remove it.
+
+---
+
+## Examples
+
+### Button Interaction
+
+```tsx
+import { motion } from "motion/react"
+
+export function Button() {
+ return (
+
+ Click me
+
+ )
+}
+```
+
+---
+
+### Reduced Motion Example
+
+```tsx
+import { motion, useReducedMotion } from "motion/react"
+
+export function FadeIn() {
+ const reduce = useReducedMotion()
+
+ return (
+
+ )
+}
+```
+
+---
+
+### Stagger List
+
+```tsx
+import { motion } from "motion/react"
+
+const container = {
+ hidden: {},
+ visible: {
+ transition: { staggerChildren: 0.08 } // keep ≤ 0.1s to avoid sluggishness
+ }
+}
+
+const item = {
+ hidden: { opacity: 0, y: 10 },
+ visible: { opacity: 1, y: 0, transition: { duration: 0.3, ease: [0.22, 1, 0.36, 1] } }
+}
+
+export function List() {
+ return (
+
+ {[1, 2, 3].map(i => (
+ Item {i}
+ ))}
+
+ )
+}
+```
+
+---
+
+### Modal with AnimatePresence
+
+```tsx
+import { motion, AnimatePresence } from "motion/react"
+
+export function Modal({ open }: { open: boolean }) {
+ return (
+
+ {open && (
+
+ )}
+
+ )
+}
+```
+
+---
+
+### Scroll Parallax
+
+```tsx
+import { useScroll, useTransform, motion } from "motion/react"
+
+export function Parallax() {
+ const { scrollYProgress } = useScroll()
+ const y = useTransform(scrollYProgress, [0, 1], [0, -80])
+
+ return
+}
+```
+
+---
+
+### Skeleton Loading
+
+```tsx
+import { motion } from "motion/react"
+
+export function Skeleton() {
+ return (
+
+ )
+}
+```
+
+---
+
+### Shared Layout (Crossfade)
+
+```tsx
+import { motion } from "motion/react"
+
+// layoutId must be unique per mounted instance.
+// If multiple instances can exist simultaneously, append a unique id:
+// layoutId={`shared-${item.id}`}
+export function Shared() {
+ return
+}
+```