Backing table: scheduled_programs — one row per program; the Laravel scheduler generates occurrences and reminders from it.
| Column | Filament component | Type | Source | FK target |
| id | — | pk | stored | — |
| factory_id | tenant scope (Filament tenancy) | FK | stored: board is per-factory | factories.id |
| module | Select + BadgeColumn (color per module) | enum: gmp / stock / retail / buy / admin / it | stored: which module the program belongs to | — |
| name | TextInput / TextColumn with url() to module page | string | stored | — |
| category | Select | string | stored: machine / facility / cleaning / vehicle / compliance / finance… | — |
| frequency | Select — staff-facing labels, no cron syntax | enum: daily / weekly / monthly / quarterly / annual | stored: scheduler translates to next_due_at | — |
| owner_type / owner_id | MorphToSelect | enum: user / contractor + FK id | stored: who gets the reminder | users / contractors |
| evidence_type | Select | enum: form / photo / contractor_cert / system_log | stored: what proof completion requires | — |
| gmp_critical | Toggle | bool | stored: true → row mirrors onto the GMP register + FM/QA escalation | — |
| last_done_at | TextColumn::date() | datetime | derived: auto-stamped when the linked record is completed | — |
| next_due_at | TextColumn::date() — red when overdue | datetime | derived: scheduler = last_done_at + frequency | — |
| remind_days_before | TextInput::numeric() | int | stored: reminder lead time per program | — |
| escalate_after_days | TextInput::numeric() | int | stored: days overdue before FM escalation fires | — |
| active | Toggle + TernaryFilter | bool | stored: paused programs stop generating occurrences | — |
Engine notes
- The Laravel scheduler (cron) runs daily: computes occurrences from frequency + last_done_at, writes reminders to the
notifications table → owner's dashboard; overdue past escalate_after_days → Factory Manager notified.
- Staff never see cron syntax — the form offers Daily / Weekly / Monthly / Quarterly / Annual (plus 6-monthly as a legacy frequency on migrated GMP rows).
- GMP-critical rows mirror onto the GMP maintenance register (maintenance.html) — same underlying rows, two views; the 3 overdue here are the same 3 overdue there.
- Completing a linked record — cleaning checklist, stocktake, month-end e-invoice run, calibration log — auto-stamps last_done_at and rolls next_due_at forward; no separate "mark done" step for flows Gaia already records.
- "+ Program" opens the shared program form spec'd in GAPS.md (maintenance-program-new) — one form serves both the GMP register and this board.