TL;DR: claude-fafm-sdk 1.5.1 ships Forgettable Memory — the missing half of a grown-up CvRDT. Until now every
edition only grew the soul: delete was absence, and merge resurrected whatever a
peer still held. Now forget writes a tombstone that rides with
the soul, joins as an LWW max-register, and suppresses the fact on emit.
Packet path and hosted namepoint path. Rare code. Honest bounds. Full arc closed.
# zero install: uvx claude-fafm-sdk --version # → 1.5.1 uvx claude-fafm-sdk forget --helpCould have been 2.0. We didn’t.
Tombstones re-open the merge oracle. That is not a patch — it’s a lattice change. Plenty of systems would stamp 2.0 on “we finally have delete.” We chose 1.5 on purpose.
Why? Because the story was always one arc, not a reboot:
- 1.1 Mergeable — join without a coordinator
- 1.2 Sendable — seal · send · merge
- 1.3 Provable — stranger receipt, one command
- 1.4 Verifiable — which key sealed it
- 1.5 Forgettable — delete is state
2.0 would imply “throw out what you learned.” 1.5 says: same frozen discipline, one deliberate re-open, dual-implementation green, hand-golden gate — then ship. Semver is not a marketing amp; it’s a contract with strangers who already trust 1.x.
And yes: if we only shipped packet-path forget and left hosted resurrection as a footgun,
that would have been a half 1.5. So 1.5.1 is the production cut —
hosted pull / sync go through the same merge_souls CvRDT. There is no production 1.5.0. First 1.5.x on PyPI is 1.5.1.
Tombstones are rare code
Most “agent memory” products only ever append. Delete is UI theater — the backend still merges whatever last wrote. Distributed systems people know better: without a delete as state, absence is not information. Peers that lag will keep bringing the corpse back.
A tombstone is not a secure erase. It is a lattice marker — a little gravestone that travels with the soul and says: “this fact lost; do not resurrect on join.” That is the difference between hope and convergence.
What we actually ship:
Soul.forget(id)/forget_text— remove the live fact and write a tombstone. Forgetting an id you no longer hold still suppresses it on later merge.- CLI —
forget <id>andforget --text "…"(id-less, matched by normalized text). - LWW-tombstone-map above field-merge — delete-wins on ties; tags join under Rule T so the association gap doesn’t resurrect.
- GC out forever as debt — we don’t pretend compaction is free; the graveyard is honest debt until a later edition designs it.
This is the same rigor that froze the merge oracle in 1.1 — not vibes, not a feature flag
named soft_delete. Dual-impl differential still green after the reopen.
What's New (1.5.0 lattice + 1.5.1 hosted)
- Forgettable Memory (packet) — tombstones on
seal/merge/merge_souls. The deliberate oracle re-open, held to the same gate as the join itself. - Hosted forget converges — namepoint
pull/syncno longer re-add facts additively. They reconcile viamerge_soulsso a local tombstone suppresses a peer that still holds the fact. Namepoint.soul()— structured hosted read returns the full soul (facts and tombstones).facts()for recall stays facts-only.- No wire change for push — tombstones were already storable; the read /
reconcile side was the bug. Id-less tombstones round-trip by
txt_hash.
Both transports — or it didn’t count
Portable memory has two roads: the packet (file, air-gap, chat) and the hosted namepoint (push / pull / sync). Convergent forget on only one road is a demo. On both, it’s a product.
# packet path (local soul): claude-fafm-sdk forget -f soul.fafm secret-id claude-fafm-sdk seal -f soul.fafm -o out.fafmp # peer merges → forgotten stays forgotten# hosted path: claude-fafm-sdk forget -f soul.fafm secret-id claude-fafm-sdk push # tombstones ride the YAML claude-fafm-sdk pull # merge_souls — not additive re-addIf you still see a forgotten fact after pull, file a bug — that was the 1.5.0→1.5.1 gap, and it is closed on production PyPI.
Honesty bounds
- Tombstone ≠ secure erase. Disk, backups, and logs can still hold bytes. We claim lattice convergence, not forensic destruction.
- Delete-wins on ties under Rule T — intentional; document it if your app wants “last writer wins including resurrection.”
- No automatic GC of the graveyard in 1.5 — debt is visible, not magical.
- 1.4 provenance unchanged — optional Ed25519 stays opt-in; base path still zero-crypto.
- Grow/update-only is retired for delete — that bound from 1.1–1.4 is lifted here, on purpose, with evidence.
Try It
# zero install: uvx claude-fafm-sdk --version uvx claude-fafm-sdk quickstart# classic floor: pip install claude-fafm-sdk==1.5.1# etch → forget → recall (ephemeral): uv run --with claude-fafm-sdk==1.5.1 python -c "from claude_fafm_sdk import Soul; s=Soul('@demo'); s.etch('never ship this', id='secret'); s.forget('secret'); print([f.id for f in s.facts])"Release: v1.5.1 · PyPI: claude-fafm-sdk 1.5.1
The Numbers
- v1.5.1 — production on PyPI (Forgettable Memory · both transports)
- 232 tests green · 2 skipped · dual-impl still green after oracle reopen
- No 1.5.0 on prod — packet + hosted ship as one cut
- PyPI: claude-fafm-sdk 1.5.1
- GitHub: v1.5.1 release
The arc — through Forgettable
Mergeable → Sendable → Provable → Verifiable → Forgettable.
- 1.1 Mergeable Memory — souls join (CvRDT)
- 1.2 Sendable Memory — SPK1 + CRC travel
- 1.3 Provable Receipt — one-command stranger proof
- 1.4 Verifiable Provenance — which key sealed it
- 1.5 Forgettable Memory — tombstones · both transports · full stop on the volume
Memory that grows, moves, proves, signs — and can finally let go without lying about the join. Championship engineering, volume car energy: you drive it every day; the track car still exists when you want F1.
Nelly never forgets… unless you ask her to. And then the whole fleet agrees.
Next: Compactable (2.0) — epoch compact pays tombstone debt; cross-epoch merge refuses — no silent zombies.
Dual edge: Memory is vast — .fafm maps it. One format. Two edges. One soul. · PM.IR. — Permanent Memory. Instant Recall.
Portable memory that can forget for real. Star the SDK if it earns it.
Star claude-fafm-sdk