1. Tasks disappearing (Mimi bug) - open tasks now always visible regardless of renewal group date; only terminal tasks filter by group age 2. Star date change recalculates task due dates - PATCH policy-group now updates dueDate on all open template tasks when renewalDate changes 3. Auto task generation on setup complete - wizard calls generate-tasks for each saved group immediately after Save & Complete 4. Manual sync date range - defaults are now dynamic (prev year to +2y) instead of hardcoded 2026-01-01 / 2026-12-31 5. Setup queue filters out clients with no active policies; shows skipped count so managers know what was excluded 6. Audit logging gap fixed - notes route now emits TASK_STATUS_CHANGED with old+new status when note submission also changes status 7. Renewal day-count warnings now shown on individual policy cards in client detail (<=30d destructive, <=90d secondary badge) 8. Client notes now show save timestamp after editing
3.9 KiB
Audit Logging Fix Plan
Scheduled: after hours
Problem
Two code paths can change a task's status without capturing the previous status in the audit log:
POST /api/tasks/[id]/notes— When a user adds a note and simultaneously changes task status, the audit entry isTASK_NOTE_ADDEDwith onlystatusChange: <new>. The old status is never recorded.src/lib/shape-import/run-import.ts— SHAPE import directly callsprisma.task.update()andprisma.task.create()withCOMPLETED/NAstatus. Zero per-task audit entries.
The notes-route gap is the most operationally urgent because users are actively using it daily. The import gap is lower risk (import is a rare admin operation).
Fix 1 — Notes Route: Capture Old Status (Priority: High)
File: src/app/api/tasks/[id]/notes/route.ts
What to change
The task is already fetched before the note is written (to check assignment). Add the old status to the audit log, and emit a separate TASK_STATUS_CHANGED entry when a status change accompanies the note.
// After fetching task (line ~42), old status is already available as task.status
if (status && ['COMPLETED', 'NOT_STARTED', 'IN_PROGRESS'].includes(status)) {
await prisma.task.update({ ... }) // existing logic, unchanged
// ADD: dedicated status-change audit entry
await prisma.auditLog.create({
data: {
userId,
action: 'TASK_STATUS_CHANGED',
entityType: 'Task',
entityId: id,
oldValues: { status: task.status },
newValues: { status, source: 'note' },
},
})
}
// Existing TASK_NOTE_ADDED entry — update to include oldStatus too
await prisma.auditLog.create({
data: {
userId,
action: 'TASK_NOTE_ADDED',
entityType: 'Task',
entityId: id,
oldValues: { status: task.status }, // ADD this
newValues: { noteId: note.id, statusChange: status ?? null },
},
})
Verification after deploy
-- Confirm old status is now captured
SELECT old_values->>'status', new_values->>'status', new_values->>'source', created_at
FROM audit_logs
WHERE action = 'TASK_STATUS_CHANGED'
ORDER BY created_at DESC
LIMIT 10;
Fix 2 — PATCH Route: Confirm old_values.status is always set (Priority: Medium)
File: src/app/api/tasks/[id]/route.ts
The TASK_UPDATED entries already capture oldValues: { status: task.status }, but only when a status field is present in the request body. Verify the task is always fetched before the update (it is — line ~30 fetches task). No code change needed; just confirm via spot-check.
-- Spot-check: any TASK_UPDATED entries missing old_values.status
SELECT COUNT(*) FROM audit_logs
WHERE action = 'TASK_UPDATED'
AND old_values->>'status' IS NULL;
Fix 3 — SHAPE Import: Per-task Audit Logging (Priority: Low)
File: src/lib/shape-import/run-import.ts
This is a bulk operation run rarely by admins. The ShapeImportRun table already records run-level stats. Acceptable to leave per-task audit logging out for now given the volume (hundreds of tasks per run would bloat the audit table).
Recommendation: Add a summary SHAPE_IMPORT_TASK_SUMMARY audit entry per import run that records counts by status (created COMPLETED, created NOT_STARTED, updated, skipped). Already partially done via ImportStats.
Deployment Steps
- Apply Fix 1 to
notes/route.ts - TypeScript check:
npx tsc --noEmit - Commit:
git commit -m "Fix: capture old status in task notes audit log" - Push + deploy:
docker compose -f /opt/stacks/horizon/docker-compose.yml up -d --build - Run verification queries above to confirm
- Optionally apply Fix 3 summary entry to shape import
Notes
- No DB schema changes required —
old_valuesandnew_valuesare alreadyjsonbcolumns. - No migration needed.
- Zero downtime deploy (standard Next.js container rebuild).
- Fix 1 is safe to apply during off-hours with no user impact.