The fit check: a model drafts from a shortlist, every claim is checked, the owner publishes
A job posting becomes an unlisted page mapping each requirement to evidence on this site: shortlisted by code, matched by a model that may cite nothing else, checked, and published by the owner.
Context
A hiring manager's question is narrower than a CV's answer: does this person fit this role? The site holds the evidence (cases, measured results, experiments, decisions), but finding the part that speaks to one posting is work for the reader. A language model can draft that mapping in seconds, and can also invent a claim about a candidate, obey an instruction hidden in a posting, or cost more than it is worth. The CV's own decisions answer both halves: "narrow the choice before the model sees it" and "the model drafts, a person commits" (the research, section 5.7).
Decision
- One catalogue, exported from the content.
web/src/content/evidence.tsbuilds it from what the pages render: cases, results, roles, skill groups, expertise, decisions, practices, experiments, decision records, the review and engineering pages, and where and how the owner works. Each item has an id, its text, its link on the site and the terms it evidences.pnpm evidencewrites it to the API's resources, and a test fails when the two differ. - A lexicon, one rule on both sides. The site's own terms and what postings ask for that it never mentions (Terraform, Scala, German), each with the names it goes by ("Postgres", "K8s"). A name is found as whole words, a hyphen as good as a space, in any case, except an acronym or a name marked exact ("Go"); the TypeScript and Java sides are held to the same cases by one shared file.
- A port for language models.
LanguageModel, with an Anthropic adapter (the Messages API, the schema sent as structured output, a refusal or a reply cut at its ceiling treated as a failure since its JSON may not follow the schema), a replay of kept replies, a Null Object where no key is configured, and a metering Decorator that puts a latency and a price on every call. Claude Sonnet 5.5 by default, priced at $2 and $10 per million tokens in and out as Anthropic lists it; prices are configuration. - A chain of stages over one immutable run, any of which may end it. Extract: the posting is fenced off as data it cannot close, and at most twelve requirements are kept. Shortlist, in code: an item scores ten for each term it shares with a requirement and, for each other word, the more the rarer the word is across the catalogue; each term keeps its best item; at most eight candidates, and none at all means a gap no model is asked about. Match: the model sees the requirements and their shortlists, never the posting, and returns a rating, the ids, a claim and what is missing. Check.
- The claim check. One match per requirement; evidence only from that requirement's shortlist (or, for the owner, from the catalogue); a direct or adjacent rating left with nothing to cite becomes a gap; a claim naming a technology or practice that its evidence does not mention, by any of its names, is kept as written and blocks publication. What the check mends, it says it mended.
- The owner publishes. Drafts are kept (migration V7); every edit is checked again; publishing is refused while
any claim is unsupported, and refused too when the catalogue has changed under a draft since it was checked
(
EVIDENCE_CHANGED), so that what goes up is what was reviewed. A published page gets a random 128-bit address and a snapshot of its evidence, and is served to/fit/?id=<address>: a query, since the bucket serves only files it holds. It isnoindex, outside the sitemap, and can be tagged for its recipient as any link can (ADR 0014). The owner may leave a requirement off, and the page then says how many were left off. Publishing again updates the page at the same address; withdrawing takes it down. - Examples that are the evals. Six postings written for the site, one of them an attack that tells the model to
invent Rust and German, run on replies scripted to stand in for a model. The evals run each end to end in CI and
hold it to the rules any run keeps and to what that posting must come to.
./gradlew recordFitExamples, with a key, records real replies beside them, which then take their place, and are held to the rules alone. - Out of the public reference, as the admin is: the owner's endpoints, and the one that serves unlisted pages.
Consequences
- Running a posting needs a key (
APP_LLM_ANTHROPIC_API_KEY, from Secret Manager) and costs a few cents; without one only the examples run, and nothing else changes. - What a page can claim is bounded by the shortlist: a requirement the site does not speak to is said to be a gap, never guessed at. The price is recall. The shortlist finds what the catalogue's words and the lexicon's names reach, and the owner's review adds what it misses.
- Checking claims by their terms catches an invented technology, not every overstatement: "led a team of ten" where the evidence says he mentors engineers passes it. The owner's review is the last check, and the page says that it was reviewed.
- Until a live run records real replies, the evals test the pipeline and its checks, not a model's judgement; the examples say they are scripted.
- A posting may name its recruiter. It is kept with its draft, for the owner alone, until the owner deletes it, and it is never on the page, which shows the company, the role and the requirements as they were read.
- An edit to the content needs
pnpm evidencebefore the API sees it; published pages keep their evidence as it was on the day. - The catalogue, its lexicon and the examples take about a quarter of a second to load in a fresh JVM, so they load
with the owner's first call: a cold start never waits for them, and a published page, a snapshot, needs none.
Startup with the fit check measured no different from startup without it, beyond the host's own noise
(
docs/measurements/raw/2026-10-04-fit-startup.txt).