Skip to content

Commit 5a7d039

Browse files
feat: manifesto-driven pass/fail rule engine (#138)
* feat: manifesto-driven pass/fail rule engine Generic rule engine that evaluates function-level, file-level, and graph-level rules against the DB and returns pass/fail verdicts. - 9 rules: cognitive, cyclomatic, maxNesting (function), importCount, exportCount, lineCount, fanIn, fanOut (file), noCycles (graph) - Exits with code 1 on any fail-level breach (CI gate) - Exposed via CLI (codegraph manifesto), programmatic API, and MCP tool Impact: 9 functions changed, 7 affected * perf: eliminate WASM re-parse for native complexity + build optimizations Port complexity algorithm to Rust (complexity.rs) so the native engine computes cognitive/cyclomatic/maxNesting during extraction, eliminating the expensive WASM re-parse fallback that caused the 1.9→4.8 ms/file regression. Additional optimizations: - Cache line counts during parse (avoids re-reading every file from disk) - Use pre-loaded nodesByNameAndFile maps for extends/implements edges (replaces inline DB queries in the edge-building loop) - Optimize structure cohesion from O(dirs×edges) to O(edges+dirs) via reverse file→dirs index and single-pass edge aggregation Impact: 44 functions changed, 46 affected * fix: add missing complexity field to native extractors and manifesto tool to MCP tests - Add complexity: None to Definition initializers in go, rust, java, csharp, and php extractors (fixes Rust compile errors) - Add 'manifesto' to MCP test ALL_TOOL_NAMES (fixes tool count mismatch) - Log errors in manifesto query catch blocks instead of silencing them - Remove redundant isTestFile filtering already handled by SQL WHERE Impact: 7 functions changed, 7 affected --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 0337318 commit 5a7d039

File tree

24 files changed

+1453
-46
lines changed

24 files changed

+1453
-46
lines changed

crates/codegraph-core/src/complexity.rs

Lines changed: 456 additions & 0 deletions
Large diffs are not rendered by default.

crates/codegraph-core/src/extractors/csharp.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
4141
line: start_line(node),
4242
end_line: Some(end_line(node)),
4343
decorators: None,
44+
complexity: None,
4445
});
4546
extract_csharp_base_types(node, &class_name, source, symbols);
4647
}
@@ -55,6 +56,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
5556
line: start_line(node),
5657
end_line: Some(end_line(node)),
5758
decorators: None,
59+
complexity: None,
5860
});
5961
extract_csharp_base_types(node, &name, source, symbols);
6062
}
@@ -69,6 +71,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
6971
line: start_line(node),
7072
end_line: Some(end_line(node)),
7173
decorators: None,
74+
complexity: None,
7275
});
7376
extract_csharp_base_types(node, &name, source, symbols);
7477
}
@@ -83,6 +86,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
8386
line: start_line(node),
8487
end_line: Some(end_line(node)),
8588
decorators: None,
89+
complexity: None,
8690
});
8791
if let Some(body) = node.child_by_field_name("body") {
8892
for i in 0..body.child_count() {
@@ -99,6 +103,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
99103
line: start_line(&child),
100104
end_line: Some(end_line(&child)),
101105
decorators: None,
106+
complexity: None,
102107
});
103108
}
104109
}
@@ -116,6 +121,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
116121
line: start_line(node),
117122
end_line: Some(end_line(node)),
118123
decorators: None,
124+
complexity: None,
119125
});
120126
}
121127
}
@@ -134,6 +140,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
134140
line: start_line(node),
135141
end_line: Some(end_line(node)),
136142
decorators: None,
143+
complexity: None,
137144
});
138145
}
139146
}
@@ -152,6 +159,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
152159
line: start_line(node),
153160
end_line: Some(end_line(node)),
154161
decorators: None,
162+
complexity: None,
155163
});
156164
}
157165
}
@@ -170,6 +178,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
170178
line: start_line(node),
171179
end_line: Some(end_line(node)),
172180
decorators: None,
181+
complexity: None,
173182
});
174183
}
175184
}

crates/codegraph-core/src/extractors/go.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
2323
line: start_line(node),
2424
end_line: Some(end_line(node)),
2525
decorators: None,
26+
complexity: None,
2627
});
2728
}
2829
}
@@ -58,6 +59,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
5859
line: start_line(node),
5960
end_line: Some(end_line(node)),
6061
decorators: None,
62+
complexity: None,
6163
});
6264
}
6365
}
@@ -80,6 +82,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
8082
line: start_line(node),
8183
end_line: Some(end_line(node)),
8284
decorators: None,
85+
complexity: None,
8386
});
8487
}
8588
"interface_type" => {
@@ -89,6 +92,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
8992
line: start_line(node),
9093
end_line: Some(end_line(node)),
9194
decorators: None,
95+
complexity: None,
9296
});
9397
// Extract interface methods
9498
for j in 0..type_node.child_count() {
@@ -107,6 +111,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
107111
line: start_line(&member),
108112
end_line: Some(end_line(&member)),
109113
decorators: None,
114+
complexity: None,
110115
});
111116
}
112117
}
@@ -120,6 +125,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
120125
line: start_line(node),
121126
end_line: Some(end_line(node)),
122127
decorators: None,
128+
complexity: None,
123129
});
124130
}
125131
}

crates/codegraph-core/src/extractors/hcl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
6666
line: start_line(node),
6767
end_line: Some(end_line(node)),
6868
decorators: None,
69+
complexity: None,
6970
});
7071

7172
// Module source imports

crates/codegraph-core/src/extractors/java.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
4040
line: start_line(node),
4141
end_line: Some(end_line(node)),
4242
decorators: None,
43+
complexity: None,
4344
});
4445

4546
// Superclass
@@ -91,6 +92,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
9192
line: start_line(node),
9293
end_line: Some(end_line(node)),
9394
decorators: None,
95+
complexity: None,
9496
});
9597
if let Some(body) = node.child_by_field_name("body") {
9698
for i in 0..body.child_count() {
@@ -107,6 +109,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
107109
line: start_line(&child),
108110
end_line: Some(end_line(&child)),
109111
decorators: None,
112+
complexity: None,
110113
});
111114
}
112115
}
@@ -124,6 +127,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
124127
line: start_line(node),
125128
end_line: Some(end_line(node)),
126129
decorators: None,
130+
complexity: None,
127131
});
128132
}
129133
}
@@ -142,6 +146,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
142146
line: start_line(node),
143147
end_line: Some(end_line(node)),
144148
decorators: None,
149+
complexity: None,
145150
});
146151
}
147152
}
@@ -160,6 +165,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
160165
line: start_line(node),
161166
end_line: Some(end_line(node)),
162167
decorators: None,
168+
complexity: None,
163169
});
164170
}
165171
}

crates/codegraph-core/src/extractors/javascript.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use tree_sitter::{Node, Tree};
2+
use crate::complexity::compute_function_complexity;
23
use crate::types::*;
34
use super::helpers::*;
45
use super::SymbolExtractor;
@@ -23,6 +24,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
2324
line: start_line(node),
2425
end_line: Some(end_line(node)),
2526
decorators: None,
27+
complexity: Some(compute_function_complexity(node)),
2628
});
2729
}
2830
}
@@ -36,6 +38,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
3638
line: start_line(node),
3739
end_line: Some(end_line(node)),
3840
decorators: None,
41+
complexity: None,
3942
});
4043

4144
// Heritage: extends + implements
@@ -77,6 +80,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
7780
line: start_line(node),
7881
end_line: Some(end_line(node)),
7982
decorators: None,
83+
complexity: Some(compute_function_complexity(node)),
8084
});
8185
}
8286
}
@@ -90,6 +94,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
9094
line: start_line(node),
9195
end_line: Some(end_line(node)),
9296
decorators: None,
97+
complexity: None,
9398
});
9499
// Extract interface methods
95100
let body = node
@@ -110,6 +115,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
110115
line: start_line(node),
111116
end_line: Some(end_line(node)),
112117
decorators: None,
118+
complexity: None,
113119
});
114120
}
115121
}
@@ -132,6 +138,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
132138
line: start_line(node),
133139
end_line: Some(end_line(&value_n)),
134140
decorators: None,
141+
complexity: Some(compute_function_complexity(&value_n)),
135142
});
136143
}
137144
}
@@ -340,6 +347,7 @@ fn extract_interface_methods(
340347
line: start_line(&child),
341348
end_line: Some(end_line(&child)),
342349
decorators: None,
350+
complexity: None,
343351
});
344352
}
345353
}
@@ -554,6 +562,7 @@ fn extract_callback_definition(call_node: &Node, source: &[u8]) -> Option<Defini
554562
line: start_line(&cb),
555563
end_line: Some(end_line(&cb)),
556564
decorators: None,
565+
complexity: Some(compute_function_complexity(&cb)),
557566
});
558567
}
559568

@@ -570,6 +579,7 @@ fn extract_callback_definition(call_node: &Node, source: &[u8]) -> Option<Defini
570579
line: start_line(&cb),
571580
end_line: Some(end_line(&cb)),
572581
decorators: None,
582+
complexity: Some(compute_function_complexity(&cb)),
573583
});
574584
}
575585

@@ -583,6 +593,7 @@ fn extract_callback_definition(call_node: &Node, source: &[u8]) -> Option<Defini
583593
line: start_line(&cb),
584594
end_line: Some(end_line(&cb)),
585595
decorators: None,
596+
complexity: Some(compute_function_complexity(&cb)),
586597
});
587598
}
588599

crates/codegraph-core/src/extractors/php.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
3939
line: start_line(node),
4040
end_line: Some(end_line(node)),
4141
decorators: None,
42+
complexity: None,
4243
});
4344
}
4445
}
@@ -52,6 +53,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
5253
line: start_line(node),
5354
end_line: Some(end_line(node)),
5455
decorators: None,
56+
complexity: None,
5557
});
5658

5759
// Extends
@@ -102,6 +104,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
102104
line: start_line(node),
103105
end_line: Some(end_line(node)),
104106
decorators: None,
107+
complexity: None,
105108
});
106109
if let Some(body) = node.child_by_field_name("body") {
107110
for i in 0..body.child_count() {
@@ -118,6 +121,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
118121
line: start_line(&child),
119122
end_line: Some(end_line(&child)),
120123
decorators: None,
124+
complexity: None,
121125
});
122126
}
123127
}
@@ -135,6 +139,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
135139
line: start_line(node),
136140
end_line: Some(end_line(node)),
137141
decorators: None,
142+
complexity: None,
138143
});
139144
}
140145
}
@@ -147,6 +152,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
147152
line: start_line(node),
148153
end_line: Some(end_line(node)),
149154
decorators: None,
155+
complexity: None,
150156
});
151157
}
152158
}
@@ -165,6 +171,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
165171
line: start_line(node),
166172
end_line: Some(end_line(node)),
167173
decorators: None,
174+
complexity: None,
168175
});
169176
}
170177
}

crates/codegraph-core/src/extractors/python.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
3939
} else {
4040
Some(decorators)
4141
},
42+
complexity: None,
4243
});
4344
}
4445
}
@@ -52,6 +53,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
5253
line: start_line(node),
5354
end_line: Some(end_line(node)),
5455
decorators: None,
56+
complexity: None,
5557
});
5658
let superclasses = node
5759
.child_by_field_name("superclasses")

crates/codegraph-core/src/extractors/ruby.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
4040
line: start_line(node),
4141
end_line: Some(end_line(node)),
4242
decorators: None,
43+
complexity: None,
4344
});
4445
if let Some(superclass) = node.child_by_field_name("superclass") {
4546
// Walk superclass node to find the constant
@@ -56,6 +57,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
5657
line: start_line(node),
5758
end_line: Some(end_line(node)),
5859
decorators: None,
60+
complexity: None,
5961
});
6062
}
6163
}
@@ -74,6 +76,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
7476
line: start_line(node),
7577
end_line: Some(end_line(node)),
7678
decorators: None,
79+
complexity: None,
7780
});
7881
}
7982
}
@@ -92,6 +95,7 @@ fn walk_node(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
9295
line: start_line(node),
9396
end_line: Some(end_line(node)),
9497
decorators: None,
98+
complexity: None,
9599
});
96100
}
97101
}

0 commit comments

Comments
 (0)