pakka / gate / the second opinion

gate.

Exit-code evidence before "done." No more vibes-based merges.

What it does

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.

in detail

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.

/01

Four reviewers, one verdict.

Reviewer, security, architect, performance run in parallel. The orchestrator aggregates. You get one exit code, not four opinions.

/02

Spec-anchored.

Every finding cites a spec section, an issue, or a prior decision in recall. Drive-by nitpicks are filtered before delivery.

/03

Patches, not opinions.

Each blocking finding ships with a proposed patch. Apply it and re-run /pakka:review to advance to the next layer.

/04

Binary verdict.

exit 0 ships, exit 1 blocks. No "looks good with caveats" — the caveats are the findings.

The reviewers

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.

/ 01

reviewer

correctness

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.

~7smedian runtime
/ 02

security

attack surface

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.

~5smedian runtime
/ 03

architect

fit + drift

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.

~9smedian runtime
/ 04

performance

cost that multiplies

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.

v0.11.0shipped
A real run

How a finding looks.

From issue #47 — adding rate limiting to the webhook endpoint. Gate blocked, two findings, one fix patch generated.

findings
security conf 0.92 · severity blocking

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"
Proposed fix Add z.uuid() guard at function entry. Reject with 400 before the bucket lookup. Patch attached, 4 lines.
reviewer conf 0.88 · severity blocking

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"
Proposed fix Hash (provider_event_id, account_id) as the idempotency key before the billing call. Patch attached, 11 lines.
architect conf 0.81 · severity advisory

Diff introduces a third rate-limiter; recall has two prior decisions to consolidate.

at src/middleware/rate_limit.ts · recall #decision-2026-03-04 — "single bucket store"
Note Non-blocking. Will surface again on the next rate-limit change unless reconciled.