@sema-agent/server 7.138.0 → 7.140.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/MIGRATION.md +457 -7
  2. package/README.md +7 -4
  3. package/README.zh-CN.md +6 -4
  4. package/USAGE.md +91 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/runner.js +1 -1
  8. package/dist/adoption/sql.js +1 -1
  9. package/dist/adoption/wire.d.ts +3 -3
  10. package/dist/approval-card.d.ts +26 -26
  11. package/dist/bench/s1/arms.d.ts +1 -1
  12. package/dist/bench/s1/arms.js +1 -3
  13. package/dist/bench/s1/live-deps.js +3 -2
  14. package/dist/bench/s1/row.d.ts +5 -2
  15. package/dist/bench/s1/row.js +2 -22
  16. package/dist/bench/s1/run-firm.js +2 -2
  17. package/dist/boot/adoption.js +1 -1
  18. package/dist/boot/device-lane.d.ts +8 -3
  19. package/dist/boot/device-lane.js +7 -2
  20. package/dist/boot/execution-env.d.ts +4 -2
  21. package/dist/boot/execution-env.js +28 -34
  22. package/dist/boot/execution-lane.d.ts +9 -1
  23. package/dist/boot/execution-lane.js +6 -1
  24. package/dist/boot/governance-seams.d.ts +3 -2
  25. package/dist/boot/governance-seams.js +9 -11
  26. package/dist/boot/memory-boundary.d.ts +14 -51
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +15 -11
  29. package/dist/boot/memory-consolidation.js +25 -14
  30. package/dist/boot/resolve-spec.d.ts +4 -6
  31. package/dist/boot/resolve-spec.js +10 -13
  32. package/dist/boot/retention-lane.d.ts +5 -5
  33. package/dist/boot/retention-lane.js +0 -6
  34. package/dist/boot/runner-deps.d.ts +6 -4
  35. package/dist/boot/runner-deps.js +3 -1
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  40. package/dist/boot/stage-03-store-backend.js +2 -3
  41. package/dist/boot/stage-04-stores.d.ts +2 -6
  42. package/dist/boot/stage-04-stores.js +5 -5
  43. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  44. package/dist/boot/stage-05-execution-env.js +2 -2
  45. package/dist/boot/stage-06-runners.d.ts +2 -6
  46. package/dist/boot/stage-06-runners.js +2 -3
  47. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  48. package/dist/boot/stage-08-reapers.d.ts +2 -6
  49. package/dist/boot/stage-09-leader.d.ts +2 -6
  50. package/dist/boot/stage-09-leader.js +1 -2
  51. package/dist/boot/stage-10-http-server.d.ts +2 -6
  52. package/dist/boot/stage-10-http-server.js +7 -6
  53. package/dist/boot/stores.d.ts +3 -6
  54. package/dist/boot/stores.js +27 -40
  55. package/dist/boot/task-list-lane.d.ts +1 -1
  56. package/dist/boot/task-list-lane.js +1 -1
  57. package/dist/boot/web-admin.js +37 -11
  58. package/dist/capabilities/execution-lane.d.ts +2 -2
  59. package/dist/capabilities/mcp-probe.js +1 -1
  60. package/dist/capabilities/scenarios.d.ts +1 -1
  61. package/dist/capability-facts.d.ts +15 -7
  62. package/dist/capability-facts.js +8 -3
  63. package/dist/config-catalog.js +9 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +33 -37
  67. package/dist/config-provider.js +51 -30
  68. package/dist/config-types.d.ts +21 -7
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +46 -16
  71. package/dist/delegated-session-owner.d.ts +4 -3
  72. package/dist/delegated-session-owner.js +1 -1
  73. package/dist/deployment-shape.d.ts +10 -9
  74. package/dist/deployment-shape.js +4 -4
  75. package/dist/device-enrollment.d.ts +41 -2
  76. package/dist/device-enrollment.js +27 -12
  77. package/dist/device-store.d.ts +58 -85
  78. package/dist/device-store.js +19 -49
  79. package/dist/device-ws-hub.js +2 -1
  80. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  81. package/dist/exec-cut.d.ts +60 -0
  82. package/dist/exec-cut.js +36 -0
  83. package/dist/execution-lane-caps.d.ts +115 -105
  84. package/dist/execution-lane-caps.js +76 -82
  85. package/dist/execution-placement.d.ts +259 -0
  86. package/dist/execution-placement.js +199 -0
  87. package/dist/field-conflict.d.ts +3 -2
  88. package/dist/field-conflict.js +1 -0
  89. package/dist/hosted-posture.d.ts +2 -2
  90. package/dist/http/admission.js +8 -19
  91. package/dist/http/checkpoint-ctx.d.ts +21 -12
  92. package/dist/http/checkpoint-ctx.js +4 -2
  93. package/dist/http/claimed-run.d.ts +1 -1
  94. package/dist/http/durable-run-leg.js +9 -5
  95. package/dist/http/execution-context.d.ts +218 -0
  96. package/dist/http/execution-context.js +330 -0
  97. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  98. package/dist/http/memory-compliance-failure-reply.js +5 -0
  99. package/dist/http/memory-layer-reply.d.ts +30 -0
  100. package/dist/http/memory-layer-reply.js +24 -0
  101. package/dist/http/resume-legs.js +63 -36
  102. package/dist/http/retired-service-deps.d.ts +1 -1
  103. package/dist/http/retired-service-deps.js +1 -1
  104. package/dist/http/route-ctx.d.ts +14 -8
  105. package/dist/http/routes/adoption.js +1 -1
  106. package/dist/http/routes/background.d.ts +29 -1
  107. package/dist/http/routes/background.js +29 -2
  108. package/dist/http/routes/devices.d.ts +13 -19
  109. package/dist/http/routes/devices.js +21 -26
  110. package/dist/http/routes/memory-bundle.js +3 -0
  111. package/dist/http/routes/memory-compliance.js +8 -1
  112. package/dist/http/routes/memory-consolidation.js +12 -1
  113. package/dist/http/routes/memory-origin.js +14 -2
  114. package/dist/http/routes/memory-policy.js +5 -0
  115. package/dist/http/routes/session-sync.js +110 -15
  116. package/dist/http/routes/sessions.d.ts +6 -0
  117. package/dist/http/routes/sessions.js +143 -22
  118. package/dist/http/routes/side-query.d.ts +12 -4
  119. package/dist/http/routes/side-query.js +4 -5
  120. package/dist/http/routes/tasks.js +41 -33
  121. package/dist/http/server.d.ts +40 -44
  122. package/dist/http/server.js +13 -2
  123. package/dist/http/session-inherit-env.d.ts +3 -4
  124. package/dist/http/session-inherit-env.js +1 -1
  125. package/dist/http/wire-types.d.ts +8 -12
  126. package/dist/identity-source.d.ts +1 -1
  127. package/dist/identity-source.js +1 -1
  128. package/dist/index.d.ts +1 -0
  129. package/dist/lane-home-probe.d.ts +68 -42
  130. package/dist/lane-home-probe.js +53 -30
  131. package/dist/leader/wire.js +5 -4
  132. package/dist/memory-bundle-engine.d.ts +16 -9
  133. package/dist/memory-bundle-engine.js +29 -6
  134. package/dist/memory-operator-faces.d.ts +85 -30
  135. package/dist/memory-operator-faces.js +136 -30
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +29 -5
  141. package/dist/memory-scope.js +16 -4
  142. package/dist/memory-sync-client.d.ts +70 -18
  143. package/dist/memory-sync-client.js +215 -117
  144. package/dist/memory-sync.d.ts +1 -1
  145. package/dist/memory-sync.js +3 -2
  146. package/dist/observability/cost-taxonomy.d.ts +2 -2
  147. package/dist/observability/fail-open.d.ts +12 -0
  148. package/dist/observability/fail-open.js +12 -0
  149. package/dist/observability/run-terminal.js +0 -1
  150. package/dist/orchestration/workflow-notify-journal.js +4 -4
  151. package/dist/org-memory-admission.d.ts +4 -3
  152. package/dist/org-memory-admission.js +6 -6
  153. package/dist/parked-decide.d.ts +34 -9
  154. package/dist/parked-decide.js +12 -19
  155. package/dist/parked-revive-surface.d.ts +12 -15
  156. package/dist/parked-revive-surface.js +3 -6
  157. package/dist/peer-seat.d.ts +13 -7
  158. package/dist/peer-seat.js +37 -3
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  161. package/dist/plugins/approval-ask-store-file.js +11 -0
  162. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  163. package/dist/plugins/approval-ask-store-memory.js +12 -0
  164. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  165. package/dist/plugins/approval-ask-store-sql.js +44 -26
  166. package/dist/plugins/approval-nonce-store.js +4 -4
  167. package/dist/plugins/blob-backend.d.ts +4 -4
  168. package/dist/plugins/blob-backend.js +7 -7
  169. package/dist/plugins/breaker-state-sql.js +2 -2
  170. package/dist/plugins/caching-session-store.d.ts +6 -2
  171. package/dist/plugins/caching-session-store.js +9 -3
  172. package/dist/plugins/checkpoint-index-columns.js +2 -2
  173. package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
  174. package/dist/plugins/checkpoint-store-sql.js +21 -23
  175. package/dist/plugins/device-store-sql.d.ts +25 -51
  176. package/dist/plugins/device-store-sql.js +68 -122
  177. package/dist/plugins/ensure-index.d.ts +1 -2
  178. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  179. package/dist/plugins/execution-placement-sql.js +223 -0
  180. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  181. package/dist/plugins/file-history-store-sql.js +9 -9
  182. package/dist/plugins/file-run-store.d.ts +6 -1
  183. package/dist/plugins/file-run-store.js +4 -0
  184. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  185. package/dist/plugins/fork-routing-session-store.js +16 -0
  186. package/dist/plugins/image-bake-store-sql.js +9 -9
  187. package/dist/plugins/image-index-sql.js +11 -11
  188. package/dist/plugins/leader-run-store-sql.js +4 -4
  189. package/dist/plugins/local-session-store.d.ts +149 -50
  190. package/dist/plugins/local-session-store.js +382 -107
  191. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  192. package/dist/plugins/mailbox-store-sql.js +4 -4
  193. package/dist/plugins/memory-engine-tidb.js +5 -5
  194. package/dist/plugins/memory-key-guards.d.ts +4 -0
  195. package/dist/plugins/memory-key-guards.js +1 -0
  196. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  197. package/dist/plugins/memory-run-store.d.ts +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +117 -8
  200. package/dist/plugins/one-time-migrations.js +408 -38
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  203. package/dist/plugins/outcome-ledger-sql.js +2 -2
  204. package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
  205. package/dist/plugins/permission-rule-store-sql.js +29 -32
  206. package/dist/plugins/pg-pool.d.ts +7 -3
  207. package/dist/plugins/pg-session-storage.d.ts +31 -20
  208. package/dist/plugins/pg-session-storage.js +120 -52
  209. package/dist/plugins/remote-env-adb.d.ts +18 -19
  210. package/dist/plugins/remote-env-adb.js +91 -66
  211. package/dist/plugins/remote-env-device.d.ts +23 -31
  212. package/dist/plugins/remote-env-device.js +19 -17
  213. package/dist/plugins/remote-env-e2b.d.ts +27 -21
  214. package/dist/plugins/remote-env-e2b.js +91 -45
  215. package/dist/plugins/remote-env-file-error.d.ts +3 -1
  216. package/dist/plugins/remote-env-file-error.js +9 -3
  217. package/dist/plugins/remote-env-host.d.ts +31 -34
  218. package/dist/plugins/remote-env-host.js +91 -72
  219. package/dist/plugins/remote-env-k8s.d.ts +13 -29
  220. package/dist/plugins/remote-env-k8s.js +63 -55
  221. package/dist/plugins/remote-env-local-docker.d.ts +24 -12
  222. package/dist/plugins/remote-env-local-docker.js +49 -61
  223. package/dist/plugins/remote-env-ssh.d.ts +11 -19
  224. package/dist/plugins/remote-env-ssh.js +74 -57
  225. package/dist/plugins/remote-shell.d.ts +100 -3
  226. package/dist/plugins/remote-shell.js +166 -2
  227. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  228. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  229. package/dist/plugins/retention-lane-store-sql.js +2 -2
  230. package/dist/plugins/retention-store-sql.d.ts +10 -7
  231. package/dist/plugins/retention-store-sql.js +13 -7
  232. package/dist/plugins/run-store-sql.d.ts +13 -3
  233. package/dist/plugins/run-store-sql.js +20 -15
  234. package/dist/plugins/schema-consistency.d.ts +21 -11
  235. package/dist/plugins/schema-consistency.js +15 -9
  236. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  237. package/dist/plugins/schema-drift-sentence.js +38 -0
  238. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  239. package/dist/plugins/schema-ensure-legs.js +2 -2
  240. package/dist/plugins/schema-live-columns.d.ts +1 -1
  241. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  242. package/dist/plugins/session-fork.d.ts +54 -0
  243. package/dist/plugins/session-fork.js +38 -1
  244. package/dist/plugins/session-row-sql.d.ts +22 -0
  245. package/dist/plugins/session-row-sql.js +81 -0
  246. package/dist/plugins/session-rules-column.d.ts +1 -1
  247. package/dist/plugins/session-rules-column.js +1 -1
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  250. package/dist/plugins/session-workspace-cell.js +1 -1
  251. package/dist/plugins/shared-memory-store-sql.js +2 -2
  252. package/dist/plugins/sql-driver.d.ts +10 -5
  253. package/dist/plugins/sql-driver.js +6 -6
  254. package/dist/plugins/sql-errors.js +1 -1
  255. package/dist/plugins/store-backend.d.ts +5 -7
  256. package/dist/plugins/store-backend.js +23 -11
  257. package/dist/plugins/store-contracts.d.ts +25 -5
  258. package/dist/plugins/task-list-store-sql.js +11 -11
  259. package/dist/plugins/tidb-pool.d.ts +12 -7
  260. package/dist/plugins/tidb-pool.js +13 -7
  261. package/dist/plugins/tidb-session-storage.js +4 -4
  262. package/dist/plugins/tidb-session-store.d.ts +57 -26
  263. package/dist/plugins/tidb-session-store.js +127 -59
  264. package/dist/plugins/tool-result-store-sql.d.ts +5 -4
  265. package/dist/plugins/tool-result-store-sql.js +21 -13
  266. package/dist/plugins/user-input-ledger-sql.js +2 -2
  267. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  268. package/dist/plugins/workflow-run-store-sql.js +19 -19
  269. package/dist/plugins/write-behind-counter.d.ts +2 -3
  270. package/dist/plugins/write-behind-counter.js +5 -5
  271. package/dist/project-memory.d.ts +9 -15
  272. package/dist/project-memory.js +5 -4
  273. package/dist/request-key-closure.js +1 -1
  274. package/dist/request-value-closure.d.ts +6 -7
  275. package/dist/request-value-closure.js +1 -3
  276. package/dist/resource-suspend.d.ts +14 -12
  277. package/dist/resource-suspend.js +1 -1
  278. package/dist/resource-window.d.ts +4 -3
  279. package/dist/run-local.d.ts +6 -0
  280. package/dist/run-local.js +34 -35
  281. package/dist/runs.d.ts +10 -5
  282. package/dist/runs.js +52 -66
  283. package/dist/security.d.ts +38 -12
  284. package/dist/security.js +8 -2
  285. package/dist/server-secret-env.d.ts +9 -5
  286. package/dist/session-children-retire.d.ts +27 -9
  287. package/dist/session-children-retire.js +9 -5
  288. package/dist/session-incarnation.d.ts +20 -10
  289. package/dist/session-incarnation.js +5 -3
  290. package/dist/session-policy-wire.d.ts +1 -1
  291. package/dist/session-row.d.ts +114 -5
  292. package/dist/session-row.js +55 -5
  293. package/dist/session-titler.d.ts +2 -2
  294. package/dist/sql-engine-posture.d.ts +1 -1
  295. package/dist/sql-engine-posture.js +1 -1
  296. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  297. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  298. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  299. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  300. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  301. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  302. package/dist/task-a2a.d.ts +1 -1
  303. package/dist/task-a2a.js +5 -6
  304. package/dist/task-cwd.d.ts +6 -1
  305. package/dist/task-mcp.d.ts +22 -9
  306. package/dist/task-mcp.js +17 -9
  307. package/dist/terminal.d.ts +27 -0
  308. package/dist/terminal.js +6 -0
  309. package/dist/tool-approval.d.ts +6 -0
  310. package/dist/tool-approval.js +8 -1
  311. package/dist/trace/project.d.ts +1 -0
  312. package/dist/trace/project.js +1 -0
  313. package/dist/trace/redact.d.ts +3 -1
  314. package/dist/trace/redact.js +80 -28
  315. package/dist/trace/self-minted-wire.d.ts +169 -2
  316. package/dist/trace/self-minted-wire.js +54 -1
  317. package/dist/url-refusal.d.ts +1 -1
  318. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  319. package/dist/web-admin-client/apply-effective.js +20 -10
  320. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  321. package/dist/web-admin-client/candidate-gate.js +1 -1
  322. package/dist/web-admin-client/http-client.d.ts +5 -4
  323. package/dist/web-admin-client/http-client.js +2 -2
  324. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  325. package/dist/web-admin-client/read-warnings.js +5 -9
  326. package/dist/web-admin-client/skills-mcp.js +3 -2
  327. package/dist/web-admin-client/types.d.ts +6 -0
  328. package/package.json +3 -3
  329. package/wire-changes/server@7.139.0.json +348 -0
  330. package/wire-changes/server@7.140.0.json +184 -0
  331. package/dist/http/device-placement.d.ts +0 -175
  332. package/dist/http/device-placement.js +0 -145
@@ -16,7 +16,7 @@ import { type MemoryOptOutGrantStore } from "./memory-optout-grant-store-sql.js"
16
16
  import { type SendFileLedger } from "./send-file-ledger.js";
17
17
  import type { UserInputLedgerStore } from "../user-input-ledger.js";
18
18
  import { TiDBResumeAnchorStore, PgResumeAnchorStore } from "./resume-anchor-store-sql.js";
19
- import { type SessionPolicyStoreLogger } from "./session-policy-store-sql.js";
19
+ import { type SessionPolicyStoreLogger } from "../stores-sql/session-policy-store-sql.js";
20
20
  import type { ServiceConfig } from "../config-types.js";
21
21
  import { TiDBRunStore, PgRunStore } from "./run-store-sql.js";
22
22
  import { TiDBCheckpointStore, PgCheckpointStore } from "./checkpoint-store-sql.js";
@@ -256,11 +256,9 @@ export interface StoreBackend {
256
256
  * `fileSessionCaptureRecordStore(controlDir)` 按平面现造,依托是**记忆引擎的控制面目录** ⇒ 记忆面暗的
257
257
  * 部署上该平面不存在,于是这一支**随引擎接线门缺席**(唯一的 `undefined` 成因,逐字理由见 LocalBackend 的实装注)。
258
258
  *
259
- * 🔴 修前 local 恒 `undefined` 且注称「缺席是一个真答案」—— 那句话在**事实层面**成立(单机的正确载体
260
- * 确实就是文件三腿)、在**接线层面**是假的:core 的 `captureCarrierUnsupported`
261
- * (core 7.13.0 `prepare-task.js`)判的是「这个席位在不在」而不是「文件形能不能用」,叠上
262
- * `isRemoteExecutionEnv` 的接口形鸭子判(`remote-env.js`)把本仓 host lane 一并算作 remote,
263
- * 于是单机默认部署上 `memoryCapture:"off"` 两条 ingress 结构性不可达([ref])。
259
+ * 🔴 local 不能答 `undefined`:引擎的 `captureCarrierUnsupported`(`prepare-task.js`)判的是「这个席位在不在」而不是「文件形能不能用」,
260
+ * 叠上执行环境块 `hostLocalPaths` 为假 —— 本地后端上跑沙箱 / 远端车道的部署,席位缺席 ⇒ `memoryCapture:"off"` 两条 ingress 结构性不可达。
261
+ * (引擎按接口形判 remote 的那些版本里 host 车道也在射程;今天按块判,host 不在,供店对它是无害的同址声明。)
264
262
  * 刻意仍不给 local 一个**内存** twin:内存形会在重启时把一条隐私记录静默丢掉(比文件三腿严格更差)。 */
265
263
  sessionCaptureRecords(): SessionCaptureRecordSource | undefined;
266
264
  /** Retired-epoch byte hygiene: GC grace-passed orphan `snapshot_blob` rows/objects — the whole-tree snapshot
@@ -350,7 +348,7 @@ export interface StoreBackend {
350
348
  */
351
349
  leaderRun?(): LeaderRunStore;
352
350
  /**
353
- * device lane 的四表店(device-executor-lane-v2 §6,车A-2 交店 / 车A-4 接线)。**SQL 后端专有**,
351
+ * device lane 的三表店(device-executor-lane-v2 §6,车A-2 交店 / 车A-4 接线;绑定写在根会话行的执行格上,与会话店同一只池)。**SQL 后端专有**,
354
352
  * `local` 刻意省略 —— 与 {@link leaderRun} 逐字同一条理由:config 层已经把「`REMOTE_EXEC=device` 且
355
353
  * 无外部 SQL 店」整个组合拒启了(`device-durable-store` 不变量),所以 local 车道上根本不存在一个需要
356
354
  * 这四张表的 device worker。消费点按 `backend?.device?.()` 取,缺席 ⇒ `boot/device-lane.ts` 的
@@ -13,7 +13,8 @@ import { LocalWorkflowParkDecisionStore } from "../orchestration/workflow-park-d
13
13
  import { StoreBootRefusalError } from "./store-contracts.js";
14
14
  import { buildStoreOpenRefusalTail } from "./store-open-refusal.js";
15
15
  import { acquireLocalBackendWithWait } from "./store-lock-wait.js";
16
- import { NO_SQL_MIGRATIONS, runSqlOneTimeMigrations } from "./one-time-migrations.js";
16
+ import { NO_SQL_MIGRATIONS, assertExecutionPlacementSwitchAdmissibleOnPool, backfillLocalAskExecutionLanes, backfillLocalExecutionPlacements, runSqlOneTimeMigrations } from "./one-time-migrations.js";
17
+ import { deployedExecutionLanes } from "../execution-lane-caps.js";
17
18
  import { assertRetiredSchemaAbsentOnPool } from "./schema-consistency.js";
18
19
  import { LocalSessionStore, createLocalSessionRepo } from "./local-session-store.js";
19
20
  import { LocalCheckpointStore } from "./local-checkpoint-store.js";
@@ -29,7 +30,7 @@ import { TiDBSendFileLedger, PgSendFileLedger, FileSendFileLedger } from "./send
29
30
  import { FileUserInputLedgerStore } from "./user-input-ledger-file.js";
30
31
  import { TiDBUserInputLedgerStore, PgUserInputLedgerStore } from "./user-input-ledger-sql.js";
31
32
  import { TiDBResumeAnchorStore, PgResumeAnchorStore } from "./resume-anchor-store-sql.js";
32
- import { TiDBSessionPolicyStore, PgSessionPolicyStore } from "./session-policy-store-sql.js";
33
+ import { TiDBSessionPolicyStore, PgSessionPolicyStore } from "../stores-sql/session-policy-store-sql.js";
33
34
  import { TiDBWorkflowJournalStore, PgWorkflowJournalStore } from "./workflow-journal-store-sql.js";
34
35
  import { TiDBFileHistoryStore, PgFileHistoryStore, FILE_HISTORY_BLOB_TABLE, FILE_HISTORY_BLOB_KEY_PREFIX } from "./file-history-store-sql.js";
35
36
  import { MinioBlobBackend, SqlBlobBackend } from "./blob-backend.js";
@@ -91,7 +92,7 @@ function legacyBlobBackend(config, dialect, pool) {
91
92
  async function legacySweepImpl(config, dialect, pool) {
92
93
  const sql = "SELECT blob_hash FROM snapshot_blob";
93
94
  let orphans;
94
- if (dialect === "tidb") {
95
+ if (dialect === "mysql") {
95
96
  const [rows] = await pool.query(sql);
96
97
  orphans = rows.map((r) => String(r.blob_hash));
97
98
  }
@@ -134,12 +135,12 @@ class TiDBBackend {
134
135
  sendFileLedger() { return new TiDBSendFileLedger(this.pool); }
135
136
  userInputLedger() { return new TiDBUserInputLedgerStore(this.pool); }
136
137
  sessionPolicy(logger) { return new TiDBSessionPolicyStore(this.pool, logger); }
137
- fileHistory() { return new TiDBFileHistoryStore(this.pool, historyBlobBackend(this.config, "tidb", this.pool), this.config.fileHistory); }
138
+ fileHistory() { return new TiDBFileHistoryStore(this.pool, historyBlobBackend(this.config, "mysql", this.pool), this.config.fileHistory); }
138
139
  memoryOptOutGrant() { return new TiDBMemoryOptOutGrantStore(this.pool); }
139
140
  sessionCaptureRecords() { return () => (this.captureRecordsInst ??= new TiDBSessionCaptureRecordStore(this.pool)); }
140
141
  captureRecordsInst;
141
- legacySnapshotBlobSweep() { return legacySweepImpl(this.config, "tidb", this.pool); }
142
- runOneTimeMigrations() { return runSqlOneTimeMigrations("tidb", this.pool); }
142
+ legacySnapshotBlobSweep() { return legacySweepImpl(this.config, "mysql", this.pool); }
143
+ runOneTimeMigrations() { return runSqlOneTimeMigrations("mysql", this.pool, { lanes: deployedExecutionLanes(this.config) }); }
143
144
  workflowJournal() { return new TiDBWorkflowJournalStore(this.pool); }
144
145
  outcomeSink() { return new TiDBOutcomeLedger(this.pool); }
145
146
  workflowRun(onWarn) { return new TiDBWorkflowRunStore(this.pool, onWarn); }
@@ -193,7 +194,7 @@ class PgBackend {
193
194
  sessionCaptureRecords() { return () => (this.captureRecordsInst ??= new PgSessionCaptureRecordStore(this.pool)); }
194
195
  captureRecordsInst;
195
196
  legacySnapshotBlobSweep() { return legacySweepImpl(this.config, "pg", this.pool); }
196
- runOneTimeMigrations() { return runSqlOneTimeMigrations("pg", this.pool); }
197
+ runOneTimeMigrations() { return runSqlOneTimeMigrations("pg", this.pool, { lanes: deployedExecutionLanes(this.config) }); }
197
198
  workflowJournal() { return new PgWorkflowJournalStore(this.pool); }
198
199
  outcomeSink() { return new PgOutcomeLedger(this.pool); }
199
200
  workflowRun(onWarn) { return new PgWorkflowRunStore(this.pool, onWarn); }
@@ -239,7 +240,9 @@ class LocalBackend {
239
240
  onError;
240
241
  discloseWarn;
241
242
  parkDecisionLedgerRoot;
243
+ executionLanes;
242
244
  constructor(root, config, onWarn, onError) {
245
+ this.executionLanes = deployedExecutionLanes(config);
243
246
  this.onWarn = onWarn;
244
247
  this.discloseWarn = onWarn ?? ((msg, meta) => console.warn(msg, meta));
245
248
  this.onError = onError;
@@ -282,7 +285,15 @@ class LocalBackend {
282
285
  }
283
286
  async ensureSchema() { }
284
287
  async runOneTimeMigrations() {
285
- return { ...NO_SQL_MIGRATIONS, holderDeathCodeRowsBackfilled: this.runStore.migrateLegacyHolderDeathCodes() };
288
+ const holderDeathCodeRowsBackfilled = this.runStore.migrateLegacyHolderDeathCodes();
289
+ const placements = await backfillLocalExecutionPlacements(this.runStore.sessionIdsWithRuns(), this.sessionStore.executionPlacementBackfill, { lanes: this.executionLanes });
290
+ return {
291
+ ...NO_SQL_MIGRATIONS,
292
+ holderDeathCodeRowsBackfilled,
293
+ executionPlacementsBackfilled: placements.backfilled,
294
+ executionPlacementSessionsUnreadable: placements.unreadable,
295
+ askExecutionLanesBackfilled: backfillLocalAskExecutionLanes(this.approvalAskStore, { lanes: this.executionLanes }),
296
+ };
286
297
  }
287
298
  async close() {
288
299
  await this.sessionStore.dispose();
@@ -364,17 +375,18 @@ export function createStoreBackend(config, onWarn, onError) {
364
375
  if (!config.pg)
365
376
  return undefined;
366
377
  const pool = createPgPool(pgPoolOptions(config.pg, config.dbQueryTimeoutMs), onWarn);
367
- return withRetiredSchemaProbe(new PgBackend(pool, config), { dialect: "pg", pool });
378
+ return withRetiredSchemaProbe(new PgBackend(pool, config), { dialect: "pg", pool }, { lanes: deployedExecutionLanes(config) });
368
379
  }
369
380
  if (!config.tidb)
370
381
  return undefined;
371
382
  const pool = createTidbPool(config);
372
- return withRetiredSchemaProbe(new TiDBBackend(pool, config), { dialect: "tidb", pool });
383
+ return withRetiredSchemaProbe(new TiDBBackend(pool, config), { dialect: "mysql", pool }, { lanes: deployedExecutionLanes(config) });
373
384
  }
374
- function withRetiredSchemaProbe(inner, target) {
385
+ function withRetiredSchemaProbe(inner, target, executionPlacementSwitch) {
375
386
  const guarded = Object.create(inner);
376
387
  guarded.ensureSchema = async () => {
377
388
  await assertRetiredSchemaAbsentOnPool(target);
389
+ await assertExecutionPlacementSwitchAdmissibleOnPool(target.dialect, target.pool, executionPlacementSwitch);
378
390
  await inner.ensureSchema();
379
391
  };
380
392
  return guarded;
@@ -276,26 +276,36 @@ export interface NewRunMeta {
276
276
  idem?: RunIdemBinding | null;
277
277
  }
278
278
  /**
279
- * `RunStore.createRun` 的结局 —— 三形,一形一句话:
279
+ * `RunStore.createRun` 的结局 —— 四形,一形一句话:
280
280
  * · `{ ok: true }` —— 新行已建、会话 claim 已拿;
281
281
  * · `{ ok: false, existing }` —— 这次提交的一把**幂等钥匙**已经指向一条行:`idem_key`(`Idempotency-Key`)或
282
282
  * `task_id`(调用方自铸的 `body.taskId`)—— 两把钥匙是**同一条律**(资源行上的唯一键),先认 `idem_key` 再认 `task_id`。
283
283
  * 这是「原子赢或观察」的**观察**臂:行原样交回,没建新行、没动会话;回执由提交面的唯一回执函数判。
284
- * · `{ ok: false, activeTaskId }` —— 会话被**别的** run 占着(双发守卫),没建行。
285
- * 另两位在每一形上恒 `undefined`(读 `created.existing` / `created.activeTaskId` 不用先窄化,判别仍是穷举的)。
284
+ * · `{ ok: false, activeTaskId }` —— 会话被**别的** run 占着(双发守卫),没建行;
285
+ * · `{ ok: false, sessionAbsent: true }` —— 根会话行不在(SQL 孪生在认领事务里锁根行读到零行:受理之后会话被删),没建行、没认领。
286
+ * 进程内两孪生(File / Memory)的账本里没有会话行,不答这一形。
287
+ * 其余几位在每一形上恒 `undefined`(读 `created.existing` / `created.activeTaskId` / `created.sessionAbsent` 不用先窄化,判别仍是穷举的)。
286
288
  */
287
289
  export type CreateRunOutcome = {
288
290
  ok: true;
289
291
  existing?: undefined;
290
292
  activeTaskId?: undefined;
293
+ sessionAbsent?: undefined;
291
294
  } | {
292
295
  ok: false;
293
296
  existing: RunRecord;
294
297
  activeTaskId?: undefined;
298
+ sessionAbsent?: undefined;
295
299
  } | {
296
300
  ok: false;
297
301
  existing?: undefined;
298
302
  activeTaskId: string;
303
+ sessionAbsent?: undefined;
304
+ } | {
305
+ ok: false;
306
+ existing?: undefined;
307
+ activeTaskId?: undefined;
308
+ sessionAbsent: true;
299
309
  };
300
310
  /** SQL 认领栅栏的结局:`held: false` = 认领此刻不是这条 run 的(或它已不在跑),`write` 一次都没调。 */
301
311
  export type ClaimFenced<T> = {
@@ -504,13 +514,16 @@ export interface SessionSummary {
504
514
  * 零迁移路线 —— result 在终局与 park 时写、认领续跑即清空([ref])⇒ 在跑 run 键诚实缺席);OMIT-when-absent,恒不铸 null。轴辨析与
505
515
  * wire 半场见 `security.ts SessionListItem.lastTaskRunId`(同名同义)。 */
506
516
  lastTaskRunId?: string;
507
- /** The session's title: the user's (`patchSessionMeta`, unconditional; `null` clears it back to automatic) or the auto-titler's
517
+ /** The session's title: the user's (`writeSessionRowCells`; `null` clears it back to automatic) or the auto-titler's
508
518
  * (cheap-model one-liner, `setTitleIfNull` — fills NULL only). null = not (yet) titled OR the lister face has no session_meta (the legacy
509
519
  * task_run aggregate / memory twins — honest degrade). Writer order: `src/session-row.ts`. */
510
520
  title: string | null;
511
- /** Pinned by the user (`patchSessionMeta`). Always present, `false` by default; the legacy task_run aggregate / memory
521
+ /** Pinned by the user (`writeSessionRowCells`). Always present, `false` by default; the legacy task_run aggregate / memory
512
522
  * twins answer `false` (no session_meta ⇒ nothing there can pin it). */
513
523
  pinned: boolean;
524
+ /** 行标签(`src/session-row.ts` 的 `sessionRowTagOf`):这一位住客「PATCH 能写的每一格」的单向摘要,present-iff 这一行可被条件写(店有行格写原语 ∧ 行上有化身);
525
+ * 只经店的列表行映射铸(`sessionRowTagKey`)。没有会话行的 lister(legacy task_run 聚合 / 内存孪生)缺席。wire 半场见 `security.ts SessionListItem.rowTag`(同名同义)。 */
526
+ rowTag?: string;
514
527
  }
515
528
  /**
516
529
  * [ref] —— 会话**枚举面**读 run 账里「每会话的 run 投影」的窄读面:`runCount` / `lastStatus` / `lastRunId` / `lastTaskRunId` 四位,
@@ -529,6 +542,13 @@ export interface SessionRunRollupSource {
529
542
  limit: number;
530
543
  }): Promise<ReadonlyArray<Pick<SessionSummary, "sessionId" | "runCount" | "lastStatus" | "lastRunId" | "lastTaskRunId">>>;
531
544
  }
545
+ /**
546
+ * 会话**此刻的 run 认领**(单活槽)的窄读面 —— run 店的 `getActiveTaskId`,同一个问题在 SQL 孪生里是写事务内对 `task_active` 的锁定读。
547
+ * local 会话店的执行席在 `markMirror` 的写锁内读它,判序与 SQL 孪生同(行不在 / 住客不同 / writer / mirror 先判,认领只拦「absent → mirror」这一笔写)。
548
+ */
549
+ export interface SessionRunClaimSource {
550
+ getActiveTaskId(sessionId: string): Promise<string | undefined>;
551
+ }
532
552
  /**
533
553
  * [ref] —— 「这个会话**最近一条腿**是哪一条 run」的窄读面(`latestRunForSession` 的回值)。
534
554
  *
@@ -68,33 +68,33 @@ export async function ensurePgTaskListSchema(q) {
68
68
  )`);
69
69
  }
70
70
  async function ensureMeta(exec, dialect, listKey) {
71
- await exec(dialect === "tidb"
71
+ await exec(dialect === "mysql"
72
72
  ? `INSERT INTO ${TASK_LIST_META_TABLE} (list_key, next_id, next_sort) VALUES (?, 1, 1) ON DUPLICATE KEY UPDATE list_key = list_key`
73
73
  : `INSERT INTO ${TASK_LIST_META_TABLE} (list_key, next_id, next_sort) VALUES ($1, 1, 1) ON CONFLICT (list_key) DO NOTHING`, [listKey]);
74
74
  }
75
75
  async function allocateOn(exec, dialect, listKey) {
76
76
  await ensureMeta(exec, dialect, listKey);
77
- const rows = await exec(dialect === "tidb"
77
+ const rows = await exec(dialect === "mysql"
78
78
  ? `SELECT next_id FROM ${TASK_LIST_META_TABLE} WHERE list_key = ? FOR UPDATE`
79
79
  : `SELECT next_id FROM ${TASK_LIST_META_TABLE} WHERE list_key = $1 FOR UPDATE`, [listKey]);
80
80
  const id = Number(rows[0].next_id);
81
- await exec(dialect === "tidb"
81
+ await exec(dialect === "mysql"
82
82
  ? `UPDATE ${TASK_LIST_META_TABLE} SET next_id = ? WHERE list_key = ?`
83
83
  : `UPDATE ${TASK_LIST_META_TABLE} SET next_id = $1 WHERE list_key = $2`, [id + 1, listKey]);
84
84
  return String(id);
85
85
  }
86
86
  async function setOn(exec, dialect, listKey, id, itemJson) {
87
87
  await ensureMeta(exec, dialect, listKey);
88
- const rows = await exec(dialect === "tidb"
88
+ const rows = await exec(dialect === "mysql"
89
89
  ? `SELECT next_sort FROM ${TASK_LIST_META_TABLE} WHERE list_key = ? FOR UPDATE`
90
90
  : `SELECT next_sort FROM ${TASK_LIST_META_TABLE} WHERE list_key = $1 FOR UPDATE`, [listKey]);
91
91
  const sort = Number(rows[0].next_sort);
92
- await exec(dialect === "tidb"
92
+ await exec(dialect === "mysql"
93
93
  ? `INSERT INTO ${TASK_LIST_ITEM_TABLE} (list_key, id, id_num, sort_seq, item_json) VALUES (?, ?, ?, ?, ?)
94
94
  ON DUPLICATE KEY UPDATE item_json = VALUES(item_json)`
95
95
  : `INSERT INTO ${TASK_LIST_ITEM_TABLE} (list_key, id, id_num, sort_seq, item_json) VALUES ($1, $2, $3, $4, $5)
96
96
  ON CONFLICT (list_key, id) DO UPDATE SET item_json = EXCLUDED.item_json`, [listKey, id, arrayIndexOf(id), sort, itemJson]);
97
- await exec(dialect === "tidb"
97
+ await exec(dialect === "mysql"
98
98
  ? `UPDATE ${TASK_LIST_META_TABLE} SET next_sort = ? WHERE list_key = ?`
99
99
  : `UPDATE ${TASK_LIST_META_TABLE} SET next_sort = $1 WHERE list_key = $2`, [sort + 1, listKey]);
100
100
  }
@@ -160,29 +160,29 @@ export function createTiDBTaskListStore(pool, listKey) {
160
160
  };
161
161
  const build = (q, inTx) => ({
162
162
  get: async (id) => {
163
- const [rows] = (await q.query(`SELECT ${jsonColumnText("tidb", "item_json")} FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? AND id = ?${inTx ? " FOR UPDATE" : ""}`, [listKey, id]));
163
+ const [rows] = (await q.query(`SELECT ${jsonColumnText("mysql", "item_json")} FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? AND id = ?${inTx ? " FOR UPDATE" : ""}`, [listKey, id]));
164
164
  return rows.length > 0 ? parseItem(rows[0].item_json, `for list ${listKey} item ${JSON.stringify(id)}`) : undefined;
165
165
  },
166
166
  set: async (id, item) => {
167
167
  if (item.metadata)
168
168
  assertJsonMetadata(item.metadata);
169
169
  const json = JSON.stringify(item);
170
- await (inTx ? setOn(execOn(q), "tidb", listKey, id, json) : tx((c) => setOn(execOn(c), "tidb", listKey, id, json)));
170
+ await (inTx ? setOn(execOn(q), "mysql", listKey, id, json) : tx((c) => setOn(execOn(c), "mysql", listKey, id, json)));
171
171
  },
172
172
  delete: async (id) => {
173
173
  const [res] = (await q.query(`DELETE FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? AND id = ?`, [listKey, id]));
174
174
  return res.affectedRows === 1;
175
175
  },
176
176
  list: async () => {
177
- const [rows] = (await q.query(`SELECT ${jsonColumnText("tidb", "item_json")} FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? ORDER BY id_num IS NULL, id_num, sort_seq${inTx ? " FOR UPDATE" : ""}`, [listKey]));
177
+ const [rows] = (await q.query(`SELECT ${jsonColumnText("mysql", "item_json")} FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? ORDER BY id_num IS NULL, id_num, sort_seq${inTx ? " FOR UPDATE" : ""}`, [listKey]));
178
178
  return rows.map((r) => parseItem(r.item_json, `for list ${listKey} (enumeration)`));
179
179
  },
180
- allocateId: async () => (inTx ? allocateOn(execOn(q), "tidb", listKey) : tx((c) => allocateOn(execOn(c), "tidb", listKey))),
180
+ allocateId: async () => (inTx ? allocateOn(execOn(q), "mysql", listKey) : tx((c) => allocateOn(execOn(c), "mysql", listKey))),
181
181
  ...(inTx
182
182
  ? {}
183
183
  : {
184
184
  mutate: (fn) => tx(async (c) => {
185
- await ensureMeta(execOn(c), "tidb", listKey);
185
+ await ensureMeta(execOn(c), "mysql", listKey);
186
186
  await c.query(`SELECT next_id FROM ${TASK_LIST_META_TABLE} WHERE list_key = ? FOR UPDATE`, [listKey]);
187
187
  return fn(build(c, true));
188
188
  }),
@@ -38,11 +38,15 @@ export declare function createTidbPool(config: ServiceConfig): Pool;
38
38
  * ── SCHEMA POLICY (clay 裁,2026-07-26) ─────────────────────────────────────────────────────────────
39
39
  * 本仓 schema 的唯一真源 = **这份代码**(此数组 + `pg-pool.ts` 的 PG 孪生)。口径:
40
40
  *
41
- * 1. **删库重建,不做增量。** 尚未上生产、单人本地,存量库一律 DROP DATABASE 重建;因此这里**不再保留
42
- * 任何 `ALTER TABLE` 增量 seam**。历史上的 21 条 ADD COLUMN / 3 条 CREATE INDEX / 1 条 MODIFY COLUMN
43
- * 已于 2026-07-26 全部折进下面的 `CREATE TABLE`(它们承载的语义已逐条搬成列上的行内注释 —— 那些注释
44
- * 是 pre-migration 行为契约的**唯一书面记录**,删注释 = 丢语义)。
45
- * 2. **不许再新增 `ALTER TABLE` seam。** 要改列/加列/改可空性 ⇒ 直接改下面的 `CREATE TABLE`,然后重建库。
41
+ * 1. **代码是唯一真源,不做增量。** 存量库缺列由部署者按拒启句里逐字打印的 `ALTER TABLE … ADD COLUMN …`
42
+ * 自己执行:列集对账与拒启句永不改库。进程自己对**已存在的表**发的 DDL 只有两种 —— 幂等加索引(`ensureIndex`,
43
+ * 只动索引、不碰列)与登记在册的一次性迁移(每条有窗、有登记行 `one-time-migrations.ts`);加列 / 改列 / 改名 /
44
+ * 删表只有后者做。因此这里**不再保留任何 `ALTER TABLE` 增量 seam**。历史上的 21 条
45
+ * ADD COLUMN / 3 条 CREATE INDEX / 1 条 MODIFY COLUMN 已于 2026-07-26 全部折进下面的 `CREATE TABLE`
46
+ * (它们承载的语义已逐条搬成列上的行内注释 —— 那些注释是 pre-migration 行为契约的**唯一书面记录**,
47
+ * 删注释 = 丢语义)。
48
+ * 2. **不许再新增 `ALTER TABLE` seam。** 要加列 ⇒ 直接改下面的 `CREATE TABLE`:新建库随它到货;带数据的存量库由列集对账拒启并逐列打印 `ALTER`,部署者自己执行(**不重建带数据的库**)。
49
+ * 改列类型 / 改可空性 / 改写存量字节 —— 列集对账只比列名、看不见这类漂移 —— 另走一次性迁移(带窗)或在升级步里写明手工语句。只有空库 / 开发机可以整库重建(销毁全部数据)。
46
50
  * (再开增量通道就等于把「代码是真源」变回「代码 + 一串按时间排的补丁才是真源」。)
47
51
  * 3. 生成的全集 SQL 基线导出到 `docs/schema/baseline-mysql.sql`(由本数组机器生成;它是**产物**,不是真源
48
52
  * —— 冲突时以本文件为准并重新生成)。
@@ -83,8 +87,9 @@ export declare const SCHEMA_INDEXES: readonly IndexSpec[];
83
87
  *
84
88
  * Per the SCHEMA POLICY on {@link SCHEMA_STATEMENTS} this is a pure CREATE run — there are NO additive
85
89
  * ALTER/CREATE-INDEX seams and no information_schema probes any more (2026-07-26): the schema contract is
86
- * drop-and-recreate, so every column / index / nullability lives in its `CREATE TABLE`. Do not reintroduce a
87
- * migration loop here; change the CREATE and rebuild the database.
90
+ * code-as-single-source, so every column / index / nullability lives in its `CREATE TABLE`; an existing database that lacks
91
+ * a column is refused at boot with the ALTER statements printed and its deployer runs them. Do not reintroduce a
92
+ * migration loop here; change the CREATE (only a throwaway dev database may be recreated, which destroys all of its data).
88
93
  *
89
94
  * Concurrency: callers run this concurrently — tests fan ALL integration suites out in parallel (each
90
95
  * beforeAll calls ensureSchema), and in production every replica calls it at boot (`--scale service=4`).
@@ -7,6 +7,7 @@ import { TIDB_ADOPTION_LOG_STATEMENTS } from "./adoption-log-sql.js";
7
7
  import { TIDB_PERMISSION_RULE_STATEMENTS } from "./permission-rule-store-sql.js";
8
8
  import { TIDB_RETENTION_STATEMENTS } from "./retention-store-sql.js";
9
9
  import { SESSION_META_INCARNATION_COLUMN } from "../session-incarnation.js";
10
+ import { sessionMetaPlacementColumnsDdl } from "./execution-placement-sql.js";
10
11
  import { SESSION_META_WORKSPACE_COLUMN } from "./session-workspace-cell.js";
11
12
  import { RUN_IDENTITY_COLUMNS } from "../identity-source.js";
12
13
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
@@ -63,11 +64,11 @@ export const SCHEMA_STATEMENTS = [
63
64
  leaf_id VARCHAR(64) NULL,
64
65
  leaf_seq BIGINT NOT NULL DEFAULT 0,
65
66
  -- title: ONE column, two writers, one law (src/session-row.ts): the user's write (PATCH /v1/sessions/:id →
66
- -- patchSessionMeta, unconditional; null = cleared back to the automatic title) and the auto-titler (cheap-model
67
+ -- writeSessionRowCells, unconditional unless the PATCH carries expectedRowTag; null = cleared back to the automatic title) and the auto-titler (cheap-model
67
68
  -- one-liner on a submit that finds it NULL; setTitleIfNull, fills NULL only) — a user-set title is never overwritten,
68
69
  -- a cleared one is filled again. Width = SESSION_TITLE_MAX_CHARS (the wire bound).
69
70
  title VARCHAR(${SESSION_TITLE_MAX_CHARS}) NULL,
70
- -- pinned: the user's pin (PATCH /v1/sessions/:id → patchSessionMeta); every list row carries it, false by default.
71
+ -- pinned: the user's pin (PATCH /v1/sessions/:id → writeSessionRowCells); every list row carries it, false by default.
71
72
  pinned BOOLEAN NOT NULL DEFAULT FALSE,
72
73
  -- instruction_source(S-690):本会话**上一次 run** 选中的项目指令文件 = 换源通告(instructions.source_changed)
73
74
  -- 的基线。NULL = 还没有任何 run 记过(首 run 只写不发);'' = 记过、那一次没有项目指令文件;其余 = 链成员相对路径
@@ -78,7 +79,7 @@ export const SCHEMA_STATEMENTS = [
78
79
  -- NULL = 还没有任何腿记过;唯一写者 = 腿的记录座(src/session-workspace.ts),列属主 = src/plugins/session-workspace-cell.ts
79
80
  -- (列定义、形读者、编解码;存量库缺这一列 ⇒ 起服列册对账拒启,拒句里给出 ALTER)。下一条腿的 TaskSpec.projectId 读它的 project.id。
80
81
  -- 服务端元数据:不进 wire 会话面、不进模型上下文;住在会话行上 ⇒ 随会话删除 / 留存清扫一起消失,fork 出的新行为 NULL。
81
- ${SESSION_META_WORKSPACE_COLUMN.tidb},
82
+ ${SESSION_META_WORKSPACE_COLUMN.mysql},
82
83
  -- #292 C18(core 5.48.0 design/323)subagent 转录持久化 —— placement 元组,**创建时一次落行、
83
84
  -- first-write 不可变**(register 的 ON DUPLICATE 臂永不重写这六列)。判别位 = placement_kind:
84
85
  -- 在场 ⇒ 该会话住在 placement 分区里(不上宿主枚举、claim 形 acquire 恒拒、release=真删除、
@@ -95,7 +96,12 @@ export const SCHEMA_STATEMENTS = [
95
96
  -- 无 id 创建 / fork / 导入新行),删除随行消失,同 id 重登记铸新的;'' = 没铸过,读口响亮拒。列定义的唯一属主 =
96
97
  -- src/session-incarnation.ts 的 SESSION_META_INCARNATION_COLUMN(存量库缺这一列 ⇒ 起服列册对账拒启,拒句里的 ALTER 由本建表语句生成)。
97
98
  -- 不上 wire、不进模型上下文、日志只打前 8 位。
98
- ${SESSION_META_INCARNATION_COLUMN.tidb},
99
+ ${SESSION_META_INCARNATION_COLUMN.mysql},
100
+ -- 执行放置:根会话行上**这一位住客**的一格 —— execution_role('writer' | 'mirror' | NULL = absent)、execution_lane、device 车道的设备与绑定属主、
101
+ -- 放下时刻、换绑序号;写只按住客谓词 (session_id, incarnation, owner) ∧ placement_kind IS NULL(换了住客 = 这一格不在)。fork_key = 建这一行的那次
102
+ -- fork 请求的键(只由 fork 创建点写,此后每个写携带)。规则在 src/execution-placement.ts,列定义与语句的唯一属主 = src/plugins/execution-placement-sql.ts
103
+ -- (存量库缺这些列 ⇒ 起服列册对账拒启;加列只由一次性迁移做)。
104
+ ${sessionMetaPlacementColumnsDdl("mysql")},
99
105
  PRIMARY KEY (session_id),
100
106
  KEY idx_session_meta_owner (owner),
101
107
  -- listPlaced 的检索腿(kind + 租户 + 年龄升序);普通会话这三列全 NULL ⇒ 不占索引选择性。
@@ -167,7 +173,7 @@ export const SCHEMA_STATEMENTS = [
167
173
  -- (rows from before these columns, unauthenticated-write acceptance, a run someone else accepted on the owner's behalf) ⇒ not
168
174
  -- qualified. Column definitions' single owner = src/identity-source.ts RUN_IDENTITY_COLUMNS (the boot column probe derives its
169
175
  -- ALTER suggestion from this statement). \`source\` above is attribution only, never an authentication source.
170
- ${RUN_IDENTITY_COLUMNS.tidb},
176
+ ${RUN_IDENTITY_COLUMNS.mysql},
171
177
  -- objective_preview: the list's "what is this task" glance — redacted + truncated at write.
172
178
  objective_preview VARCHAR(160) NULL,
173
179
  -- cancel_requested: durable cross-replica CANCEL flag. The running instance's heartbeat tick honors it
@@ -302,7 +308,7 @@ export const SCHEMA_STATEMENTS = [
302
308
  -- approval_card(core 7.55.0 #1468 P1 阶段二):门上的卡词**照存**(core checkpointIndexFacts(cp).approvalCard:TOOL_APPROVAL_CARDS 的词,或本引擎不认的串 ——
303
309
  -- 留着,更晚的引擎读得对);非审批门 / 无卡 / 非串 = NULL。写口与一次性迁移 M29 的存量回填同一只派生(plugins/checkpoint-index-columns.ts)。
304
310
  -- 读者:到期 facet 的生成谓词(由 core CARD_FACET_OVERRIDES + PAUSE_REGISTRY 生成;**不存 facet 本身** —— 存一份会在 core 改表那天陈旧)、运维队列行的卡。
305
- -- 本仓不发 ALTER:存量库缺这一列 ⇒ 起服列册对账拒启并给 ADD COLUMN 句(持久数据:表里是在飞的停驻,不删表重建)。
311
+ -- 本仓不发 ALTER:存量库缺这一列 ⇒ 起服列册对账拒启并给 ADD COLUMN 句(持久数据:表里是在飞的停驻,只加列)。
306
312
  -- 列型 TEXT 而不是定长 VARCHAR:引擎照存集外词,长度不由本仓定(191 字的集外词照存)。TEXT 自己的容量是 65,535 **字节**(plugins/checkpoint-index-columns.ts 的
307
313
  -- APPROVAL_CARD_COLUMN_MAX_BYTES):引擎铸的卡词是闭集短词,超出只会是手改 / 外来写者的存量行 —— M29 回填对这样的行计数 + 点名跳过,不写、不挡起服。
308
314
  approval_card TEXT NULL,
@@ -457,7 +463,7 @@ export const SCHEMA_STATEMENTS = [
457
463
  principal VARCHAR(190) NULL,
458
464
  -- 会话规则(三只桶,引擎的规则条目文法)。列定义唯一属主 = src/plugins/session-rules-column.ts 的 SESSION_RULES_COLUMN(一列一文法:
459
465
  -- 7.128.x 及以前这一列叫 rules、按整串工具名读;存量库由一次性迁移 M17 改名,带退役命令桶的行原样留着、读回判坏并逐行点名,见那份文件与 one-time-migrations.ts)。
460
- ${SESSION_RULES_COLUMN.tidb},
466
+ ${SESSION_RULES_COLUMN.mysql},
461
467
  rev BIGINT NOT NULL,
462
468
  -- gen (core 7.25.0 StoredSessionRules.gen, S-529 二期):这一行的**血统**。rev 只在一条血统内计数,
463
469
  -- deleteBySession 终结血统并把下一代的号留在 session_policy_rev_mark.gen 上,于是活规则读者能把
@@ -9,8 +9,8 @@ import { jsonColumnText } from "./sql-json-column.js";
9
9
  import { incarnationFromCell } from "../session-incarnation.js";
10
10
  const SESSION_EVENT_BY_TYPE_HINT = "/*+ USE_INDEX(session_event, idx_session_event_type) */";
11
11
  export const SESSION_EVENT_LATEST_BY_TYPE_SQL = {
12
- floor: `SELECT ${SESSION_EVENT_BY_TYPE_HINT} ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? AND type = 'compaction' ORDER BY seq DESC LIMIT 1`,
13
- epochAnchor: `SELECT ${SESSION_EVENT_BY_TYPE_HINT} seq, ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? AND seq < ? AND type IN ('prompt_epoch','compaction') ORDER BY seq DESC LIMIT 100`,
12
+ floor: `SELECT ${SESSION_EVENT_BY_TYPE_HINT} ${jsonColumnText("mysql", "payload")} FROM session_event WHERE session_id = ? AND type = 'compaction' ORDER BY seq DESC LIMIT 1`,
13
+ epochAnchor: `SELECT ${SESSION_EVENT_BY_TYPE_HINT} seq, ${jsonColumnText("mysql", "payload")} FROM session_event WHERE session_id = ? AND seq < ? AND type IN ('prompt_epoch','compaction') ORDER BY seq DESC LIMIT 100`,
14
14
  };
15
15
  const isDupKey = isMysqlDupKeyError;
16
16
  export class TiDBSessionStorage extends BaseSessionStorage {
@@ -35,7 +35,7 @@ export class TiDBSessionStorage extends BaseSessionStorage {
35
35
  if (!meta)
36
36
  return null;
37
37
  const { floorEntryId, floorSeq } = await TiDBSessionStorage.computeFloor(pool, sessionId);
38
- const [eventRows] = await pool.query(`SELECT seq, ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? AND seq >= ? ORDER BY seq ASC`, [sessionId, floorSeq]);
38
+ const [eventRows] = await pool.query(`SELECT seq, ${jsonColumnText("mysql", "payload")} FROM session_event WHERE session_id = ? AND seq >= ? ORDER BY seq ASC`, [sessionId, floorSeq]);
39
39
  const entries = eventRows.map((r) => decodeSessionEventPayload(r.payload, sessionId, r.seq));
40
40
  const placement = placementFromRow(meta);
41
41
  const metadata = { id: sessionId, createdAt: toIso(meta.created_at), incarnation: incarnationFromCell(sessionId, meta.incarnation), ...(placement !== undefined ? { placement } : {}) };
@@ -58,7 +58,7 @@ export class TiDBSessionStorage extends BaseSessionStorage {
58
58
  const inMem = await super.getEntry(id);
59
59
  if (inMem)
60
60
  return inMem;
61
- const [rows] = await this.pool.query(`SELECT ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? AND entry_id = ?`, [this.sessionId, id]);
61
+ const [rows] = await this.pool.query(`SELECT ${jsonColumnText("mysql", "payload")} FROM session_event WHERE session_id = ? AND entry_id = ?`, [this.sessionId, id]);
62
62
  if (rows[0] === undefined)
63
63
  return undefined;
64
64
  return decodeSessionEventPayload(rows[0].payload, this.sessionId, id);
@@ -15,12 +15,13 @@ import type { Pool } from "mysql2/promise";
15
15
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
16
16
  import type { SessionWorkspace } from "../session-workspace.js";
17
17
  import type { RetentionDeclaration } from "@sema-agent/core";
18
- import { type SessionFace } from "../session-incarnation.js";
19
18
  import type { SessionSummary } from "./store-contracts.js";
20
- import type { SessionRowPatch } from "../session-row.js";
19
+ import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
21
20
  import { type OrphanOwnerLog } from "../delegated-session-owner.js";
22
21
  import { type PlacementAcquireOpts } from "./session-placement.js";
23
22
  import { type StagingHandle } from "../session-sync-kernel.js";
23
+ import { type ForkTarget, type ForkToOutcome } from "./session-fork.js";
24
+ import { type ExecutionPlacementSeat } from "../execution-placement.js";
24
25
  export declare class TiDBSessionStore implements SessionStore {
25
26
  private readonly pool;
26
27
  /** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法逐字见
@@ -38,6 +39,13 @@ export declare class TiDBSessionStore implements SessionStore {
38
39
  private readonly pending;
39
40
  /** 委派子代孤行那一行 warn 的去处(`delegated-session-owner.ts`;装配给部署的 logger,缺席 ⇒ 缺省 logger,不静默)。 */
40
41
  private readonly log;
42
+ /**
43
+ * 执行席 —— 根会话行上这一位住客的那一格的唯一读口与 place / markMirror 两只写口(规则在 `src/execution-placement.ts`;
44
+ * 实现与语句的唯一属主 = `execution-placement-sql.ts`,两孪生同一只实现,本店只交自己的池)。
45
+ */
46
+ readonly executionPlacement: ExecutionPlacementSeat;
47
+ /** 本店的池包成的 SqlDriver:执行席与会话行格写原语的事务都经它借连接(连接初始化钉住事务读语义,`sql-driver.ts`)。 */
48
+ private readonly db;
41
49
  constructor(pool: Pool, opts?: {
42
50
  readonly log?: OrphanOwnerLog;
43
51
  readonly retention?: RetentionDeclaration;
@@ -83,7 +91,7 @@ export declare class TiDBSessionStore implements SessionStore {
83
91
  }): Promise<PlacedSessionRow[]>;
84
92
  /**
85
93
  * core `SessionStore.headOf` —— 这一行的属主、住客、叶子,**一条 PK SELECT、一行**(两次独立读在删除 / 重登记的缝里能拼出混世代组合;
86
- * 单行单读天然一致)。`undefined` = 无此会话。本店按 id 问这一行的**唯一**读口:授权口的属主、parked 两读的住客、SSE 探针的叶子、
94
+ * 单行单读天然一致)。`undefined` = 无此会话。本店按 id 问这一行的**唯一**读口:授权口的属主、k8s 工作区空间的住客、SSE 探针的叶子、
87
95
  * resume-at 的叶子都是它的投影(此前的 `ownerOf` / `sessionFaceOf` / `incarnationOf` / `getLeafId` / `getHead` 五只读全部并进来;规则见
88
96
  * `session-incarnation.ts` 头注)。化身那一格经 `headFromRow` 的 `absent` 读法(空 ⇒ 住客未知);要一位活住客的读者经 `sessionFaceOfHead` 响亮拒空。
89
97
  * 只读、不取会话、不上锁、绕过暖缓存 —— 跑到一半读也安全(写的安全是追加时的 CAS)。
@@ -135,21 +143,24 @@ export declare class TiDBSessionStore implements SessionStore {
135
143
  * row of the list face (no such session / placed). The run-preview subqueries are scoped to the same id (an index read on
136
144
  * `task_run.session_id`, not the whole-ledger aggregate the list pays). */
137
145
  sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
138
- /** The session row's writable bits — the ONLY writer of a user-set title / pin (writer order: `src/session-row.ts`):
139
- * `title` unconditional (a string = the user's title — the auto-titler's {@link setTitleIfNull} fills NULL only, so it always
140
- * wins; `null` = cleared back to the automatic title, which the auto-titler fills again on a later submit), `pinned` as given.
141
- * Only a row of the list face takes it (`placement_kind IS NULL`), and only the occupant the caller verified (`incarnation = ?`
142
- * from `face` — a session deleted and re-registered by anyone after the caller's gate is another occupant: zero rows updated).
143
- * The read-back carries the same condition, so the answer is that occupant's list row or `undefined` (the decision does not
144
- * read `affectedRows`: an UPDATE that wrote identical values and one that matched nothing differ there by driver flags, the
145
- * conditioned read-back does not). `updated_at` is NOT touched (a rename is not activity — the list's newest-first order stays put). */
146
- patchSessionMeta(sessionId: string, patch: SessionRowPatch, face: SessionFace): Promise<SessionSummary | undefined>;
146
+ /** {@link sessionSummaryOf} 那一读 + 读到它时行上的住客(写原语的应答行要复核住客,`settleSessionRowWrite`)。 */
147
+ private listRowOf;
148
+ /**
149
+ * 会话行格写原语 —— 用户标题 / 置顶的唯一写口(规则与判序:`src/session-row.ts` 头注「写原语」;事务与语句:`session-row-sql.ts`,PG 孪生同一只实现)。
150
+ * 一只经 `SqlDriver.connect()` 的事务锁住根行、交 core 判官判行期望(属主 + 化身)→ 写者身份(客户端写在镜像上 ⇒ `mirror`)→ 标签 → 写;
151
+ * 应答行在提交 / 回滚之后另读(列表行同一只投影,含新 `rowTag`)、复核仍是判过的那一位住客。只写列表面上的行(委派分区行 ⇒ `absent`);
152
+ * `updated_at` 不动(改名不是活动)。重载按入口分:`replay` 的结局只有 `written` / `absent`。
153
+ */
154
+ writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionSummary>>;
155
+ writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionSummary>>;
147
156
  /**
148
157
  * The list projection's ONE SELECT + row mapper ({@link listSessions} and {@link sessionSummaryOf} both read through it).
149
158
  * `runSessionId` scopes the two run-preview subqueries to one session (the single-row read); the list leaves them whole.
150
159
  * Placeholder order follows the text: subquery params, then `where` params, then the LIMIT.
160
+ * `sm.incarnation` is read for the row tag only (`rowTag`, `sessionRowTagKey` — present-iff the row has a token) and the answer row's occupant
161
+ * check; it never enters the list row itself.
151
162
  */
152
- private selectSummaries;
163
+ private selectListRows;
153
164
  /** Cheap pre-LLM probe: "none" = session_meta row not yet REGISTERED (createRun fires
154
165
  * before the Runner's session acquire — the titler retries), "untitled" / "titled" as read. */
155
166
  probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
@@ -193,6 +204,17 @@ export declare class TiDBSessionStore implements SessionStore {
193
204
  * bypassed — and re-seq it dense from 0 into the new session, ids/parents/payload byte-verbatim.
194
205
  */
195
206
  fork(sourceId: string, owner?: string | null): Promise<string | null>;
207
+ /**
208
+ * **fork 续写的店契约**(三店同一只私有体;core `fork` 是它的投影):把源会话的整份历史拷进调用方给的目标 id。
209
+ * 1. **原子目标创建**:事务首句 `INSERT INTO session_meta (…, fork_key) VALUES (:target, …)`,主键即排他(meta 先,随后拷事件、COMMIT);
210
+ * 2. **赢家分类**:撞上已在的目标 ⇒ 输方整笔回滚,在新视图里读目标、按属主与 fork 键分类({@link classifyForkTarget});读不到(赢方在分类前回滚或被删)
211
+ * ⇒ 整笔重跑一次,再撞而仍读不到 ⇒ {@link ForkTargetUnsettledError}(不猜);
212
+ * 3. 单事务 ⇒ 无中间态;`fork_key` 只由这里写(此后每个写携带:UPDATE 只碰点名列,整段换装经 `carriedRowCells` 带回)。
213
+ * 源不在 ⇒ `null`(先于目标判:源被删之后的同键重试答「源不在」)。新行 = 新住客(化身新铸),执行格 absent(INSERT 列名显式)。
214
+ */
215
+ forkTo(sourceId: string, owner: string | null, target: ForkTarget): Promise<ForkToOutcome | null>;
216
+ /** {@link forkTo} 的一次尝试:`"unsettled"` = 撞上目标而回滚之后读不到它(交给外层重跑一次)。 */
217
+ private forkToOnce;
196
218
  /**
197
219
  * 2c session-sync — EXPORT a session's full durable entry log for cross-backend migration. This is the
198
220
  * READ half of {@link fork}, surfaced as the core `SessionRepo.exportEntries` seam: the WHOLE `session_event` log,
@@ -239,29 +261,38 @@ export declare class TiDBSessionStore implements SessionStore {
239
261
  * exactly-one-root / leaf-resolvable / structural refs present) — we persist its VERBATIM output and do NOT
240
262
  * re-implement any of those checks in SQL ([[core-service-boundary]]; the same discipline fork's hand-SQL
241
263
  * was warned about). A violation throws `SessionError("invalid_session")` and NOTHING is written.
242
- * The write itself mirrors fork: re-seq dense from 0, entry_id/parent_id/payload/ts verbatim, in ONE txn with
243
- * `session_meta` LAST as the commit point (a mid-import crash leaves orphan events with no meta = invisible to
244
- * wake, never a half-session). The leaf is the replayed leaf (core's `leafIdAfterEntry`), matching core's import.
264
+ * The write: re-seq dense from 0, entry_id/parent_id/payload/ts verbatim, in ONE txn (COMMIT is the sole commit point), written
265
+ * **lock-or-create, meta first** (读写序律,`execution-placement-sql.ts` 头注):the `session_meta` row is occupied first (an existing row ⇒
266
+ * refused before anything else about it is read), locked, the run claim on the root is read on THIS connection (a claim ⇒
267
+ * `SessionActiveRunError`, nothing written), then the entries. The leaf is the replayed leaf (core's `leafIdAfterEntry`), matching core's import.
245
268
  */
246
269
  importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
247
270
  /**
248
271
  * 2c session-sync (§7/§8) — IDEMPOTENT REPLACE: purge any existing conversation log for `sessionId` then re-import
249
- * `entries` verbatim under `owner`, ALL in ONE txn. Unlike {@link importEntries} (a plain INSERT that crashes on a
250
- * duplicate PK when the session already exists at the destination), replace is safe to run OVER an existing session
272
+ * `entries` verbatim under `owner`, ALL in ONE txn. Unlike {@link importEntries} (fresh-only: an existing session at the
273
+ * destination is refused), replace is safe to run OVER an existing session
251
274
  * — the §7 fast-forward path and a §8 retry-to-completion both land on it. The validate gate runs FIRST (fail-closed,
252
- * before any write), then DELETE events + the OWNER-GUARDED meta delete (defense-in-depth, mirrors {@link deleteSession}
253
- * — a wrong-owner caller can never replace another tenant's session), then the SAME shared insert body importEntries
254
- * uses (so there is ONE source of the insert logic, no copy-paste drift). session_meta is still written LAST = commit
255
- * point, so a mid-replace crash (post-delete, pre-insert) leaves orphan-free state (no meta = invisible to wake).
275
+ * before any write), then the OWNER-GUARDED meta delete (defense-in-depth, mirrors {@link deleteSession}
276
+ * — a wrong-owner caller can never replace another tenant's session: its delete matches nothing and the re-insert collides on the PK),
277
+ * then the SAME shared write body importEntries uses (session_meta first, the in-txn claim read, then the events swapped) — ONE source of
278
+ * the write logic, no copy-paste drift. COMMIT is the sole commit point: a mid-replace crash rolls the whole swap back.
256
279
  */
257
280
  replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
258
281
  /**
259
- * Shared insert half of {@link importEntries} / {@link replaceEntries} (ONE source of the insert logic, no drift):
260
- * write the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim), THEN session_meta LAST as
261
- * the commit point. Runs inside the caller's open transaction (`conn`) so the caller owns begin/commit/rollback.
282
+ * Shared write half of {@link importEntries} / {@link replaceEntries} (ONE source of the write logic, no drift), in the global lock
283
+ * order (根 `session_meta` 先于 `task_*` 与 `session_event`):
284
+ * ① session_meta FIRST — `create` occupies the row (`ON DUPLICATE KEY UPDATE session_id = session_id`: an existing row is left as it is),
285
+ * `replace` is a plain INSERT after the caller's owner-guarded delete (a foreign row collides on the PK — the existing refusal);
286
+ * owner = the RE-STAMPED authenticated principal (never the bundle's); 化身与 fork 键按 `carriedRowCells`(或新铸),执行格 = mirror
287
+ * —— 导入落下的行是「写者在别处」(列名与值同出 `importedRowCells`);
288
+ * ② `create`: lock the row and judge it is the one this call occupied (化身 = 本次铸的)— a row that existed before ⇒ refused before
289
+ * anything else about it is read (no claim read on someone else's row);
290
+ * ③ 读写序律:the run claim on the root, read on THIS connection after the row lock (`lockRootAndReadClaim`) — a claim ⇒ `SessionActiveRunError`;
291
+ * ④ the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim); `replace` drops the old log first.
292
+ * Runs inside the caller's open transaction (`conn`) so the caller owns begin/commit/rollback (COMMIT is the sole commit point).
262
293
  * `validated` is the OUTPUT of {@link validateEntriesForImport} (the caller runs the gate before opening the txn).
263
294
  */
264
- private insertValidatedLog;
295
+ private writeImportedLog;
265
296
  /**
266
297
  * 2c session-sync P1d-β (PUSH streaming, §15) — begin a STAGED import into `realSessionId` under the shadow id
267
298
  * `${realSessionId}#stg-${token}`. The route mints the uuidv7 `token` (the store never calls uuidv7/Date.now itself