Skip to content
Arrit Gashi
← All work
Shipped2026Mobile app · Developer tool

AXIOMO

An offline answer engine that looks facts up and refuses to guess — no model, no network, no invention.

Cover art for AXIOMO on a near-black ground: the wordmark in tracked-out amber capitals, the line Facts, with receipts. Offline., a sentence reading Every answer names its source and its as-of date — or says plainly that it does not know, three chips reading 0 models, 0 network calls and 0 false hits allowed, and a phone showing two answers, Capital of Kosovo — Pristina and Who wrote The Hobbit? — J. R. R. Tolkien, each with the receipt Curated seed · Wikipedia · as of 2025-06.
Cover art, composed from the app's own palette and wordmark treatment and one real screen: two answers with their receipts.
p95 fuzzy lookup at 1M surface forms
0.75 ms
wrong answers avoided by refusing to guess
22.5%
golden queries, zero false hits allowed
188

The problem

Asking a factual question means choosing between a language model that can fabricate a confident wrong answer and a search engine that needs connectivity and returns pages instead of values. Neither puts the source and the as-of date next to the number. AXIOMO's governing rule is that the engine retrieves and never invents: below the confidence threshold, "I don't have that" beats a guess, and a false hit is the only fatal bug class.

What I built

A pure TypeScript engine with zero runtime dependencies and no framework imports, so the same code runs byte-identically in Node for the test harness and inside the React Native app. The pipeline normalises the query, classifies it by rule into one of six intents, links the entity, resolves the fact, and passes it through a confidence gate.

Every property carries a type signature in the data pack — domain, range, unit, volatility, and whether it holds one value or many. The build fails on any fact that violates its signature, and the resolver refuses structurally rather than numerically: a country has no height, so "how tall is France" is rejected on domain mismatch. The question form overrides the property's default type too, so "when is the capital of France" refuses rather than answering a place name to a time question.

Entity linking uses a byte-encoded trie with spaces stripped from the keys, so "newyork" is an exact walk for "new york" — the split-and-merge class that edit distance handles badly. Fuzzy search is a bounded edit-distance traversal of the same structure, with the budget scaled to the length of the span as the user typed it.

The hard part

Measuring why fuzzy matches cannot answer

The instinct is to accept a close match when it is unambiguous. Measurement killed that. With fuzzy matching allowed to produce answers, a correctly spelled name that simply is not in the database resolves to a single confident wrong neighbour 0.47% of the time at the 809 surface forms the pack held when this was measured — and 22.5% of the time at a million, because the edit-distance ball around real names gets denser as the corpus grows. Two live examples from the existing data: "capital of Prussia" returned Moscow, and "capital of Siberia" returned Monrovia.

No margin, popularity or ratio gate tested brought that below 2.4%. Since answering an out-of-database name at all is the one fatal bug class, the fix had to be structural rather than a threshold adjustment: the answer path was removed. A match now returns success only on an exact surface-form hit at score 1.0. Anything merely within the edit budget is demoted to a pre-verified "did you mean?" chip that the user must tap. Widening the fuzzy budget then became safe precisely because a fuzzy result can no longer become an answer.

Outcome

  • Fuzzy lookup at 0.75ms p95 across a million surface forms, against 35ms for the linear edit-distance scan it replaced at a tenth of that size.
  • A 188-query golden set enforced in CI as the release contract: zero tolerated false hits, 100% of must-miss queries missing, and a p99 engine latency under 30ms.
  • Continuous integration that typechecks both packages, rebuilds the binary data pack and fails if the committed artifact differs by a byte, then exports a real production iOS bundle.
  • A workspace-symlink integrity check, added because a missing link let the tests pass while every app build broke.
  • Live on the US App Store as a free download, 22 September 2026.

Screens

  • The empty transcript: the AXIOMO wordmark and tagline, the promise Every answer comes from a verified local database — or it honestly says it doesn't know, eight example chips such as Capital of Kosovo, 5 km in miles and Who wrote The Hobbit?, the question input, and a bottom row with New, History and Browse.

    The promise on the first screen. Every example is a real query against the local pack.

  • Three refusals in one transcript. capital of Prussia: Prussia: no capital stored yet — a real gap, later packs may fill it. how tall is France: France is a country — height isn't stored for a country. where is tiktok from: 2 things are called tiktok — Tik Tok, 2009 single by Kesha, United States with a Wikidata receipt; TikTok, video-focused social media platform, Not stored for this one. Each card offers Search online.

    Three ways of not answering, each named: a gap, a domain mismatch, and a fork where only one owner has the row. capital of Prussia once returned Moscow.

  • The profile for Kosovo, country in southeastern Europe: Capital Pristina, Head of state Albulena Haxhiu, Head of government Albin Kurti, Continent Europe, Currency Euro, Official languages Albanian and Serbian, Population 1,586,659, Area 10,909 km², Founded February 17, 2008, Highest point Gjeravica — every row with its own source and as-of date, Curated seed · Wikipedia or Wikidata.

    A profile is a list of receipts. Each row carries its own source and as-of date, and rows from different sources sit side by side.

  • The query Mercury and the response 2 things are called Mercury. Tap the one you meant: a card for Mercury, first planet from the Solar System and smallest among all, and a card for Mercury, chemical element with symbol Hg and atomic number 80, each with a chevron, then a Search online link.

    Two exact matches, no guess. The engine will not pick an owner when more than one answers; the user taps.

  • The Browse sheet titled Browse · 169,537 things with a Done button and a Search a name field. Under Today · September 11, people born or died on this date — Franz Beckenbauer born 1945, Bashar al-Assad born 1965, Taraji P. Henson born 1970, down to Neem Karoli Baba died 1973 — then a Lists section beginning Continents by land area, Largest countries by total area and Chemical elements by atomic number.

    Everything the pack knows, browsable offline: 169,537 things at the time of capture, with today's births and deaths at the top.

Want something like this built? I take on a small number of projects at a time.