Lattice is a curated, stack-ordered index of the 112 tools behind production AI systems, organized into 9 layers from inference and serving through routing, retrieval, fine-tuning, agent frameworks, orchestration, guardrails, prompts and evals, plus an off-stack learning section. It ships 112 per-tool pages, 6 head-to-head comparisons, 11 MDX essays, a Cmd-K palette, /compare and /all facets, feed.xml, llms.txt and sitemap.xml. All 152 routes are prerendered at build time and served as a Cloudflare Worker via OpenNext. There is no CMS, no runtime rendering, and no soft failure mode: the build is the editor. This essay is about what that discipline catches and what it costs. You can browse the result at lattice.kunjshah.in.
The premise: a directory is a database wearing a website costume
Most tool directories rot the same way. Someone adds an entry, nobody rechecks the licence, a comparison table quietly drifts, a broken link survives three redesigns, and a year later the site is a landfill with a nice search box. The entries were probably accurate on the day they were written. That is precisely the problem: accuracy at publish time is not accuracy, it is a snapshot, and snapshots decay.
The fix is not more editorial willpower or a quarterly reminder in someone's calendar. The fix is to treat the directory as structured data with invariants, put that data one build away from production with no editorial bypass, and make the build fail when the data lies. If a fact is worth publishing, it is worth asserting. Everything on the site is derived from the data model, and the build is the only path to production. The visual design is a costume; the database underneath is the product.
What the guards actually catch
Because the guards run on every build, the failures they catch never reach a reader, which makes them easy to underestimate. The classes are concrete: an entry added without a classification fails immediately instead of shipping as a mystery row; a comparison written against a tool that was renamed or dropped fails instead of rendering a lie; licence and cost fields past 6 months fail instead of aging silently into misinformation; a test regression in search ranking fails instead of degrading the one interaction every visitor uses. None of these are hypothetical risks; they are the default failure mode of every static reference site that relies on humans remembering to recheck facts. The guards convert "someone should probably re-verify this" into "the deploy is red until someone does."
The two-layer data model and the assertions that keep it honest
The data model splits what a tool IS from what it LINKS to. The IS layer is the classification: kind, deployment, licence, language, cost, useWhen, skipWhen, alternatives. The LINK layer is everything referential: URLs, docs, related toolings. An entry can be link-complete and still be a publishing error if its classification is missing or stale, because the classification is what makes the index navigable.
Three build-time assertions enforce the split:
- A tool without classification throws. You cannot half-publish a tool. Missing kind, missing deployment shape, missing use/skip guidance: the build stops with the offending entry named.
- A comparison naming an unknown tool throws. Head-to-head pages cannot reference phantoms, so a comparison can never render against a tool that does not exist in the index.
- Licence or cost data older than 6 months fails the build. Staleness is not a warning in a log nobody reads; it is a hard stop in the only path to production.
These are deliberately unfriendly. A guard you can click through with an override flag is not a guard, it is a suggestion. Each assertion corresponds to a failure I actually want to be woken up for, and none of them allow partial success.
Staleness as a build failure
The 6-month rule for licence and cost data is the assertion I expect to annoy people most, including me. It means the site cannot quietly drift into confidently wrong information about what a tool costs or how it is licensed, which is exactly the information a reader is most likely to act on when choosing a stack. A wrong link is an inconvenience; a wrong licence or cost claim is a decision made on false premises.
The consequence is that maintenance becomes scheduled, not remembered. When the build fails, you refresh the data or you do not ship. There is no third option where the site deploys anyway and someone files a ticket. The cost is real: someone has to re-verify, and sometimes re-verification means a tool gets reclassified or dropped instead of updated. But the cost is paid in small, visible installments instead of one embarrassing launch of confidently wrong facts, and the failure message tells you exactly which entries need attention.
Static delivery as a feature
152 prerendered routes, no runtime rendering, no per-request server logic, served as a Cloudflare Worker via OpenNext, all built on Next.js 16, React 19, MDX and Tailwind v4. For reference content with zero personalization, this is the right architecture and not a compromise. Pages are invariant, so they are cacheable everywhere; there is no CMS to exploit or misconfigure, no preview environment leaking drafts, and no per-request failure mode between the reader and the page.
Adversarial traffic, which any public reference site eventually attracts, hits static assets and an edge worker, not an application with a database behind it. The attack surface is as small as a static file can make it. There is also an operational argument: no runtime means no runtime incidents, no connection pools, no cold-start tuning, no database migrations on a Tuesday.
The trade-offs are honest ones. No server-side personalization, by design; every page is the same for every reader. Content changes require a rebuild, measured in minutes, which is fine for reference material and wrong for a news feed. And the data pipeline must be sound, because there is no runtime net to catch bad inputs. That is precisely what the guards and tests exist for: they are the net, moved to build time where failures are cheap and loud.
What the 134 tests cover
The stack includes Vitest, and the 134 unit tests are pointed at the data, not at trivia. They cover four families of concern:
- Data invariants. No orphan pages, so every tool is reachable from navigation and indexes; no unknown references, so comparisons, alternatives and cross-links all resolve to real entries; required classification fields present on every record.
- Search ranking sanity. The Cmd-K palette ranks what it should. Regressions in scoring, tokenization or field weighting fail the suite rather than silently degrading the one interaction every user touches.
- Frontmatter integrity. The 11 MDX essays carry the metadata the site assumes: titles, dates, categories, tags. Malformed frontmatter fails before it ever renders.
- JSON-LD escaping. Structured data is a script context; unescaped content there is an injection risk, so escaping is tested explicitly instead of trusted.
The tests are invariants over trivia on purpose. I do not care whether a button has the right class name; I care whether the database underneath the site is internally consistent on every single build. That is the layer where reference sites actually fail in public, and it is the layer normal test suites skip because it is not component-shaped.
What it costs
The data model forces discipline on every tool you add. You do not get to publish "some LLM gateway thing" without committing to a kind, a deployment shape, a licence, a cost shape, and explicit use-when and skip-when guidance. Alternatives must exist in the index. If you cannot fill the classification honestly, the entry does not ship, and that is the correct outcome.
The guards are annoying on purpose. Adding a tool is slower than pasting a link into a CMS, and the friction is the feature: it is the difference between a directory and a dump. It also selects for contributors who can be precise, which shapes the whole corpus.
What you get in exchange is that content quality is a CI property, not an editorial hope. A green build means the reference is internally consistent, free of dangling references, and fresh within 6 months on the fields readers rely on most. The process is stricter than the output needs to look, which is exactly what you want from infrastructure nobody sees.
If you are building a reference site of your own, the transferable lesson is this: put your facts in a database, assert the invariants at build time, let staleness fail the deploy, and test the data, not the markup. The case study has the implementation details: Lattice on this site, or browse it at lattice.kunjshah.in.