AGENTS.md is a wiki page, not a merge gate

Most teams have written a team-rules file. Many have a CONTRIBUTING.md. Some have an AGENTS.md (or its sibling CLAUDE.md). Almost none of those files actually block a PR.

The shape is always the same: someone writes the rules in good faith, files the doc in the repo root, links it from the PR template, and three months later a new engineer pushes a PR that ignores every rule. The reviewer either copy-pastes a paragraph from the doc into a comment, or skips it because "this one is fine." The doc becomes a wiki page — nice to have, easy to ignore, fast to forget.

We bet it does not have to be that way. So CodeOtter reads the doc, treats it as a typed input to the review pipeline, fails the PR when a rule is violated, and learns from every reviewer dismissal. The doc stops being a wiki page and starts being a merge gate. This post walks through the wire-level mechanics.

Zero-config detection at the repo root

Every repository onboarded in CodeOtter is automatically inspected at its root via its forge API (GitHub, GitLab, Bitbucket) for AGENTS.md and/or CLAUDE.md. If either (or both) files exist, team guidelines are automatically injected into the review context — into the System 1 state under the review_guidelines field, and into the System 2 prompt under a system message block. No .coderabbit.yaml. No .greptile.yaml. No manual mapping. The detection happens on every review run, so a doc added in the middle of a sprint starts working on the next push with zero operator action.

When a rule is violated, the engine raises a finding and the Guidelines merge gate (one of the 9 typed binary pre-merge gates: Title, Description, Security, Complexity, Tests, Docs, Scope, Guidelines, Issue Requirements) flips to fail. The PR cannot be merged while the gate is open.

The detection is deterministic, not heuristic. Either the file is there or it is not. If the team deletes the file, the gate stops firing. If the team adds it, the gate starts firing on the next push. There is no manual toggling, no enable flag, no migration to write.

Smart Context Outside-Diff Call Graph Impact Slicing

A naive rule engine reads each doc in isolation and flags every diff hunk that "looks suspicious" against the rule text. That produces two failure modes:

  1. False positives. A function is renamed but the rule fires anyway because the renamed symbol contains a banned substring.
  2. False negatives. A change to a public function silently breaks an unmodified caller in another file, and the rule engine never sees the call site because the call site is not in the diff.

CodeOtter's Smart Context Outside-Diff Call Graph Impact Slicing closes both. On every diff it identifies the function, method, and class symbols that were added, modified, or deleted, and then it scans the rest of the repository for callers and importers of those symbols. The rule engine is then asked the right question: given the rule and the modified symbol's blast radius, is this change safe?

Concretely: if parseUserInput is modified and authMiddleware.ts imports it but is not in the diff, the rule engine is given both the modified symbol's new shape and the caller's expected signature. If the change broke the contract, the Guidelines gate fires on the call site, even though the call site was not touched in the PR. This is the difference between "this PR is fine, it only touched three files" and "this PR is fine, but it broke the auth flow that those three files are downstream of."

The slicing is what makes the gate trustworthy enough to actually block. Without it, every team would flip it off after the first false positive. With it, you flip it on once and never touch it again.

Inline suggestion blocks that the author can commit with one click

Findings are rendered in the review report with line-level :L<line> anchors and an exact suggestion code replacement. Each card has two buttons:

  • Copy fix — paste the replacement anywhere.
  • Post inline suggestion to GitHub — calls POST /api/review-suggestions and posts a committable ```suggestion block on the affected line of the PR diff. The author then clicks Commit suggestion on GitHub, and the fix lands as a separate commit on the PR branch.
- if (!user.email) throw new Error("email required");
+ if (!user?.email?.length) throw new ValidationError("user.email required");

This is not a paste-by-hand, IDE-switcher, "I'll fix it later" workflow. The fix lands as a separate commit on the PR branch, so the next review re-runs against the fix, the Guidelines gate flips back to pass, and the merge unblocks. There is no merge conflict to resolve by hand because the suggestion block is generated from the current line range, not a stale snapshot.

The team-learning loop: 1-click "Dismiss & remember rule"

The flip side of "the gate fires" is "the gate fires too often." A team will hit the same false positive on every PR until the rule is either corrected or the pattern is taught to the engine. CodeOtter supports both, and they are one click apart.

When a reviewer dismisses a finding as not-applicable, "Dismiss & remember rule" persists that finding's shape as a repository team learning under repoSettings[repo].learnings. On every future review run, the learning is re-injected as repository_team_learnings into both the System 1 state and the System 2 prompt. Neither engine raises the same false positive twice.

The persist record is per-repository, not global. Each repo onboards its own set of learnings — a frontend/ repo's "no Tailwind" rule does not leak into a backend/ repo that already uses Tailwind by choice.

Admins see active learnings as badges on every PR review and can inspect, edit, or remove them under Settings → Repositories → ⚙️. A learning is just a typed record: { finding_pattern, dismissed_by, dismissed_at, reason }. There is no hidden fine-tuning, no opaque vector store, no magic embedding. The rule is text, and the admin can read it.

The loop is the win. The doc stops being a wiki page because every dismissal teaches the engine. The team does not have to re-state the rule in twenty PR comments. The reviewer does not have to re-explain "yes, this is fine" to every new engineer.

The merge gate is no longer optional

The shape of the system after one month of use:

  • A team writes AGENTS.md once.
  • The system enforces it on every push via the Guidelines merge gate.
  • A reviewer dismisses a false positive with one click; the engine learns.
  • An author commits the suggested fix with one click on GitHub; the gate flips back to pass.
  • The doc never gets stale, never gets ignored, and never has to be re-stated in a PR comment.

That is what we wanted. The doc is a merge gate. The merge gate is the doc.

Where this still loses, honestly

  • AGENTS.md is convention, not law. If your team writes vague rules ("code should be clean"), the gate will fire inconsistently. Tight rules ("this PR requires user.email to be validated at the boundary") work much better.
  • Cross-repo rule inheritance is opt-in. A team learnings record is scoped to the repository that produced it. If you want a frontend/ repo to inherit a shared-standards/ repo's learnings, you copy them over.
  • The gate is binary. "Guideline" is "pass when no rule is violated" — there is no "warning" tier. Some teams want a yellow "reviewer should look at this" tier before the gate flips red. We do not have that yet. It is on the roadmap.
  • The dismiss + model combination for the dismissed-learning injection is heuristic. The system re-injects the learning text verbatim into both prompts. If your learning is too long, it can crowd out the actual diff hunk. Keep dismissals to one or two sentences.
  • No hosted tier. CodeOtter is free to self-host, zero per-seat billing. There is no SaaS escape hatch — your AGENTS.md and your team learnings stay inside your infrastructure. License is GNU Affero General Public License v3.0 (AGPL-3.0), OSI-approved copyleft.

Where to go next

If you want a CodeOtter review on your own repo, follow the README quickstart. Drop an AGENTS.md at your repo root, push a PR that violates a rule, and watch the Guidelines gate close.