Contract Checker

Weight: 10 | Tier: Pro | Requires LLM

💡

Works on any PR

This signal runs on every PR that touches both backend and frontend files — no test plan required. It analyzes the diff directly for contract mismatches.
💡
The #1 source of runtime crashes in fullstack PRs: the backend returns one shape, the frontend expects another.

What It Does

When a PR touches both backend (API routes, controllers, handlers) and frontend (components, pages, hooks) files, the Contract Checker reads the full diff and verifies that response shapes match consumer expectations. It catches mismatches like:

  • Backend returns { totals: { targets: N } } but frontend expects { totalTargets: N }
  • Backend uses snake_case, frontend expects camelCase
  • Frontend accesses fields the backend doesn't send
  • Status enum mismatches: "COMPLETED" vs "DONE"

Fast Path

The signal only runs when the PR diff contains both producer and consumer files. If a PR only touches backend code or only touches frontend code, the signal returns immediately with a pass — no LLM call needed.

File classification uses path patterns:

  • Producer patterns: /routes/, /api/, /controllers/, /handlers/
  • Consumer patterns: /app/, /pages/, /components/, .tsx

Contract-over-Assertion Trust

When the Contract Checker verifies that a file pair is compatible, the executor adapter trusts that result. If an assertion item fails because it can only read one file at a time, but the Contract Checker already confirmed cross-file compatibility, the failure is overridden to a pass. This prevents false negatives from single-file limitations.

Scoring

Score = (compatible contracts / total contracts) * 100. A score of 0 means every detected contract has a mismatch. As an LLM-based signal, it does not trigger the failure cap, but incompatible contracts appear as action items in the PR comment.

Real-World Example

In a SiegeKit PR that added a reports page with a new API endpoint, the backend returned { totals: { targets: N } } but the frontend's TypeScript interface expected { totalTargets: N }. This would have caused a runtime crash with undefined values. The Contract Checker would have caught this mismatch automatically.