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.
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 review-merge-request workflow implements IronFlow's WorkflowHandler trait. Each step is an IronFlow operation with automatic retry and structured logging.
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?;
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::<FindingsOutput>();
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.
The worker posts results to the GitLab MR:
The GitLab commit status changes to success or failed. If the review has Critical findings, the pipeline can block the merge.
Each finding has a precise structure:
pub struct Finding {
pub file: String,
pub line: u32,
pub end_line: Option<u32>,
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<String>,
pub analysis_chain: Vec<String>,
pub ai_fix_prompt: Option<String>,
}
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.
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:
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.
Each project can customize the review via .coderift/context.md:
4 strict layers: rest, services, repositories, entities.
Violations to flag:
- rest importing repositories directly
- services doing raw SQL
- repositories containing business logic
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.
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.