Repository navigation
feat(history): unified change history - #337
Merged
Merged
Conversation
Adds History::Tracked, recording creates, updates ({field: [old, new]}),
deletes (with snapshot), approvals and restores in the history table with
request metadata, retention and undelete. Replaces GroupHistory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
18 tasks done
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #336
Unified change history: every tracked model records its changes in the
historytable, filterable byresource_type(table name) +resource_id, orcorrelation_id(parent), newest first.Changes
history: uuidv7 id,resource_type,resource_id,correlation_id,action,changed_fields,diff,snapshot,user_id,user_email,context,created_at.group_historyrows (group id becomescorrelation_id) and dropsgroup_history. Oldhistoryrows are dropped, as nothing read them.History::Trackedmixincreate: every field setupdate:{"field": [old, new]}delete: full snapshotapprove: diff + snapshotrestore: written by undeleteon_persisthook, so the values recorded are the ones written after all callbacks have run, inside the same transaction.history_ignore(an update that only changes ignored fields is not recorded) andhistory_redact(secrets never stored).history_resource_id,history_correlation_id,history_record_as.#change_history,.change_history(id),History.for_resource,History.correlated. The method ischange_history, nothistory, because Booking already has ahistoryattribute..undelete(id): restores the latest delete snapshot with its original id. Cascaded children are not restored.History::MetadataHistory.with_metadata, so changes made by cascading callbacks are attributed too, or per record withhistory_metadata=.PLACE_HISTORY_RETENTION_DAYS=30,PLACE_HISTORY_MAX_ENTRIES=250,PLACE_HISTORY_MIN_ENTRIES=5.History.cleanup_expiredruns at most hourly per process. It removes every entry of resources deleted more than 30 days ago.approve_draft!records anapproveentry.event_id, creates skipped,ext_data.<key>changed fields kept.booking_id:instance_start, correlated to its booking.changes,changes_json,attributes_json.on_persisthook (feat: add on_persist hook spider-gazelle/pg-orm#23), and aggregates ignore ORDER BY (fix: ignore ordering when calculating aggregates spider-gazelle/pg-orm#24).Fix included
User#destroyguarded the last admin withPgAdvisoryLock, which takes a session lock on whichever pooled connection is free and hands it back to the pool. Advisory locks are re-entrant per connection, so a second fiber could get in alongside the first, and CI once deleted every admin.ensure_admin_remainsnow takespg_advisory_xact_lockinside the destroy transaction instead.PgAdvisoryLockitself still needs fixing in pg-orm.Downstream
This is a breaking change for rest-api.
GroupHistoryandacting_userare gone, so rest-api needs a follow-up PR to:History.with_metadataper request/group_historyReview
./testpassed, 857 examples (after merging master), 0 failures.crystal tool formatand ameba are clean.spec/history_spec.cr:ON DELETE CASCADE.update_all,delete_allor raw SQL.🤖 Generated with Claude Code