deer-flow/backend/docs/THREAD_LIFECYCLE.md
Ryker_Feng 98b8e4657e
feat(chats): add archive and restore (#5236)
* feat(chats): add archive and restore

* test(chats): observe archive search requests in pagination e2e

* docs(gateway): move thread lifecycle details out of inherited guidance

* docs(chats): add concise archive and restore RFC

* docs(chats): move archive RFC discussion to issue 5237
2026-09-06 22:30:26 +08:00

2.5 KiB

Gateway thread lifecycle invariants

Read this guide before changing thread branching, regeneration, edit replay, or archive/search metadata behavior. It supplements the Gateway module guide.

Branch/regenerate checkpoint invariant: app/gateway/checkpoint_lineage.py walks parent_config rather than globally ordered checkpoint history so replay anchors stay on the selected lineage after regenerations create sibling branches. New conversation branches persist the pre-user replay anchor before their visible head through the state mutation graph, which preserves materialized state in both full and delta checkpoint modes. Only an explicitly absent legacy parent link may use chronological compatibility lookup; cycles, dangling links, and depth-limit exhaustion fail closed. Existing single-checkpoint branches are never repaired by copying a raw checkpoint because delta state is not self-contained in one tuple. Both lookups additionally require the replay base to be a settled checkpoint (has_pending_tasks — no scheduled next tasks). A checkpoint with pending tasks is a mid-run snapshot: resuming from it replays the writes of the node that was about to run. Message ids alone cannot exclude those, because middleware may rewrite a message's id inside the run that produced it — DynamicContextMiddleware moves the first user turn to {id}__user and gives {id} to the injected reminder, so every checkpoint written before it holds the same prompt under an unmatched id. Selecting one of those re-added the original prompt after the edited one, and the model answered the question the edit was replacing (#4531). next is not derivable on the degraded raw-checkpoint read path, which reports no tasks; absence of evidence stays permissive there rather than failing closed. Edit replay resolves its base through the same lineage-first path as regenerate; it must pass head_checkpoint or it silently degrades to the chronological scan that cannot tell sibling branches apart.

Chat archive

POST /api/threads/search accepts an optional strict boolean archived: omitted or null preserves the unfiltered API, true selects only JSON boolean metadata.deerflow_archived=true, and false includes missing/null/non-true legacy flags. Both SQL and Memory thread stores filter before limit/offset and retain owner isolation. PATCH validates archive flags as booleans; pin/archive-only boolean metadata writes use touch=False to preserve activity ordering. Archive never changes runtime status, checkpoints, files, schedules, or read permissions.