Skip to content

Match facilitator affiliations exactly and case-sensitively#1815

Merged
maebeale merged 1 commit into
mainfrom
maebeale/exact-facilitator-match
Jun 21, 2026
Merged

Match facilitator affiliations exactly and case-sensitively#1815
maebeale merged 1 commit into
mainfrom
maebeale/exact-facilitator-match

Conversation

@maebeale

@maebeale maebeale commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

REVIEW NEEDED: 📖 Read — light-logic: small, contained logic changes with low blast radius

What is the goal of this PR and why is this important?

  • Facilitator status, "facilitator since" dates, tenure badges, program status, the org-active determination, the editor card highlight, and the live JS figures all keyed off a fuzzy match (LIKE '%facilitator%' / case-insensitive include?), which counted variants like "Lead Facilitator" and "Co-Facilitator".
  • Restrict every facilitator decision to the exact, case-sensitive title "Facilitator" (whitespace-trimmed) so a single canonical role governs them.

How did you approach the change?

  • Made Affiliation#facilitator? and the .facilitators scope exact (BINARY TRIM(title) = "Facilitator"; BINARY is needed for case sensitivity under MySQL's default collation). Every call site flows through one of them.
  • The highlight rule (exact_facilitator?, previously the only exact matcher) and the business-logic rule now agree, so I collapsed them into one #facilitator? and dropped the redundant exact_facilitator?.
  • Updated the two Stimulus controllers (affiliation_dates, affiliation_facilitator_warning) to mirror the same trimmed exact rule.

Anything else to add?

  • "Facilitator" is confirmed to be the only canonical affiliation title in use, so no data migration is needed.
  • The cosmetic title-chip sort heuristics in a few view partials are intentionally left fuzzy — they only order display pills, not the facilitator-status determination.

Comment thread app/models/affiliation.rb

scope :facilitators, -> { where("title LIKE ?", "%facilitator%") }
# Only the exact, case-sensitive title "Facilitator" counts — variants like
# "Lead Facilitator" or "facilitator" are deliberately excluded. BINARY forces

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: BINARY is required here — the DB is MySQL/Trilogy with a case-insensitive default collation, so a plain where(title: "Facilitator") would still match "facilitator". This makes the SQL scope agree with the case-sensitive Ruby == in #facilitator?.


def facilitator_since_date
@facilitator_since_date ||= affiliations.where("title LIKE ?", "%Facilitator%").minimum(:start_date)
@facilitator_since_date ||= affiliations.facilitators.minimum(:start_date)

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: These two methods previously matched fuzzily on their own (LIKE '%Facilitator%'); routing them through .facilitators keeps the exact-match rule in one place.

@maebeale maebeale marked this pull request as ready for review June 21, 2026 19:27
Facilitator status, "facilitator since" dates, tenure badges, program
status, the org-active determination, the editor card highlight, and the
live JS figures all keyed off a fuzzy match (`LIKE '%facilitator%'` /
case-insensitive include), which swept in variants like "Lead Facilitator"
and "Co-Facilitator". Restrict to the exact, case-sensitive title
"Facilitator" (whitespace-trimmed) so a single canonical role drives those
decisions.

Since the highlight rule (previously the only exact matcher,
`exact_facilitator?`) and the business-logic rule now agree, collapse them
into one exact `#facilitator?` / `.facilitators` and drop the redundant
`exact_facilitator?`. BINARY is needed for case sensitivity under MySQL's
default collation; the JS controllers mirror the same trimmed exact rule.

The cosmetic title-chip sort heuristics are intentionally left fuzzy —
they order display pills, not the facilitator-status determination.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@maebeale maebeale force-pushed the maebeale/exact-facilitator-match branch from b72c70d to 6d617bd Compare June 21, 2026 19:36
Comment thread app/models/affiliation.rb
# A facilitator affiliation is one whose title is *exactly* "Facilitator"
# (trimmed, case-sensitive). Variants like "Lead Facilitator" or "facilitator"
# are deliberately excluded. Mirrors the .facilitators scope so in-memory and
# SQL checks agree.

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: Collapsed the former exact_facilitator? into #facilitator? — now that business logic is exact too, the two predicates were identical. BINARY TRIM mirrors this strip + case-sensitivity in SQL.

// Facilitator since/end — same logic filtered by title. Mirror
// Affiliation#facilitator?: an exact, case-sensitive match on "Facilitator"
// (trimmed), so the live figure matches what the server will render.
const facilitatorAffiliations = affiliations.filter(a =>

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: Both facilitator Stimulus controllers now mirror the server's exact, trimmed rule so the live figures match what gets rendered/saved (per the CLAUDE.md JS-mirroring convention).

@maebeale maebeale merged commit b172fd4 into main Jun 21, 2026
3 checks passed
@maebeale maebeale deleted the maebeale/exact-facilitator-match branch June 21, 2026 20:04
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.

1 participant