Treat a future-start facilitator affiliation as Upcoming, not Active - #2336
Open
maebeale wants to merge 23 commits into
Open
Treat a future-start facilitator affiliation as Upcoming, not Active#2336maebeale wants to merge 23 commits into
maebeale wants to merge 23 commits into
Conversation
maebeale
added a commit
that referenced
this pull request
Aug 22, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
added a commit
that referenced
this pull request
Aug 22, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/active-facilitator-status
branch
from
August 22, 2026 15:36
f049d93 to
b3f21ee
Compare
maebeale
added a commit
that referenced
this pull request
Aug 23, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/active-facilitator-status
branch
from
August 23, 2026 00:40
b3f21ee to
2764311
Compare
maebeale
marked this pull request as ready for review
August 23, 2026 15:55
maebeale
added a commit
that referenced
this pull request
Aug 23, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/active-facilitator-status
branch
from
August 23, 2026 16:05
2764311 to
926dba2
Compare
maebeale
added a commit
that referenced
this pull request
Aug 24, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/active-facilitator-status
branch
from
August 24, 2026 02:15
a2edc07 to
83dbeeb
Compare
An affiliation whose start date is still in the future is not genuinely active yet, so #active? and the `active` scope now exclude it — Active means started, not ended, and not flagged inactive (the same rule as status_on == "Active"). This flows through the person edit form's facilitator status and the people directory, and to org status. The people directory's facilitator-status filter gains an "Upcoming" option (and stops lumping these people under "Inactive"), and the affiliation editor row shows an "Upcoming" badge, live-toggled as the start date is edited. `active_or_pending` is left intact for registration dedup, which still counts scheduled future trainings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An org whose only facilitator affiliation is scheduled for a future start was landing in "Formerly active", which is wrong for one that was never active. It now reads as "Upcoming" — a distinct bucket (precedence: active > upcoming > formerly_active > never_active) with its own amber badge, an "Upcoming" option on the organization index filter, and a live chip on the edit form. Upcoming orgs still appear under the "Inactive" (not-active) filter umbrella too. Adds Affiliation#upcoming? and mirrors the bucket logic in the affiliation-dates Stimulus controller. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…DR-0001 The people directory's "Inactive" facilitator filter is a not-active umbrella, so it now returns upcoming (future-start) facilitators too — matching the org index's "Inactive" (formerly_or_never) option. Upcoming remains its own filter option and status. ADR-0001 D3 now lists the Upcoming bucket and spells out that "Inactive" includes it, for both people and orgs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
isUpcoming/isPast parsed the date inputs as UTC midnight but compared against a local-midnight "today", so a start/end equal to today could be misjudged in timezones ahead of UTC (badging a today-start affiliation as Upcoming). Compare the inputs' own YYYY-MM-DD strings against a local YYYY-MM-DD "today" instead — matching the server's strict `> today` / `< today`. Adds a headless system spec covering past/ended/today/tomorrow/future. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The affiliation-status demo had no upcoming facilitator, so the Upcoming badge, status, and filter couldn't be seen in dev. Adds "A7 Upcoming facilitator": an active Counselor role plus a Facilitator affiliation dated one month out, so the person reads Upcoming (and appears under both the Upcoming and Inactive filters). add_affiliation now takes an optional start_date. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Point A7's future-dated Facilitator affiliation at a dedicated org whose only facilitator is that upcoming one, so the org's program-status chip reads "Upcoming" and it appears under the org index's Upcoming (and Inactive) filters — letting the org-level Upcoming state be seen in dev, not just the person-level. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upcoming is a benign not-yet-active state that falls under the "Inactive" (not-active) umbrella, so amber (a warning hue) overstated it. Use blue for the affiliation-row Upcoming badge and the org program-status chip (org_upcoming). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the people-index "Affiliated since" column to "Facilitator since" and show the facilitator-since year (matching the edit form and org index) instead of the earliest any-role affiliation. Under the year, show a blue "Upcoming"/"Inactive" label for non-active facilitators (nothing for active or non-facilitators). Both new decorator methods compute from the eager-loaded affiliations — no per-row query. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arts An affiliation that isn't currently active now shows a blue "Inactive" badge (ended, flagged, or not-yet-started); a future-start one additionally shows "Upcoming". So an ended row reads Inactive only, while an upcoming row reads Inactive + Upcoming — Inactive is the not-active state, Upcoming the "starting soon" qualifier. Both toggle live in the inactive-toggle controller. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The public people directory is a directory of active facilitators, so a person is "published" (visible to non-admins, white row) only with a currently-active Facilitator affiliation — a non-facilitator role or a lapsed/upcoming facilitator is admin-only (blue row) and excluded from the non-admin search scope. Renames with_active_affiliations → with_active_facilitator_affiliations and updates #published? to match; both now key off Affiliation.facilitators.active. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…itator Broaden facilitators_inactive to the full not-active umbrella: people with an ended/flagged or upcoming facilitator affiliation AND people with no facilitator affiliation at all (never active). facilitator_status_label likewise reads "Inactive" for a never-facilitator. Mirrors the org index's Inactive (formerly_or_never), which already includes Never active. ADR-0001 D3 sharpened. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the not-also-active exclusion from facilitators_upcoming: a person who is an active facilitator at one org and has an upcoming facilitator affiliation at another is now returned by both the Active and Upcoming filters, since each is a membership test on that person's affiliations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The two indexes offer the same facilitator-status taxonomy, so a reader shouldn't have to re-learn the order between them: Inactive is the not-active umbrella and the narrower buckets it contains follow it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… SQL
The legacy column predates Upcoming, so a program whose facilitator is
scheduled but hasn't started has no "right" value to store — Pending is a
fair record of it, and flagging every such org as drift is noise the
warning was never meant to carry.
The active scope had grown a verbatim copy of with_status("Active"),
which is the duplication #active? already avoids by going through
#status_on.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Narrowing `.active` to exclude future starts also reached the write path: sync_organization_status_with_affiliations stamped an org "Inactive" the moment its only facilitator was dated to a future training, and nothing re-runs the callback when that start date arrives. It also defeated the new :upcoming exemption in legacy_status_mismatch?, since a forced "Inactive" buckets as :formerly_active rather than :never_active. Leaving it at active_or_pending would only mirror the problem — an Inactive org gaining an upcoming facilitator would read "Active" before anyone had facilitated. So an upcoming-only program is now a no-op in both directions. The two mismatch specs passed vacuously: without an "Inactive" status row the callback early-returns, so they never exercised it. They seed it now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The people index labelled someone "Upcoming" and then rendered an empty Affiliation(s) cell beside it, because the column had moved to the narrowed active? — the org they're about to facilitate for was the one thing the label was about. The profile's affiliations tab dropped them the same way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The column kept the member_since fallback it had as "Affiliated since", so a Counselor with no facilitator role showed a membership year under a heading claiming they facilitated then. The fallback still applies to a real facilitator whose rows carry no start date. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
todayISO() read the browser's local date while the ERB badges render against the request's Date.current — and requests run in the signed-in user's zone (ApplicationController#set_time_zone_from_user), not the machine's. Anyone whose OS zone sits ahead of their profile zone saw a row starting today badged "Upcoming" while the server disagreed. The server passes its own today in as a value, so the two can't drift. The system spec pins the user's zone for the same reason: it was building "starts today" in a different zone than the page renders in, and failed for real once the clock crossed midnight UTC. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
D3 named the chip's four buckets and then described the filter's Inactive umbrella in the same breath, which reads as though Inactive were a fifth chip. Spell out that Inactive is a filter option only, and list the dropdown's five options in display order. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`inactive` is only re-derived when an affiliation is saved (set_inactive_from_dates), so a term that simply lapsed still reads inactive: false until something touches the row. Filtering on the flag alone credited a registrant's shout-out to an organization they had already left. Judge it the way every other display does — flagged OR ended — via a new Affiliation#inactive_on?, the in-memory twin of status_on == "Inactive". That keeps a not-yet-started affiliation, which is what we want here: a registrant at a training to be trained is credited to the org they're about to facilitate for. Also pins the program-status anchor with a test: an org whose only facilitator affiliation is minted at a training reads New at that training on every day of a multi-day event and looking back years later, because the anchor is the event's start date and never "today". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The attendees roster called itself cross-event and fell back to the year anchor, but every row already knows the registration that linked the org — the query joins event_registrations and then plucks the event away. Keep the event start date and judge each (organization, event) pair there, so an org linked at a 2018 training reads New while the same org at a 2024 one reads Ongoing, and no row carries the "no event in view" caveat any more. Also gates the Upcoming chip on admin. The org index is becoming more than admin-facing, and to everyone else a program that starts next month is simply not active today; showing "Upcoming" there would promise a state of the world that hasn't arrived. Non-admins get plain "Inactive", coloured like Never active since the org has never facilitated. Admins keep Upcoming, which is the distinction they act on. status_bucket_styles takes the same flag, because the org edit form is admin-or-owner and its live chip would otherwise contradict the server render for an owner. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/active-facilitator-status
branch
from
August 24, 2026 13:07
79599f7 to
a89f2ff
Compare
The roster now anchors program status on each row's event, which invites the same move for the Affiliation status column beside it. It shouldn't move: that column answers where a person stands *now* — the point is to spot a lapsed affiliation and chase it — and its filter (person_affiliation_status_ids → Affiliation.with_status) also judges today. Re-anchoring the column on the event would leave it contradicting the filter that selected the rows. Written down so the next reader doesn't "fix" it. Both comments also still named the old "Pending" status; the taxonomy has been Active / Upcoming / Inactive since Affiliation::STATUSES. Sorts Tailwind classes on the files this branch touches (ai/tw-sort) — pre-existing drift, reordering only, no class added or dropped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 🔬 changes shared active/
active?semantics feeding person + org facilitator status, filters, decorators, policies, and two Stimulus controllersGoal
status_on == "Active".Affiliations
#active?→status_on == "Active"; new#upcoming?; theactivescope gains the start-date clause.active_or_pendingunchanged — registration dedup and scenario end-dating still count future starts.set_time_zone_from_user), so a viewer whose OS zone sits ahead of their profile zone would otherwise see a row starting today badged Upcoming while the server disagreed.People
member_sincefallback would have printed a membership year under that heading; it still applies to a real facilitator whose rows carry no start date). Upcoming/Inactive label under the year.with_active_affiliations→with_active_facilitator_affiliations, behindpublished/PersonPolicy. A Counselor-only or lapsed person is now admin-visible only.Organizations
:upcomingstatus bucket (precedence: active → upcoming → formerly_active → never_active), blue:org_upcomingbadge.Organization.program_status: newupcomingbucket;formerly_activeexcludes it;formerly_or_never("Inactive") still includes it, so upcoming orgs also surface under Inactive.Organization::PROGRAM_STATUS_FILTER_OPTIONS, ordered like the people filter (Active, Inactive, Upcoming, Formerly active, Never active).affiliation_dates_controller.jsmirrors the new bucket for the edit form's live status chip.:upcomingis compared as:never_active— a storedPending(or blank/Unknown) on an upcoming org isn't drift.Notes for reviewers
organization_statusis a no-op for upcoming-only programs. The affiliation-save callback deactivates on all-lapsed/none and reactivates on active, but leaves an org alone when its only facilitator hasn't started — stamping it Inactive would stick (nothing re-runs when the start date arrives) and stamping it Active would claim a program that hasn't begun. Either way the legacy-drift warning above would misfire. The displayed bucket (organization_status_bucket, ADR-0001 D3) andpublished?re-derive live regardless.mainafter Judge each scholarship's program status at its own training #2367 (the scholarship program-status anchoring, split out of this branch) merged — those commits dropped out, so this PR is now purely the Upcoming taxonomy.Tests
#active?/#upcoming?/.active; people Active/Upcoming/Inactive buckets; org bucket precedence +program_statusscope buckets; a system spec for the editor's live badges.Inactivestatus row — without it the callback early-returned and they passed vacuously.