The output was four fields long. Every one of them empty.
Title: not provided. Tags: unclassified. Summary: blank. Info points: completely absent β not thin, not partial, zero rows.
Upstream, someone had asked for "deep analysis." The stage-two model was supposed to cluster the info points, reconstruct the causal chain, test the author's stance, weight source quality against time sensitivity. Every one of those operations consumes rows. There were no rows. The operator on the other end did the only correct thing available: refused, and asked for the raw text, the real Phase-1 JSON, or the upstream logs.
I read a lot of crypto research in a bull market. That refusal is the most technically honest document that has crossed my desk this quarter.
Context
Bull markets industrialize research. The 2026 stack is standard issue now: a crawl farm, an extraction API, a judgment layer with a large context window, a renderer that outputs four thousand words and a chart. Every fund, every DAO grant committee, every exchange listing desk runs some variant of it. Crawl, extract, judge, publish.

The cost of the last two stages collapsed. A project report that consumed a junior analyst for two weeks in 2021 costs a few thousand tokens today. So the reports got longer. They did not get denser.
The reason is architectural, not stylistic. Length is manufactured at the judgment layer. Truth is manufactured at the extraction layer. Everything downstream can only re-weight what upstream caught. When the extractor returns null, the judge faces two options: emit null, or emit prose. One of those is unsellable.
So the honest refusal I received is the outlier. The default behavior at that fork is to keep writing. I have audited enough of these pipelines to know how the fork resolves in production. It resolves inside the schema. A required array carrying minItems: 1 is not a validation rule. It is an instruction to invent. A model that must return a non-empty list of information points, handed an empty page, will return information points β not from the page, from its priors. The output validates. The report ships.
Core
Start with the field taxonomy, because the collapse begins there.
Every research schema I have seen splits into two classes. Extraction fields: entities, claims, numbers, timestamps, source URLs, contract addresses. Judgment fields: one-line summary, author stance, article purpose, confidence score.
The judgment fields are where the reader lives. They are the only part most readers will ever see.
The extraction fields are the only part that can be checked.
A judgment field must be traceable to an extraction row. Not "consistent with" β traceable. If the summary asserts TVL grew forty percent in the quarter, a row must exist with a number, a timestamp, and a resolvable source. If the stance reads "bullish but hedging," rows of hedged language must exist. That claim-to-row link is the entire epistemic value of the stack. Break it and you own a very fast machine for producing confident sentences with no referent.
The link breaks by default in most deployed systems, and it breaks four ways. All four are mundane. None are exotic.
One. The crawler returns 200 and nothing else. Cloudflare interstitial. Consent wall. JS-rendered body. Geo-block. The HTTP status is clean; the HTML is fourteen hundred bytes of script tags. Extraction runs against a page that contains no page. Empty array. No exception thrown, because zero results from a valid fetch is not an error condition.
Two. The chunk merge drops everything. Long article, chunked into forty windows with overlap. The merge step concatenates under a token cap, or deduplicates on a key that happens to be identical across every chunk β page hash, URL hash β collapsing forty rows into one, or into none. That step exists to save tokens. It works.
Three. Schema validation filters the survivors. Strict type: string on a field where the extractor occasionally emits a number. additionalProperties: false on an object carrying one useful stray key. A nullable field declared non-nullable. Each silently deletes a real row. Apply that across four hundred rows and your survivor set is a residue of the schema, not a sample of the article.
Four. Nobody asserts on cardinality. Zero rows and two hundred rows take the same path to the judge. One check β if len(points) == 0: halt β would have prevented the empty template from ever rendering. It is two lines. It is missing everywhere.
Now the interesting part. What the judge does with an empty evidence set.
The judgment layer is a next-token predictor. Given a non-empty output slot and an empty evidence set, it fills the slot. This is not deception in the human sense. It is the path of least resistance in a loss landscape where every training example of a good report is a filled slot. The model has seen ten thousand well-formed research summaries. It has never seen one that says "the source page was a Cloudflare challenge."
So it writes. Thesis. Three risks. A valuation frame. A conclusion. Four thousand words, internally consistent, none of them touching a contract address.
I have started applying one question to every report that crosses my desk this cycle: show me the rows.
In February a restaking deck landed. Six pages on TVL growth, points-program mechanics, a proprietary risk model. I asked for the extraction rows. Nine came back. Seven traced to the project's own blog. One traced to a tweet. One traced to a Dune query with no saved state. The load-bearing sentence of the entire document β TVL up forty percent on the quarter β traced to nothing. The author had read the number somewhere. The somewhere did not survive contact with the pipeline.
Second one, an RWA project. The report cited two independent audits. I pulled the citations. Both PDFs were scope statements, the pages that list what the auditors were hired to examine. Neither contained findings. The word audit was doing labor the documents never authorized. Minted nothing, promised everything.
Third, a report on a rollup describing blob costs as structurally cheap. The data was correct. The sentence was wrong. The dashboard showed current blob fees; it did not show the thirty-day median, the surge pattern, or what the demand curve does when it meets a fixed supply of blob space. The number was real. The interpretation was a forecast wearing the costume of an observation. I have been writing about that market for two years. The number is the easy part. The sentence is where people get hurt.
Here is the part that should trouble anyone allocating this quarter. The consistency gate that catches all of this is cheap. One prompt. A few hundred tokens. For each judgment sentence, does a supporting row exist, and does that row's source resolve. Run it before render. It does not get run, and the reason is not technical. The gate makes reports shorter and less impressive, and in a bull market the deliverable is the impression.
There is a second, uglier reason. A null result is a valid result. "This project has no extractable on-chain information, the docs are marketing, and the cited audit is a scope statement" is a correct Phase-1 output. It also gets the report killed. So the pipeline is tuned β not by any single decision, by a hundred small ones β never to produce it.
Contrarian
The bulls are right about one thing, and I will say it plainly because the timeline will not.
The tooling works. Extraction recall on well-formed pages is genuinely high now. Contract-address resolution, entity linking, timestamp normalization β all of it beats the paid analyst desks of 2021 and costs an order of magnitude less. What I received was not fraud. It was plumbing. Zero rows from a valid fetch throws nothing, and the team that shipped it is not trying to deceive anybody; they are shipping fast because the market is up and the mandates are flowing.
And the empty template is more useful than a full one. A pipeline that fails loudly on null tells you exactly where its seams are. A pipeline that fails silently β that returns a confident, populated, well-rendered nonsense report β is the one you cannot audit, because there is nothing to point at.
The empty template is a free penetration test. Most teams will never receive one. This one arrived with the failure already localized to Phase 1, which is the only good news in the file.
Takeaway
Ask for the rows. Not the summary. The rows.

If the sentence has no row behind it, the report is fiction, regardless of word count or chart quality. Somewhere in this bull market a committee is about to size a position on a document whose extraction layer returned zero and whose judgment layer filled the hole. Code is truth. Intent is fiction. The ledger keeps score β it just does not send a notification when the numbers you are reading were never extracted from anywhere.