Description
Automatic Notification Expiry keeps a workspace inbox current by archiving notification rows whose time-to-live has elapsed. The nightly notification-ttl-sweep job sets each eligible row’s archived_at to its own expires_at value. That soft-delete posture hides expired entries from the active inbox while preserving when the item actually expired for audit and diagnosis.
The sweep runs once per company and refuses to execute without a tenant scope. Its predicate ignores notifications with no expiration, notifications whose TTL is still in the future, and rows already archived, making repeated runs safe and predictable.
Selling Points
- Removes stale notifications from active inbox views without requiring manual cleanup.
- Preserves the row and its true expiration instant instead of hard-deleting operational history.
- Applies cleanup independently within each company boundary.
- Makes reruns idempotent, so an interrupted or repeated sweep does not corrupt prior state.
- Reports the number of archived notifications for operational visibility.
User Story
A user receives several time-sensitive completion and warning notifications. After their configured TTL passes, the nightly sweep archives them automatically, so the next inbox view shows only current items. An administrator can still inspect the stored archived rows and see the original expiration time rather than a cleanup-time approximation.
Extension Surface
The feature is automatable through the registered job contract. packages/jobs/src/catalog/foundation-cron.ts supplies the stable notification-ttl-sweep id, nightly cadence, timeout, priority, and non-overlap policy. Operators can invoke that same id through the generic Jobs command without a notification-specific script, while TTL values on notification rows configure which items qualify.
Capabilities & Limits
The handler archives every unarchived row whose non-null expiration is at or before the database clock, scopes the update by company, returns an exact sweptCount, and produces a clean zero-count result when nothing qualifies. Setting archived_at = expires_at preserves the domain event time, and the leading TTL predicate can use the expiration index.
This is not a hard-delete or a general retention engine. Notifications without expires_at remain untouched, and already archived rows are not rewritten. A missing company id returns a typed fatal result rather than sweeping globally. The foundation job uses zero retries and skip concurrency, so recovery is the next nightly run or a manual operational rerun.
Implementation Map
packages/jobs/src/catalog/notification-ttl-sweep.tspackages/jobs/src/catalog/foundation-cron.tspackages/jobs/src/scheduler.tspackages/jobs/src/runner.tspackages/cli/src/commands/jobs.ts