Unsay: An AI Medication-Safety Agent that Goes Back and Un-Says What It Told You. When the FDA Escalates a Recall, It Corrects Every Named Patient It Already Reassured, in Seconds.
Agent memory has a failure mode retrieval quality cannot fix: stale context. Similarity to a stored memory does not prove the memory is still true. For most agents that is embarrassing. In a pharmacy it is a Class I recall. Unsay's fix is a bitemporal schema on CockroachDB (`valid_from`/`valid_to` for the world, `asserted_at`/`retracted_at` for this system's belief), and a join no vector store can express: every answer still standing that leaned on a claim version we no longer believe. The replay still works past the AS OF SYSTEM TIME horizon, and every correction is exactly-once across a region failure.

Claim 1: repair, not just recall
When the FDA publishes a recall, some number of answers already given are now wrong. They were correct when given. Nothing about them changed; the world moved underneath them. Finding them is one SQL query. Read it as English: every answer still standing that leaned on a version of a claim we no longer believe. A vector database cannot express this query. Not slowly, not approximately, at all. It has no notion that a memory has versions, and no record of which version a given answer consumed. The most it can return is today's nearest neighbours, which tells you nothing about what you said in March. Reconstructing history is an insight layer; Unsay closes the loop by re-deciding each affected answer against current memory, and where the verdict moves, drafting a correction and queueing a notification for a named person.


Claim 2: the replay does not expire
The obvious way to build 'what did the agent know when it decided' on CockroachDB is to store the read timestamp and replay it with `AS OF SYSTEM TIME`. It is elegant, needs no extra tables, and is correct right up until the garbage collector moves past the timestamp you saved. CockroachDB's own docs say so: `gc.ttlseconds` 'is not meant to be a solution for long-term retention of history; for that you should handle versioning in the schema design at the application layer.' The default window is 4 hours. 25 hours is the largest value Cockroach Labs regularly tests. Unsay's durable mechanism is the bitemporal schema those docs prescribe, and `AS OF SYSTEM TIME` remains as a fast path inside the window. `scripts/expiry.py` asks one question both ways at three different ages: inside the window, the two agree exactly (a control that matters, because it proves the bitemporal model is reconstructing real history rather than inventing a convenient one); past the horizon, one route is gone and the other still answers.
![Terminal output of scripts/expiry.py comparing bitemporal reconstruction to AS OF SYSTEM TIME at three ages. Inside the GC window: both agree, v[2] = v[2], agree=True. Past the horizon: route A (bitemporal) ANSWERED with the correct historical claim; route B (AS OF SYSTEM TIME) FAILED with 'batch timestamp must be after replica GC threshold'.](/projectImages/unsay_06_expiry.png)
The properties the design carries
Provenance is written atomically with the answer. The decision row and its read set commit together or not at all. There is no code path that stores an answer without recording what produced it, because an answer whose provenance was lost can never be repaired, and a memory system that drops provenance under load effectively has none. Corrections are exactly-once across a region failure: the correction, the status change, the outbox entry and the audit line are one transaction. The outbox dedupe_key is a deterministic hash of (decision, new verdict, triggering fact version), so a sweep killed halfway and restarted recomputes the identical key and the unique constraint turns the replay into a no-op. 'Zero duplicate patient notifications' is a property of the schema, not a hope about how the process exits. Residency is enforced by storage, not by code: `patient` is REGIONAL BY ROW, so an EU patient's memory lives in eu-west-1 because CockroachDB puts it there. The local cluster runs 9 nodes across 3 simulated AWS regions under SURVIVE REGION FAILURE, which places 5 replicas so no region holds a majority.

Unsay: An AI Medication-Safety Agent that Goes Back and Un-Says What It Told You. When the FDA Escalates a Recall, It Corrects Every Named Patient It Already Reassured, in Seconds.
View the project
Assay: A Skincare Tracker with an Error Bar. Cropping the Same Photo Moves Texture by 5.81 Points, So a Verdict Only Fires When the Change Beats That Floor on Your Face.
The measurement exists: YouCam's Skin Analysis API scores sixteen skin outputs from a photograph, and it is a genuinely good instrument (byte-identical input gives byte-identical output). The problem is that a score reported without its error is a number you cannot make a decision with. On my own face with one variable changed at a time, brightness ±8% moves blemishes by 4.85 points and cropping the same photograph differently moves texture by 5.81. A realistic four-week treatment effect is about five. Assay measures its own error first, then calls a change real only when it beats that floor.

Bloom: Nobody Cancels, They Just Stop Coming. Bloom Reads Each Client's Own Visit Rhythm, Flags the Ones Drifting from It, and Writes a Personal Note to Each. Rules Decide Who, Gemini Decides What to Say.
A typical salon loses about 40% of its clients every year, a first-timer who does not rebook within 30 days has about a one-in-five chance of ever returning, and a loyal regular is worth several hundred dollars a year. The signal is invisible because it is an absence, spread across hundreds of people who each have their own rhythm. Bloom's architecture is the idea: rules decide WHO (deterministic risk engine on each client's own median visit gap), Gemini decides WHAT TO SAY (a short note in the owner's voice, referencing that person's real history). The demo of the whole thesis is two clients: Aisha and Jane are both 44 days since their last visit; Aisha comes every 8 weeks, Jane comes every 4. Any tool that flags 'no visit in 60 days' is wrong about one of them.