Automated code review with CodeRift
Code review is the bottleneck of most teams. The merge request is open, the author waits, the reviewer is busy with something else. When the review finally comes, it's often superficial: a quick glance at the diff, a "LGTM", merge. Logic bugs, security flaws and architecture violations slip through. I built CodeRift to solve this problem. It's an automated code review platform that analyzes every GitLab merge request with a 7-step pipeline: diff parsing, AST analysis via tree-sitter, TOML rule engine, parallel specialized AI agents, cross-file correlation, false positive validation, and inline comment publishing directly in the MR. The whole thing is orchestrated by IronFlow, my Rust workflow engine. The architecture CodeRift follows an API + Workers model. The API (Rust/Axum/SQLx) handles persistence, GitLab OAuth authentication and webhooks. IronFlow workers poll the API for pending reviews, execute them, and post results. The API/Worker separation is the same as IronFlow: the API owns persistence and never executes. Scaling means launching more workers. The 7-step review pipeline The review-merge-request workflow implements IronFlow's WorkflowHandler trait. Each step is an IronFlow operation with automatic retry and structured logging. Steps 1-3: Preparation The worker updates the review status in the database, posts a "review in progress" note on the GitLab MR, and sets the commit status to running. The developer immediately sees that the AI review has started. ctx.operation("update-review-status", &update_status_op).await?; ctx.http( "post-review-started", HttpConfig::post(¬es_url) .header("PRIVATE-TOKEN", &CONFIG.gitlab.bot_token) .json(serde_json::json!({ "body": review_started_message(language) })), ).await?; commit_status::set(ctx, "set-commit-status-running", &proj, &payload.head_sha, payload.review_id, "running", "AI review in progress...").await?; Step 4: The review pipeline (the core) This is where the work happens. The pipeline follows this sequence: TOML rules: CodeRift loads a set of rules embedded at compile time (include_dir!). Each rule targets a language, file patterns, and defines a regex pattern with exceptions. For example, the rust-unwrap rule detects .unwrap() in production code while excluding test files: [[rules]] id = "rust-unwrap" severity = "error_handling" score = 7 title = ".unwrap() in production code" languages = ["rust"] file_patterns = ["*.rs"] exclude_patterns = ["*_test.rs", "tests/*"] type = "regex" pattern = '\.unwrap\(\)' negative_pattern = '#\[test\]|#\[cfg\(test\)\]|mod tests' Rules cover Rust, TypeScript, Python, Go, SQL, and common patterns (OWASP, prompt injection). Each project can add its own rules or disable server rules via a .coderift/context.md file. AST analysis: tree-sitter parses the modified files and extracts a symbol index (definitions, references, cross-file edges). This index serves two purposes: building a dependency graph to group related files into the same chunks, and detecting structural patterns that regex can't see. Chunking: files are grouped into chunks of 5 (configurable) for parallel review. When the dependency graph is available, related files stay in the same chunk to give the agent context on both sides of an interface. Specialized AI agents: each chunk is reviewed by 3 agents in parallel, each with a different focus: Agent Model Focus Security Opus 4.6 Injections, XSS, SSRF, access control, secrets Bugs Opus 4.6 Incorrect logic, edge cases, type errors Performance Sonnet 4.6 N+1, unnecessary allocations, algorithmic complexity Each agent has a configurable USD budget and a maximum of 4 turns. The IronFlow provider manages execution and cost tracking. If an agent fails (schema error, timeout), the pipeline continues with results from the others. let mut agent = Agent::new() .system_prompt(system) .prompt(prompt) .model(Model::OPUS_46) .max_turns(4) .max_budget_usd(budget) .output::(); Cross-file correlation: a Sonnet 4.6 agent receives all findings from all chunks and identifies vulnerabilities that span multiple files. For example, an unvalidated input in a handler that reaches a SQL query in another file. False positive validation: a final agent filters out irrelevant findings. Common false positives: an .unwrap() in test code, a pattern flagged in a comment, a finding targeting unchanged code, or a score inflated relative to actual risk. Steps 5-6: Publishing The worker posts results to the GitLab MR: A summary with a walkthrough of the MR, files grouped by functional area, Mermaid sequence diagrams for non-trivial flows, and pre-merge checks (security, error handling, tests). Inline comments on the relevant lines, with severity (Bug, Security, Performance, Error Handling, Suggestion), a criticality score (Critical/Major/Moderate/Minor), and a fix suggestion. Step 7: Commit status The GitLab commit status changes to success or failed. If the review has Critical findings, the pipeline can block the merge. Findings Each finding has a precise structure: pub struct Finding { pub file: String, pub line: u32, pub end_line: Option, pub severity: Severity, // Bug | Security | Performance | ErrorHandling | Suggestion pub score: u8, // 0-10, maps to Critical/Major/Moderate/Minor pub title: String, pub comment: String, pub suggestion: Option, pub analysis_chain: Vec, pub ai_fix_prompt: Option, } The analysis_chain field contains the agent's step-by-step reasoning. The ai_fix_prompt field is a ready-to-use prompt to automatically fix the finding. Why not just call Claude Code? The first version of this system used an n8n node that invoked Claude Code CLI. It worked for small MRs, but the limitations showed up fast: No structured context: Claude saw a raw diff without understanding the relationships between files No rules: impossible to reliably encode project conventions No deduplication: the same findings came back in duplicates or triples No cost control: a 2000-line diff could cost $15 No validation: no filter on false positives CodeRift solves each of these problems. The structured pipeline (rules + AST + specialized agents + validation) produces more accurate findings than the "send the entire diff to an LLM and hope" approach. Per-project configuration Each project can customize the review via .coderift/context.md: # Architecture 4 strict layers: rest, services, repositories, entities. Violations to flag: - rest importing repositories directly - services doing raw SQL - repositories containing business logic # Frontend API types in app/types/generated.ts are auto-generated. Never manually redefine a type that exists in this file. This file is injected into agent prompts as untrusted context (sandboxed, with no ability to alter review instructions). Agents use it to understand project conventions and flag specific violations. The stack CodeRift is built with the ecosystem I developed: IronFlow for workflow orchestration, the same layered architecture (entities/repositories/services/rest) as Netir, and MCP RTK optionally to reduce tokens on connected MCP servers. The Claude Code setup and skills I use daily served as the foundation for structuring CodeRift's review prompts.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to