gate.
Exit-code evidence before "done." No more vibes-based merges.
A diff doesn't ship until four agents agree.
When the model says "done," gate intercepts. It reads the diff, the spec, and the test output, then runs four reviewer agents in parallel. Each returns a verdict + confidence + line-anchored findings. The exit code is the only word that counts.
The model is a brilliant junior with a deadline. Gate is the senior who reads the PR. It doesn't argue with the diff — it returns evidence.
Findings are line-anchored and cite the spec. A finding without a citation is rejected before you ever see it.
Four reviewers, one verdict.
Reviewer, security, architect, performance run in parallel. The orchestrator aggregates. You get one exit code, not four opinions.
Spec-anchored.
Every finding cites a spec section, an issue, or a prior decision in recall. Drive-by nitpicks are filtered before delivery.
Patches, not opinions.
Each blocking finding ships with a proposed patch. Apply it and re-run /pakka:review to advance to the next layer.
Binary verdict.
exit 0 ships, exit 1 blocks. No "looks good with caveats" — the caveats are the findings.
Four briefs. Run in parallel.
Each agent is small, single-purpose, and given the spec as ground truth. None of them know about the others — gate aggregates.
reviewer
Reads the diff against the spec. Flags missing edge cases, wrong return types, off-by-one bugs, untested code paths. The closest thing to a careful peer reviewer that fits in a turn.
security
Treats every input as hostile. Flags unvalidated parameters, insecure defaults, leaked secrets, injection paths, broken auth. Will block on a missing length check faster than it will block on a bad name.
architect
Reads the diff against the surrounding system. Flags new abstractions duplicating existing ones, layering violations, premature generalisation, and decisions that contradict prior architecture notes in recall.
performance
Reads the diff for regressions that scale with input. Flags N+1 queries, O(n²) hot paths over unbounded input, allocation in tight loops, synchronous I/O in per-event paths. A finding with no stated scale is not a finding.
How a finding looks.
From issue #47 — adding rate limiting to the webhook endpoint. Gate blocked, two findings, one fix patch generated.
Missing input validation on user_id before bucket lookup.
at src/middleware/rate_limit.ts:42–48 · spec §3.2 — "all path-parameters validated before key derivation" z.uuid() guard at function entry. Reject with 400 before the bucket lookup. Patch attached, 4 lines.
No idempotency key on retry path; duplicate webhooks will be billed twice.
at src/routes/webhook.ts:108–121 · spec §4.1 — "retries MUST be idempotent at the billing boundary"(provider_event_id, account_id) as the idempotency key before the billing call. Patch attached, 11 lines.