返回 DeepSeek-Reasonix
DSH_RUNTIME_ALIGNMENT.md
根目录 / docs / DSH_RUNTIME_ALIGNMENT.md
1 # DSH runtime alignment
2
3 This document fixes the executable contracts for the Reasonix runtime
4 alignment. It is normative for new sessions; legacy fields are display-only.
5
6 ## Todo contract
7
8 `todo_write` accepts one object containing a required `todos` array. Every item
9 contains exactly `content` and `status`. Content is trimmed and must be non-empty
10 and unique within the replacement list. Status is one of `pending`,
11 `in_progress`, or `completed`. The complete array replaces the previous array;
12 `[]` is a valid clear. Ordering, deletion, replanning, pending-only lists, and
13 multiple in-progress items are valid.
14
15 The successful result is canonical JSON containing the normalized full list and
16 counts. Validation failure leaves the prior state unchanged. Result truncation
17 or duplicate presentation must never replace this state-bearing result.
18
19 Todo lifetime is one real host turn. A committed top-level turn start clears the
20 current list. Tool rounds, compaction, steering, approval answers, and Ask
21 answers stay in the same turn and do not clear it. A completed, failed, or
22 cancelled turn retains its last successful list for display until the next turn
23 starts. Goal continuation is a new turn and starts empty.
24
25 `complete_step`, hierarchical levels, `activeForm`, `step_id`, host advancement,
26 approved-plan seeding, Goal restoration, and completed-prefix protection are
27 retired execution behavior. The hidden `complete_step` tombstone only returns an
28 actionable retirement error. Old transcript records remain readable.
29
30 ## Runtime ownership
31
32 The session-scoped turn-loop is the execution authority: it uniquely owns
33 foreground admission, cancellation, the active turn identity, FIFO pending
34 input, and the level-triggered wake. `session.Runtime` is the persistence
35 authority: session identity, the single-writer lease, in-memory event receive,
36 and background write-behind. Durable turn events record facts and never
37 recreate a live executor after process restart. Runtime phases are `idle`,
38 `executing`, `cancelling`, `finishing`, `recovery_required`, and `closed`;
39 compatibility booleans are derived from the phase and current owners. There is
40 no parallel Activity permit and no second admission gate on `session.Runtime`.
41
42 Stop is session-scoped. A supplied UI turn id is diagnostic only and cannot be a
43 precondition for cancellation. Cancellation signals the bound turn-loop before
44 storage, notification, or callback cleanup, and each turn uses a fresh cancel
45 context so Stop cannot poison the next turn. The existing 15-second tool
46 straggler grace applies to owned work. A turn whose owned work cannot converge
47 is represented as `recovery_required`; late work cannot resume that turn or
48 commit a newer runtime generation. The Runtime binds the turn-loop with an
49 exact generation so an old Controller unbind cannot clear a replacement's
50 control.
51
52 Ask, approval, Plan, recovery, and MCP decisions use `PendingPromptOwner` as one
53 registry. Every identity binds prompt id, kind, turn, and runtime epoch. Resolve
54 is single-winner and rejects stale runtime or turn identities. Callbacks run
55 outside the registry lock. Cancellation drains every registered prompt, and the
56 runtime snapshot derives `pendingPrompt` from this registry rather than an
57 approval-only side channel.
58 Resolution events preserve `answered`, `rejected`, or `cancelled`. A Plan
59 resolution and its Plan state are one logical batch. Turn termination closes any
60 remaining requests, so a restart cannot recreate answerable authority.
61
62 ## Capability catalog
63
64 `skill.Store.Snapshot` is the discovery boundary. A generation has immutable,
65 stable-order candidates and an O(1) name index. Concurrent cold callers share a
66 single discovery scan; cancelled waiters do not cancel a scan needed by other
67 callers. Invalidation retains the previous complete generation until the new
68 one is complete, and refresh retries are bounded to two generations. Create,
69 update, delete, and detected root changes invalidate the generation.
70 Windows uses a cancellable bounded polling generation because
71 `ReadDirectoryChangesW` registration can block teardown; other platforms keep
72 fsnotify. Both paths cover external edits and missing-root creation.
73
74 `use_capability search` consumes one catalog and one MCP schema snapshot. Skill
75 argument lookup uses the store index and cannot rescan roots per result. List is
76 paged at 50 entries by default and 100 maximum. Its cursor binds to the catalog
77 fingerprint; a changed catalog rejects the cursor and requires a restart.
78 Discovery never connects an MCP server or calls `tools/list`.
79
80 ## Compatibility and cache boundary
81
82 The changed todo schema and capability pagination are one intentional stable
83 prefix revision. Within the new contract, tool order, JSON schema bytes, and
84 the delivery marker stay deterministic; runtime state, timestamps, catalog
85 generations, and todo contents never enter the system prompt.
86
87 Legacy todo fields, completion declarations, Goal todo payloads, and dismissal
88 records are retained only so existing history can be shown. Continuing work
89 does not activate them. New Goal state writes no todo payload, approved Plans do
90 not generate todo calls, and the frontend keeps dismissal only as an in-memory
91 view preference.
92
93 ## v3 session boundary
94
95 `internal/session` defines the ownership-cutover codec
96 `reasonix.session.linear/v3.1`. The retired `reasonix.session.events/v3`
97 prototype and `reasonix.session.linear/v3` preview cannot be opened for
98 execution and must pass through the restricted importer. Unknown required
99 events, damaged complete records, and unexplained projection operations remain
100 read-only and cannot resume execution. A session has one active write handle. Fork and edit-resend create an
101 independent child session instead of adding writable heads to one log. One
102 physical JSONL record contains one complete logical batch with contiguous event
103 sequences. Cold reads omit an unterminated tail. After acquiring the exclusive
104 lease, a writer preserves its original bytes and truncates back to the last
105 complete batch before restart recovery. Complete malformed records, gaps,
106 unknown codecs, and unknown required events fail closed.
107
108 Three layers own distinct facts. The in-memory `Session` owns the typed event
109 log, sequence allocation, the operation-idempotency table, and the projection.
110 `PersistenceBinding` owns the write-behind queue, the durable watermark, and the
111 single drain chain. The physical `Store` owns the JSONL bytes, the writer lease,
112 and the rebuildable offset index. The handle exposes no projection, operation
113 table, or accepted commit list, so business state cannot be reconstructed from
114 disk layout. `Session.PrepareBatch` copies the payload, validates the schema, and
115 computes the operation digest before the commit lock is taken; the digest
116 covers only caller-supplied fields, so retrying one logical batch stays
117 idempotent. `Session.CommitPrepared` then performs the idempotency check,
118 sequence assignment, whole-batch append, and projection swap under one short
119 memory lock. A batch reaches the binding's queue after the commit lock is
120 released, and that enqueue never performs file I/O.
121
122 Create returns only after the in-memory Session, writer handle, and immutable
123 session ID have been published. `session/title` and `session/config` are the
124 authoritative title and model-selection facts; the latter includes the
125 connection revision. Rebuilding an Agent replaces model context and appends
126 configuration without creating another Controller that competes for the same
127 writer. Directory indexes and legacy model sidecars are not v3 state sources.
128
129 The host owns each published Runtime through a `RuntimeOwner`, which is the only
130 authority that can terminate an exact instance. `Service.Open` attaches a client
131 by returning a `ClientBinding` and never hands out close authority, so a failed
132 attach can only withdraw the caller's own grant. Controllers receive a
133 `ClientBinding` that may send and observe work, but releasing a tab or connection
134 cannot close the shared writer or cancel an active turn. The last binding marks an idle
135 Runtime for retirement; an active Runtime finishes under host ownership and is
136 retired afterward. Prepare failures and stale cleanup callbacks can discard
137 only the exact candidate or instance they own. Cancellation reaches the bound
138 turn-loop without first taking the Runtime or persistence lock, so its receipt
139 does not wait for a commit, observer, or disk operation. Session event
140 commits require only that the current Controller still holds the write lease;
141 Stop does not revoke `history/replace`, `turn/end`, interaction wrap-up, or
142 diagnostic writes. Session accept is first; the compatibility ledger and
143 frontend projection advance only after Session receives the batch.
144
145 `Append` means the live session accepted a fact. It validates the whole batch,
146 assigns sequences, retains an immutable copy, updates the in-memory projection,
147 and publishes to observers. It does not imply durability. The first pending
148 event starts a fixed 200 ms write-behind window; later appends do not extend the
149 deadline, and each handle has one drain chain. A background write failure keeps
150 the original ordered batch and pauses automatic retry. The next explicit
151 `Flush` retries safely. An uncertain write or fsync result becomes an explicit
152 uncertain persistence state and never causes a tool rerun.
153
154 The agent flushes before every model adapter call and before entering a
155 top-level tool body. A failed checkpoint prevents the downstream call. Todo,
156 approval, assistant-message, and `turn/end` appends do not force individual
157 flushes. Idle is not a durability guarantee. Export, cold-disk verification,
158 writer handoff, and clean shutdown wait for an explicit flush. Live snapshots
159 carry both event and durable sequences, and the former may be newer.
160
161 The new root is `sessions-v4`. Legacy continuation resolves the transcript and
162 its paired preview event directory as one import decision, and it classifies
163 before it publishes anything. It freezes the legacy transcript under its write
164 lease and the paired preview under directory and writer ownership locks, then
165 parses both from the frozen copies only. A structured preview wins when the
166 frozen legacy messages equal its messages or are a strict prefix of them; the
167 transcript wins when the preview is the strict prefix; divergent work remains
168 read-only and produces no target at all. Only after the source decision is final
169 does migration build and validate one v3 session in a same-filesystem temporary
170 directory and publish it by atomic rename. The canonical source path, head, digest, and target codec determine
171 the target ID, so identical input is idempotent and changed input creates a new
172 target. Original artifacts are copied byte-for-byte under `legacy/`; unknown
173 content is never decoded and re-encoded. Goal import excludes todos and
174 automatic continuation. Old unfinished runtime and approval records remain
175 history and never recreate live authority.
176
177 Each reachable head in a legacy schema-2 log migrates to a separate linear
178 session. `legacyHeadId` participates in both the deterministic target ID and the
179 migration-map key. Catalog listing reads the manifest head, the log revision (stat plus a bounded
180 head/tail identity sample), and rebuildable scalar metadata only. It never
181 builds the offset index. Cache entries are pinned to the exact log revision, so
182 an appended byte invalidates the entry without replaying the log. Missing
183 metadata is reported as pending and rebuilt with at most two concurrent
184 streaming reducers. Warm listing reads no event bodies. Cold
185 history pages validate records as a stream and stop at the requested page
186 boundary rather than materializing the complete log.
187
188 At the final coordinated cutover, Desktop host RPC moves to protocol version 5.
189 The Electron shell sends and validates the version from its embedded command
190 contract, so shell and service cannot drift through separately maintained
191 constants. Serve advertises `execution-v2`, `session-history-v1`, and
192 `session-identity-v1`, and `session-ownership-v1`. New Desktop builds reject execution control against a
193 remote missing any capability instead of emulating the new state machine over
194 old RPCs or path identities.
195
196 ## Regression matrix
197
198 - Whole-list tests cover empty, pending-only, multiple in-progress, out-of-order
199 completion, trimming, duplicate content, unknown fields, and invalid status.
200 - Replay tests must cover equal counts with different statuses, duplicate output
201 presentation, compaction, branch isolation, and a new-turn clear.
202 - Catalog tests use 1,154 and 10,000 candidates and assert scan/read counts:
203 exactly one shared cold scan, zero warm scans, and O(1) indexed skill lookup.
204 - Cancellation tests use channels as ordering barriers for model streams,
205 serial and parallel tools, prompt publication, answer/cancel races, hooks,
206 discovery, and compaction. Time-based sleeps are not correctness evidence.
207 - Runtime tests assert prompt registry projection, stale answer rejection,
208 session-level Stop without a turn id, and explicit recovery-required state.
209
209 lines MARKDOWN