Show HN: Farol – a risk map of a pull request for the person who approves it Developer Winnin released Farol, a free MIT-licensed CLI and GitHub Action that scores each pull request's merge risk from 0 to 100 and ranks the riskiest files first, running after AI review rather than replacing it. Farol works from static analysis using the compilers' own view of the code — the TypeScript type checker, go/types and Python ast — so the same pull request always gets the same answer, and it never executes or transmits the code. The tool assigns one of three verdicts (fast track, quick review, careful review) based on reach, diff coverage, security findings, contract breaks and dependency changes, and is installable via npx @winninjs/farol or the winnin/farol/action GitHub Action. A free, open-source CLI and GitHub Action. After your AI review, farol tells the person who approves how risky the merge is and which files carry the risk, so the riskiest changes get the most attention. Website https://winnin.github.io/farol/ · Live demo https://winnin.github.io/farol/demo/ · Get started get-started · Reference Agents write the code and agents review it. The last call is still a person's: approve the merge or not. That approval is the scarce part, and spread evenly over every pull request it turns into a skim, which is how a one-line change to a shared hook takes down forty pages. farol comes after the AI review, not instead of it. Your review agent comments on the lines in the diff. farol gives the person who approves what the agent cannot reliably give: the risk of accepting the change, and where it lives . The goal is not only a faster review: low-risk pull requests can go fast, and the risky ones get read line by line, starting with the files that carry the risk. It works from static analysis the compilers' own view of your code, not a model's guess , so the same pull request always gets the same answer. The Claude Code team describes the same principle in How the Claude Code team does code review https://claude.dev/blog/solving-code-review-at-anthropic/ : review effort follows the risk of the change. As they put it, "The change sets the tier, not the author." farol Portuguese for lighthouse does the triage before the reviewer opens the diff: - What does this change reach? Every existing page, API operation, RPC method, job and script that runs the changed code, with the call chain to each. - How risky is the merge? A verdict fast track , quick review , careful review and a 0–100 risk score from reach, diff coverage, security findings, contract breaks and dependency changes. - Where do I start? The riskiest files first, with inline diffs, untested lines marked, and a checklist of what to try by hand. Free and open source MIT . Run it as a CLI with npx @winninjs/farol , or on every pull request with the GitHub Action winnin/farol/action . No account, no hosted service: it runs on your machine or in your CI runner, comments on every PR and never blocks a merge. It never executes your code and never sends it anywhere. See farol on a real pull request → https://winnin.github.io/farol/demo/ The demo holds the reports farol produced in CI for a pull request on the FastAPI full-stack template https://github.com/fastapi/full-stack-fastapi-template : a FastAPI backend and a React frontend with TanStack Router. The pull request adds a status to items and an Insights page, with the kind of problems a reviewer should catch: a breaking API change, SQL built with an f-string, a shared component without tests. - Backend report https://winnin.github.io/farol/demo/backend.html : ⚠ careful review, a breaking contract change and a high-severity security finding. - Frontend report https://winnin.github.io/farol/demo/frontend.html : ⚠ careful review, a new page and the existing pages a shared component reaches. The reports are interactive: mark files as reviewed, mark a page as behind a feature flag, open the inline diffs, follow the test plan and copy the review record from the Result tab. They are the same files the pull request's comments link to the pull request https://github.com/erich-oliveira-winnin/full-stack-fastapi-template/pull/1 ; the copies live in docs/demo/ https://github.com/winnin/farol/blob/main/docs/demo . | | AI review agents | farol | |---|---|---| | Reads | the diff, and whatever context fits in the prompt | the whole program at the base and the head, with the compilers TypeScript type checker, go/types , Python ast | | Tells you | what may be wrong in these lines | what these lines reach, how risky the merge is, and where to look first | | Answers are | probabilistic: worded differently on every run | deterministic: the same PR gives the same report, and every claim comes with its call chain | | When unsure | may sound confident anyway | says unknown and asks for a careful review | | Runs | a model, usually a hosted service | locally or in your CI: it never executes your code and never sends it anywhere | Use both, in that order. The agent reviews the lines first; then farol tells the person who approves how much attention the merge needs, which files deserve it, which pages and endpoints to test by hand and which contract changes need their clients checked. The agent's review is not enough exactly when a small change has a large reach, and that is what farol shows. - Review attention goes where the risk is. A 40-file PR usually has two or three files that can actually hurt. farol ranks them, so the reviewer reads those closely and skims the rest with a clear conscience. - It follows the code, not the file names. farol builds a declaration graph with the real compilers the TypeScript type checker, go/types , Python's ast . A change to formatPrice lists the twelve pages that render it, not "1 file changed". - Contract breaks are caught before clients are. Removed response fields, new required parameters, changed types in GraphQL, FastAPI, Go handlers and gRPC, with the services that call each endpoint. - Review the whole PR, or just what changed since your last look. Every push gets two reports: the full pull request, and only the commits since the previous push. After a round of fixes, the reviewer checks the new commits instead of starting over. - It uses what your CI already has. Coverage, lint, security and audit reports become diff coverage and findings on the changed lines. Nothing runs twice. - Fail closed. When farol cannot read part of a change, it says so unknown and asks for a careful review. It never calls something safe because it did not understand it. - Built for the human in the loop. Mark files as reviewed, mark pages as behind a feature flag, check off findings, and paste the result back in the PR as a record of what was reviewed. - Live demo live-demo - farol after your AI review farol-after-your-ai-review - Get started get-started - Recipes recipes : React Router react-frontend-react-router · Next.js nextjs · TanStack Router tanstack-router · TypeScript API typescript-api-graphql-or-rest · Go go-service · Python python-service · Datadog add-datadog-traffic-and-errors - What is supported what-is-supported - Reading the review reading-the-review - How it works how-it-works · Security security-and-privacy · Limitations limitations · FAQ faq - Reference https://github.com/winnin/farol/blob/main/docs/reference.md every command, option and configuration key You need Node.js 22+ and git plus go for Go projects, python3 3.11+ for Python ones . Nothing to install: run farol with npx @winninjs/farol . 1. Describe your project in a farol.config.json at its root. One line is enough to start; pick your stack: | Your project | farol.config.json | Full recipe | |---|---|---| | React app with React Router | { "routes": { "file": "src/routes.tsx" } } the file that builds the route table | React Router react-frontend-react-router | | Next.js app Pages or App Router | { "routes": { "adapter": "next" } } | Next.js nextjs | | React app with TanStack Router | { "routes": { "adapter": "tanstack-router", "dir": "src/routes" } } | TanStack Router tanstack-router | | TypeScript API GraphQL, REST | { "surface": "backend" } | TypeScript API typescript-api-graphql-or-rest | | Go service | { "language": "go", "surface": "backend" } | Go go-service | | Python service FastAPI, Flask, Django, Celery… | { "language": "python", "surface": "backend" } | Python python-service | 2. Check what farol sees: the pages, endpoints, jobs and scripts it found. npx @winninjs/farol routes 3. Analyze a branch against its target and open the report. npx @winninjs/farol analyze --base origin/main --head HEAD --out farol.json npx @winninjs/farol html --in farol.json --out farol.html && open farol.html xdg-open on Linux cd path/to/your/react-app echo '{ "routes": { "file": "src/routes.tsx" } }' farol.config.json npx @winninjs/farol routes lists your pages: "/", "/products", "/settings"… git checkout my-feature-branch npx @winninjs/farol analyze --base origin/main --head HEAD --out farol.json npx @winninjs/farol html --in farol.json --out farol.html && open farol.html The report already shows the verdict, the score, the files to start with, the pages the change reaches and what to test by hand. Next steps: add your coverage, lint and audit reports and run farol on every PR with the recipe for your stack recipes . Every option is in the reference https://github.com/winnin/farol/blob/main/docs/reference.md and, per stack, in examples/README.md https://github.com/winnin/farol/blob/main/examples/README.md . Running it often? npm install --global @winninjs/farol gives you a plain farol command. Each recipe has three parts: the farol.config.json to commit, the reports to produce, and the GitHub Actions workflow. The workflows in examples/ https://github.com/winnin/farol/blob/main/examples are complete and share one shape: 1. tests runs your tests, linter and audit and keeps the reports. It runs the PR's code, so it has no secrets and a read-only token. If you already have a test job, keep it and add its upload step. 2. farol runs after it pass or fail , reads git and the reports, and posts a new comment on every push with a link that opens the HTML report in the browser, plus a second report with only the commits since the previous push. It never runs the PR's code. 3. Drafts are skipped, fork PRs get no comment their token is read-only , and every farol step is continue-on-error . Pin the action to a commit once it works for you: winnin/farol/action@