Skip to content

Back to the catalog

Show HN: Farol – A risk map of a pull request for the person who approves it
github.comrepo

Show HN: Farol – A risk map of a pull request for the person who approves it

farol is a free, MIT-licensed CLI and GitHub Action that runs after an AI code review and gives the human approver a risk verdict (fast track, quick review, careful review) and a 0-100 score for a pull request. It uses the TypeScript type checker, go/types and Python ast to trace which pages, APIs and jobs a change reaches, and it adds diff coverage, security findings and contract breaks. It never executes or sends code.

TL;DR Ranks the riskiest files in a pull request with deterministic static analysis, so the human approving after an AI review knows where to look.
Document

What it claims

farol scores how risky a pull request is to merge and ranks the files that carry the risk. It runs after an AI review, not in place of one. The output is deterministic: the same PR gives the same report, and every reach claim comes with its call chain. It is shadow-only. It comments, but it never approves, requests changes or fails a check. When it cannot read part of a change, it labels the PR "unknown" and asks for careful review instead of calling it safe.

How it works

  1. Diff the merge-base against the head. Renames are detected, and whitespace-only and trailing-comma lines are ignored.
  2. Parse both trees without running them, using the TypeScript compiler API, go list plus go/types offline, and Python ast. This builds entry points and a declaration-use graph.
  3. Compare declarations by a hash of their code, so comments and formatting do not count.
  4. Find the shortest chain from each changed declaration to every entry point that runs it.
  5. Combine reach × untested share, findings, contract changes and dependencies into a score.

The score is described as the chance that at least one risky item breaks something. Any of the following forces "careful review": a score of 60 or more, a breaking contract change, a high-severity finding, an untested central file, or unreadable code. Reach is per declaration for TypeScript frontends, Go and Python. It is only per module for TypeScript/JavaScript backends (per resolver for GraphQL), JavaScript files and renamed files.

Each push produces two reports: the full PR, and only the commits since the previous push (analyze --since <sha>). Optional Datadog traffic and error data is shown for information and never changes the score.

Evidence

The README reports results on 662 past commits from four repositories. Fast track had 0 problems in 56 commits, and the score's AUC was 0.72 to 0.83. The repositories and the definition of "problem" are not given. Analysis takes about 3 s for a 300-file Python service, and 8 to 10 s for a large TypeScript monorepo or a cold-cache Go service.

How to use it

It needs Node.js 22+ and git, plus go for Go projects or python3 3.11+ for Python ones. Start with a one-line farol.config.json, for example { "language": "python", "surface": "backend" } or { "routes": { "adapter": "next" } }. Then run:

npx @winninjs/farol routes
npx @winninjs/farol analyze --base origin/main --head HEAD --out farol.json
npx @winninjs/farol html --in farol.json --out farol.html

Coverage, lint and audit reports from existing CI become diff coverage and findings. Python uses coverage.py JSON, ruff with S rules, and pip-audit. Go uses -coverprofile, golangci-lint with gosec, and govulncheck. TypeScript uses Istanbul, ESLint and npm/pnpm audit. A report that is not supplied shows as "not verified", never as passing. Go analysis needs go mod download for both base and head first.

The Action is winnin/farol/action, and the README advises pinning it to a commit SHA. Only the comment command is GitHub-specific. analyze, html and report work on any platform. Draft PRs are skipped, and fork PRs get no comment.

flowchart LR
  PR[PR code] --> T[tests job: no secrets, read-only token]
  T -->|coverage, lint, audit reports| F[farol job: never runs PR code]
  D[farol-usage: Datadog daily] -.->|optional, score unchanged| F
  F --> C[PR comment + HTML report]

Limits

  • Static reading only. Runtime-built values are invisible: computed route paths, getattr, importlib, DI containers and registries filled in loops. Unresolved calls are assumed to reach more, never less.
  • TypeScript frontends. Only React Router, Next.js and file-based TanStack Router are read. Code-based TanStack trees, Remix conventions, Vue and Svelte are not.
  • Python. Django REST Framework routers are not read.
  • Go. Prefixes from middleware other than chi/gin/echo groups are not applied, and wrapped routers such as Gitea's web.Router are not read.
  • Contract checks cover GraphQL, FastAPI/Pydantic, Go handlers and gRPC. Plain TypeScript REST endpoints and frontends are not covered.
  • Thresholds. The authors call the score and verdict thresholds heuristics checked on a few repositories, and recommend running farol calibrate on your own history.

Organisations named here

Technologies named here

Techniques named here

  • Static-analysis risk triage of pull requests
  • Risk scoring and verdicts
  • Fail-closed analysis
  • Incremental review since last push
  • Contract change detection

Also named here

  • static analysis