关于 ln-644-dependency-graph-auditor
用于代码库审计的技能,构建模块依赖图并检查循环依赖、架构边界和稳定依赖原则。它支持按项目架构自适应检测,并可借助基线逐步处理遗留问题。
主要功能
- 01从导入语句构建依赖图
- 02检测成对及传递循环依赖
- 03验证依赖边界规则
- 04计算耦合度与稳定性指标
- 05使用基线跟踪新增违规
适用场景
- 01检查模块之间的循环依赖
- 02验证项目依赖是否符合架构规则
- 03渐进式治理遗留依赖问题
MCP 相关
可优先使用 hex-graph 分析依赖拓扑、循环和架构指标;代码读取可使用 hex-line。若 MCP 不可用或未索引,则改用内置工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ levnikolaevich/ claude-code-skills --skill ln-644-dependency-graph-auditor安装后在 Claude Code 中输入/ln-644-dependency-graph-auditor确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/ac_validation_rules.mdagent_delegation_pattern.mdagent_instructions_writing_guide.mdagent_review_memory.mdagent_review_workflow.mdagent_skill_roots_contract.mdai_ready_architecture.mdaudit_coordinator_aggregation.mdaudit_coordinator_domain_mode.mdaudit_output_schema.mdreferences/agents/agent_registry.jsonagent_registry.json.SOURCE.mdagent_result_classifier.mjsagent_runner.mjsreferences/agents/prompt_templates/general_analysis.mditerative_refinement.mdrefinement_perspectives.mdreview_base.mdscenario_validator.mdscope_analysis.mdtask_decomposition.mdtraceability_validator.mdreferences/agents/prompt_templates/modes/code.mdcontext.mdplan_review.mdstory.mdreferences/agents/schemas/code_review_schema.jsoncontext_review_schema.jsonstory_review_schema.jsonreferences/agents/test/agent_result_classifier.mjs详细介绍
路径: 文件路径(
references/、../ln-*)均相对于此技能目录。
类型: L3 Worker 类别: 6XX 审计
依赖图审计器
L3 Worker,用于构建和分析模块依赖图,以强制执行架构边界。
目的与范围
- 根据导入语句构建模块依赖图(Python、TS/JS、C#、Java)
- 检测循环依赖:成对依赖(HIGH)+ 通过 DFS 检测传递依赖(CRITICAL)
- 验证边界规则:禁止、允许、必需(遵循 dependency-cruiser 模式)
- 计算 Robert C. Martin 指标(Ca、Ce、不稳定性)+ Lakos 聚合指标(CCD、NCCD)
- 验证稳定依赖原则(SDP)
- 支持基线/冻结,以逐步采用旧版代码(遵循 ArchUnit FreezingArchRule)
- 自适应: 三级架构检测——自定义规则 > 文档 > 自动检测
不在范围内(由其他 Worker 负责):
- I/O 隔离违规(基于 grep)-> ln-642-layer-boundary-auditor
- API 契约违规 -> ln-643-api-contract-auditor
- 代码重复 -> ln-623-code-principles-auditor
输入(来自 ln-640)
- architecture_path: string # Path to docs/architecture.md
- codebase_root: string # Root directory to scan
- output_dir: string # e.g., ".hex-skills/runtime-artifacts/runs/{run_id}/audit-report"
# Domain-aware (optional, from coordinator)
- domain_mode: "global" | "domain-aware" # Default: "global"
- current_domain: string # e.g., "users", "billing" (only if domain-aware)
- scan_path: string # e.g., "src/users/" (only if domain-aware)
# Baseline (optional)
- update_baseline: boolean # If true, save current state as baseline
当 domain_mode="domain-aware" 时: 所有 Grep/Glob 操作均使用 scan_path 替代 codebase_root。为所有发现项添加 domain 字段。
工作流
必须阅读: 加载 references/two_layer_detection.md,了解检测方法。
必须阅读: 加载 references/mcp_tool_preferences.md 和 references/mcp_integration_patterns.md
当依赖拓扑、循环依赖或架构指标能够显著改善审计时,优先使用 hex-graph。有条件时,优先使用 hex-line 读取本地代码和配置。如果 MCP 不可用、不受支持或未建立索引,则使用内置的 Read/Grep/Glob/Bash 继续,并在报告中说明所用的备用方案。
阶段 1:发现架构(自适应)
必须阅读: 加载 references/dependency_rules.md——使用三级优先级链、架构预设和自动检测启发式规则。
架构检测采用三级优先级——显式配置优先于文档,文档优先于自动检测:
# Priority 1: Explicit project config
IF docs/project/dependency_rules.yaml exists:
Load custom rules (modules, forbidden, allowed, required)
SKIP preset detection
# Priority 2: Architecture documentation
ELIF docs/architecture.md exists:
Read Section 4.2 (modules, layers, architecture_type)
Read Section 6.4 (boundary rules, if defined)
Map documented layers to presets from dependency_rules.md
Apply preset rules, override with explicit rules from Section 6.4
# Priority 3: Auto-detection from directory structure
ELSE:
scan_root = scan_path IF domain_mode == "domain-aware" ELSE codebase_root
Run structure heuristics:
signals = {}
IF Glob("**/domain/**") AND Glob("**/infrastructure/**"):
signals["clean"] = HIGH
IF Glob("**/controllers/**") AND Glob("**/services/**") AND Glob("**/repositories/**"):
signals["layered"] = HIGH
IF Glob("**/features/*/") with internal structure:
signals["vertical"] = HIGH
IF Glob("**/adapters/**") AND Glob("**/ports/**"):
signals["hexagonal"] = HIGH
IF Glob("**/views/**") AND Glob("**/models/**"):
signals["mvc"] = HIGH
IF len(signals) == 0:
architecture_mode = "custom"
confidence = "LOW"
# Only check cycles + metrics, no boundary presets
ELIF len(signals) == 1:
architecture_mode = signals.keys()[0]
confidence = signals.values()[0]
Apply matching preset from dependency_rules.md
ELSE:
architecture_mode = "hybrid"
confidence = "MEDIUM"
# Identify zones, apply different presets per zone (see dependency_rules.md Hybrid section)
FOR EACH detected_style IN signals:
zone_path = identify_zone(detected_style)
zone_preset = load_preset(detected_style)
zones.append({path: zone_path, preset: zone_preset})
Add cross-zone rules: inner zones accessible, outer zones forbidden to depend on inner
阶段 2:构建依赖图
必须阅读: 加载 references/import_patterns.md——使用语言检测、导入 Grep 模式、模块解析算法和排除列表。
scan_root = scan_path IF domain_mode == "domain-aware" ELSE codebase_root
# Step 1: Detect primary language
tech_stack = Read(docs/project/tech_stack.md) IF exists
ELSE detect from file extensions: Glob("**/*.py", "**/*.ts", "**/*.cs", "**/*.java", root=scan_root)
# Step 2: Extract imports per language
FOR EACH source_file IN Glob(language_glob_pattern, root=scan_root):
imports = []
# Python
IF language == "python":
from_imports = Grep("^from\s+([\w.]+)\s+import", source_file)
plain_imports = Grep("^import\s+([\w.]+)", source_file)
imports = from_imports + plain_imports
# TypeScript / JavaScript
ELIF language == "typescript" OR language == "javascript":
es6_imports = Grep("import\s+.*\s+from\s+['\"]([^'\"]+)['\"]", source_file)
require_imports = Grep("require\(['\"]([^'\"]+)['\"]\)", source_file)
imports = es6_imports + require_imports
# C#
ELIF language == "csharp":
using_imports = Grep("^using\s+([\w.]+);", source_file)
imports = using_imports
# Java
ELIF language == "java":
java_imports = Grep("^import\s+([\w.]+);", source_file)
imports = java_imports
# Step 3: Filter internal only (per import_patterns.md Exclusion Lists)
internal_imports = filter_internal(imports, scan_root)
# Step 4: Resolve to modules
FOR EACH imp IN internal_imports:
source_module = resolve_module(source_file, scan_root)
target_module = resolve_module(imp, scan_root)
IF source_module != target_module:
graph[source_module].add(target_module)
阶段 3:检测循环依赖(ADP)
hex-graph 加速: 对于包含 .hex-skills/codegraph/index.db 的项目,使用 analyze_architecture(verbosity="full"),并检查其返回的 cycles,即可立即检测循环依赖。这些循环和耦合指标以工作区模块为级别,因此单包仓库可能会被折叠为一个模块。如果图输出对于包内分析过于粗略,则回退到基于 grep 的 DFS 或符号/文件级追踪。
根据 Robert C. Martin(《Clean Architecture》第 14 章):“组件依赖图中不得存在循环依赖。”
# Pairwise cycles (A <-> B)
FOR EACH (A, B) WHERE B IN graph[A] AND A IN graph[B]:
cycles.append({
type: "pairwise",
path: [A, B, A],
severity: "HIGH",
fix: suggest_cycle_fix(A, B)
})
# Layer 2: Test-only dependencies (devDependencies, test imports) -> skip cycle
# Plugin/extension architecture with documented bidirectional design -> downgrade to LOW
# Transitive cycles via DFS (A -> B -> C -> A)
visited = {}
rec_stack = {}
FUNCTION dfs(node, path):
visited[node] = true
rec_stack[node] = true
FOR EACH neighbor IN graph[node]:
IF NOT visited[neighbor]:
dfs(neighbor, path + [node])
ELIF rec_stack[neighbor]:
cycle_path = extract_cycle(path + [node], neighbor)
IF len(cycle_path) > 2: # Skip pairwise (already detected)
cycles.append({
type: "transitive",
path: cycle_path,
severity: "CRITICAL",
fix: suggest_cycle_fix_transitive(cycle_path)
})
rec_stack[node] = false
FOR EACH module IN graph:
IF NOT visited[module]:
dfs(module, [])
# Folder-level cycles (per dependency-cruiser pattern)
folder_graph = collapse_to_folders(graph)
Repeat DFS on folder_graph for folder-level cycles
打破循环依赖的建议(摘自《Clean Architecture》第 14 章):
- DIP——在被依赖模块中提取接口,在依赖方模块中实现该接口
- 提取共享组件——将共享代码移至双方都依赖的新模块
- 领域事件 / 消息总线——对于跨领域循环依赖,通过异步通信解耦
阶段 4:验证边界规则
# Load rules from Phase 1 discovery
# rules = {forbidden: [], allowed: [], required: []}
# Check FORBIDDEN rules
FOR EACH rule IN rules.forbidden:
FOR EACH edge (source -> target) IN graph:
IF matches(source, rule.from) AND matches(target, rule.to):
IF rule.cross AND same_group(source, target):
CONTINUE # cross=true means only cross-group violations
boundary_violations.append({
rule_type: "forbidden",
from: source,
to: target,
file: get_import_location(source, target),
severity: rule.severity,
reason: rule.reason
})
# Check ALLOWED rules (whitelist mode)
IF rules.allowed.length > 0:
FOR EACH edge (source -> target) IN graph:
allowed = false
FOR EACH rule IN rules.allowed:
IF matches(source, rule.from) AND matches(target, rule.to):
allowed = true
BREAK
IF NOT allowed:
boundary_violations.append({
rule_type: "not_in_allowed",
from: source,
to: target,
file: get_import_location(source, target),
severity: "MEDIUM",
reason: "Dependency not in allowed list"
})
# Check REQUIRED rules
FOR EACH rule IN rules.required:
FOR EACH module IN graph WHERE matches(module, rule.module):
has_required = false
FOR EACH dep IN graph[module]:
IF matches(dep, rule.must_depend_on):
has_required = true
BREAK
IF NOT has_required:
boundary_violations.append({
rule_type: "required_missing",
module: module,
missing: rule.must_depend_on,
severity: "MEDIUM",
reason: rule.reason
})
阶段 5:计算图指标
hex-graph 加速: 对于包含 .hex-skills/codegraph/index.db 的项目,使用 analyze_architecture(verbosity="full"),并检查返回的 coupling 指标,以即时计算 Ca/Ce/I。图不可用时,改用手动计算。
必须阅读: 加载 references/graph_metrics.md——使用“指标定义”“各层阈值”“SDP 算法”和“Lakos 公式”。
# Per-module metrics (Robert C. Martin)
FOR EACH module IN graph:
Ce = len(graph[module]) # Efferent: outgoing
Ca = count(m for m in graph if module in graph[m]) # Afferent: incoming
I = Ce / (Ca + Ce) IF (Ca + Ce) > 0 ELSE 0 # Instability
metrics[module] = {Ca, Ce, I}
# SDP validation (Stable Dependencies Principle)
FOR EACH edge (A -> B) IN graph:
IF metrics[A].I < metrics[B].I:
# Stable module depends on less stable module -- SDP violation
sdp_violations.append({
from: A, to: B,
I_from: metrics[A].I, I_to: metrics[B].I,
severity: "HIGH"
})
# Threshold checks (per graph_metrics.md, considering detected layer)
FOR EACH module IN metrics:
layer = get_layer(module) # From Phase 1 discovery
thresholds = get_thresholds(layer) # From graph_metrics.md
IF metrics[module].I > thresholds.max_instability:
findings.append({severity: thresholds.severity, issue: f"{module} instability {I} exceeds {thresholds.max_instability}"})
IF metrics[module].Ce > thresholds.max_ce:
findings.append({severity: "MEDIUM", issue: f"{module} efferent coupling {Ce} exceeds {thresholds.max_ce}"})
# Lakos aggregate metrics
CCD = 0
FOR EACH module IN graph:
DependsOn = count_transitive_deps(module, graph) + 1 # Including self
CCD += DependsOn
N = len(graph)
CCD_balanced = N * log2(N) # CCD of balanced binary tree with N nodes
NCCD = CCD / CCD_balanced IF CCD_balanced > 0 ELSE 0
IF NCCD > 1.5:
findings.append({severity: "MEDIUM", issue: f"Graph complexity (NCCD={NCCD:.2f}) exceeds balanced tree threshold (1.5)"})
级联链扩展: 对于服务文件(**/services/**),将模块级图扩展至函数级。为每个公共函数查找最长副作用链(标记见 references/ai_ready_architecture.md)。如果 chain_length >= 3:添加到 cascade_findings。在阶段 8 的 DATA-EXTENDED JSON 中输出 "runtime_cascades" 数组。严重程度:高(4+)、中(3)。
阶段 6:基线支持
灵感来自 ArchUnit FreezingArchRule——支持在遗留项目中渐进式采用。
baseline_path = docs/project/dependency_baseline.json
IF file_exists(baseline_path):
known = load_json(baseline_path)
current = serialize_violations(cycles + boundary_violations + sdp_violations)
new_violations = current - known
resolved_violations = known - current
# Report only NEW violations as findings
active_findings = new_violations
baseline_info = {new: len(new_violations), resolved: len(resolved_violations), frozen: len(known - resolved_violations)}
IF input.update_baseline == true:
save_json(baseline_path, current)
ELSE:
# First run -- report all
active_findings = all_violations
baseline_info = {new: len(all_violations), resolved: 0, frozen: 0}
# Suggest: output note "Run with update_baseline=true to freeze current violations"
阶段 7:计算评分
必须阅读: 加载 references/audit_worker_core_contract.md 和 references/audit_scoring.md。
penalty = (critical * 2.0) + (high * 1.0) + (medium * 0.5) + (low * 0.2)
score = max(0, 10 - penalty)
注意: 启用基线时,惩罚分仅根据 active_findings(新违规项)计算,不包括冻结项。
阶段 8:编写报告
必须阅读: 加载 references/audit_worker_core_contract.md 和 references/templates/audit_worker_report_template.md。
按照 references/audit_summary_contract.md 编写 JSON 摘要。在托管模式下,调用方会同时传入 runId 和 summaryArtifactPath;在独立模式下,工作器会根据共享合约生成自己的运行级制品路径。
# Build markdown report in memory with:
# - AUDIT-META (standard penalty-based: score, counts)
# - Checks table (cycle_detection, boundary_rules, sdp_validation, metrics_thresholds, baseline_comparison)
# - Findings table (active violations sorted by severity)
# - DATA-EXTENDED: {graph_stats, cycles, boundary_violations, sdp_violations, metrics, baseline}
IF domain_mode == "domain-aware":
Write to {output_dir}/644-dep-graph-{current_domain}.md
ELSE:
Write to {output_dir}/644-dep-graph.md
阶段 9:返回摘要
Report written: .hex-skills/runtime-artifacts/runs/{run_id}/audit-report/644-dep-graph-users.md
Score: 6.5/10 | Issues: 8 (C:1 H:3 M:3 L:1)
关键规则
必须阅读: 加载 references/audit_worker_core_contract.md。
- 自适应架构——绝不假设项目采用某一种风格;根据项目结构或文档进行检测
- 三级优先级——自定义规则 > architecture.md > 自动检测
- 混合支持——项目可能混合多种风格;按区域应用不同预设
- 自定义 = 安全模式——未检测到模式时,仅检查循环 + 指标(不报告错误的边界违规)
- 仅限内部模块——将标准库和第三方依赖排除在图外(仅包含项目模块)
- 基线模式——存在基线时,仅报告新违规项
- 循环修复——始终提供可执行的建议(DIP、提取共享模块、领域事件)
- 文件 + 行号——始终提供违规项的确切导入位置
完成定义
必须阅读: 加载 references/audit_worker_core_contract.md。
- 已发现架构(已应用自适应三级检测)
- 已根据导入语句构建依赖图(仅限内部模块)
- 已检测循环依赖(成对检查 + 传递性 DFS + 文件夹级)
- 已验证边界规则(禁止 + 允许 + 必需)
- 已计算指标(各模块的 Ca、Ce、I + 汇总 CCD、NCCD)
- 已验证 SDP(稳定模块不依赖不稳定模块)
- 如存在基线,已应用基线(仅报告新违规项)
- 若考虑领域:所有 Grep/Glob 均限定在 scan_path 范围内,发现项已标记领域
- 已按照 audit_scoring.md 计算评分
- 已将报告写入
{output_dir}/644-dep-graph[-{domain}].md(通过一次原子性 Write 调用) - 已按照合约写入摘要
参考文件
- 边界规则和预设:
references/dependency_rules.md - 指标和阈值:
references/graph_metrics.md - 导入模式:
references/import_patterns.md - 评分算法:
references/audit_scoring.md
版本: 1.0.0 最后更新: 2026-02-11