rubyforgood / rubyforgood/awbw

[Parked] Restore semantic User auth events on the PaperTrail timeline

Open
#2,249 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Ruby
Stars
15
Forks
26
Avg merge
12h 42m
Merged PRs (30d)
242

Description

🤖 From Claude:

Status (2026-08-23): parked — staying on Ahoy for now. The activity timeline is not moving onto PaperTrail; the Ahoy-based change log (shipped in #2245) is what we're keeping. This issue is therefore decoupled from #2245 and only becomes relevant if a PaperTrail-based timeline is revived later. Left open as a record of what that migration would need.

Background

If the activity timeline were moved onto PaperTrail (not planned right now — see status above), three of Ahoy's semantic auth.* events wouldn't survive the move — they change skipped columns or bypass PaperTrail entirely, so no version is written at all:

Ahoy event Trigger Why no version
auth.password_changed encrypted_password change encrypted_password is in has_paper_trail skip: (secret)
auth.password_first_set encrypted_password (welcome flow) same skip
auth.account_setup_completed welcome_instructions_token cleared done via update_columns — bypasses callbacks/PaperTrail

Separately, the column-backed auth events (login, admin granted/revoked, locked/unlocked, deactivated/reactivated, email changed/confirmed, reset sent, welcome sent, account deleted) do produce versions — but as raw column diffs (super_user: false → true), not the semantic label Ahoy emitted.

Do we have the timestamps we'd need? No.

Present *_at columns cover most events, but two are missing:

  • password_changed_at — needed for password_changed / password_first_set
  • setup_completed_at (or welcome_completed_at) — needed for account_setup_completed

Proposed plan

  1. Add non-secret timestamp columns password_changed_at and setup_completed_at, stamped whenever those events happen. Stamping them creates a real (non-secret) version, restoring the three lost events. Fix the update_columns bypass so the setup-completion write goes through a normal update (or explicitly stamp the timestamp).
  2. Semantic labels for the timeline. Two options — decide in this issue:
    • (a) Derive in the timeline presenter from the version's changeset (e.g. super_user true→false ⇒ "Admin revoked"). No schema change; labeling logic lives in one place with the rest of the timeline rendering.
    • (b) action_note meta column on versions, populated at write time via has_paper_trail meta: (a proc that names the semantic event). Captures intent the diff can't always recover, but duplicates labeling logic and only helps User.
    • Recommendation: (a) for the column-backed events (the diff already says what happened), plus the new timestamps from step 1 for the three bypass cases. Reserve action_note for cases where intent genuinely isn't recoverable from the diff.
    • password_first_set vs password_changed is still derivable under (a): the password_changed_at transition disambiguates them — nil → T is first-set, T → T' is a change — so this pair doesn't need action_note.
  3. Attribute the actor (whodunnit) on self-serve Devise flows. The lost/column-backed events fire in controllers that run without a logged-in userPasswordsController and WelcomeController both skip_before_action :authenticate_user! (and confirmation/unlock link flows are the same). set_paper_trail_whodunnit reads current_user, so every version these flows write lands with nil whodunnit and the timeline's "By" column comes up blank — where Ahoy attributed the actor. Set PaperTrail.request.whodunnit to the resource itself (self-serve) in these actions, mirroring the existing updated_by: current_user || @user pattern already used in welcome_controller.rb. Without this, restoring the events isn't enough — they'd read as "by (nobody)".
  4. Keep the User skip: list as-is (secrets stay out of the trail); the new timestamps are the non-secret signal.

Out of scope

  • Attachment add/remove (avatar/logo) — separate residual gap, tracked with the broader attachment question.
  • Analytics vs. audit: PaperTrail replaces the account change timeline only. Ahoy's page views / prints / downloads / search intent / visits, and its association + rich-text change capture, have no version equivalent and stay on Ahoy. "Retire Ahoy" is scoped to the change-audit timeline, not engagement analytics.
  • Not tied to #2245 — that PR ships the Ahoy-based change log we're keeping; this is speculative future work for a PaperTrail timeline.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue is explicitly parked while the Ahoy-based timeline remains in use, so no implementation should start unless a PaperTrail migration is revived. If revived, begin with PasswordsController, WelcomeController, and the User PaperTrail skip list; resolve the timestamp, semantic-label, and whodunnit decisions before changing the version timeline. Done would restore the listed auth events with meaningful labels and actor attribution without recording secrets.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
authentication, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.