Draft: Enforce the targeted message schedule
What does this MR do and why?
Three fixes to targeted messages, found while prototyping plan targeting in #634504.
TargetedMessageResolverignoredstarts_atandends_at. !216492 (merged) added the schedule so messages display during set windows, but nothing filtered on it, so a message showed as soon as an admin created it and never expired. The resolver now drops messages outside the window.starts_at_not_in_pastran on every save. Once a message had started, any edit failed validation, including moving the end time to end it early. It now runs only whenstarts_atchanges, and the edit form no longer resends an unchanged start time.- The admin form passed the CSV limit hint to
GlFormFieldsasdescription, which the component ignores, so the hint never rendered. It now goes ingroupAttrs.
Specs now use an :active factory trait instead of relying on the factory's default offsets. That includes the billing feature examples in ee/spec/support/shared_examples/features/targeted_messages_shared_examples.rb, which would otherwise fail once the schedule is enforced.
Behaviour change on GitLab.com. targeted_messages_admin_ui has been enabled on gprd since 29 July 2025 (#528820). Once this deploys, any message whose window has closed stops showing, and a message scheduled for later shows only once it starts. Someone with production access should check for live rows outside their window before this merges.
References
Screenshots or screen recordings
The only visible change is the "Namespace IDs are limited to a maximum of 50,000." hint under the CSV upload field, which now renders.
How to set up and validate locally
-
Simulate GitLab.com and enable the flag in a Rails console:
Feature.enable(:targeted_messages_admin_ui) -
In Admin > Targeted messages, create a message for a group you own, starting 10 minutes from now.
-
Open the group's Settings > Billing page. Before this change the banner shows straight away. After it, the banner appears only once the start time passes.
-
After the message starts, edit it and move the end time. Before this change the save fails with "Starts at cannot be in the past". After it, the save succeeds.
Tests
Run locally in Caproni:
- RSpec: every targeted message spec passes except 3
GETrenders inee/spec/requests/admin/targeted_messages_controller_spec.rb, which fail in my local test environment because the icon sprite isn't in the asset manifest. The untouchedspec/requests/admin/broadcast_messages_controller_spec.rbfails the same 3 ways there. - I couldn't run
ee/spec/features/groups/billing_spec.rblocally because Gitaly won't start from this checkout, so CI has to confirm the shared example fix. - Jest: 42 of 42 pass.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.