Skip to content

Clarify and enforce soft-delete vs archive behavioral semantics (INV-8) #20

@rororowyourboat

Description

@rororowyourboat

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

  1. Add guard in decider: reject any mutation commands on deleted aggregates (except cascade events)
  2. Verify no "undelete" path exists for any aggregate
  3. Ensure projection queries consistently filter deletedAt IS NOT NULL
  4. Document the distinction in AGENTS.md / contributing guide
  5. Add tests: attempt to update/archive/unarchive a deleted entity -> should reject

Acceptance Criteria

  • Deleted aggregates reject all mutation commands
  • No undelete path exists or can be created
  • Projection queries consistently exclude deleted entities
  • Archive/unarchive only works on non-deleted entities

References

  • System Design Spec: .plans/21-system-design-spec.md Section 2 (INV-8)
  • Audit finding: "Soft-delete semantics are inconsistent across aggregates"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions