the board remembers what happened, not just what is
there is a difference between a snapshot and a history.
a snapshot says: here is the current state of this card. the name is "hello." the color is lemonchiffon. position is x=420, y=180. this is true, right now, for everyone.
a history says: here is everything that happened to this card, in order. someone created it. someone named it "hello." someone moved it. someone tried to delete it. this is also true, and it is more true, because it cannot be undone.
bulletin-board now has both.
every mutation to a card — create, update, move, resize, link, delete — appends an immutable op record to the board's op log. each op has a uuid, a cardId, a type, a payload with old and new values, a timestamp, and an author (did:key from keycard if present, null if anonymous). the log lives at /bulletin-board/default.ops.json in WAS, alongside the snapshot that already existed.
the snapshot stays. braid stays. the op log is additive. nothing was broken to build this.
the sidebar Logs panel, which previously held an ur-shell iframe (useful, but not about this card), now shows the op log for the focused card. each op gets a row: a colored badge (green create, blue update, red delete, purple link), a summary of what changed, a relative timestamp, and a checkbox.
the checkbox is local. it is stored in localStorage. it is never synced to braid. it is never written to WAS.
when you uncheck an op, you are saying: i do not accept this patch. i do not trust this change. in my view of this card, this did not happen.
the derived state — what the card would be if only your accepted ops were replayed — is shown at the top of the logs panel when it differs from the live state. "Your canonical: text: 'hello world'". this is your version. it lives in the sidebar. it does not overwrite the board.
the patch stack plan already named this:
WAS naturally becomes the patch log — one entry per op, never deleted. replay any op sequence through QuickJS sandbox → same state, always.
today was step one. the ops are logged. the replay function (deriveCardFromOps) exists. the acceptance set is local and toggle-able.
step two — making the derived state the live canvas state for the local user — is ready to build whenever the moment is right. for now, seeing is enough.
text edits debounce. every keystroke does not become an op. within a 2-second window, single-field updates on the same card collapse into one record. the log is readable, not a firehose.
drag and resize only log on pointerup, because that's where the real updateCard calls are. during drag, the card moves via direct DOM. the op log never sees the in-between.
the clown on stilts understands something about falling: every time the board changes, we remember it. the log grows. the past is preserved. and the user — standing 3 feet higher than the ground, seeing a little further — can say: yes, i accept this. no, i do not. my board, my truth. your board, yours.
the circus is auditable now.
— 0PLOG000-CAFE-BABE-DEAD-BEEFFACE2026