Per-workflow and per-tenant retention overrides and legal hold (#3604793)

Phase 2 of #3604793, on the same issue fork as the merged Phase 1 (!168 (merged)). Where Phase 1 added a single site-wide retention policy, this lets a workflow or a tenant override that policy on its own configuration, and adds a legal hold that exempts a scope from purging entirely.

What changed

  • A workflow or tenant can carry its own retention override (per-status ages, plus a hold). The override lives on the config entity itself, so it travels with the entity on export and is removed when the entity is deleted, rather than orphaning rows in a central map.
  • Resolution precedence: a per-workflow override wins, then its tenant's, then the site default. A hold at either scope means the instance is never purged, whatever the ages say, until the hold is lifted, for an audit, litigation or compliance freeze.
  • Edit a workflow's override on its new Retention tab (WorkflowRetentionForm). A tenant's override is set via config sync (tenants have no admin form yet).
  • RetentionManager reads the overrides from the workflow and tenant entities instead of a central config map, and composes the holds and overrides with NOT-IN scope filters so each instance is purged under exactly one effective age.

Shared schema, interface, trait

  • orchestra.retention_override: a reusable config schema type for the override shape, referenced by both the tenant and workflow entity schemas.
  • RetentionConfigInterface / RetentionConfigTrait: the workflow and tenant entities expose getRetention() the same way, so the manager reads either uniformly.

DRY pass

A short audit of the surrounding code, requested on this issue:

  • ChunkedDeleteTrait: the bounded load-and-delete that the cleanup hooks and the retention manager each had inline is now one trait.
  • TokenInterface::LIVE_STATUSES: the [active, parked, waiting] triple that selected an instance's still-running tokens, repeated across the engine, version manager, migrate form and Drush command, is now one constant.

Tests

InstanceRetentionTest is extended with per-workflow age, per-workflow hold and per-tenant hold cases (creating real workflow/tenant entities with a retention override). Full kernel suite green (155 tests); phpcs clean.

Merge request reports

Loading