Caducidade — the 30-day guard after the inquiry
useWorkflow.executeTransition contains an explicit caducidade (lapse) guard on the
PRELIMINARY_INQUIRY → CHARGES_ISSUED transition:
The Nota de Culpa must be issued within 30 days after the end of the Inquérito Prévio, otherwise the procedure lapses (caducidade).
Implementation:
const dueMs = new Date(ipConcluded).getTime() + 30 * 24 * 60 * 60 * 1000;
const overdue = Date.now() > dueMs;
if (overdue && !metadata?.caducity_override) throw new Error("Caducidade: …");
ipConcluded is cases.ip_concluded_at. An operator can proceed anyway by passing
metadata.caducity_override = true with a justification.
What to verify against the statute
The 30-day figure is asserted in a code comment, not cited. Portuguese law (Código do Trabalho) has multiple relevant limits around the disciplinary procedure — e.g. the 60-day prescrição from knowledge of the infraction, and time limits tied to the inquiry. Confirm that "30 days after IP conclusion → caducidade" is the correct rule, its exact article, and whether it is calendar or working days (the code assumes calendar).
Gotchas that make this legally fragile
- UTC calendar-ms arithmetic, no Europe/Lisbon timezone, no holidays — a deadline can land a day off (naive deadline math).
ip_concluded_atis stamped at click time, not the real conclusion date (legal time vs click time).- The override is enforced only client-side — nothing in the DB stops a direct status update (client-side integrity).
- No test pins this rule (zero test coverage).
Rebuild implication
Encode this rule server-side, with a statute citation, an explicit day-kind, correct timezone, an auditable override (who/why/when), and a test.