1. Give fact revisions explicit meaning
A collection timestamp is not a fact version. Fetching an unchanged page tomorrow should not invalidate every evaluation. Compute a fingerprint from the normalized fields actually supplied to the scorer, while retaining source and collection metadata separately. Correcting a value for the same edition creates a new revision. A regional variant may instead require a different identity. Decide this before changing records, because overwriting a US specification with an EU specification can hide a legitimate difference rather than correct an error.
2. Store the dependency edges
The in-memory example gives each evaluation a product ID and the fact hash it used. In a service, use an indexed dependency table rather than scanning all report JSON. A correction can then identify affected evaluations precisely. Keep their old payloads if historical snapshots are intentionally preserved; mark them stale for new decisions instead of rewriting what a published snapshot originally meant. A database update and its invalidation marker should be committed together so a request cannot observe corrected facts with an unmarked old score.
import hashlib, json, sqlite3
hash_of = lambda facts: hashlib.sha256(json.dumps(facts, sort_keys=True).encode()).hexdigest()
c = sqlite3.connect(':memory:')
c.executescript('''
CREATE TABLE facts(product TEXT PRIMARY KEY, version TEXT, payload TEXT);
CREATE TABLE evaluations(id TEXT PRIMARY KEY, product TEXT, fact_version TEXT, stale INTEGER DEFAULT 0);
CREATE INDEX eval_product ON evaluations(product, fact_version);
''')
old, new = {'weight_g': 210}, {'weight_g': 204}
c.execute('INSERT INTO facts VALUES (?,?,?)', ('p1', hash_of(old), json.dumps(old)))
c.execute('INSERT INTO evaluations VALUES (?,?,?,0)', ('e1','p1',hash_of(old)))
c.commit()
with c:
c.execute('UPDATE facts SET version=?,payload=? WHERE product=?', (hash_of(new),json.dumps(new),'p1'))
c.execute('UPDATE evaluations SET stale=1 WHERE product=? AND fact_version<>?', ('p1',hash_of(new)))
assert c.execute('SELECT stale FROM evaluations WHERE id=?', ('e1',)).fetchone()[0] == 1
print('changed facts marked their dependent evaluation stale')
3. Follow the report and language layers
A complete report may depend on several evaluation IDs and a panel-choice record. Its conclusion can repeat a corrected specification even when the chart already uses a fresh number. Store these dependency edges too. Translations should reference the narrative content version, not just a product name. Regenerate only the affected text when meaning changes. If a historical report remains public, retain its date and indicate that it is a snapshot. A refreshed current report and an old public snapshot are different artifacts.
4. Test the visible result, not only the write
Test an unchanged fetch, a meaningful field correction, a new regional variant and an interrupted transaction. Verify that an unrelated product’s evaluation remains reusable. Then open a report and inspect the table, chart, prose and translated explanation. This last step catches presentation caches that a database-level test cannot see. Do not automatically rerun every provider when a review date passes; use review dates to prioritize source checks and pay for new analysis only when the dependency evidence justifies it.
