Skip to content

IMM-05 · Renewal tracking

SOP: SOP_Immigration_Applications.md §7 / §8 Step 7Actors: System (daily deadline-scheduler); IMM Operators receive notifications. Pre-state: A filed application with a StatutoryDeadline(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 becomes DUE_SOON.

0. Prerequisites

  • At least one PASS_RENEWAL deadline row for the tenant.
  • The daily deadline-scheduler worker 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):

  1. Scans the tenant's existing PASS_RENEWAL deadlines (created at submit time — the sweep never creates new ones).
  2. Re-derives each one's status from dueDate vs a 60-day lead time:
    • UPCOMING — more than 60 days out (risk LOW);
    • DUE_SOON — within 60 days (risk MEDIUM);
    • OVERDUE — past due (risk HIGH).
  3. Writes the status only when it changes (idempotent — a repeat sweep with the same now is a no-op) and emits a DEADLINE_STATE event (module = IMM) on every real transition, with fromStatus in the payload.
  4. On the first crossing into DUE_SOON (the UPCOMING → DUE_SOON edge only — a later sweep that finds it already DUE_SOON, or an OVERDUE → DUE_SOON re-dating, does not re-alert), creates one Notification of type PASS_RENEWAL_DEADLINE_APPROACHING per active IMM_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

sql
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:

http
GET   /admin/jurisdictions
GET   /admin/jurisdictions/:id          -- detail incl. immThresholds
PATCH /admin/jurisdictions/:id/config   -- merge-update immThresholds

PATCH 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).
  • OVERDUE re-dated to DUE_SOON → status updates and a DEADLINE_STATE event fires, but no notification (guarded to the UPCOMING → DUE_SOON edge).
  • 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.

Internal use only — BreezyCorp