Claude Code skill for interactive system flow tracing across CODE, API, AUTH, DATA, NETWORK layers. SQLite persistence, Mermaid export, git merge driver.
SaferSkills independently audited audit-flow (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Step 1: Read schema.sql
# ALWAYS read the schema first to understand tables, constraints, views
cat .claude/skills/audit-flow/schema.sqlStep 2: Check if DB exists — NEVER recreate
# Check for existing database
ls -la .audit/audit.db 2>/dev/null && echo "DB EXISTS - DO NOT RECREATE" || echo "No DB - safe to init"Step 3: If DB exists, show current state
python .claude/skills/audit-flow/scripts/audit.py list| Action | Why Forbidden |
|---|---|
rm .audit/audit.db | Destroys audit history |
audit.py init when DB exists | Overwrites existing data |
DROP TABLE | Destroys audit history |
sqlite3 .audit/audit.db < schema.sql when DB exists | Overwrites existing data |
Rule: If .audit/audit.db exists, ONLY use audit.py list, show, export, or INSERT operations. NEVER recreate.
Interactive tracing of system flows with SQLite persistence. Supports multiple named flows per session, non-linear flows (branching/merging), and multi-format exports.
Directory structure by purpose:
docs/audits/{name}-{YYYY-MM-DD}/docs/ideation/{name}-{YYYY-MM-DD}.md (single file, no subdirectory unless artifacts needed)docs/audits/{name}-{YYYY-MM-DD}/ (same as audits — captures evidence)docs/audits/{name}-{YYYY-MM-DD}/ (same as audits — captures structural analysis)Required files:
Lazy initialization: Create subdirectories only when artifacts exist
screenshots/ network-traces/ diagrams/ code-samples/ test-results/ evidence/Naming: {audit-name}-{type}.md
Invariant: SQLite = sole source of truth. Context window: volatile, compacts without notice, hallucinates state.
🚨 CRITICAL: NEVER DESTROY EXISTING DATA
.audit/audit.db exists → it contains irreplaceable audit historyinit when DB exists — use list to see what's thereConstraints:
| Operation | Rule | Blocked rationalization |
|---|---|---|
| Entry | Read schema.sql FIRST, check if DB exists SECOND | "I'll just start working" |
| Init | ONLY if .audit/audit.db does NOT exist | "Let me reinitialize to start fresh" |
| Schema | Read schema.sql BEFORE any SQLite command — understand tables, constraints, views first | "I know the schema from context" |
| Write | INSERT each tuple/edge/finding before moving to the next code location | "I'll batch-insert at the end" |
| Read | SELECT from DB before referencing tuple IDs, counts, or flow structure | "I remember the flow so far" |
| Export | audit.py export only — never generate mermaid/markdown from context | "Let me generate mermaid directly" |
| Resume | audit.py show <session> before any operation that references prior tuples | "I have the full trace in context" |
| Reference | Query tuple IDs from DB — IDs are DB-assigned, never inferred | "The tuple ID should be N" |
| Default | When uncertain of flow state → query DB before proceeding | (any unlisted rationalization) |
Checkpoint: Every 5 tuples → audit.py show <session> <flow>
Name: ___
Purpose: security-audit | documentation | compliance | ideation | brainstorming | debugging | architecture-review | incident-review
Description: ___ (optional)Initialize directory immediately. Lazily create subdirectories when artifacts are generated.
[fine] Function-level trace (~50-200 tuples)
Use: Security audits, debugging
[coarse] Boundary-level trace (~10-30 tuples)
Use: Documentation, high-level flows
Choose: fine / coarseAsk at decision points: trace deeper? mark concern? add finding (severity)? note?
Ask format: json | yaml | md | mermaid | all
Post-export: Generate INDEX.md manifest. Organize artifacts by type. Prune empty directories.
| Command | Purpose |
|---|---|
/audit-flow start | New session (name, purpose, granularity) |
/audit-flow flow {name} | Add new flow to session |
/audit-flow add {layer} {desc} | Add tuple to current flow |
/audit-flow link {from} {to} {rel} | Create edge (supports conditions for branches) |
/audit-flow finding {desc} | Record finding |
/audit-flow show | View session/flow details |
/audit-flow export | Export (json/yaml/md/mermaid) |
/audit-flow git-setup | Configure git merge/diff drivers (once) |
Layers: CODE | API | NETWORK | AUTH | DATA
Relations: TRIGGERS | READS | WRITES | VALIDATES | TRANSFORMS | BRANCHES | MERGES
| Relation | Meaning | Use When | NOT For |
|---|---|---|---|
TRIGGERS | A causes B to execute | Function calls, event handlers, HTTP requests | Static observations |
READS | A consumes data from B | Cookie reads, DB queries, config lookups | Mutations |
WRITES | A mutates data in B | Cookie writes, DB inserts, state updates | Read-only access |
VALIDATES | A checks/verifies B | Auth checks, input validation, expiry checks | Chaining analyst observations |
TRANSFORMS | A converts/maps data for B | Token exchange, response formatting | Unrelated processing |
BRANCHES | A has conditional paths | if/else, switch, error vs success | Must have condition label |
MERGES | Multiple paths converge at B | Parallel paths rejoin, error recovery | Single-path flow |
CRITICAL: BRANCHES Must Have Conditions. Every BRANCHES edge requires a condition describing which path. Example: BRANCHES [token expired] vs BRANCHES [token valid].
Flow steps = things the SYSTEM DOES (function calls, data reads, network requests). Verified by tracing code.
Observations = things the ANALYST NOTES (missing features, potential risks). Record as findings, not tuples.
Wrong pattern:
T50 "NO cross-tab sync" ← observation, not a system action
T51 "React state NOT shared" ← observation
T50 --VALIDATES--> T51 ← chaining observations as flowCorrect pattern:
-- Record as finding instead:
INSERT INTO findings (flow_id, session_id, severity, category, description)
VALUES (?, ?, 'medium', 'state-management',
'No cross-tab sync: React state not shared across tabs');Rule: NEVER chain observations with VALIDATES. If describing what the system DOESN'T do, use a finding.
Session (audit container)
└── Flow (named DAG with entry point)
└── Tuple (node: layer + action + subject)
└── Edge (relation + optional condition)# Core
python .claude/skills/audit-flow/scripts/audit.py init # Initialize DB
python .claude/skills/audit-flow/scripts/audit.py list # List sessions
python .claude/skills/audit-flow/scripts/audit.py show <session> # Show flows
python .claude/skills/audit-flow/scripts/audit.py show <session> <flow> # Show flow details
python .claude/skills/audit-flow/scripts/audit.py export <session> # Export all
python .claude/skills/audit-flow/scripts/audit.py export <session> -f <flow> # Export one flow
python .claude/skills/audit-flow/scripts/audit.py validate <session> # Validate flows
# Git integration
python .claude/skills/audit-flow/scripts/audit.py git-setup # Configure merge/diff drivers (once)
python .claude/skills/audit-flow/scripts/audit.py db-merge %O %A %B # Git merge driver (auto-called)
# CSV backup/portability (optional)
python .claude/skills/audit-flow/scripts/audit.py csv-export # DB → .audit/csv/*.csv
python .claude/skills/audit-flow/scripts/audit.py csv-import # .audit/csv/*.csv → DB
python .claude/skills/audit-flow/scripts/audit.py csv-merge <theirs_dir> # Merge two CSV setsBranching: One tuple → multiple outgoing edges with conditions
INSERT INTO edges (from_tuple, to_tuple, relation, condition)
VALUES (5, 6, 'BRANCHES', 'token valid'),
(5, 7, 'BRANCHES', 'token expired');Merging: Multiple tuples → one tuple
INSERT INTO edges (from_tuple, to_tuple, relation)
VALUES (6, 8, 'TRIGGERS'),
(9, 8, 'MERGES'); -- refresh path merges back.gitattributes - Git merge/diff driver config for audit.dbCapture on session start: commit hash, branch, working tree status. Include in all exports.
Run python .claude/skills/audit-flow/scripts/audit.py validate <session> before export.
| Check | Severity | Description |
|---|---|---|
| BRANCHES without condition | ERROR | Every BRANCHES edge needs a condition label |
| Node count >= 60 | ERROR | Split into sub-flows |
| Node count >= 40 | WARN | Consider splitting |
| Orphan nodes | WARN | Node with no edges (disconnected) |
| Duplicate labels | WARN | Same action text without subject disambiguation |
| No entry point | WARN | All nodes have incoming edges |
Post-export features (automatic):
1. action, 2. action)--> solid (TRIGGERS/VALIDATES/TRANSFORMS/BRANCHES/MERGES), -.-> dotted (READS), ==> thick (WRITES)--direction LR flag for horizontal layoutsAll diagrams MUST be produced by `audit.py export`. Never hand-craft mermaid. The exporter enforces:
([label]):::entryPoint1. action, 2. action, 3. action(), "", <>, |, [] characters (auto-sanitized)Reading flow must be obvious. A reader opening the diagram cold must immediately see:
Node label rules:
handleCallback(), exchangeCodeForTokens()Size limits:
Problem: SQLite is binary — git merge can't auto-resolve .audit/audit.db.
Solution: Custom git merge driver. audit.db stays in git (small, single file). On conflict, git calls audit.py db-merge to auto-merge using SQL.
python .claude/skills/audit-flow/scripts/audit.py git-setupThis configures (in .git/config):
merge.sqlite-audit — calls audit.py db-merge %O %A %B on conflictdiff.sqlite — sqlite3 .dump for readable git diff outputAlso requires .gitattributes (already in repo):
.audit/audit.db diff=sqlite merge=sqlite-auditaudit.db normally — git add .audit/audit.db && git commitgit diff shows SQL text (via textconv)git merge with conflict → git calls the merge driver automaticallyupdated_at wins), remaps IDs| Table | Merge Key | Conflict Resolution |
|---|---|---|
| sessions | name (unique) | Keep later updated_at |
| flows | (session_name, flow_name) | Follow parent session winner |
| tuples | Parent flow | All tuples from winning flow kept |
| edges | Both endpoint tuples | Kept if both endpoints survive |
| findings | (session_name, category, description) | Dedup by content |
All INTEGER PKs remapped sequentially. Foreign keys updated.
CSV export/import still available for portability and backup:
python .claude/skills/audit-flow/scripts/audit.py csv-export # DB → .audit/csv/*.csv (QUOTE_ALL)
python .claude/skills/audit-flow/scripts/audit.py csv-import # CSV → DB (recreates from scratch)Principles:
file:line references{name}-{type}.mdaudit.py git-setup)audit.py validateFlat structure, empty directories, orphan nodes, unlabeled branches, generic identifiers, missing git context, no manifest, hand-crafted mermaid, bare-verb labels.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.