Technical paper · how it keeps working
Self-healing
How the citation and value checks keep working when the free sources they rely on do not.
The sources are free and shared, and they break: OpenAlex now gives each address a small daily budget, and a service can change what it sends without any error. None of that may turn into an accusation against a citation, and none of it should need a person to notice.
- A changed or broken reply is an outage, not "no results". A reply that is not the shape the source normally sends (an HTML error page, a renamed field) makes the result
unavailable. - A source is tested before it is believed. Before anyone is told "not found", each source that said so is asked for a work that certainly exists. A source that cannot find it is broken without an error, and its answer is discarded (
unavailable, with the reason). A passed test is trusted for ten minutes, and citations checked at the same moment share one test. - A failing source is left alone, then probed. Three failures in a row, or a
Retry-After, and it is not asked for a while (never longer than 15 minutes, even if it asks for 20 hours); then one request is let through to see. Short waits are retried inside the call. - An old answer beats "could not tell". A conclusive result is kept for a week. If the sources are down when it expires, the earlier answer is returned marked
stale: true, with a message saying so. recheck(report)checks again only what could not be settled (retryable), once the sources are back.repair(text, report)fixes what needs no guessing: a real paper with an invented DOI gets the DOI of the record it matched, but only after that new link is looked up and found to be the paper. It returns the new text and a list of every change. Everything else is left to you.health()asks each source for its known work and says which arehealthy,degraded(answering, but wrongly) ordown(and when it will be tried again).
What is remembered is about the citation and its link: a paper first cited with a good DOI never makes a later invented DOI pass.
What regulates itself, and what does not
The safeguards above react while Assay runs. Be clear about what that is:
| Does it happen by itself? | |
|---|---|
| Leaving a failing or throttled source alone, then probing it | Yes, at run time (the breaker) |
| Refusing to accuse when a source gives a changed or unreadable reply | Yes (shape check) |
| Refusing to believe "not found" from a source that cannot find a known work | Yes (self-test with a canary) |
| Serving an earlier answer, marked stale, when sources are down | Yes |
| Noticing that a source has permanently changed (a renamed field, a retired canary work) | No, not by the running library. assay health asks every built-in source for a known work and reports degraded when one answers wrongly; the repository runs it weekly (.github/workflows/sources-health.yml) and opens an issue. A person then fixes the source. |
| Adapting to a changed reply | No. Detecting a change and fixing for it are different things; Assay does the first. |
| Finding and adopting new sources, or changing which services it calls | No, deliberately. See below. |
So Assay is self-regulating, not self-updating. The safeguards stop a failing source from causing a wrong verdict; keeping the sources themselves current is maintenance, and the weekly check is what tells the maintainers when it is due.
Adding your own source
You can add a source; Assay will not add one to itself. A new source is a new thing whose answers are believed, and a library that adopted sources automatically could be pointed at a poisoned one, and would break its promise about which hosts it contacts. So adding one is a decision a person takes, in code or in data.
With data, for any service that answers JSON over https (the description is checked when you make it; a reply of the wrong shape is an outage; a canary is required):
import { createAssay, defaultSources, jsonSource } from '@extrafemi-ai/assay';
const myIndex = jsonSource({
name: 'My Institution Repository',
url: 'https://repo.example.org/api/search?title={title}&author={surname}',
headers: { 'x-api-key': 'key:myrepo' }, // 'key:NAME' is read from keys.NAME below
results: 'data.items', // where the array of records is in the reply
fields: { title: 'name', authors: 'writers', year: 'meta.year', doi: 'ids.doi' },
authorName: 'fullName', // authors are objects: where the name is inside each
canary: { title: 'A work this repository certainly holds', surname: 'Surname', authors: ['Surname'] },
});
const assay = createAssay({ sources: [...defaultSources, myIndex], keys: { myrepo: process.env.REPO_KEY! } });
With code, implement SearchSource ({ name, canary, search(query, http, ctx) }), or for numbers ValueSource, or seededSource for figures a person read off a primary publication. Add the source to your own scheduled check with createAssay({ sources }).health(): it will tell you when your source starts answering wrongly. To add a source to Assay itself, open a pull request with the source, a fixture recorded from the real service, a canary, and a note of whether it was verified live.