JM: Track event registrations with PaperTrail - #2245
Draft
maebeale wants to merge 10 commits into
Draft
Conversation
Give EventRegistration a versioned audit trail (status, payment flags, attendance, certificate, scholarship changes) and surface it as the existing Change Log dropdown on the registration edit page. Ahoy stays on these records; PaperTrail adds a per-record, reify-able history alongside it. Start with the parent record only and expand to child records as we see what the log reads like. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the registration audit trail to its child records so a full timeline can reconstruct what changed and by whom, including after a record is deleted (destroy versions persist in the versions table). - EventAttendanceTimeEntry: sign-in/out corrections - EventRegistrationChecklistCompletion: onboarding step completions - EventRegistrationOrganization: org link add/remove - Comment: comment history (app-wide, polymorphic — not registration-only) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A scholarship's award amount, agreement signature, and task completion are part of a registration's story via the allocation that links them. Track the scholarship so the timeline captures award/acceptance changes; its comments and joining allocation are already versioned. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Track the submission envelope (event linkage, metadata, create/delete) so the timeline and audit trail can see when a registrant submitted or a submission was removed. The answer rows live in form_answers (untracked here — see PR note). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundation for making PaperTrail the single activity-timeline source (retiring Ahoy) with both person and per-record timelines. - FormAnswer: version the actual submitted answers (envelope was already tracked on FormSubmission). - User: version account changes, skipping only secrets (password + reset/ confirm/unlock/welcome tokens). Sign-in timestamps, counts, and IPs are kept as the login-activity signal (parity with Ahoy). - Add versions.subject_person_id (indexed) so a person's timeline is one indexed sweep instead of reifying and walking associations. Every timeline-tracked model populates it uniformly via has_paper_trail meta; Comment resolves its polymorphic commentable to the subject person. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add subject_person_id meta to the already-versioned money/membership models so their versions land on a person's activity timeline: - Payment, Membership, ProfessionalLicense: direct person_id - MembershipInvoice (member), ContinuingEducationRegistration (registrant): delegated resolvers - Allocation, Refund: polymorphic resolvers walking allocatable/refundable (and Refund's own person recipient) to the subject person - Discount stays unscoped by design (fans out to many people; scoped via its Allocation versions) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/papertrail-event-registrations
branch
from
August 18, 2026 10:25
3b08b6d to
72872ce
Compare
An IP is PII and the versions table is append-only, so versioning it fights data-retention/erasure. Skip the two sign-in IP columns; the live IP stays on the user record. Sign-in timestamps and counts remain versioned as the login-activity signal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the six self-evident subject_person_id delegator comments; condense the polymorphic resolver, User skip-list, Discount, and migration comments to the gotcha they actually carry, per the comment policy.
Extend PaperTrail + subject_person_id to the records a person's timeline needs beyond registrations: Person (self), Affiliation (person), Address/ContactMethod (polymorphic — person-owned only), Asset (owner's person). Organization is tracked but unscoped — it has many affiliates, so it surfaces to a person via their affiliation, not a single subject_person_id. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the event-registration coordination graph. EventStaff is person-scoped (a staffing role belongs on that person's timeline); Event, EventForm, and RegistrationTicketCallout have no single person, so they get a subjectless global trail like Discount. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
@jmilljr24 looks like this is what would be needed to properly track registration lifecycles (with an eye to person timelines and addition of subject_person_id) |
Collaborator
|
@maebeale check my slack message |
maebeale
marked this pull request as ready for review
August 18, 2026 14:48
maebeale
marked this pull request as draft
August 18, 2026 14:48
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.
🤖 suggested review level: 5 Inspect 🔬 versioning + person-scoping across ~22 models incl. a User secret skip-list, polymorphic subject resolvers, and a new indexed
versionscolumn a person/record timeline depends onGoal
Make PaperTrail the source for record-change activity (person-level and per-record), working toward a timeline that interleaves PaperTrail (mutations) + Notifications (communications) + Ahoy (visits/views). This PR lays the tracking + person-scoping foundation; the timeline query/UI is the follow-up. Ahoy stays.
Records now versioned (create / update / destroy)
EventRegistration,EventAttendanceTimeEntry,EventRegistrationChecklistCompletion,EventRegistrationOrganization.Scholarship,FormSubmission,FormAnswer,Comment,User(account changes).Payment,Allocation,Refund,Discount,ContinuingEducationRegistration,Membership,MembershipInvoice,ProfessionalLicense.Person,Affiliation,Address,ContactMethod,Asset,Organization.Event,EventForm,RegistrationTicketCallout,EventStaff.Associations related to coordinating a person's event registration
The full graph around
EventRegistration. ✅ = versioned; subject = how a version reaches a person's timeline (a column, a resolver method, or unscoped where a record relates to many people); new = added in this branch's latest work.Core
EventRegistration✅ —registrant→Person,belongs_to :event. subject:registrant_idFinancial — all flow through the polymorphic
AllocationledgerAllocation✅ (resolver) ·Payment✅ (person_id) ·Scholarship✅ (recipient_id) ·Refund✅ (resolver) ·Discount✅ unscoped (fans out to many people)Continuing education / attendance
ContinuingEducationRegistration✅ (resolver) ·EventAttendanceTimeEntry✅ (resolver →registrant_id) ·ProfessionalLicense✅ (person_id)Roster / onboarding
EventRegistrationChecklistCompletion✅ ·EventRegistrationOrganization✅ ·Affiliation✅ (person_id, auto-minted facilitator link) ·EventStaff✅ new (person_id)Communication / notes
Comment✅ (resolver) ·Notification❌ not versioned — stays its own timeline source (communications, read directly)Form intake (linked via registrant + event, not a direct FK)
FormSubmission✅ (person_id) ·FormAnswer✅ (resolver)Event-side config — subjectless global trail (shared config, fans out to every registrant)
Event✅ new ·EventForm✅ new ·RegistrationTicketCallout✅ newParents
Person✅ (id) ·Event✅ new (subjectless)subject_person_idonversions(new, indexed)PaperTrail::Version.where(subject_person_id:)), not a reify-and-walk per row.has_paper_trail meta:— direct column where the person is local, a resolver method where it's on a parent or polymorphic (Comment,Allocation,Refund,Address/ContactMethodby type,Assetby owner, CE, membership invoice).Discount,Organization, and the event-side config (Event,EventForm,RegistrationTicketCallout) — each relates to many people (or no single one), so no single owner; the money/roster ones surface to a person via theirAllocation/Affiliationversions.subject_person_id; all resolvers checked.User — secrets & IPs excluded, activity kept
has_paper_trail skip:drops the secrets (encrypted_password+ reset/confirm/unlock/welcome tokens) and the two sign-in IPs (PII in an append-only log fights retention/erasure; the live IP still sits on the record). Sign-in timestamps and counts stay versioned — the login-activity signal. Semantic auth-event labels + the 3 events that skipped columns swallow are a follow-up: #2249.Architecture (agreed)
Final timeline = PaperTrail (record changes) + Notifications (communications, read directly) + Ahoy (visits/views). PaperTrail is mutation-only and won't absorb page views — those stay in Ahoy. Notifications stay their own source (not versioned). So "remove Ahoy" narrows to removing the redundant Ahoy lifecycle/mutation tracking once PaperTrail covers it; Ahoy's visit/view tracking lives on.
Notes / follow-ups
createanddestroyrecorded by default → full lifecycle incl. deleted records. The Change Log partial'sversions.size > 1guard is display-only.subject_person_id; record: by item + children; deleted-record viewer).Personavatar /Organizationlogo are directhas_one_attached(not columns), so their attachment changes aren't versioned — revisit with the attachment question.