> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Assistant reaction retention proposal

> A bounded cleanup plan that preserves reaction retry and retirement guarantees.

## Status and current behavior

This is a proposal, not an enabled cleanup service or a migration. Native mobile
Assistant uses `private.chat_reaction_operations` for actor-bound receipts and
`private.chat_retired_reaction_operations` to reject delayed retired writes.
Existing web Chat toggle callers do not use these operation receipts.

The current default retains these records. Receipt rows already cascade with
their actor, workspace, conversation or message; retirement rows cascade with
their actor. There is no age-based expiry. Do not delete either table's records
by age under the current protocol: an old request with a forgotten operation ID
could otherwise apply its desired state after a newer choice.

See [native reaction operations](https://docs.tuturuuu.com/platform/features/assistant-live.md#native-mobile-reaction-operations)
for the shipping retry and sign-out contract.

## Proposed expiry contract

Introduce a versioned write contract with server-issued, actor-bound operation
tickets. Bind the ticket to the operation ID, canonical intent and an absolute
server expiry. A proposed starting policy is a seven-day admission window;
confirm that product window before implementation.

Require the ticket for both desired-state writes and retirement. Reject arbitrary
unissued IDs and expired unseen operations without mutating reactions. Preserve
the current actor authentication, conversation admission and intent-mismatch
checks. Issuance needs bounded actor quotas and its own abuse controls.

Check expiry using fresh server time after acquiring the operation lock, before
any new mutation. An ingress-only check or client timestamp is insufficient.
Keep operation/retirement lock ordering and message serialization. Bound database
transaction lifetime and test requests that expire while waiting for locks.

A terminal expiry response lets the client stop replaying that operation and
read the exact current message. A new reaction still requires a new explicit
choice. Expiry must not silently claim that the previous operation never applied.
Provide localized recovery on mobile before changing its replay window.

Only enable deletion after the legacy unbounded desired-state write contract is
closed, its admitted transactions have drained, and old clients fail closed.
Old signed-out journals need terminal reconciliation through the new contract;
they must not be discarded during migration. Existing web POST toggles remain a
separate compatibility path.

After those prerequisites, proposed storage limits are 30 days for terminal
receipts and 90 days for retirement fences. Eligibility must also exceed the
ticket expiry plus a verified transaction-drain grace period. These are proposed
limits, not current promises. Add reliable expiry/creation columns and indexed
eligibility through a new reviewed migration; do not modify the shipped migration.

## Cloudflare cleanup owner and bounds

Use a dedicated Cloudflare Worker with an hourly Cron Trigger and a Durable
Object holding the enabled state, stop generation and bounded run cursor. Do not
use Vercel Cron, Next background work or a database scheduler.

Start disabled. Each admitted run may perform at most ten database batches of
250 rows, with a fixed execution deadline and no automatic retry loop. Select
only eligible rows using indexed expiry and stable keys; deleting an already
deleted row is a successful no-op. Resume unfinished work at the next scheduled
run and finish immediately when there are no eligible rows.

Persist and acknowledge a stop generation before disabling execution. Check it
before every database batch and define completion only after an already-admitted
batch drains. Bound the database statement as well as the Worker request; a
client timeout alone does not establish cancellation. No queue or continuation
may schedule itself after the stop fence.

Record rows selected/deleted, batch count, duration and the oldest eligible age
without actor, message, emoji or operation payloads. Define daily database and
Worker operation budgets and stop on exhaustion. Request rate limits, alerts and
per-invocation CPU limits do not bound total scheduled spend.

## Acceptance before activation

* Apply the additive migration in an isolated full-chain fixture and regenerate
  database types. Independently review service-role access and cleanup admission.
* Prove expiry before dispatch, while waiting on both locks, after an unknown
  response, after sign-out and after a newer add/remove. Exercise concurrent
  connections and verify an expired delayed request cannot change the result.
* Prove old clients and arbitrary retirement IDs fail closed, and mobile retains
  unknown intent until terminal reconciliation. Cover localized expiry recovery.
* Verify retry, restart and cursor behavior with both empty and maximum batches.
  Assert operation budgets, deadline behavior, durable stop and in-flight drain.
* Run exact-commit CI and the real Worker/Durable Object runtime. Obtain scoped
  activation and rollback authorization before enabling the schedule. Rollback
  stops cleanup first and must not reopen an unbounded legacy mutation path.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.