clearfelt-review · v0.2.0 · TypeScript

Ask an AI research tool why, and it hands you a paragraph.

Ask this one, and it hands you a receipt.

An entity and an objective in, a change plan out. Every recommendation cites a finding, the finding cites evidence, the evidence cites the observation, the observation cites the page it was read off. Follow it to the bottom and you land on a real form field, not on "trust me".

andrei-ionut-nita / clearfelt-reviewView on GitHub ↗

One canonical model, traceable findings, prioritised recommendations. 361 deterministic tests, zero LLM calls in core.

TypeScriptMIT licencedocs in README
$node packages/core/dist/cli.js init acme
A recommendation, traced to sourcefixtures/commercial-saas
$ clearfelt-review trace reviews/lighthouse-metrics/r-20260907-001 R-0002

R-0002  Let the trial start without a card
├── F-0002  [derived] A card is required before any trial usage.
│   └── E-0002  A prospective user must provide a card number before the trial account exists.
│       └── OBS-0004  The trial signup form requires a card number before the trial account is created.
│           └── S-0001  https://lighthouse-metrics.example/pricing
└── O-0002  A card-free trial removes a procurement step from evaluation
    └── ...
runs in reverse with --reverse361 deterministic testszero LLM calls in core

What makes it different

Layers stay separate.

An observation is not a finding. A finding is not a recommendation. Each level must cite the one below it, and a collapsed layer fails validation rather than quietly shipping.

Priority is computed.

There is no priority field. It is derived from impact, effort, confidence and urgency, with the arithmetic printed alongside, so nothing can claim to be P0 without earning it.

What you told it is not evidence.

"Our main competitor is X" enters as an assertion to investigate. A finding that rests only on what you said is rejected, because that is your belief handed back to you as research.

Absence is recorded honestly.

"No pricing found after checking /pricing, /product and /faq" is an observation. "They do not publish pricing" is a claim. The first must say where it looked; the second is not allowed.

Gaps appear as gaps.

Research that was blocked, paywalled or fruitless is logged and reported. "We researched this sufficiently" and "we could not find out" never render the same way.

Quality is a report, not a score.

No LLM judges the output. Mechanical checks catch a recommendation restating its own finding, a falsifier that cannot fail, or generic consultant theatre, against named failure categories.

What it deliberately is not

It is not a website auditor. It does not take a URL, call an LLM, and hand back a polished, unfalsifiable PDF, the shape almost every "AI analysis" tool takes because it is the easiest thing to build and the hardest thing to trust. That shape is the one thing this project is built to refuse, in every layer: the model, the CLI, the tests, and the four gated stages the reasoning layer runs inside rather than around.

What you can ask it to do

trace

Follows one recommendation all the way down to the page it came from.

validate

Checks every claim still cites something real, and fails if a layer has collapsed.

quality

Flags recommendations that restate their own finding, or read as generic advice.

coverage

Shows what was researched, what was blocked, and what is still unknown.

Full command reference in the README →

Quick start

Scaffolds a run and takes you to the first approval gate. The four stages run as Claude Code skills, each blocked from writing outside what the current stage permits, whatever its prompt says.

pnpm install
pnpm run build

node packages/core/dist/cli.js init acme
onboard · agree what is in scoperesearch · gather sourced observationsanalyse · turn those into findingsrecommend · propose the changes