[schemas] thought_audit table for multi-participant attribution#230
[schemas] thought_audit table for multi-participant attribution#230txcfi-scott wants to merge 1 commit into
Conversation
Adds thought_audit: an append-only log of every capture/update/delete on the thoughts table, with source + author_session_id for attribution across Claude Desktop / Codex / ChatGPT / importer writes. Strictly additive — no existing thoughts columns altered, no existing functions replaced. Grants are INSERT-only, so nothing downstream can rewrite history without an explicit migration. An optional second SQL file adds a thought_provenance view and a thoughts_by_session() RPC for querying by session id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for the contribution. This is a well-designed audit schema — the deliberate choices are sound: append-only ( Two notes for the maintainer. The audit table is storage-only with no trigger, so coverage depends on every mutation tool writing a row — it pairs naturally with this contributor's #228 ( — Alan (community reviewer; non-binding) |
|
@alanshurafa Thanks. The non-FK on |
What it does
Adds a
thought_audittable plus anauthor_session_idconvention, so who changed what, when becomes answerable once more than one participant writes to the same Open Brain.The problem. Once you have Claude Desktop, Codex, a ChatGPT API worker, and a nightly importer all writing to the same
thoughtstable, updates and deletes are effectively anonymous — nothing records which session or which agent did what. For a personal brain that is fine. For a shared brain, or for debugging "why did this thought disappear?", it is painful.The solution. An append-only audit table that records every mutation, plus a small convention (
metadata.author_session_id) that lets you cluster all writes from a single agent session.How it stays safe
thought_audit.thought_iddeliberately has no FK tothoughts(id)so audit rows survive deletion of their subject (the most important audit event to preserve).service_rolegetsSELECT, INSERTon the audit table — neverUPDATEorDELETE. Nothing downstream can rewrite history without an explicit, reviewed migration that temporarily grants DELETE.Example audit trail
One
SELECT * FROM thought_audit WHERE thought_id = '...' ORDER BY created_atreturns the full provenance.What it requires
thoughtstable).No additional services. No LLM calls.
Companion integrations
This schema is storage-only — the actual audit writes live in the mutation tools. Two companion integrations in separate PRs wire them up:
integrations/update-thought-mcp([integrations] update_thought MCP with if_unchanged_since #228) — the update tool; its README documents the audit-insert hook.integrations/delete-thought-mcp([integrations] delete_thought MCP #229) — the delete tool; its README documents the audit-insert hook with prior-content preservation.If those PRs are still open when this one is reviewed, the README examples here are the authoritative reference. The schema itself has zero runtime dependency on the integrations — you can install it and write audit rows from your own mutation code if you prefer.
Tested
schema.sqlandauthor-session-id.sqltwice against a fresh and a populated Supabase Open Brain. Both runs complete idempotently with no errors.UPDATE thought_audit ...andDELETE FROM thought_audit WHERE ...asservice_role; both correctly reject with "permission denied for table thought_audit" — confirming the append-only grant works.thought_id,author_session_id, andcreated_at; all three indexes get used.deleteaudit row remains withdiff.previous_contentpreserved.Files touched
Only
schemas/thought-audit/— stays within the contribution scope check.