Problem
Cora v0.2.0 relies entirely on the LLM for issue detection. This means:
- Cost — Every review costs LLM tokens, even for obvious patterns like
println! in production code or hardcoded API keys
- Inconsistency — The LLM may catch
unwrap() on one run but miss it on the next (especially with non-zero temperature)
- Coverage gaps — Some patterns are universally wrong (hardcoded secrets, SQL injection) and should NEVER be missed, regardless of LLM quality
Alibaba's Open Code Review uses "Template-engine-based rule matching" as the first pass — deterministic rules that catch known-bad patterns before the LLM even runs. This is a no-brainer for cora.
Proposed Solution: Pre-LLM Rule Engine
Architecture
Step 1: Deterministic Rules (zero LLM tokens, instant)
─────────────────────────────────────────────────────
Run all rules against changed files → produce findings
Time: ~10-50ms (regex + simple AST matching)
Step 2: Inject Rule Findings into LLM Prompt
──────────────────────────────────────────────
[Pre-LLM Analysis]
⚠️ src/auth/login.rs:42 — println!() in production code (rule: no-debug)
🔴 src/api/handler.rs:78 — String concatenation in SQL query (rule: sql-injection)
⚠️ Cargo.toml:15 — dependency "serde@1.0.0" has known CVE (rule: audit)
LLM validates these findings AND discovers deeper issues.
Step 3: LLM Review (same as current)
──────────────────────────────────
LLM receives rule findings as additional context.
It can:
- Confirm the finding (add to output)
- Dismiss as false positive (omit from output)
- Expand with deeper analysis (e.g., "yes this is SQL injection, AND here's the fix")
Step 4: Merge
─────────────
Final output = LLM issues (deduped with rule findings)
Rule findings NOT confirmed by LLM are still reported
(deterministic rules don't need LLM validation)
Built-in Rules
Priority 0 — Security (always on):
| Rule ID |
Pattern |
Severity |
Languages |
sec-hardcoded-secret |
password =, api_key =, token =, secret = with string literals |
critical |
All |
sec-sql-concat |
String concatenation in SQL query patterns ("SELECT *" +, format!("SELECT, f"SELECT) |
critical |
All |
sec-hardcoded-url |
http:// (non-HTTPS) in production code |
major |
All |
sec-tls-disabled |
tls_built_in_root_certs(false), verify=False, InsecureRequestWarning |
critical |
Rust, Python |
Priority 1 — Bugs (always on):
| Rule ID |
Pattern |
Severity |
Languages |
bug-unwrap |
.unwrap() without error handling on Result/Option |
minor |
Rust |
bug-expect |
.expect("...") in production paths (not tests) |
minor |
Rust |
bug-println |
println!, dbg!, print! in non-test files |
minor |
Rust |
bug-todo |
TODO, FIXME, HACK, XXX comments |
info |
All |
bug-dead-code |
Unused imports (language-specific patterns) |
info |
All |
Priority 2 — Quality (opt-in):
| Rule ID |
Pattern |
Severity |
Languages |
qual-naming |
Non-snake_case function names, non-camelCase types |
info |
Rust, Go |
qual-clone |
.clone() on large types (potential perf issue) |
info |
Rust |
qual-error-ignore |
let _ = result, result?; without logging |
minor |
All |
qual-long-function |
Functions >100 lines (heuristic) |
info |
All |
Custom Rules via Config
Users can define project-specific rules:
# .cora.yaml
rules:
enabled: true # default: true
custom:
- id: "no-console-log"
pattern: "console\\.(log|debug|info|warn|error)\\("
severity: "minor"
languages: ["javascript", "typescript"]
message: "Remove console.log statements"
- id: "no-hardcoded-port"
pattern: ":8080|:3000|:5000"
severity: "info"
languages: ["all"]
message: "Use environment variable for port configuration"
exclude: ["test/", "tests/"]
- id: "no-unwrap-in-handler"
pattern: "\\.unwrap\\(\\)"
severity: "major"
languages: ["rust"]
message: "Use proper error handling in API handlers"
exclude: ["tests/", "#\[test"]
Rule Matching Engine
pub struct RuleEngine {
rules: Vec<Rule>,
}
pub struct Rule {
id: String,
pattern: Regex,
severity: Severity,
languages: Vec<String>,
message: String,
exclude_patterns: Vec<Regex>, // paths to skip (tests/, generated/)
}
impl RuleEngine {
pub fn run(&self, diff: &DiffChunk, file_path: &str) -> Vec<RuleFinding> {
// 1. Check if file language matches rule
// 2. Check if file path matches any exclude pattern
// 3. Match regex against changed lines only (not full file)
// 4. Return findings with file, line, severity, message
}
}
Performance
- Time: ~10-50ms for typical PR (regex matching on changed lines only)
- Token cost: ZERO LLM tokens for rule execution
- Prompt impact: ~200-400 tokens for rule findings injected into LLM prompt
- Net effect: Slightly higher prompt tokens (+200) but catches 100% of deterministic patterns
Interaction with Other v0.3 Features
| Feature |
Interaction |
| Context Chain (#114) |
Rules run on changed lines → context chain provides depth for LLM confirmation |
| File Bundling (#115) |
Rules run PER BUNDLE → findings scoped to bundle's files |
| Line Positioning (#116) |
Rule findings have exact line numbers (deterministic) → no positioning drift |
--progress Events
{"type":"rules_started","rule_count":12}
{"type":"rules_complete","findings":3,"time_ms":23}
{"type":"rule_finding","rule_id":"sec-sql-concat","file":"src/api/handler.rs","line":78,"severity":"critical"}
{"type":"rule_finding","rule_id":"bug-println","file":"src/auth/login.rs","line":42,"severity":"minor"}
{"type":"rule_finding","rule_id":"bug-todo","file":"src/engine/llm.rs","line":156,"severity":"info"}
Acceptance Criteria
Risks
| Risk |
Mitigation |
| False positives on regex rules |
Exclude patterns for test/generated files; severity "info" for noisy rules |
| Regex evasion (obfuscated secrets) |
LLM still catches what rules miss (defense in depth) |
| Performance on 100+ file PRs |
Rules only scan changed lines, not full files; ~1ms per file |
References
- Alibaba Open Code Review: "Fine-grained rule matching" — template-engine rules vs language-driven
- Semgrep: static analysis rule patterns (good reference for regex rule design)
- Rust clippy: lint rule categorization (must/warn/deny → our critical/major/minor/info)---
Part of #147 (v0.4 Engine Pipeline) — Phase 1
Problem
Cora v0.2.0 relies entirely on the LLM for issue detection. This means:
println!in production code or hardcoded API keysunwrap()on one run but miss it on the next (especially with non-zero temperature)Alibaba's Open Code Review uses "Template-engine-based rule matching" as the first pass — deterministic rules that catch known-bad patterns before the LLM even runs. This is a no-brainer for cora.
Proposed Solution: Pre-LLM Rule Engine
Architecture
Built-in Rules
Priority 0 — Security (always on):
sec-hardcoded-secretpassword =,api_key =,token =,secret =with string literalssec-sql-concat"SELECT *" +,format!("SELECT,f"SELECT)sec-hardcoded-urlhttp://(non-HTTPS) in production codesec-tls-disabledtls_built_in_root_certs(false),verify=False,InsecureRequestWarningPriority 1 — Bugs (always on):
bug-unwrap.unwrap()without error handling onResult/Optionbug-expect.expect("...")in production paths (not tests)bug-printlnprintln!,dbg!,print!in non-test filesbug-todoTODO,FIXME,HACK,XXXcommentsbug-dead-codePriority 2 — Quality (opt-in):
qual-namingqual-clone.clone()on large types (potential perf issue)qual-error-ignorelet _ = result,result?;without loggingqual-long-functionCustom Rules via Config
Users can define project-specific rules:
Rule Matching Engine
Performance
Interaction with Other v0.3 Features
--progressEvents{"type":"rules_started","rule_count":12} {"type":"rules_complete","findings":3,"time_ms":23} {"type":"rule_finding","rule_id":"sec-sql-concat","file":"src/api/handler.rs","line":78,"severity":"critical"} {"type":"rule_finding","rule_id":"bug-println","file":"src/auth/login.rs","line":42,"severity":"minor"} {"type":"rule_finding","rule_id":"bug-todo","file":"src/engine/llm.rs","line":156,"severity":"info"}Acceptance Criteria
excludepatterns skip test files by default.cora.yamlwith regex pattern + severityrules.enabled: falsedisables feature--progressemits per-rule events with timingRisks
References
Part of #147 (v0.4 Engine Pipeline) — Phase 1