返回 DeepSeek-Reasonix
SESSION_DRAFT_LIFECYCLE.md
根目录 / docs / SESSION_DRAFT_LIFECYCLE.md
1 # Local Session Draft Lifecycle
2
3 Reasonix Desktop treats a manually opened local conversation as an editor draft
4 until the first execution is durably accepted. Opening a draft does not create a
5 Session, Topic, Controller, session lease, MCP runtime, or session-list row. Once
6 the first submission creates the formal Session, a temporarily untitled row uses
7 the localized new-session title (and then the normal preview/title projection);
8 internal canonical routes such as `session-id:...` are never display names.
9
10 ## Identity and storage
11
12 The visible conversation surface is either a draft target (`workspaceId`,
13 `draftId`) or a formal Session target (`SessionRef`, `tabId`). Drafts never use a
14 fake tab or Topic ID.
15
16 Draft state is stored in `desktop/drafts-v1.sqlite` under the Reasonix user state
17 directory. Schema version 4 contains active drafts, versioned frozen submission operations,
18 conflict copies, and the last restorable draft surface. There is one active draft
19 per local Workspace, including one independent global Workspace draft.
20
21 Persisted content includes composer text, structured invocations, attachment and
22 workspace references, pasted blocks, selected text references, Session
23 references, model provenance plus a compatibility mirror, effort/mode/approval/
24 quality settings, Goal settings, and MCP selection. Runtime objects, credentials,
25 preview URLs, pending paste state, and submission UI flags are not persisted.
26
27 Edits synchronously update an application-level store and are saved with a 250 ms
28 debounce and a per-DraftID revision compare-and-swap loop. Navigation never waits
29 for a failed or conflicting save: the source draft continues in the background
30 and remains marked dirty/error/conflict in the project tree. Submission waits for
31 the captured source edit version, and normal Electron shutdown waits for every
32 loaded draft, pending attachment task, and restore-target write. Failure or
33 conflict cancels normal close; the last backend-confirmed revision remains the
34 crash-recovery boundary.
35
36 Opening draft data and committing the page restore target are separate writes.
37 Every navigation claims the shared navigation intent before its first await, so
38 a stale draft open cannot install a page or overwrite the next startup target.
39
40 ## First execution
41
42 The renderer captures DraftID, Workspace, generation, content, settings, and
43 navigation intent before context expansion. The backend records that immutable,
44 versioned snapshot before creating runtime state:
45
46 ```text
47 active draft
48 -> reserved (OperationID + SessionID + TopicID + SubmissionID)
49 -> starting (create/open exactly that Session and bind its Workspace)
50 -> dispatching | dispatching_shell
51 -> accepted (durable submission receipt)
52 -> converted draft
53 ```
54
55 The operation does not call `EnsureBlankTab`. Retries reuse its reserved Session
56 and Topic. A terminal retry gets a new OperationID and SubmissionID but retains
57 the already reserved Session and Topic. Workspace pending-create cleanup is
58 conditional on the old OperationID so a late failure cannot erase a newer claim.
59
60 Normal turns, structured invocations, initial Goals, and shell commands share
61 this path. New shell submissions persist an acceptance receipt before command dispatch. An unknown shell or
62 model dispatch result is never replayed automatically.
63
64 An untouched new draft inherits the current settings default live. The persisted
65 `modelSource: "default"` marker distinguishes that inheritance from an explicit
66 picker or `/model` choice; its concrete `model` field is only a compatibility
67 mirror for previous readers and is excluded from editable-draft identity. A
68 catalog/settings refresh updates the visible effective model without making the
69 draft dirty. An explicit choice writes `modelSource: "explicit"` and remains
70 pinned.
71
72 Frontend and backend draft comparisons both exclude the inherited concrete model,
73 including lost-save-acknowledgement recovery and pre-submit validation. Catalog
74 refresh, save, and reopen reads share version fencing so late responses cannot
75 replace a newer effective-model projection.
76
77 The first submission resolves the effective inherited model again, validates and
78 canonicalizes it, and freezes it in snapshot v5 before Session reservation.
79 Frozen and explicitly selected draft models are strict execution choices: an
80 unavailable model is reported before reservation and never falls back to another
81 model. Plugin model references are passed to the runtime resolver and fail there
82 when unavailable. A retry applies the operation's frozen settings to the same
83 reserved Session identity.
84 Request fingerprints are computed before model-alias normalization, so retrying
85 the original request still identifies the same operation. Restoring a resumable
86 operation shows its frozen model in both the composer and picker; cancellation
87 or terminal failure releases editing to inherit the live default again.
88
89 The renderer switches to the formal Session only after the acceptance receipt
90 and draft conversion are committed. Transcript snapshot/follow then supplies any
91 events that arrived before the RPC response. Completion in a background project
92 updates project state but does not steal the visible surface.
93
94 ## Recovery and cancellation
95
96 Sending synchronously locks the source editor, then saves its captured version
97 before expanding references. A request ID and source digest bind that snapshot
98 to the durable operation. Lost Begin responses are reconciled by reading state;
99 they do not unlock the editor or allocate another submission. Restoring a draft
100 also restores its operation, including the explicit Continue action. Reads do
101 not create a Controller or dispatch work. Polling continues for unknown results.
102
103 Cancellation first records `cancel_requested`; editing remains locked until the
104 worker has stopped. A cross-process execution lease prevents another process
105 from declaring a live worker interrupted. A separate short publication lease
106 serializes cancellation with Controller publication. Retry configuration applies
107 to the real Controller under turn admission, and admission checks its identity.
108
109 Attachment completion always settles its task registration, even if generation
110 has changed. Only current-generation results may edit content. Discard captures
111 the project and versions before confirmation; navigation cannot retarget it.
112 Normal exit also waits for local submission preparation to persist its operation.
113
114 | Persisted phase or interruption | Recovery behavior |
115 | --- | --- |
116 | No operation record | Keep the editable draft; submit normally. |
117 | `reserved` or `starting` after restart | Mark `resume_required`; user continuation reuses the same identities. |
118 | Session exists but is not yet bound | Bind the same Session to the recorded Workspace. |
119 | Controller startup failed | Keep the Session identity; a terminal retry reuses it. |
120 | `dispatching` with a known receipt | Atomically accept and convert the draft. |
121 | `dispatching` without a known receipt | Mark `dispatch_unknown`; do not replay. |
122 | `dispatching_shell` after interruption | Reconcile the durable receipt; accepted commands become sessions. A legacy dispatch marker without proof remains unknown. Never rerun the command automatically. |
123 | `accepted` but draft still active | Complete conversion without redispatch. |
124 | Session was archived or deleted externally | Stop recovery and report the lifecycle conflict. |
125
126 Cancellation is keyed by OperationID. Before dispatch it conditionally cancels
127 the operation and its matching Workspace reservation. At or after durable
128 acceptance it becomes cancellation of the formal Session; it never deletes a
129 receipt to pretend execution did not happen.
130
131 ## Commands, attachments, and capabilities
132
133 - `/new`, `/clear`, `/compact`, and other history-dependent management commands
134 are unavailable in a draft and do not create a Session.
135 - `/model` and `/effort` edit draft settings. `/theme` changes appearance
136 directly. Goals, skills, custom commands, and ordinary text start the unified
137 first-execution flow.
138 - Configured MCP servers are projected from Workspace configuration without a
139 Controller or process launch. Draft selection is persisted per Session;
140 runtime capability availability is revalidated during Controller startup.
141 - Attachment operations use an explicit composer target and Workspace root.
142 Async completion writes directly to the captured DraftID even when another
143 project is visible. Pending saves block submission. Reads reject path escapes and symlinked files
144 or attachment directories. Missing files remain repairable draft references.
145
146 ## Compatibility
147
148 ### One-time legacy empty-session cleanup
149
150 The first upgraded startup freezes a versioned set of default-title Sessions
151 and legacy Topic placeholders that already belong to registered local or global
152 Workspaces. The background worker runs only after Session migration, draft
153 operation reconciliation, and tab restoration. Identities created after that
154 freeze, including draft reservation and retry Sessions, can never enter the
155 batch.
156
157 A candidate moves to Trash only when the complete canonical or legacy artifact
158 set proves it has no user, assistant, or tool message; accepted model or shell
159 submission; Goal; pinned context; inbox item; job; checkpoint; recovery state;
160 derived relationship; active draft operation; runtime; or unsent restored UI
161 state. An empty-string message is still usage. System-only initialization and
162 empty derived containers are not usage. Corrupt, inaccessible, unsupported, or
163 future-version data is retained as `unknown` rather than treated as empty.
164
165 Default titles are matched exactly after trimming. Sessions with content keep
166 their title, order, identity, and data; no numbered-title migration is applied.
167 Conclusive empty Sessions use the existing reversible archive lifecycle without
168 opening a fallback Controller. Topic-only placeholders keep a schema-v1 recovery
169 snapshot of their original scope, title, order, pin, and group position and are
170 shown in Trash without fabricating a SessionRef. Restoring either form preserves
171 its identity and permanently excludes it from this cleanup batch.
172 Busy or unreadable candidates stay visible in their original project. Trash
173 shows the pending count and a **Recheck** action that retries only the frozen
174 batch; it never discovers identities created after the upgrade boundary.
175
176 The independent
177 `desktop/legacy-empty-session-cleanup-v1.json` sidecar has atomic writes,
178 cross-process worker ownership, stable operation IDs, and strict unknown-version
179 protection. Older releases ignore it. Legacy JSONL migration sources are retained
180 even when their mapped canonical Session is archived.
181
182 | Format or API | New-reader behavior | Previous-reader behavior | Result |
183 | --- | --- | --- | --- |
184 | Session v5 | Unchanged identity, events, and transcript protocol. | Unchanged. | Compatible. |
185 | Workspace registry v3 | Formal Session ownership only; recorded create operations remain recoverable. | Existing Sessions remain readable. | Compatible. |
186 | Draft SQLite v1 | Migrated to v2 by adding the reserved Topic ID, then to v3. | Not applicable to releases without drafts. | Forward migration covered. |
187 | Draft SQLite v2 | Transactionally upgraded to v3. Pending operations resume only when their draft revision proves the settings match. | A v2 build refuses to write v3. | No inferred execution settings. |
188 | Draft SQLite v3 | Versioned frozen request and settings in `request_json`; all saves use CAS. | Releases without drafts ignore the independent file. | Rollback preserves drafts for a later upgrade. |
189 | Draft SQLite v4 | Transactional migration adds request identity, source digest, operation revision and separate expanded execution bytes. Snapshot versions remain independent of schema versions. | v3 refuses to write v4. | Original fingerprints and Session/Submission IDs remain intact. |
190 | Draft model provenance / snapshot v5 | New drafts persist an additive `modelSource`; inherited defaults keep a concrete compatibility mirror but freeze the current effective model only at submission. Untouched legacy revision-1 drafts are safely marked inherited; edited legacy drafts retain their concrete model because provenance is ambiguous. Existing v3/v4 operation snapshots remain resumable. | Previous readers ignore `modelSource` and use the valid concrete mirror. They reject a future v5 operation snapshot rather than resuming it with changed semantics. If a previous reader edits and rewrites a draft, a later upgrade conservatively treats the concrete model as pinned. | No explicit user choice is silently replaced and no operation is resumed with an inferred model. |
191 | Unknown future draft schema | Read/write is refused and the file is retained. | Not applicable. | No silent downgrade overwrite. |
192 | Upgrade-time blank Sessions | Only frozen, default-title, conclusively unused local candidates are moved to Trash once. Content-bearing or uncertain Sessions are unchanged. | Older releases ignore the cleanup sidecar and continue to read formal Sessions and Trash. | Reversible lifecycle-compatible cleanup. |
193 | Legacy cleanup sidecar v1 | Records frozen identities, decisions, stable archive operations, placeholder recovery metadata, and restore protection. Unknown versions are retained and cleanup stops. | Ignored. | No downgrade overwrite or candidate rescan. |
194 | Legacy runtime APIs | `EnsureBlankSurface` and related APIs retain their semantics. | Unchanged. | Remote, IM, automation, recovery, and worktree flows remain isolated. |
195
196 Draft IDs and submission IDs are host metadata only. They are excluded from
197 provider-visible requests, and the existing prompt/tool ordering is unchanged.
198
199 ## Operational notes
200
201 Diagnostics record IDs, phase, reuse/recovery counts, and durations for draft
202 open/save, Session creation, runtime readiness, and acceptance. They do not log
203 draft text, attachment contents, or credentials.
204
205 The first implementation deliberately does not delete attachment files when a
206 draft is discarded. MCP prompt discovery uses existing configured/cached
207 capability data; actual capability discovery and validation occur at execution.
208
208 lines MARKDOWN