Liminalis
Lens 03 legislation
Corpus~10,467 manifest rows
SourceLegiScan records + extracted bill text
Legislation lens

The lens that proved the engine wasn't about fiction.

Two lenses for creative work prove very little — they share too many assumptions. The third lens was deliberately chosen from an unrelated domain: a corpus of roughly ten thousand pieces of US state legislation, at a scale no hand-curated project reaches. It shipped without a single special case in ingestion, chunking, extraction, retrieval, assembly, the access barriers, the job model or the agent API.

§ 01 staged ingest
Designthe cheap half and the expensive half are separate buttons
§ 01  Getting ten thousand rows in

Cost is a first-class part of the workflow.

At corpus scale, “upload and let it think” is a way to spend real money by accident. So ingestion is explicitly staged, previews are free, and the expensive step is a separate, deliberate action — with actual spend read back from the credit ledger by document, not estimated.

  1. Stage

    Upload a corpus bundle or point at a server path. Extracts and counts the manifest. Costs nothing.

  2. Scan

    A manifest-only report of exactly what an import would produce — around eighty milliseconds across ten thousand rows.

  3. Batch

    Define a batch by filter: states, sides, statuses, policy types. Preview is instant and free.

  4. Ingest

    Create and embed the documents. Cheap. They become searchable and citable immediately.

  5. Release for analysis

    The expensive half — entity, fact, summary and motif extraction. A separate action, with a durable, poll-safe estimate before you commit.

Corpus — batch 04 staged
state: TX, FL, MO year: 2023–2025 status: passed
Manifest rows matched1,284
With retrievable text1,031
Stubs (no text available)253
Ingest — create & embedest. low
Analysis — extract & summariseseparate step
Batch 03 actual spendread from ledger

ANALYSIS 62% · RESUMES FROM DISK AFTER RESTART

Preview and scan cost nothing. Analysis is a separate, deliberate release — and reported spend is read back from the ledger by document id, not estimated after the fact. Representative composite of the application UI.
A master stop, and why it's lossless

An effectively unbounded corpus needs an off switch. A project-level pause gates the autonomous background workers while deliberately not gating chat or explicitly submitted jobs — the line is autonomous versus requested. And it is lossless by construction: a paused document is simply never marked as processed, so it stays pending and the whole backlog resumes on the next poll. There is no catch-up path to get wrong, because there is no catch-up path.

§ 02  What it finds

Two analyses that cost almost nothing to run.

Both are deliberately built out of things the system already has — embeddings and the index — rather than out of model calls. Neither needs an LLM at all.

Bill families

Model legislation, found by similarity

Single-link clustering over bill embeddings, with the similarity threshold calibrated against the source data's own companion and successor relationships rather than guessed. Members are stamped with a family id, a role, a scope and a novelty score. Costs embeddings; no model calls.

Cross-state families are the entire point. Same-state companion bills are already published by the data source and are not a finding. Near-identical text filed in six states in the same session is.

Escalation matrix

Where a policy category appeared first

Policy category × state, where each cell is the year that category first showed up in that state — plus a corpus-wide ranking of which categories tend to precede which. Free, index-only, no model calls.

It is deliberately not built on the Novel lens's weave grid. They look alike and mean completely different things; sharing the machinery would have been a false economy.

Escalation matrix — first appearance by category index-only · no model calls
Policy category AL AZ FL ID MO OK TN TX UT WV
Category A 2021 2020 2020 2022 2021 2022 2021 2020 2022 2023
Category B 2022 2021 2021 2022 2023 2022 2021 2021 2023
Category C 2023 2022 2022 2023 2023 2022 2022 2023
Category D 2023 2023 2023 2023 2023
Category E 2024 2024 2024
First-appearance year per category per state; darker means earlier. Category labels are generic here — the real matrix uses the corpus's own curated policy types. Representative composite of the application UI.
§ 03  Corpus insights

Charts that tell you what they're not showing you.

Four views over the corpus, all reading a free index-only endpoint — opening the panel spends nothing. What is more interesting than the charts is the set of rules they had to obey.

Corpus Insights index-only · free to open
Family 14 TX · 2 FL · 2 MO · 2 TX HB •••• · 2023 TX SB •••• · 2023 FL HB •••• · 2023 FL SB •••• · 2024 MO HB •••• · 2024 MO SB •••• · 2024 6 members · 3 states

Grouping shows membership by state — never a derivation chain

Every view declares its denominator. Representative composite of the application UI — figures are illustrative.
Constraint 01

No derivation chains

Clustering computes similarity, not descent. Drawing bill→bill parentage would render an unfalsifiable claim about who copied whom as if it were data. So the tree groups membership by state and stops there.

Constraint 02

Every chart states its denominator

Only around 28% of bills have a recorded roll call. A margin chart over that subset, drawn without saying so, silently reads as a chart over the whole corpus. So it says so, on the chart.

Constraint 03

Data quality is encoded, not hidden

How a bill's text was converted is recorded on the bill. Roughly a fifth of rows have no retrievable text at all and are kept as stubs rather than quietly dropped — an absence you can see beats a corpus that looks complete.

§ 04 the one we deleted
98.6%of rows reported zero insertions
§ 04  A feature that shipped nowhere

“Amendment aggressiveness” was built, exposed, measured, and deleted.

The idea was good: score how aggressively a bill rewrites existing statute by comparing inserted text against struck-through text. It was fully implemented and already exposed as a filterable facet in the document browser.

Then someone checked the distribution before shipping it. 98.6% of rows reported zero insertions — because two of the dominant PDF-to-text converters detect strikethrough and miss insertion markup entirely. The metric wasn't measuring legislative aggression. It was measuring which converter had processed the file.

It was reverted, and the reasoning was written down next to the renderer so that a future engineer with the same good idea finds the answer before rebuilding it.

A confident, filterable, completely wrong signal is worse than no signal, because people will act on it.
§ 05  Reuse of engine machinery

The point-in-time barrier is the GM wall, wearing a different hat.

“What was law as of a given date”

A real retrieval filter with exactly the same shape as the Campaign lens's GM-only wall: out-of-scope material is removed before context assembly, not filtered out of the answer afterwards. One mechanism, two domains, zero duplicated logic — which is the lens thesis in a single feature.

Its limitation ships disclosed in the code rather than discovered later: without a reliable repeal date it can include something since repealed, but it will never exclude something that was in force. The failure mode is known and it is the safe direction.

Document browser

The tree stops working at ten thousand documents

So at corpus scale it is replaced by search plus lens-declared facet filtering, served straight from the index with flat paging. Entirely lens-agnostic — it works the same way in a novel project with too many scenes.

Export

A default that is a judgement call

Corpus export runs as a background job, and including individual legislators' names is off by default. Naming people as template distributors in a file that will travel is a deliberate act, so it requires a deliberate click.

Honest scope

The Legislation lens ships the corpus pipeline, the browser, the two analyses, the charts, the point-in-time barrier and export. It does not yet ship lens-specific validation, and it carries no chat commands of its own. It is a working third lens, not a finished product in its category — and the reason it exists is to prove the engine generalises.