vibe-graph — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited vibe-graph (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.
Dual-layer dependency graph for vibe-* projects. Starts as a spec graph before code exists. Grows into a full dependency map as the project is built. Updated automatically from git diff — no agent self-reporting.
Three concepts from graphify integrated throughout:
Maps technical relationships between files. What imports what. What's tested by what. What route calls what handler. Every edge tagged EXTRACTED / INFERRED / AMBIGUOUS with confidence score. Rationale nodes stored per file — why the file works the way it does. Used by: bug diagnosis, blast radius tracing, test generation, parallel safety.
Maps semantic relationships between features, agents, models, components. What concept owns what files. What concepts depend on each other. Starts from SPEC.md and ARCHITECTURE.md before code exists. Nodes transition from planned to built as features are implemented. Used by: review boundary checking, impact assessment, progress tracking.
vibe-graph: init ← called by vibe-new-app after scaffold created
vibe-graph: build ← called by vibe-init after CODEBASE.md written
vibe-graph: update ← called by vibe-add-feature and vibe-fix-bug at session end
vibe-graph: rebuild ← full rebuild from scratch (recovery command)
vibe-graph: query [file or concept] ← manual query during debugging
vibe-graph: status ← graph health, coverage, god nodes, ambiguous edgesEvery node in both graphs has one of three states:
planned → exists in SPEC.md or ARCHITECTURE.md, no code yet
all edges are INFERRED at this stage
partial → some tasks complete, feature not fully implemented
built → all tasks complete, file exists, EXTRACTED edges confirmed
deleted → file was removed — state transitions, node never removed# Verify git is initialised
git status 2>/dev/null || { echo "Not a git repo — vibe-graph requires git"; exit 1; }
# Create graph directory
mkdir -p vibe/graph
# Check for existing graph
ls vibe/graph/DEPENDENCY_GRAPH.json 2>/dev/null && echo "GRAPH EXISTS" || echo "NEW GRAPH"Called by: vibe-new-app after scaffold is created. Purpose: Build the spec graph from SPEC.md and ARCHITECTURE.md before any code exists. All nodes are planned state. All edges are INFERRED — code hasn't been written yet.
Read references/SPEC_TO_GRAPH.md before this step.
Step 1 — Read planning documents:
cat vibe/SPEC.md
cat vibe/ARCHITECTURE.md
cat vibe/PLAN.md 2>/dev/null
cat AGENT_ARCH.md 2>/dev/nullStep 2 — Extract planned concepts from SPEC.md:
For each feature, agent, model, route, and component in SPEC.md: Create a concept node with state: planned. Apply confidence scores per SPEC_TO_GRAPH.md rules. Explicit feature dependencies → confidence: 0.95. Acceptance criteria references → confidence: 0.90.
Step 3 — Extract planned file nodes from ARCHITECTURE.md:
For each planned file in the folder structure section: Create a file node with state: planned. Assign concept with INFERRED tag and appropriate confidence. Folder comment = 0.85. File naming = 0.85. Pattern inference = 0.85.
Step 4 — For agentic projects, read AGENT_ARCH.md: Extract agent topology — which agents call which, tool assignments, verifier relationships, HITL checkpoints. All AGENT_ARCH.md edges: source: INFERRED, confidence: 0.95.
Step 5 — Write initial graphs:
Write vibe/graph/DEPENDENCY_GRAPH.json — all planned file nodes. Write vibe/graph/CONCEPT_GRAPH.json — all planned concept nodes. Write vibe/graph/.graph-meta.json:
{
"version": 2,
"created": "[ISO timestamp]",
"last_updated": "[ISO timestamp]",
"update_count": 0,
"mode": "spec",
"nodes_planned": 0,
"nodes_built": 0,
"god_nodes": [],
"ambiguous_count": 0,
"project": "[project name]"
}Step 6 — Generate initial graph.html: Read references/GRAPH_HTML_TEMPLATE.md for visual format. Planned nodes: lighter colour, dashed border. INFERRED edges: dashed lines. EXTRACTED edges: solid lines. No EXTRACTED edges yet — all relationships are INFERRED at init.
Step 7 — Tell vibe-new-app:
vibe-graph: init complete
Planned concepts: [N]
Planned files: [N]
INFERRED edges: [N] (all edges — code not yet written)
Graph location: vibe/graph/
State: SPEC GRAPH — edges transition to EXTRACTED as files are builtCalled by: vibe-init after CODEBASE.md is written. Purpose: One-time build for existing projects from CODEBASE.md.
Read references/CODEBASE_TO_GRAPH.md before this step.
Step 1 — Read CODEBASE.md in full:
cat vibe/CODEBASE.mdStep 2 — Parse CODEBASE.md sections into nodes with confidence tags:
Routes, models, components, agents — per CODEBASE_TO_GRAPH.md. All relationships extracted from CODEBASE.md: source: EXTRACTED, confidence: 1.0.
Step 3 — Attempt rationale extraction: For each file found in CODEBASE.md, if the source file is accessible:
cat [file_path] | grep -n "# WHY:\|# HACK:\|# NOTE:\|# IMPORTANT:\|# REASON:\|# DECISION:\|# TRADEOFF:\|# WARNING:\|# TODO:\|# FIXME:" 2>/dev/nullExtract rationale comments and first-paragraph docstrings. Store in rationale array on the file node. If file not accessible — leave rationale: [], populate on first update.
Step 4 — Compute hashes for all files:
git log -1 --format="%H %ai" -- [file_path]
git hash-object [file_path]Step 5 — Build concept assignments: Per CODEBASE_TO_GRAPH.md concept assignment rules. Feature folder Touches field → EXTRACTED, 1.0. Naming match → INFERRED, 0.85. Fallback → foundation, INFERRED, 0.70.
Step 6 — Compute god nodes:
import json
from pathlib import Path
graph = json.loads(Path('vibe/graph/DEPENDENCY_GRAPH.json').read_text())
degree = {}
for file_path, node in graph.items():
if not isinstance(node, dict):
continue
imports = node.get('imports', [])
imported_by = node.get('imported_by', [])
degree[file_path] = len(imports) + len(imported_by)
god_nodes = sorted(degree.items(), key=lambda x: x[1], reverse=True)[:5]
result = []
for file_path, connections in god_nodes:
node = graph.get(file_path, {})
entry = {
'file': file_path,
'connections': connections,
'concept': node.get('concept', 'foundation'),
'type': node.get('type', 'unknown'),
'risk': 'HIGH' if connections > 10 else 'MEDIUM' if connections > 5 else 'LOW'
}
if connections > 10:
entry['srp_flag'] = True
entry['srp_note'] = f'{connections} connections — consider splitting for SRP'
result.append(entry)
print(json.dumps(result, indent=2))Write god nodes to .graph-meta.json.
Step 7 — Collect AMBIGUOUS edges: Run collect_ambiguous() per CODEBASE_TO_GRAPH.md. Write count and list to .graph-meta.json.
Step 8 — Write all output files.
Step 9 — Tell vibe-init:
vibe-graph: build complete
Files indexed: [N]
Concepts mapped: [N]
EXTRACTED edges: [N]
INFERRED edges: [N]
AMBIGUOUS edges: [N] (see vibe-graph: status for details)
Rationale nodes: [N] (WHY/HACK/DECISION comments extracted)
God nodes: [top file] ([N] connections), [2nd], [3rd]
State: BUILT — graph reflects current codebaseCalled by: vibe-add-feature and vibe-fix-bug at session end. Purpose: Incremental update — only files changed this session.
Step 1 — Read git diff to find changed files:
git diff --name-only HEAD
git diff --name-only --cachedStep 2 — For each changed file:
Check if it's a new file (not in graph) or an update to an existing node.
Step 3 — Transition INFERRED → EXTRACTED:
For each changed file, read it and extract actual imports:
# Python
grep -n "^import \|^from " [file_path]
# TypeScript/JS
grep -n "^import \|^require(" [file_path]
# Go
grep -n "^import" [file_path]For each actual import found:
EXTRACTED, confidence: 1.0, add line number evidenceStep 4 — Extract rationale comments from changed files:
grep -n "# WHY:\|# HACK:\|# NOTE:\|# IMPORTANT:\|# REASON:\|# DECISION:\|# TRADEOFF:\|# WARNING:" [file_path]
# Also extract first 5 lines of module docstring if presentUpdate rationale array on the node. Append new entries, never overwrite existing.
Step 5 — Update concept node state:
For each concept affected by changed files:
files list, tasks_complete countStep 6 — Recompute god nodes: God nodes change as files are added and connections grow. Recompute after every update that adds 3+ new edges.
Step 7 — Update graph.html: Re-render subgraph of changed nodes and their neighbours. Transition planned→built (colour change). INFERRED→EXTRACTED (dashed→solid line).
Step 8 — Update .graph-meta.json:
{
"last_updated": "[ISO timestamp]",
"update_count": "[N+1]",
"files_updated_this_run": "[N]",
"mode": "incremental",
"god_nodes": "[recomputed list]",
"ambiguous_count": "[updated count]",
"inferred_to_extracted_this_run": "[N]"
}Step 9 — Report back:
vibe-graph: update complete
Files re-indexed: [N] (from git diff)
EXTRACTED edges confirmed: [N] (INFERRED → EXTRACTED transitions)
New EXTRACTED edges: [N]
Rationale nodes added: [N]
AMBIGUOUS edges: [N] (run vibe-graph: status to review)
God nodes updated: [top file] ([N] connections)
Nodes built/total: [N]/[N] ([%] complete)Called by: vibe-fix-bug, vibe-test, vibe-review at task start. Also available manually for debugging.
Read references/GRAPH_QUERY_PATTERNS.md for the full query protocol.
vibe-graph: query scout_agent.py
vibe-graph: query competitor-discovery
vibe-graph: query POST /api/brand-dnaReturns for a file query:
DEPENDENCY GRAPH — src/agents/scout_agent.py
─────────────────────────────────────────────
State: built
Concept: competitor-discovery [EXTRACTED · 1.0]
Type: agent
GOD NODE CHECK: Not a god node (3 connections)
RATIONALE (read before diagnosing or reviewing):
WHY [line 12]: Directory-first approach — Tavily better SMB coverage than LinkedIn API
HACK [line 47]: 2s rate limit — Tavily free tier hard limit
DECISION [line 89]: Returns top 5 only — UI constraint from IdentityScreen
CERTAIN (EXTRACTED — load immediately for blast radius):
→ src/agents/base_agent.py [EXTRACTED · 1.0 · line 3]
→ src/tools/tavily.py [EXTRACTED · 1.0 · line 7]
← src/graph/pipeline.py [EXTRACTED · 1.0 · pipeline line 12]
↔ tests/test_scout_agent.py
⇒ src/agents/verifiers/scout_verifier.py [EXTRACTED · 1.0]
⇒ GET /api/competitors/{brand_id}
PROBABLE (INFERRED ≥0.80 — load if needed):
→ src/utils/rate_limiter.py [INFERRED · 0.75 · instantiated at line 34]
AMBIGUOUS (human review needed):
(none)
Blast radius — certain: 6 files (~7,200 tokens)
vs loading CODEBASE.md: 25,000 tokens
Saving: 17,800 tokens (71%)Returns for a god node:
⚠️ GOD NODE — src/agents/base_agent.py
Connections: 14 (HIGH)
SRP flag: Consider splitting — 14 connections indicates high coupling
Files connected: [list the 14]
→ Any bug here affects 14 files. Any review here is a priority read.Returns for a concept query:
CONCEPT GRAPH — competitor-discovery
─────────────────────────────────────
State: built
Type: feature
Files:
src/agents/scout_agent.py built [EXTRACTED · 1.0]
src/agents/verifiers/scout_verifier.py built [EXTRACTED · 1.0]
src/api/competitors.py built [EXTRACTED · 1.0]
tests/test_scout_agent.py built [EXTRACTED · 1.0]
Depends on:
→ brand-dna-extraction [INFERRED · 0.95 · "uses brand industry from Brand DNA"]
Depended on by:
← content-gap-analysis [EXTRACTED · 1.0]
Tasks: 6/6 complete ✅Full rebuild from scratch. Recovery command — use when graph has drifted.
mode: "full-rebuild"GRAPH STATUS — Brandbot
──────────────────────────────────────────────────────────
Last updated: 2026-04-13 09:15:00 (2h ago)
Update count: 14 incremental updates
Mode: incremental
Coverage:
Files indexed: 47/47 (100%)
Concepts mapped: 8/8 (100%)
EXTRACTED edges: 124 (verified from source)
INFERRED edges: 18 (derived from specs/patterns)
AMBIGUOUS edges: 3 (needs review — see below)
Rationale nodes: 31 (WHY/HACK/DECISION comments)
GOD NODES (highest coupling — read first in review):
⚠️ src/agents/base_agent.py 14 connections · HIGH · SRP check recommended
src/models/brand_dna.py 8 connections · MEDIUM
src/graph/pipeline.py 7 connections · MEDIUM
src/api/brand_dna.py 6 connections · MEDIUM
src/config.py 5 connections · LOW
Build progress (from concept graph):
brand-dna-extraction: ✅ built (6/6 files)
competitor-discovery: ✅ built (6/6 files)
content-gap-analysis: 🔄 partial (4/6 files)
article-creation: ⬜ planned (0/8 files)
AMBIGUOUS — needs human review:
1. src/shared/validator.py
Issue: Used by both brand-dna-extraction and competitor-discovery
Action: Determine primary concept, update CODEBASE.md
2. src/utils/rate_limiter.py → src/tools/tavily.py
Issue: Inferred dependency, confidence 0.45
Action: Check if RateLimiter uses Tavily directly or just wraps it
3. src/api/auth.py
Issue: Named 'auth' but no auth concept in CONCEPT_GRAPH
Action: Assign to foundation or create auth concept
INFERRED EDGES (not yet confirmed by source):
src/agents/scout_agent.py → src/utils/rate_limiter.py (0.75)
src/agents/analyst_agent.py → src/models/competitor.py (0.80)
... [N more]
Graph health: ⚠️ 3 AMBIGUOUS edges need review
Run: vibe-graph: status to see full details
Resolve: manually update CODEBASE.md and run vibe-graph: update
Files: vibe/graph/DEPENDENCY_GRAPH.json (47 nodes, 142 edges)
vibe/graph/CONCEPT_GRAPH.json (8 concepts)
vibe/graph/graph.html (interactive — solid=EXTRACTED, dashed=INFERRED)
vibe/graph/.graph-meta.json (god nodes, ambiguous count, meta)Read references/GRAPH_QUERY_PATTERNS.md for the full query protocol.
At diagnosis start, instead of loading CODEBASE.md:
1. Check if suspected file is a god node
→ If yes: warn about large blast radius upfront
2. Read rationale nodes for the suspected file
→ Know WHY the file works the way it does BEFORE proposing a fix
→ This prevents fixing a bug by breaking a documented design decision
3. Query blast radius with confidence tiers
→ CERTAIN (EXTRACTED): load immediately — these are definitely involved
→ PROBABLE (INFERRED ≥0.80): load if CERTAIN tier doesn't resolve
→ Skip AMBIGUOUS — flag separately
4. Query concept for upstream/downstream propagation
→ Check if bug could originate from or propagate to adjacent conceptsAt blast radius detection start:
1. Get changed files from git diff (already done by vibe-graph: update)
2. For each changed file: get EXTRACTED connections
→ Certain test scope (full regression)
3. For each changed file: get INFERRED ≥0.80 connections
→ Probable test scope (smoke tests)
4. Write tests tier by tier — certain first, probable second
5. Files marked ⚠️ no test — create test fileAt review start, before reading any source files:
1. Read god nodes from .graph-meta.json
→ These are read first — highest coupling = highest review priority
→ SRP flag on any god node > 10 connections = check for SRP violation
2. Run concept boundary pre-screening (EXTRACTED edges only)
→ Cross-concept imports = potential violations
→ AMBIGUOUS edges = surface separately, not as violations
3. Read rationale nodes for all files in scope
→ Any code that contradicts a WHY or DECISION rationale = P1 finding (intent drift)
4. Files with violations or SRP flags → deep read
5. Files with no violations, not god nodes → shallow review onlyBefore dispatching subagents:
1. Check for direct file conflicts (Task A and Task B both write same file)
→ BLOCK: move one task to next wave
2. Check for god node conflicts (both tasks touch files connected to same god node)
→ WARN: ask before dispatching, consider sequencing
3. High-SRP-flag god nodes are never written by two waves simultaneously
→ Treat as implicit serialisation pointGit diff is the only source of truth for what changed. Never rely on agent memory, task manifests, or Touches field alone. git diff --name-only HEAD before every incremental update.
Never fabricate confidence. If the evidence for an edge is weak — mark INFERRED with low confidence. If unclear — mark AMBIGUOUS. Never upgrade AMBIGUOUS to EXTRACTED without source evidence.
Never remove a node — only transition state. A deleted file transitions to state: deleted, not removed from the graph. Deletion is a graph event — other files may still import it.
Concept assignment is append-only. Once assigned, a file stays in its concept. Multiple concepts → add secondary_concepts field. Never reassign primary concept without logging in DECISIONS.md.
Rationale nodes are append-only. New rationale comments are appended to the array. Never overwrite or remove existing rationale entries. If a rationale comment is deleted from source — log it as a deletion event, don't remove it from the graph.
AMBIGUOUS edges must be resolved, not ignored. vibe-graph: status surfaces them every time. They accumulate until a human resolves them. Never silently treat AMBIGUOUS as EXTRACTED.
The graph is a read-optimisation, not a constraint. If diagnosis reveals a file that should be in the blast radius but isn't in the graph — read the file, update the graph, continue. The graph is a starting point.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.