Files
echo/.references/references-only/reference vault/memory/decisions/2026-05-19-cpas-backdated-snapshot-refresh.md
T
2026-06-05 00:49:20 -05:00

3.0 KiB

type, date, project, tags
type date project tags
decision 2026-05-19 cpas
cpas
snapshot
backdating
audit
mpm

CPAS — back-dated insert refreshes downstream snapshots

Context

On main branch (post ba2b631 documentation, pre any roll-off model overhaul), the CPAS app stores prior_active_points as an immutable snapshot at violation insert time. The PDF endpoint reads this snapshot to render "Prior Active Points → New Total → Tier".

When a violation is logged with a back-dated incident_date that precedes existing violations, the new violation's own snapshot is computed correctly, but existing later-dated violations' snapshots are stale — they were frozen before the back-dated event existed and so omit it from their 90-day prior window. Re-downloading their PDFs shows the pre-backdate prior/total/tier.

Decision

Refresh downstream snapshots on back-dated insert. Treat back-dating as a timeline rewrite and exempt it from the otherwise strict snapshot-immutability rule. Negate, restore, amend, and hard delete are NOT timeline rewrites and continue to leave snapshots alone.

Implementation (in server.js)

  • New helper recomputeSnapshotsAfter(employeeId, incidentDate) finds all violations for the employee with incident_date > new_date AND incident_date <= new_date + 90 days, recomputes each via the existing getPriorActivePoints() formula, and only writes UPDATEs where the value changed.
  • POST /api/violations now wraps the insert + recompute in a single db.transaction() so a partial failure can't leave the system with a new violation and stale sibling snapshots.
  • When refresh affects ≥1 row, a violation_snapshots_recomputed audit entry is written with reason: 'backdated_insert', trigger_incident_date, and an affected[] array of {id, incident_date, old, new} for full traceability.
  • Forward-dated inserts (the common case) do zero UPDATEs — affected query returns rows but the equality check skips them.

Docs

  • AGENTS.md "Prior-Points Snapshot" section updated with the back-dating exception
  • AGENTS.md "What NOT to Do" updated — the "do not modify prior_active_points" line now carves out the back-dating path explicitly

Verification

  • node --check server.js passes
  • Not runtime-tested in this session (better-sqlite3 won't compile on local Node v24 — see project file). Verification path: docker build, log V1 with a recent date, log V2 with an earlier back-dated date, re-download V1's PDF, confirm "Prior Active Points" now includes V2.

Relationship to roll-off model overhaul

The existing projects/cpas.md "Roll-off model fix (2026-05-27)" section describes a different implementation of recomputeSnapshotsAfter that widens the window to "all later violations" under a clean-cycle roll-off rule. That work is not on main as of today. If/when the roll-off overhaul lands on main, the helper's window scope will need to be widened to match (current scope is the legacy 90-day rolling window).