Context
From the system design audit, soft-delete semantics are inconsistent across aggregates. The spec now resolves the distinction but it should be enforced in code.
Design Decision (from spec)
INV-8:
- Archived = hidden from default views, restorable via
unarchive command. Thread/Client support archive.
- Deleted = permanent tombstone.
deletedAt set, excluded from ALL queries, NO undelete command exists. Irreversible.
| State |
Visible |
Mutable |
Restorable |
Events Projected |
| Active |
Yes |
Yes |
N/A |
Yes |
| Archived |
Settings only |
Limited (unarchive) |
Yes |
Yes |
| Deleted |
Never |
No |
No |
Excluded from queries |
Proposed Changes
- Add guard in decider: reject any mutation commands on deleted aggregates (except cascade events)
- Verify no "undelete" path exists for any aggregate
- Ensure projection queries consistently filter
deletedAt IS NOT NULL
- Document the distinction in AGENTS.md / contributing guide
- Add tests: attempt to update/archive/unarchive a deleted entity -> should reject
Acceptance Criteria
References
- System Design Spec:
.plans/21-system-design-spec.md Section 2 (INV-8)
- Audit finding: "Soft-delete semantics are inconsistent across aggregates"
Context
From the system design audit, soft-delete semantics are inconsistent across aggregates. The spec now resolves the distinction but it should be enforced in code.
Design Decision (from spec)
INV-8:
unarchivecommand. Thread/Client support archive.deletedAtset, excluded from ALL queries, NOundeletecommand exists. Irreversible.Proposed Changes
deletedAt IS NOT NULLAcceptance Criteria
References
.plans/21-system-design-spec.mdSection 2 (INV-8)