IMM-05 · Renewal tracking
SOP:
SOP_Immigration_Applications.md§7 / §8 Step 7Actors: System (dailydeadline-scheduler); IMM Operators receive notifications. Pre-state: A filed application with aStatutoryDeadline(PASS_RENEWAL)(see IMM-04). Post-state: The renewal deadline's status reflects how close it is (UPCOMING → DUE_SOON → OVERDUE); IMM operators are notified once when it first becomesDUE_SOON.
0. Prerequisites
- At least one
PASS_RENEWALdeadline row for the tenant. - The daily
deadline-schedulerworker job is registered.
1. Steps (system-driven)
The daily deadline-scheduler handler (apps/worker/src/handlers/deadline-scheduler.ts) lists all tenants under a bypass context, then per tenant runs syncStatutoryDeadlines and sweepPassRenewals (packages/db/src/deadlines.ts).
sweepPassRenewals (D1-5-IMM Workstream 5):
- Scans the tenant's existing
PASS_RENEWALdeadlines (created at submit time — the sweep never creates new ones). - Re-derives each one's status from
dueDatevs a 60-day lead time:UPCOMING— more than 60 days out (riskLOW);DUE_SOON— within 60 days (riskMEDIUM);OVERDUE— past due (riskHIGH).
- Writes the status only when it changes (idempotent — a repeat sweep with the same
nowis a no-op) and emits aDEADLINE_STATEevent (module = IMM) on every real transition, withfromStatusin the payload. - On the first crossing into
DUE_SOON(theUPCOMING → DUE_SOONedge only — a later sweep that finds it alreadyDUE_SOON, or anOVERDUE → DUE_SOONre-dating, does not re-alert), creates oneNotificationof typePASS_RENEWAL_DEADLINE_APPROACHINGper activeIMM_OPERATOR/PLATFORM_ADMIN, linking to/dashboard/imm?entityId=<entityId>.
Renewal itself is re-initiated manually — when notified, the operator starts a fresh application (IMM-01). The sweep never auto-creates a run.
2. Verification
Database
SELECT entity_id, status, risk_level, due_date FROM statutory_deadlines
WHERE deadline_type = 'PASS_RENEWAL';
-- status reflects 60-day lead time vs due_date
SELECT recipient_id, type, payload_json->>'link' AS link
FROM notifications
WHERE type = 'PASS_RENEWAL_DEADLINE_APPROACHING';
-- one row per IMM operator, created once on the UPCOMING → DUE_SOON edge
SELECT event_class, outcome, payload->>'fromStatus' AS from_status
FROM events
WHERE event_class = 'DEADLINE_STATE' AND payload->>'deadlineType' = 'PASS_RENEWAL';Manual trigger (dev)
To exercise the sweep without waiting for the daily cron, enqueue the deadline-scheduler job (or invoke handleDeadlineScheduler against a test DB). To force a transition, back-date a PASS_RENEWAL deadline's due_date to within 60 days, then run the sweep and confirm one notification per IMM recipient and a DEADLINE_STATE event with from_status = 'UPCOMING'.
3. Threshold config reference (operator/admin)
The per-pass-type salary thresholds that gate eligibility (IMM-03) are edited by PLATFORM_ADMIN at /dashboard/jurisdictions:
GET /admin/jurisdictions
GET /admin/jurisdictions/:id -- detail incl. immThresholds
PATCH /admin/jurisdictions/:id/config -- merge-update immThresholdsPATCH body: { "immThresholds": { "EMPLOYMENT_PASS": 5600, "S_PASS": 3150 } }. Validation: each key must be one of the six known pass types, each value a non-negative finite number; the whole patch is rejected on the first offence. The write records an audit event jurisdiction.imm_thresholds.set under Tenant 0. The value is read at eligibility eval time; fallback is 5000 SGD.
4. Negative & edge cases
- Repeated sweep, same
now→ no writes, no events, no notifications (idempotent). OVERDUEre-dated toDUE_SOON→ status updates and aDEADLINE_STATEevent fires, but no notification (guarded to theUPCOMING → DUE_SOONedge).- No IMM operators configured → status still transitions and events still emit; no notification rows created.
Done
This completes the immigration userflow set. See the run lifecycle for the full state machine and SOP_Immigration_Applications.md for the consolidated process.