Active-window status synchronization
sync_active_window_statuses reconciles denormalized status fields for tenant
entities based on their start_on/expires_on window. In the managed platform,
run it through the allowlisted active-window-sync platform schedule so each
active tenant receives an isolated datasource and table locator.
Current execution model
The seeded platform schedule is named active-window-sync and is due every 15
minutes. The unified platform worker claims due schedules; the schedule runner
iterates active tenants in slug order, enters TenantConnectionManager scope,
executes the command, records a platform job, then cleans up before the next
tenant.
To invoke the managed fan-out intentionally:
bin/cake platform schedule run active-window-sync
Do not call the tenant command once against an arbitrary default datasource
and assume the fleet was processed.
Workflow-first behavior
For a normal (non-dry-run) execution, the command first looks for an active
active-window-sync workflow definition in the current tenant. If found, it
dispatches ActiveWindow.SyncTriggered for each Kingdom branch in that tenant
(or once with a null kingdom when none exists).
If no active definition exists or dispatch fails, it runs the legacy reconciliation logic. A dispatch failure is logged and reported before fallback. The legacy path remains a compatibility mechanism; new transition orchestration belongs in the workflow definition/provider.
Legacy transitions
The fallback discovers core and loaded-plugin tables whose entity extends
ActiveWindowBaseEntity and whose schema has a status column. In a transaction
per table it changes:
Upcoming → Currentwhenstart_on <= now;Current → Expiredwhenexpires_onis not null andexpires_on <= now.
When a table has modified_by, the fallback assigns the system member ID 1.
It saves without validation/build-rule checks, so eligibility must remain
encoded in the discovery and transition contract.
This status reconciliation boundary is not identical to the
ActiveWindowBehavior::find('current') filter at the exact expiration instant:
the finder uses expires_on >= effective, while synchronization expires at
expires_on <= now. Code that depends on that instant must supply and test an
explicit effective time rather than mixing finder and status assumptions.
Dry run
bin/cake sync_active_window_statuses --dry-run
Dry-run bypasses workflow dispatch and previews the legacy changes without saving. Run this only when the CLI process is already connected to the intended single tenant (for example the local primary tenant). The command itself does not accept a tenant selector. Fleet-safe selection belongs to the platform schedule runner.
Monitoring and failure handling
Review:
- the
active-window-syncrow’s last run/success/failure fields; - central platform job/event records for each tenant target;
- scheduler/application logs for the scrubbed failure; and
- the affected tenant’s workflow execution logs when workflow-first dispatch ran.
One tenant failure is recorded centrally and must not contaminate the next scope. Do not “repair” statuses in the platform database; all affected records are tenant-owned.
Development and testing
When adding an ActiveWindowBaseEntity subclass:
- include compatible
status,start_on, and nullableexpires_onfields; - use the established status constants;
- decide whether automatic discovery is intended;
- add exact-boundary and dry-run tests;
- test it through two sequential tenant scopes; and
- update the workflow provider if transitions require domain side effects.
Relevant sources:
app/src/Command/SyncActiveWindowStatusesCommand.phpapp/src/Model/Behavior/ActiveWindowBehavior.phpapp/src/Model/Entity/ActiveWindowBaseEntity.phpapp/src/Services/Platform/PlatformScheduleRunner.php