* 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
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.