+- **`@objectstack/spec` (`@objectstack/spec/data`):** `RECYCLE_BIN_OBJECT` (`'sys_recycle_bin'`) and `RECYCLE_BIN_ENTRY_FIELDS`, the entry's field list the object is built from: `object_name`, `record_id`, `record_count`, `snapshot`, `deleted_by`, `deleted_at`, `retention_days` and `purge_after` (the deadline stamped at delete time), all read-only, with `RecycleBinEntrySchema` as the entry's value shape. The object itself is registered with the engine slice, so no table and no platform-object name exist until a writer does. `RECYCLE_BIN_RETENTION_DAYS_ENV` (`'OS_RECYCLE_BIN_RETENTION_DAYS'`) and `RECYCLE_BIN_DEFAULT_RETENTION_DAYS` (30). `RecycleBinRetentionResolver`, the host hook's contract: `(token: string | undefined) => number | undefined`, called with the same opaque token `resolveFeatures` receives, answering a positive whole number of days or `undefined` to keep the default. `RecycleBinRetentionDaysSchema` and `RecycleBinRetentionDaysEnvSchema` hold the answer and the variable to that rule, and `recycleBinRetentionRefusal(source, value)` is the refusal text: zero or anything that is not a positive whole number is a configuration defect, never "purge now" or "keep forever". The framework knows days, never plans. `RecycleBinSnapshotSchema` (with `RecycleBinSnapshotRowSchema` and `RecycleBinClearedReferenceSchema`): the cascade set an entry holds, the master first and its details in cascade order, secret-channel refs left out, plus the `set_null` references the delete cleared. `RECYCLE_BIN_RESTORE_REFUSAL_CODES` and `RECYCLE_BIN_RESTORE_REFUSAL_STATUS` (409). `RECORD_RESTORED_EVENT` (`'data.record.restored'`): a restore emits it, never a create; `DataEventType` and `RealtimeEventType` list only emitted events, so the name joins them in the change that emits it. `RECYCLE_BIN_GRANT_KEYS`.
0 commit comments