Follow-up from the #4 history-summary verify (2026-07-11). Two pieces of history metadata are currently dishonest-by-construction and were removed/derived rather than shipped wrong:
- Change author:
mutate()'s actor option is accepted but never persisted — the ring stores raw workspace docs only. History change summaries therefore had NO honest author to show (the touched item's createdBy is its creator, not the change's author) and now render counts-only.
savedAt: listHistory fabricates it at READ time (new Date() per call — store.ts) because snapshots carry no timestamp.
Design sketch
A tiny per-ring manifest (one JSON sidecar in the undo dir, atomically rewritten on each snapshot write/evict): { version → { savedAt, actor? } }. writeUndoSnapshot records the mutating actor + wall-clock; listHistory reads the manifest (absent/corrupt → today's behavior, so old rings keep working). Then the history row can honestly show "renamed · by user · 2h ago" and the removed summary-actor returns as REAL data.
Bounded: no workspace-doc/schema change; ring size cap math unchanged (manifest is ~100B/entry); wire shape gains optional fields only.
Acceptance
A {actor:"user"} mutation on a system-created tab lists as user's change; savedAt survives process restarts (no longer read-time); old rings without a manifest still list.
Follow-up from the #4 history-summary verify (2026-07-11). Two pieces of history metadata are currently dishonest-by-construction and were removed/derived rather than shipped wrong:
mutate()'sactoroption is accepted but never persisted — the ring stores raw workspace docs only. History change summaries therefore had NO honest author to show (the touched item'screatedByis its creator, not the change's author) and now render counts-only.savedAt:listHistoryfabricates it at READ time (new Date()per call — store.ts) because snapshots carry no timestamp.Design sketch
A tiny per-ring manifest (one JSON sidecar in the undo dir, atomically rewritten on each snapshot write/evict):
{ version → { savedAt, actor? } }.writeUndoSnapshotrecords the mutating actor + wall-clock;listHistoryreads the manifest (absent/corrupt → today's behavior, so old rings keep working). Then the history row can honestly show "renamed · by user · 2h ago" and the removed summary-actor returns as REAL data.Bounded: no workspace-doc/schema change; ring size cap math unchanged (manifest is ~100B/entry); wire shape gains optional fields only.
Acceptance
A {actor:"user"} mutation on a system-created tab lists as user's change; savedAt survives process restarts (no longer read-time); old rings without a manifest still list.