Maintenance finalSnapshot behavior (locked)
Purpose
finalSnapshot is an immutable JSON column on maintenance_requests that captures a consistent, point-in-time view of a request when it becomes closed (terminal).
It exists so that:
- UI can render closed tasks from a stable structure even if users are later renamed, deleted, or cache is cold.
- Analytics and support can trust “what the ticket looked like at close” without recomputing from live joins.
Compliance / retention / erasure processes are out of scope for v1; may extend this column or externalize copies later.
When it is written
Created only when status first becomes a terminal value:
completedcancelleddeclined
Rules:
- Write once — never updated or recomputed after insert.
- Same transaction as the terminal status (and related timestamp/notes columns for that transition).
- If the transaction fails, neither terminal status nor snapshot should persist partially (implementation must enforce atomicity).
Closed tasks — UI data sources
Closed = status ∈ { completed, cancelled, declined } (see README).
| Data | Source |
|---|---|
Structure: title, priority, category, location, department, relatedUsers | finalSnapshot (preferred) |
Notes: cancellationNotes, declinedNotes, completion notes / hours (if present) | Row columns |
Timestamps: completedAt, cancelledAt, declinedAt, etc. | Row columns |
archivedAt does not change whether a task is closed.
Snapshot JSON shape (versioned)
Top-level v allows future migrations of the shape.
{
"v": 1,
"status": "cancelled",
"takenAt": "2026-03-28T12:00:00.000Z",
"relatedUsers": [
{
"id": "user-id",
"name": "Jane Doe",
"role": "employee",
"type": "submittedBy",
"profilePic": "https://…",
"canViewProfile": true
}
],
"department": {
"id": "dept-id",
"name": "Engineering"
},
"title": "Fix HVAC",
"priority": "high",
"category": "hvac",
"location": "Building A"
}Required fields (v: 1)
| Field | Description |
|---|---|
v | Schema version (1). |
status | Terminal status at write time. |
takenAt | ISO timestamp when the snapshot was taken (same moment as terminal transition commit). |
relatedUsers | Array per Missing users; no email. |
department | { id, name } — department is part of the snapshot (frozen at terminal time). |
title, priority, category, location | Copy of display fields at terminal time. |
Notes (cancellationNotes, declinedNotes, completion bodies) are not duplicated inside v: 1 by default; they live on the row (see README).
Missing users (structured fallback)
If a referenced user cannot be resolved at snapshot write time, include a placeholder entry:
{
"id": "original-user-id",
"name": null,
"role": "employee",
"type": "submittedBy",
"profilePic": null,
"canViewProfile": false,
"isMissing": true,
"missingReason": "USER_DELETED"
}Live relatedUsers (non-closed) uses the same shape when a user is missing (see ../cache/user.md).
Cache interaction (read path)
| Task state | relatedUsers source |
|---|---|
Open (pending, in_progress) | Built from live user cache + row IDs |
| Closed | relatedUsers inside finalSnapshot only (do not rebuild from live cache for display authority) |
Active tasks: resolve names/avatars via central user cache; DB stores IDs only on the maintenance row.
List vs detail (takenAt)
| Endpoint | finalSnapshot | takenAt field |
|---|---|---|
| List | Do not embed full snapshot | See below |
| Detail | Include full finalSnapshot for closed tasks | N/A as list field |
takenAt on list items
| Row state | takenAt |
|---|---|
Closed (completed, cancelled, declined) | finalSnapshot.takenAt (must exist) |
Not closed (pending, in_progress) | null |
Do not infer a synthetic takenAt for open tasks from updatedAt in v1 (keeps semantics explicit).
Purge and snapshot
Hard delete removes the row; finalSnapshot is lost unless copied elsewhere. Audit / maintenance logs should record purge metadata (see product rules). Implementation detail: log structured meta on purge for traceability.
Related documentation
- status-transitions.md — terminal transitions.
- permissions-matrix.md — purge/archive.
- ../cache/maintenance.md — cache invalidation when rows change.