Skip to content

Make the profile the source of truth for author credit - #2093

Draft
maebeale wants to merge 9 commits into
mainfrom
maebeale/profile-visibility-preferences
Draft

Make the profile the source of truth for author credit#2093
maebeale wants to merge 9 commits into
mainfrom
maebeale/profile-visibility-preferences

Conversation

@maebeale

@maebeale maebeale commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

🤖 suggested review level: 5 Inspect 🔬 changes how every author credit renders app-wide, narrows search results, and adds an admin page

Name display was asked in two unrelated places with two different value sets — people.display_name_preference (people index) and <item>.author_credit_preference (every credit, on 8 forms). AuthorCreditable deliberately called person.full_name rather than person.name, so the per-item answer won and the profile was ignored. One facilitator could render three different ways with no single place to fix it.

Now: the profile formats every credit. The 8 per-item selects come off. The stored column is retained as the consent record, human-editable only on a new admin reconciliation page.

Two axes on the profile

Column Governs
Name format display_name_preference (4 values) Person#name and author credits
Anonymity contributions_anonymous (new) Author credits only — the person stays listed and formatted normally on the people index

The one asymmetry

A stored author_credit_preference no longer drives display — except anonymous, which is always honored. Anonymity is true if the profile says so or the item says so; neither can strip it from the other. Three reasons it has to work this way:

  1. Anonymity is inherently per-item — a person may want four stories credited and the fifth not.
  2. Admins need a per-item lever on the reconciliation page.
  3. There is no backfill, so without it every item currently storing anonymous would start rendering its author's real name on deploy.

Search now honors the preference

Anonymous → matches nothing. first_name_only → not findable by last name. first_name_last_initial → matches the initial, not the whole last name. This also meant dropping the person_first/person_last SearchCop attributes on Story/CommunityNews, which bypassed the gate entirely.

Reconciliation page (/author_credit_divergences, linked from admin home)

Content whose snapshot disagrees with its author's profile, grouped by person, filterable by person_id / type / preference / include_reconciled. Resolvable in both directions: apply one preference to the person's profile (suggested = most restrictive across their content), or edit a single item's stored value.

Worklist UX (low-tech admin): filters in a card (incl. a person remote lookup) apply live with no Filter button; per-section counts up top with jump links; each card shows the action after the supporting data; and every fix (Save / Apply to profile / Credit) posts over Turbo, re-rendering just the results frame + flash instead of reloading. A stored value can be cleared to None (follow profile) (normalizes to nil), guarded so it can't silently de-anonymize an item submitted anonymously.

Also swept

Four places where a real name escaped past an anonymized credit: the variation-ideas index link, the story form's subtitle, Workshop#author_name, and two dead ResourceDecorator methods. Plus two phantom strong params (profile_show_name_preference, promoted_to_story) that referenced columns that don't exist.

Verified

Full suite green (5338 examples, 0 failures), rubocop clean, brakeman 0 warnings. Drove it in the running app: each format change propagates to the index and every credit at once; both anonymity paths suppress the credit and the link; search gating confirmed against real data; both warning variants render; the reconciliation page lists, filters, and saves.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 4, 2026 14:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 4, 2026 14:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

end
end

context 'via search_by_params (the user-facing path)' do

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Behavior change worth knowing: an author name can no longer be one term of an AND full-text query (search("John technology") used to work). Honoring the credit preference needs per-person branching that a flat SearchCop index cannot express, so person-name search moved entirely to by_credited_person_name, OR-ed in by search_by_params. Single-term author search still works.

<%= link_to divergence_record_title(record), polymorphic_path(record),
target: "_blank", rel: "noopener",
title: "Opens in a new tab",
class: "text-blue-700 hover:underline" %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Deliberate deviation from the eyebrow convention. These link to 8 different destinations, none of which has a return_to mechanism today (they use hardcoded utility-link rows), so retrofitting all eight was out of scope here — using target="_blank" instead. Happy to do the eyebrow work as a follow-up if you would rather link in-page.

end
end

context 'via search_by_params (the user-facing path)' do

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Behavior change worth knowing: an author name can no longer be one term of an AND full-text query (search("John technology") used to work). Honoring the credit preference needs per-person branching a flat SearchCop index cannot express, so person-name search moved entirely to by_credited_person_name, OR-ed in by search_by_params. Single-term author search still works.

<%= link_to divergence_record_title(record), polymorphic_path(record),
target: "_blank", rel: "noopener",
title: "Opens in a new tab",
class: "text-blue-700 hover:underline" %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Deliberate deviation from the eyebrow convention. These rows link to 8 different destinations, none of which has a return_to mechanism today (they use hardcoded utility-link rows), so retrofitting all eight was out of scope — using target="_blank" instead. Happy to do the eyebrow work as a follow-up if you would rather link in-page.

<%= link_to divergence_record_title(record), polymorphic_path(record),
target: "_blank", rel: "noopener",
title: "Opens in a new tab",
class: "text-blue-700 hover:underline" %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Deliberate deviation from the eyebrow convention. These rows link to 8 different destinations, none of which has a return_to mechanism today (they use hardcoded utility-link rows), so retrofitting all eight was out of scope — using target="_blank" instead. Happy to do the eyebrow work as a follow-up if you would rather link in-page.

maebeale and others added 3 commits August 6, 2026 12:29
Name display was asked twice with two value sets: once on the person
profile (people index) and once per item (every credit), so the same
facilitator could render three different ways with no single place to
fix it. Point credits at the profile instead.

Anonymity stays a per-item latch — a person may want four stories
credited and the fifth not, and nothing should be able to de-anonymize
an item that was submitted anonymously.

Co-Authored-By: Claude <noreply@anthropic.com>
Strip the per-item credit select from all 8 forms and show a warning
only where the stored snapshot disagrees with the profile. The stored
column stays as the consent record, editable in one place.

Search now honors the preference too: an anonymous credit matches
nothing, first_name_only isn't findable by last name, and
first_name_last_initial matches only the initial. Dropped the
person_first/person_last SearchCop attributes that bypassed this.

Co-Authored-By: Claude <noreply@anthropic.com>
Main introduced shared/_badge while this branch was in flight; the
reconciled pill was hand-rolled.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 6, 2026 18:20
@maebeale
maebeale force-pushed the maebeale/profile-visibility-preferences branch from bdcfd5d to c1d2886 Compare August 6, 2026 18:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

maebeale and others added 4 commits August 8, 2026 07:35
Sort each person's suggested preference into the worklist and name it
inline (e.g. "Suggested: First name only — most restrictive across
Maria Johnson's content") so the hint is specific to the person rather
than a generic line. Add a person name-search picker to the filters and
order groups by first then last name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keeps the admin worklist open while checking a profile, matching the
content links that already open in a new tab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 8, 2026 12:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Clearing a single column is what makes that column safe to drop, so each
one gets its own subsection and its own all-clear note naming the field.

Guess who each free-text name refers to so an admin confirms rather than
looks every one up, resolved in one query for the page.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 8, 2026 12:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Every fix on the author credit divergences page (Save stored consent,
Apply to profile, Credit to a person) did a full-page redirect. Respond
with a Turbo Stream that re-renders just the results frame and flash, so
the worklist updates in place with no page flip. Non-Turbo requests still
redirect.

Also widen the remote-select left padding so the search icon never
overlaps the placeholder or selected value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 8, 2026 18:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants