Skip to content

OpsWeave — Soft-Delete Strategy

AUDIT-FIX: L-09 — Documents which entities use soft-delete vs hard-delete.


Principle

Entities that require audit trails or may be referenced by historical records should use soft-delete (is_active = 0). Junction tables, configuration data, and ephemeral records may use hard-delete with referential integrity checks.


Current State (v0.2.x)

Soft-Delete (is_active = 0)

EntityTableDelete ApproachNotes
Customerscustomersis_active = 0Checks for open tickets before deactivation. Existing tickets retain the customer reference.

Hard-Delete (row removal)

EntityTableReferential CheckNotes
User Membershiptenant_user_membershipsSelf-delete preventedRemoves user from tenant
Group Membershipuser_group_membershipsNoneJunction table
Assignee Groupsassignee_groupsCascading (members first)Removes group + memberships
AssetsassetsChecks linked ticketsCMDB items — consider soft-delete
Asset Relationsasset_relationsNoneDAG edges
Tickets (Categories)ticket_categories409 if tickets assignedHard-delete only if unused
Workflow Templatesworkflow_templatesChecks active instancesHas is_active column but hard-deletes
Workflow Stepsworkflow_stepsVia template ownershipCascading with template
Service Descriptionsservice_descriptionsChecks catalog references
Horizontal Catalogshorizontal_catalogChecks for items
Horizontal Catalog Itemshorizontal_catalog_itemsNoneJunction table
Vertical Catalogsvertical_catalogsCascading (overrides first)Enterprise only
Vertical Overridesvertical_catalog_overridesNone
Compliance Frameworksregulatory_frameworksChecks requirements
Compliance Requirementsregulatory_requirementsChecks mappings
Requirement Mappingsrequirement_service_mappingsNoneJunction table
Asset Regulatory Flagsasset_regulatory_flagsNoneJunction table
SLA Definitionssla_definitionsChecks assignmentsHas is_active column but hard-deletes
SLA Assignmentssla_assignmentsNone
KB Articleskb_articlesRemoves links firstUses status field, not is_active
KB Article Linkskb_article_linksNoneJunction table
Email Configsemail_inbound_configsNoneHas is_active column but hard-deletes
System Settingssystem_settingsNoneGlobal config

Tables with is_active Column Not Used for Soft-Delete

These tables have the column but currently hard-delete:

  • tenants — no delete operation implemented
  • users — no delete operation (membership removal instead)
  • workflow_templates — hard-delete with instance check
  • sla_definitions — hard-delete with assignment check
  • email_inbound_configs — hard-delete

Recommendation

Entities that should migrate to soft-delete in a future release:

EntityReason
AssetsCMDB items are referenced by tickets, SLA chains, compliance flags. Hard-delete breaks historical context.
Workflow TemplatesCompleted workflow instances reference the template. Soft-delete preserves audit trail.
KB ArticlesAlready has status: 'archived' — use that instead of hard-delete.
SLA DefinitionsHistorical tickets reference SLA tiers. Deactivation is safer than deletion.
Email ConfigsReceived messages reference the config. Deactivation preserves message history.

Migration Pattern

typescript
// Instead of:
await db.delete(entity).where(eq(entity.id, id));

// Use:
await db.update(entity).set({ is_active: 0, updated_at: now }).where(eq(entity.id, id));

Ensure all list queries filter WHERE is_active = 1 by default, with an optional include_inactive parameter for admin views.

Released under the AGPL-3.0 License.