@sema-agent/server 7.146.0 → 7.147.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 (398) hide show
  1. package/MIGRATION.md +384 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +170 -66
  5. package/deploy/facts/lanes.json +7 -0
  6. package/deploy/sema-up/README.md +2 -2
  7. package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
  9. package/deploy/sema-up/chart/templates/server.yaml +5 -4
  10. package/deploy/sema-up/chart/values.yaml +1 -1
  11. package/deploy/sema-up/cluster-up.sh +2 -1
  12. package/deploy/sema-up/lib/flagshape.sh +4 -0
  13. package/deploy/sema-up/lib/lanes.json +2 -5
  14. package/deploy/sema-up/sema-up.sh +12 -9
  15. package/deploy/sema-up/stack/compose.yaml +5 -4
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -3
  17. package/dist/approval-card.d.ts +4 -4
  18. package/dist/approval.d.ts +3 -3
  19. package/dist/audit.js +2 -2
  20. package/dist/bench/s1/live-deps.js +3 -1
  21. package/dist/boot/crash-last.js +2 -1
  22. package/dist/boot/device-lane.js +5 -5
  23. package/dist/boot/engine-lease.d.ts +12 -11
  24. package/dist/boot/execution-env.d.ts +2 -1
  25. package/dist/boot/execution-env.js +45 -33
  26. package/dist/boot/execution-lane.d.ts +37 -9
  27. package/dist/boot/execution-lane.js +74 -20
  28. package/dist/boot/exit-boundary.d.ts +1 -1
  29. package/dist/boot/exit-last.js +2 -1
  30. package/dist/boot/governance-seams.d.ts +2 -2
  31. package/dist/boot/leader.js +1 -1
  32. package/dist/boot/memory-boundary.d.ts +11 -11
  33. package/dist/boot/memory-boundary.js +3 -3
  34. package/dist/boot/memory-consolidation.d.ts +13 -24
  35. package/dist/boot/memory-consolidation.js +0 -13
  36. package/dist/boot/org-memory.js +3 -2
  37. package/dist/boot/parked-revive-gate.d.ts +1 -1
  38. package/dist/boot/parked-revive-gate.js +1 -1
  39. package/dist/boot/reapers.js +1 -1
  40. package/dist/boot/resolve-spec.js +91 -111
  41. package/dist/boot/retention-lane.d.ts +4 -42
  42. package/dist/boot/session-faces.d.ts +1 -0
  43. package/dist/boot/session-faces.js +8 -2
  44. package/dist/boot/stage-01-config.d.ts +1 -1
  45. package/dist/boot/stage-01-config.js +6 -2
  46. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
  47. package/dist/boot/stage-02-web-admin-pull.js +10 -8
  48. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  49. package/dist/boot/stage-04-stores.d.ts +1 -1
  50. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  51. package/dist/boot/stage-05-execution-env.js +3 -3
  52. package/dist/boot/stage-06-runners.d.ts +3 -3
  53. package/dist/boot/stage-06-runners.js +1 -3
  54. package/dist/boot/stage-07-capability-layer.d.ts +3 -3
  55. package/dist/boot/stage-07-capability-layer.js +4 -9
  56. package/dist/boot/stage-08-reapers.d.ts +3 -3
  57. package/dist/boot/stage-09-leader.d.ts +4 -3
  58. package/dist/boot/stage-10-http-server.d.ts +4 -3
  59. package/dist/boot/stage-10-http-server.js +2 -1
  60. package/dist/boot/stores.d.ts +9 -0
  61. package/dist/boot/stores.js +18 -7
  62. package/dist/boot/web-admin.js +3 -3
  63. package/dist/capabilities/center-plugins.d.ts +2 -0
  64. package/dist/capabilities/center-plugins.js +6 -2
  65. package/dist/capabilities/composite-members.d.ts +1 -0
  66. package/dist/capabilities/composite-members.js +1 -0
  67. package/dist/capabilities/execution-lane.d.ts +7 -7
  68. package/dist/capabilities/execution-lane.js +2 -2
  69. package/dist/capabilities/hands-lane.d.ts +1 -1
  70. package/dist/capabilities/scenarios.d.ts +32 -18
  71. package/dist/capabilities/scenarios.js +13 -9
  72. package/dist/capabilities/settings-contract.d.ts +29 -0
  73. package/dist/capabilities/settings-contract.js +14 -0
  74. package/dist/capabilities/skills.d.ts +5 -5
  75. package/dist/capability-facts.d.ts +38 -0
  76. package/dist/capability-facts.js +34 -1
  77. package/dist/center-credential-renewer.js +3 -2
  78. package/dist/config-catalog.js +6 -5
  79. package/dist/config-invariants.d.ts +18 -3
  80. package/dist/config-invariants.js +36 -19
  81. package/dist/config-lkg.d.ts +1 -1
  82. package/dist/config-lkg.js +5 -6
  83. package/dist/config-provider.js +2 -0
  84. package/dist/config-types.d.ts +190 -186
  85. package/dist/config.d.ts +68 -5
  86. package/dist/config.js +80 -54
  87. package/dist/degenerate-instrument.js +1 -1
  88. package/dist/deploy-facts.js +3 -2
  89. package/dist/deployment-shape.d.ts +21 -12
  90. package/dist/deployment-shape.js +24 -6
  91. package/dist/env-facts.d.ts +7 -5
  92. package/dist/env-facts.js +10 -6
  93. package/dist/execution-config-root.d.ts +5 -5
  94. package/dist/execution-config-root.js +5 -5
  95. package/dist/execution-env-purpose.d.ts +14 -0
  96. package/dist/execution-env-purpose.js +29 -0
  97. package/dist/execution-lane-caps.d.ts +49 -86
  98. package/dist/execution-lane-caps.js +52 -62
  99. package/dist/execution-placement.d.ts +18 -2
  100. package/dist/execution-placement.js +19 -0
  101. package/dist/file-history-disclosure.d.ts +2 -2
  102. package/dist/fleet/drain-entry.d.ts +17 -10
  103. package/dist/fleet/drain-entry.js +1 -1
  104. package/dist/fleet/serving-lease.js +3 -4
  105. package/dist/history-anchor.d.ts +22 -0
  106. package/dist/history-anchor.js +14 -0
  107. package/dist/hooks/command-spawn.d.ts +1 -4
  108. package/dist/hooks/command-spawn.js +0 -11
  109. package/dist/hooks/hook-runner.d.ts +52 -8
  110. package/dist/hooks/hook-runner.js +160 -36
  111. package/dist/http/approval-sweeps.d.ts +2 -5
  112. package/dist/http/approval-sweeps.js +3 -2
  113. package/dist/http/checkpoint-ctx.d.ts +17 -0
  114. package/dist/http/checkpoint-ctx.js +6 -0
  115. package/dist/http/claim-latency.d.ts +10 -0
  116. package/dist/http/claim-latency.js +12 -0
  117. package/dist/http/dispatch.js +1 -1
  118. package/dist/http/durable-run-leg.js +2 -1
  119. package/dist/http/execution-context.d.ts +3 -1
  120. package/dist/http/execution-context.js +4 -0
  121. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  122. package/dist/http/memory-compliance-failure-reply.js +3 -0
  123. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  124. package/dist/http/memory-store-refusal-reply.js +3 -0
  125. package/dist/http/owner-access.d.ts +11 -0
  126. package/dist/http/owner-access.js +7 -4
  127. package/dist/http/principal-gate.d.ts +3 -14
  128. package/dist/http/principal-gate.js +0 -13
  129. package/dist/http/request-guards.d.ts +1 -1
  130. package/dist/http/resume-legs.d.ts +7 -10
  131. package/dist/http/resume-legs.js +79 -50
  132. package/dist/http/rewind-files-reply.d.ts +34 -0
  133. package/dist/http/rewind-files-reply.js +36 -0
  134. package/dist/http/route-ctx.d.ts +32 -16
  135. package/dist/http/route-table.js +2 -0
  136. package/dist/http/routes/approvals-assistant.js +5 -25
  137. package/dist/http/routes/capabilities.js +8 -2
  138. package/dist/http/routes/devices.js +1 -1
  139. package/dist/http/routes/memory-origin.d.ts +1 -1
  140. package/dist/http/routes/notify-wake.js +2 -1
  141. package/dist/http/routes/projects.d.ts +70 -0
  142. package/dist/http/routes/projects.js +86 -0
  143. package/dist/http/routes/rules.js +30 -2
  144. package/dist/http/routes/runs.js +8 -8
  145. package/dist/http/routes/session-sync.js +27 -10
  146. package/dist/http/routes/sessions.d.ts +7 -1
  147. package/dist/http/routes/sessions.js +104 -2
  148. package/dist/http/routes/tasks.js +9 -3
  149. package/dist/http/server.d.ts +24 -10
  150. package/dist/http/server.js +17 -6
  151. package/dist/http/stored-body-replay.d.ts +5 -3
  152. package/dist/http/stored-body-replay.js +10 -1
  153. package/dist/http/wire-types.d.ts +19 -10
  154. package/dist/index.d.ts +2 -2
  155. package/dist/index.js +1 -1
  156. package/dist/instruction-source-notice.d.ts +1 -1
  157. package/dist/memory-embedder-reembed.d.ts +38 -0
  158. package/dist/memory-embedder-reembed.js +20 -0
  159. package/dist/memory-incident-sink.d.ts +0 -28
  160. package/dist/memory-incident-sink.js +5 -0
  161. package/dist/memory-scope.d.ts +2 -2
  162. package/dist/memory-scope.js +1 -1
  163. package/dist/memory-store-refusal.d.ts +17 -4
  164. package/dist/memory-store-refusal.js +1 -1
  165. package/dist/memory-sync-client.d.ts +2 -0
  166. package/dist/memory-sync-client.js +13 -11
  167. package/dist/memory-sync.js +1 -1
  168. package/dist/model-compat.d.ts +35 -21
  169. package/dist/model-compat.js +49 -21
  170. package/dist/model-entry-refusal.d.ts +11 -1
  171. package/dist/model-entry-refusal.js +7 -0
  172. package/dist/namespaced-rule-name.d.ts +18 -1
  173. package/dist/namespaced-rule-name.js +12 -0
  174. package/dist/observability/fail-open.d.ts +17 -9
  175. package/dist/observability/fail-open.js +17 -9
  176. package/dist/observability/metrics.d.ts +2 -0
  177. package/dist/observability/metrics.js +2 -0
  178. package/dist/observability/secret-env-scrub.d.ts +30 -4
  179. package/dist/observability/secret-env-scrub.js +15 -3
  180. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  181. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  182. package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
  183. package/dist/orchestration/workflow-completion-inbox.js +63 -45
  184. package/dist/org-memory-admission.d.ts +5 -2
  185. package/dist/org-memory-admission.js +25 -10
  186. package/dist/parked-decide.d.ts +33 -26
  187. package/dist/parked-decide.js +36 -44
  188. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  189. package/dist/plugins/approval-ask-store-file.js +37 -4
  190. package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
  191. package/dist/plugins/approval-ask-store-memory.js +11 -2
  192. package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
  193. package/dist/plugins/approval-ask-store-sql.js +3 -1
  194. package/dist/plugins/blob-backend.d.ts +38 -195
  195. package/dist/plugins/blob-backend.js +33 -229
  196. package/dist/plugins/caching-session-store.d.ts +9 -4
  197. package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
  198. package/dist/plugins/checkpoint-index-columns.js +3 -2
  199. package/dist/plugins/checkpoint-store-face.d.ts +1 -1
  200. package/dist/plugins/device-store-sql.d.ts +2 -2
  201. package/dist/plugins/device-store-sql.js +5 -10
  202. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  203. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  204. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  205. package/dist/plugins/execution-placement-sql.js +94 -54
  206. package/dist/plugins/file-run-store.d.ts +26 -12
  207. package/dist/plugins/file-run-store.js +63 -63
  208. package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
  209. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  210. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  211. package/dist/plugins/inbox-payload-column.js +13 -0
  212. package/dist/plugins/local-checkpoint-store.d.ts +5 -0
  213. package/dist/plugins/local-checkpoint-store.js +11 -7
  214. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  215. package/dist/plugins/local-data-dir-modes-m39.js +182 -0
  216. package/dist/plugins/local-engine-high-water.d.ts +11 -0
  217. package/dist/plugins/local-engine-high-water.js +59 -0
  218. package/dist/plugins/local-session-store.d.ts +91 -25
  219. package/dist/plugins/local-session-store.js +124 -45
  220. package/dist/plugins/local-task-attachment-store.d.ts +2 -0
  221. package/dist/plugins/local-task-attachment-store.js +10 -11
  222. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  223. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  224. package/dist/plugins/memory-embedder.d.ts +1 -1
  225. package/dist/plugins/memory-embedder.js +2 -2
  226. package/dist/plugins/memory-engine-pg.d.ts +4 -3
  227. package/dist/plugins/memory-engine-pg.js +4 -4
  228. package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
  229. package/dist/plugins/memory-engine-sql-apply.js +1 -1
  230. package/dist/plugins/memory-engine-tidb.d.ts +8 -5
  231. package/dist/plugins/memory-engine-tidb.js +7 -8
  232. package/dist/plugins/one-time-migrations.d.ts +145 -35
  233. package/dist/plugins/one-time-migrations.js +272 -120
  234. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  235. package/dist/plugins/owner-only-file-mode.js +141 -0
  236. package/dist/plugins/permission-rule-store-file.d.ts +8 -8
  237. package/dist/plugins/permission-rule-store-file.js +14 -2
  238. package/dist/plugins/pg-pool.d.ts +2 -1
  239. package/dist/plugins/pg-pool.js +11 -4
  240. package/dist/plugins/release-version.d.ts +20 -0
  241. package/dist/plugins/release-version.js +28 -0
  242. package/dist/plugins/remote-env-adb.d.ts +20 -4
  243. package/dist/plugins/remote-env-adb.js +39 -11
  244. package/dist/plugins/remote-env-e2b.d.ts +12 -3
  245. package/dist/plugins/remote-env-e2b.js +6 -4
  246. package/dist/plugins/remote-env-host.d.ts +9 -1
  247. package/dist/plugins/remote-env-host.js +6 -5
  248. package/dist/plugins/remote-env-k8s.d.ts +5 -2
  249. package/dist/plugins/remote-env-k8s.js +15 -4
  250. package/dist/plugins/remote-env-local-docker.d.ts +4 -1
  251. package/dist/plugins/remote-env-local-docker.js +3 -3
  252. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  253. package/dist/plugins/remote-env-ssh.js +26 -24
  254. package/dist/plugins/remote-shell.d.ts +59 -30
  255. package/dist/plugins/remote-shell.js +175 -62
  256. package/dist/plugins/retention-declaration.d.ts +37 -0
  257. package/dist/plugins/retention-declaration.js +9 -0
  258. package/dist/plugins/retention-deployment.d.ts +126 -0
  259. package/dist/plugins/retention-deployment.js +88 -0
  260. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  261. package/dist/plugins/retention-lane-store-sql.js +103 -1
  262. package/dist/plugins/retention-sql.d.ts +29 -0
  263. package/dist/plugins/retention-sql.js +8 -0
  264. package/dist/plugins/schema-consistency.d.ts +0 -6
  265. package/dist/plugins/schema-consistency.js +1 -2
  266. package/dist/plugins/schema-ensure-legs.d.ts +1 -1
  267. package/dist/plugins/schema-ensure-legs.js +11 -1
  268. package/dist/plugins/session-ext-table.d.ts +74 -0
  269. package/dist/plugins/session-ext-table.js +121 -0
  270. package/dist/plugins/session-fork.d.ts +1 -1
  271. package/dist/plugins/session-row-sql.d.ts +8 -7
  272. package/dist/plugins/session-row-sql.js +15 -7
  273. package/dist/plugins/session-store-sql.d.ts +131 -0
  274. package/dist/plugins/session-store-sql.js +298 -0
  275. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  276. package/dist/plugins/session-workspace-cell.js +2 -2
  277. package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
  278. package/dist/plugins/shared-memory-store-sql.js +13 -13
  279. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  280. package/dist/plugins/sql-driver.d.ts +23 -7
  281. package/dist/plugins/sql-driver.js +41 -8
  282. package/dist/plugins/sql-errors.d.ts +5 -2
  283. package/dist/plugins/sql-errors.js +4 -0
  284. package/dist/plugins/store-backend.d.ts +35 -8
  285. package/dist/plugins/store-backend.js +184 -62
  286. package/dist/plugins/store-contracts.d.ts +9 -2
  287. package/dist/plugins/store-contracts.js +1 -0
  288. package/dist/plugins/task-attachment-store.d.ts +6 -8
  289. package/dist/plugins/task-attachment-store.js +15 -10
  290. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  291. package/dist/plugins/text-column-capacity.d.ts +21 -0
  292. package/dist/plugins/text-column-capacity.js +5 -0
  293. package/dist/plugins/tidb-pool.d.ts +3 -2
  294. package/dist/plugins/tidb-pool.js +19 -31
  295. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  296. package/dist/read-face-posture.d.ts +6 -6
  297. package/dist/read-face-posture.js +2 -2
  298. package/dist/request-value-closure.d.ts +5 -5
  299. package/dist/request-value-closure.js +1 -1
  300. package/dist/rules-consent.d.ts +41 -13
  301. package/dist/rules-consent.js +101 -53
  302. package/dist/run-local.js +17 -13
  303. package/dist/runs.d.ts +48 -30
  304. package/dist/runs.js +26 -16
  305. package/dist/runtime-caps-resolver.d.ts +15 -8
  306. package/dist/runtime-caps-resolver.js +14 -10
  307. package/dist/sealed-key-layout.d.ts +2 -3
  308. package/dist/sealed-key-layout.js +0 -1
  309. package/dist/sealed-key.js +7 -5
  310. package/dist/security.d.ts +5 -3
  311. package/dist/sema-config-dir.js +2 -2
  312. package/dist/server-secret-env.d.ts +2 -0
  313. package/dist/service-version.d.ts +2 -1
  314. package/dist/service-version.js +2 -2
  315. package/dist/session-error-wire.d.ts +1 -1
  316. package/dist/session-incarnation.d.ts +8 -5
  317. package/dist/session-resident-background.d.ts +85 -32
  318. package/dist/session-resident-background.js +75 -7
  319. package/dist/session-row.d.ts +1 -1
  320. package/dist/session-sync-kernel.d.ts +1 -1
  321. package/dist/session-workspace.d.ts +11 -2
  322. package/dist/session-workspace.js +5 -0
  323. package/dist/sighup-idle.d.ts +1 -1
  324. package/dist/store-posture.d.ts +2 -1
  325. package/dist/stored-request-hooks-m20.d.ts +3 -2
  326. package/dist/stored-request-hooks-m20.js +19 -14
  327. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  328. package/dist/stores-sql/blob-index-sql.js +154 -0
  329. package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
  330. package/dist/stores-sql/checkpoint-store-sql.js +11 -27
  331. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  332. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  333. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +23 -76
  334. package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
  335. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
  336. package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
  337. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  338. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  339. package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
  340. package/dist/stores-sql/session-write-hooks.d.ts +59 -0
  341. package/dist/stores-sql/session-write-hooks.js +2 -0
  342. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
  343. package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
  344. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +32 -122
  345. package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
  346. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  347. package/dist/stores-sql/tool-result-store-sql.js +1 -1
  348. package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
  349. package/dist/stores-sql/windowed-session-storage.js +61 -0
  350. package/dist/task-a2a.d.ts +7 -1
  351. package/dist/task-a2a.js +15 -14
  352. package/dist/task-cwd.d.ts +11 -3
  353. package/dist/task-cwd.js +17 -1
  354. package/dist/task-mcp.d.ts +10 -2
  355. package/dist/task-mcp.js +13 -16
  356. package/dist/task-settings.d.ts +9 -4
  357. package/dist/task-settings.js +17 -6
  358. package/dist/tool-approval.d.ts +21 -8
  359. package/dist/tool-approval.js +10 -6
  360. package/dist/trace/core-keyset-guard.d.ts +9 -3
  361. package/dist/trace/engine-notice-wire.d.ts +1 -1
  362. package/dist/trace/engine-notice-wire.js +2 -0
  363. package/dist/trace/exit-seat.d.ts +23 -1
  364. package/dist/trace/exit-seat.js +3 -0
  365. package/dist/trace/project.d.ts +33 -16
  366. package/dist/trace/project.js +12 -9
  367. package/dist/trace/redact.d.ts +1 -1
  368. package/dist/trace/redact.js +61 -73
  369. package/dist/trace/self-minted-wire.d.ts +263 -10
  370. package/dist/trace/self-minted-wire.js +90 -2
  371. package/dist/trace/sema-provenance.d.ts +45 -41
  372. package/dist/trace/sema-provenance.js +81 -10
  373. package/dist/trace/task-notification-facets.d.ts +70 -2
  374. package/dist/trace/task-notification-facets.js +71 -1
  375. package/dist/trace/wire-projection-faces.d.ts +38 -2
  376. package/dist/trace/wire-projection-faces.js +26 -1
  377. package/dist/unread-request-members.d.ts +66 -0
  378. package/dist/unread-request-members.js +41 -0
  379. package/dist/user-input-ledger.d.ts +2 -2
  380. package/dist/web-admin-client/apply-effective.js +22 -26
  381. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  382. package/dist/web-admin-client/candidate-gate.js +53 -5
  383. package/dist/web-admin-client/center-request.d.ts +4 -1
  384. package/dist/web-admin-client/center-request.js +2 -1
  385. package/dist/web-admin-client/effective-keys.js +0 -1
  386. package/dist/web-admin-client/facade.d.ts +1 -1
  387. package/dist/web-admin-client/http-client.d.ts +1 -0
  388. package/dist/web-admin-client/http-client.js +17 -9
  389. package/dist/web-admin-client/restart-signal.js +5 -2
  390. package/dist/web-admin-client/skills-mcp.d.ts +20 -6
  391. package/dist/web-admin-client/skills-mcp.js +26 -21
  392. package/dist/web-admin-client/types.d.ts +34 -49
  393. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
  394. package/package.json +13 -12
  395. package/scripts/drain-check-local.mjs +2 -28
  396. package/scripts/drain-check.mysql.sql +2 -18
  397. package/scripts/drain-check.pg.sql +2 -18
  398. package/wire-changes/server@7.147.0.json +813 -0
@@ -1,92 +1,40 @@
1
1
  /**
2
- * **执行放置的 SQL 唯一模块** —— `session_meta` 上 `execution_*` 七列 + `fork_key` 一列的列定义、住客谓词、行 → 格解码、每一种转换的语句,
3
- * 与「写事务内先锁根行再读认领」那一读。规则在 `src/execution-placement.ts` 头注;本模块只说 SQL。
2
+ * **执行放置的 SQL 唯一模块** —— 执行放置七列 + `fork_key` 一列的列定义、行 → 格解码、每一种转换的语句,与「写事务内先锁根行再读认领」那一读。
3
+ * 规则在 `src/execution-placement.ts` 头注;本模块只说 SQL。
4
4
  *
5
- * ## 属主
6
- * · **列定义唯一属主**(两方言):中央 DDL(`tidb-pool.ts` 的 `CREATE TABLE session_meta`、`pg-session-storage.ts` 的 `PG_SESSION_SCHEMA`)内插
7
- * {@link sessionMetaPlacementColumnsDdl}(先例:`SESSION_META_INCARNATION_COLUMN`);起服列册对账(`schema-consistency.ts`)从同一份建表文本解析期望列集。
8
- * 存量库的加列只由一次性迁移做,`ensureSchema` 不 ALTER(SCHEMA POLICY)。
9
- * · **语句唯一属主**:会话店(执行席、导入写口)与设备店(首绑 / 换绑 / 绑定数)、一次性迁移(搬绑定 / 回填)的事务都借这里的语句,别处零手写。
10
- * 语句是方言无关的 `{ text, values }`(调用方用自己的连接跑:SqlDriver 的 `SqlTxConn`、node-pg 的客户端、mysql2 的连接都行)。
5
+ * ## 格住在部署旁表
6
+ * 八列住 `session_ext`(会话的部署格旁表,建表 / 外键 / 补行的唯一属主 = `session-ext-table.ts`;一会话一行,`ON DELETE CASCADE` 随 `session_meta` 行走),
7
+ * 不在引擎座表 `session_meta` 上(一张表一个 DDL 属主)。旁表行不在 = 八列全缺省(absent / 非键控 fork);写者在锁住根行之后按需补行。
11
8
  *
12
- * ## 住客谓词(写与 judged 复核的唯一定址)
13
- * `session_id = :root AND incarnation = :inc AND owner <=> :owner(PG:IS NOT DISTINCT FROM)AND placement_kind IS NULL` —— core 行期望判官 `sessionRowMatches`
14
- * (属主 + 化身;会话行格写原语在锁住的行上判的就是它,`session-row.ts`)的 SQL 拼法(同一化身的属主会变:local M10 认领、identical 重戳、SQL 导入提交点改属主)。
15
- * 0 行一律按住客复读分类(判官在 `execution-placement.ts`),永不对现住客重试。
9
+ * ## 属主
10
+ * · 列定义住旁表属主(`session-ext-table.ts` 的 `EXECUTION_PLACEMENT_COLUMNS`;一次性迁移 M32 / M41 读同一份)。
11
+ * · **语句唯一属主**:执行席、会话店的事务内钩(导入落 mirror)、设备店(首绑 / 换绑 / 绑定数)与一次性迁移的事务都借这里的语句,别处零手写。
12
+ * 语句是方言无关的 `{ text, values }`(调用方用自己的连接跑)。
16
13
  *
17
- * ## 读写序律(写事务内的认领读)
18
- * 改根住客 / 放置的每个写者(换绑 CAS · 导入三写口 · identical 补 mirror · 删除终刀 · 留存删除)在写事务内、用该事务自己的连接先 `FOR UPDATE` 锁根
19
- * `session_meta` 行,再以新语句锁定读 `task_active(root)`({@link lockRootAndReadClaim}):有认领 ⇒ 不写。全局锁序:设备行 → 根 meta → `task_*`
20
- * (`run-store-sql.ts` 的 `createRun` 同序先锁根行)。认领持有者自己的初始放置写(place / bind)是唯一例外,不读认领、仍按住客谓词。
14
+ * ## 写序律(一条,所有部署格的写者同一条)
15
+ * 改住客 / 放置的每个写者(place · 首绑 / 换绑 · identical 补 mirror · 导入写口 · 删除终刀 · 留存删除)在写事务内、用该事务自己的连接:
16
+ * ① `FOR UPDATE` 锁根 `session_meta` 行(读住客:属主 + 化身 + 委派判别位)→ ② 需要时新语句锁定读认领 `task_active(root)`({@link lockRootAndReadClaim})
17
+ * → ③ 补旁表行、再锁定读这一格({@link readCellOn};先补后锁:锁定读永远落在一行在的记录上,InnoDB 间隙锁不会让两只补行互等)→ 判官(住客对不对、格是什么)
18
+ * → ④ 一句按格谓词定址的 UPDATE。
19
+ * 住客由 ① 的锁钉住(删除 / 重登记 / 改属主都要先拿这把锁),所以写语句只按格谓词定址、不再带住客谓词;持锁判过「可写」之后 UPDATE 必中一行,0 行即引擎异常。
20
+ * 全局锁序:设备行 → 根 meta → `task_*` → 旁表行(`run-store-sql.ts` 的 `createRun` 同序先锁根行)。认领持有者自己的初始放置写(place / 首绑)是唯一不读认领的写。
21
21
  */
22
22
  import type { SqlDialect, SqlDriver, SqlExec } from "./sql-driver.js";
23
23
  import type { DeviceOwner } from "../device-store.js";
24
24
  import { type ExecutionLane } from "../execution-lane-caps.js";
25
- import { type SessionFace } from "../session-incarnation.js";
26
25
  import { type ExecutionPlacementCell, type ExecutionPlacementReading, type ExecutionPlacementSeat } from "../execution-placement.js";
27
26
  /** 一条语句:文本 + 按序的参数(`?` / `$n` 已按方言铸好)。 */
28
27
  export interface PlacementStatement {
29
28
  readonly text: string;
30
29
  readonly values: readonly unknown[];
31
30
  }
32
- /**
33
- * `session_meta` 的八列(执行放置七列 + `fork_key`)—— 两方言各一句,**唯一属主**。宽度:车道词 ≤ 32、设备 id 与 `device` 表同宽 64、
34
- * 属主两列与设备店同宽 190、fork 键 255(服务端拼的不透明串)。PG 文本列 `COLLATE "C"`(与同表同形)。全部可空:NULL = absent / 非键控 fork。
35
- */
36
- export declare const SESSION_META_PLACEMENT_COLUMNS: readonly [{
37
- readonly name: "execution_role";
38
- readonly mysql: "execution_role VARCHAR(16) NULL";
39
- readonly pg: "execution_role VARCHAR(16) COLLATE \"C\"";
40
- }, {
41
- readonly name: "execution_lane";
42
- readonly mysql: "execution_lane VARCHAR(32) NULL";
43
- readonly pg: "execution_lane VARCHAR(32) COLLATE \"C\"";
44
- }, {
45
- readonly name: "execution_device_id";
46
- readonly mysql: "execution_device_id VARCHAR(64) NULL";
47
- readonly pg: "execution_device_id VARCHAR(64) COLLATE \"C\"";
48
- }, {
49
- readonly name: "execution_device_owner_tenant";
50
- readonly mysql: "execution_device_owner_tenant VARCHAR(190) NULL";
51
- readonly pg: "execution_device_owner_tenant VARCHAR(190) COLLATE \"C\"";
52
- }, {
53
- readonly name: "execution_device_owner_subject";
54
- readonly mysql: "execution_device_owner_subject VARCHAR(190) NULL";
55
- readonly pg: "execution_device_owner_subject VARCHAR(190) COLLATE \"C\"";
56
- }, {
57
- readonly name: "execution_placed_at";
58
- readonly mysql: "execution_placed_at DATETIME(3) NULL";
59
- readonly pg: "execution_placed_at TIMESTAMPTZ(3)";
60
- }, {
61
- readonly name: "execution_rev";
62
- readonly mysql: "execution_rev BIGINT NULL";
63
- readonly pg: "execution_rev BIGINT";
64
- }, {
65
- readonly name: "fork_key";
66
- readonly mysql: "fork_key VARCHAR(255) NULL";
67
- readonly pg: "fork_key VARCHAR(255) COLLATE \"C\"";
68
- }];
69
- /** 中央 DDL 内插的那一段(列与列之间逗号分隔,不带首尾逗号)。 */
70
- export declare function sessionMetaPlacementColumnsDdl(dialect: SqlDialect): string;
71
- /** 导入提交点同句落 mirror 的 SET 片段与守卫谓词(会话店的提交点 UPDATE 内插这两段:`SET …, ${MIRROR_ON_COMMIT_SET} WHERE session_id = ? AND ${MIRROR_ON_COMMIT_GUARD}`)。 */
72
- export declare const MIRROR_ON_COMMIT_SET = "execution_role = 'mirror'";
73
- export declare const MIRROR_ON_COMMIT_GUARD = "(execution_role IS NULL OR execution_role <> 'writer')";
74
- /** 导入新建 / 换装重插的那一行在执行与 fork 两格上的值:`fork_key` = 沿用的键(或 NULL)、`execution_role` = mirror(列名与值同出一处)。 */
75
- export declare function importedRowCells(forkKey: string | null): {
76
- readonly columns: string;
77
- readonly values: readonly unknown[];
78
- };
79
- /** 读一行放置要的列(住客两列 + 委派判别位 + 执行七列;行 → 读数的唯一解码 {@link placementReadingFromRow} 吃的就是这一组)。导入写口的事务内锁读内插它。 */
80
- export declare const SESSION_META_PLACEMENT_READ_COLUMNS = "owner, incarnation, placement_kind, execution_role, execution_lane, execution_device_id, execution_device_owner_tenant, execution_device_owner_subject, execution_placed_at, execution_rev";
81
- /** 按根读一行(`lock` ⇒ `FOR UPDATE`:写事务内的当前读;非事务读不加锁)。 */
82
- export declare function readPlacementRowStatement(dialect: SqlDialect, root: string, opts: {
83
- readonly lock: boolean;
84
- }): PlacementStatement;
31
+ /** 按根的一次池读(非事务;不加锁):根行 LEFT JOIN 旁表行。写事务内的当前读是 {@link readPlacementOn}(两句锁定读)。 */
32
+ export declare function readPlacementRowStatement(dialect: SqlDialect, root: string): PlacementStatement;
85
33
  /**
86
34
  * place:absent → writer(L[, D]),`execution_rev = 0`。非 device 车道由会话店的执行席用(`device` 缺);device 车道的首绑由设备店在锁住设备行的
87
- * 同一事务里用(`device` 在场)。时刻用调用方的钟(与 `updated_at` 同一只)。
35
+ * 同一事务里用(`device` 在场)。时刻用调用方的钟。只在写序律 ① 锁住根行、判官答「可写」之后、{@link ensureSessionExtRowStatement} 补过行之后跑。
88
36
  */
89
- export declare function placeWriterStatement(dialect: SqlDialect, root: string, occupant: SessionFace, cell: {
37
+ export declare function placeWriterStatement(dialect: SqlDialect, root: string, cell: {
90
38
  readonly lane: ExecutionLane;
91
39
  readonly device?: {
92
40
  readonly id: string;
@@ -94,14 +42,14 @@ export declare function placeWriterStatement(dialect: SqlDialect, root: string,
94
42
  };
95
43
  }, now: Date): PlacementStatement;
96
44
  /** bind:writer(device, ∅) → writer(device, D)(fresh 首绑撞上迁移回填的格),`execution_rev = 0`。 */
97
- export declare function bindDeviceStatement(dialect: SqlDialect, root: string, occupant: SessionFace, device: {
45
+ export declare function bindDeviceStatement(dialect: SqlDialect, root: string, device: {
98
46
  readonly id: string;
99
47
  readonly owner: DeviceOwner;
100
48
  }, now: Date): PlacementStatement;
101
- /** rebind:writer(device, D) → writer(device, D'),按住客 ∧ 两属主列 ∧ rev 定址,`execution_rev + 1`(恒变列)。属主两列不动(换绑只在同一属主的设备之间)。 */
102
- export declare function rebindDeviceStatement(dialect: SqlDialect, root: string, occupant: SessionFace, owner: DeviceOwner, expectedRev: number, targetDeviceId: string, now: Date): PlacementStatement;
49
+ /** rebind:writer(device, D) → writer(device, D'),按格 ∧ 两属主列 ∧ rev 定址,`execution_rev + 1`(恒变列)。属主两列不动(换绑只在同一属主的设备之间)。 */
50
+ export declare function rebindDeviceStatement(dialect: SqlDialect, root: string, owner: DeviceOwner, expectedRev: number, targetDeviceId: string, now: Date): PlacementStatement;
103
51
  /**
104
- * 带设备列的根会话行(非委派分区行上 `execution_device_id` 非空):绑定数的读只用它**缩小**读的行 —— 一条绑定必有设备列,反之不然;
52
+ * 带设备列的根会话格(旁表上 `execution_device_id` 非空、根行不是委派分区行):绑定数的读只用它**缩小**读的行 —— 一条绑定必有设备列,反之不然;
105
53
  * 是不是一条绑定只由行的唯一解码 {@link placementCellFromRow} + `boundDeviceOf` 判(SQL 不另判一遍)。两方言同一句。
106
54
  */
107
55
  export declare function deviceCellRowsStatement(): PlacementStatement;
@@ -110,16 +58,23 @@ export declare function deviceCellRowsStatement(): PlacementStatement;
110
58
  * (设备在而属主 / rev / 时刻缺、设备落在别的车道)原样抛 {@link ExecutionPlacementRowError},不当一条绑定数进去,也不当没有。
111
59
  */
112
60
  export declare function countDeviceBindingsOn(conn: PlacementRowsOn): Promise<number>;
113
- /** identical 臂补 mirror:absent / mirror → mirror,WHERE 住客谓词 ∧ 不是 writer。 */
114
- export declare function markMirrorStatement(dialect: SqlDialect, root: string, occupant: SessionFace): PlacementStatement;
115
- /** 回填(一次性迁移):无执行格、非委派分区、有 run 行的会话行 ⇒ writer(表唯一项)。幂等(第二次跑零行)。 */
61
+ /** identical 臂补 mirror(导入写口同一句):absent / mirror → mirror,WHERE 不是 writer。写序律同 {@link placeWriterStatement}。 */
62
+ export declare function markMirrorStatement(dialect: SqlDialect, root: string): PlacementStatement;
63
+ /** 回填(一次性迁移 M32,窗内;写的是 7.139–7.146 形 —— 八列在 `session_meta` 上,M41 随后连同其余部署格搬进旁表):无执行格、非委派分区、有 run 行的会话行 ⇒ writer(表唯一项)。幂等(第二次跑零行)。 */
116
64
  export declare function backfillWriterStatement(dialect: SqlDialect, lane: ExecutionLane, now: Date): PlacementStatement;
117
65
  /**
118
- * 搬绑定(一次性迁移):按 `sourceTable`(退役改名后的绑定表,列 = `root_session_id, device_id, owner_tenant, owner_subject, bound_at, rev`)整行覆盖
66
+ * 搬绑定(一次性迁移 M32,窗内;写 7.139–7.146 形的 `session_meta` 八列,同 {@link backfillWriterStatement}):按 `sourceTable`(退役改名后的绑定表,列 = `root_session_id, device_id, owner_tenant, owner_subject, bound_at, rev`)整行覆盖
119
67
  * 根会话行的 device 位(`rev` 原值、`bound_at → execution_placed_at`)。只碰非委派分区行。MySQL 协议那张表的列是 VARBINARY ⇒ 转成字符串再比 / 再写。
120
68
  * `sourceTable` 是调用方的常量表名(不是用户输入)。
121
69
  */
122
70
  export declare function migrateDeviceBindingsStatement(dialect: SqlDialect, sourceTable: string): PlacementStatement;
71
+ /**
72
+ * 搬绑定的**旁表形**(一次性迁移 M32 ③,窗内;库已是旁表形 —— M41 之后、或本版起的新库,执行格住 `session_ext`):与 {@link migrateDeviceBindingsStatement}
73
+ * 同一组覆盖(`rev` 原值、`bound_at → execution_placed_at`、只碰非委派分区行),按写序律先补旁表行(旁表属主的补行语句 {@link ensureSessionExtRowsStatement},不碰任何格)、
74
+ * 再一句 UPDATE 只写执行格(受影响行数 = 搬了几行,与 7.139–7.146 形同一读法)。
75
+ * 两句按序跑;`sourceTable` 是调用方的常量表名。
76
+ */
77
+ export declare function migrateDeviceBindingsOntoSideTableStatements(dialect: SqlDialect, sourceTable: string): readonly [ensure: PlacementStatement, move: PlacementStatement];
123
78
  /** 搬绑定的孤儿计数(绑定行指向的根会话行不在,或是委派分区行 ⇒ 丢弃):迁移收据里点名的那个数。 */
124
79
  export declare function countOrphanBindingsStatement(dialect: SqlDialect, sourceTable: string): PlacementStatement;
125
80
  /** 一行放置读不通(角色 / 车道不在闭集,或列的组合不是四态之一)—— 响亮拒,不读作 absent(那等于把一行坏数据当成「还没放」放行给首交)。 */
@@ -144,18 +99,38 @@ export interface RootClaimReading {
144
99
  readonly activeTaskId: string | null;
145
100
  }
146
101
  /**
147
- * 读写序律那一读(头注):**同一连接**上先 `SELECT … FROM session_meta WHERE session_id = ? FOR UPDATE` 锁根行,再以新语句
148
- * `SELECT task_id FROM task_active WHERE session_id = ? FOR UPDATE` 锁定读认领(PG 读已提交下取新快照、TiDB 悲观事务是当前读)。
149
- * 只在写事务内调;池级 `getActiveTaskId` 不是它(另一只连接,不在锁序里)。根行不在 ⇒ 不读认领。
102
+ * 写序律 ②:根行已被本事务锁住之后,以新语句 `SELECT task_id FROM task_active WHERE session_id = ? FOR UPDATE` 锁定读认领(PG 读已提交下取新快照、
103
+ * TiDB 悲观事务是当前读)。只在写事务内、锁根行之后调(会话店的事务内钩在店锁住根行之后调它;其余写者经 {@link lockRootAndReadClaim} 一步做完 ① ②)。
104
+ */
105
+ export declare function readClaimOn(conn: PlacementRowsOn, dialect: SqlDialect, root: string): Promise<string | null>;
106
+ /**
107
+ * 写序律 ① ②(头注):**同一连接**上先锁根行,再锁定读认领。只在写事务内调;池级 `getActiveTaskId` 不是它(另一只连接,不在锁序里)。根行不在 ⇒ 不读认领。
150
108
  */
151
109
  export declare function lockRootAndReadClaim(conn: PlacementRowsOn, dialect: SqlDialect, root: string): Promise<RootClaimReading>;
152
- /** 写事务内按根的当前读(`FOR UPDATE`)→ 读数。 */
110
+ /**
111
+ * 写序律 ③:锁住的旁表这一格。只在根行已被本事务锁住之后调。**先补行、再锁定读**(补行 = 缺省一行,读法与行不在同一):锁定读永远落在一行在的记录上 ——
112
+ * InnoDB 可重复读下,锁定读一只不在的键拿的是间隙锁(彼此兼容),两只事务随后各自补行时插入意向锁互等 ⇒ 死锁。之后的格写({@link writeCellOn})只剩一句 UPDATE。
113
+ */
114
+ export declare function readCellOn(conn: PlacementRowsOn, dialect: SqlDialect, root: string): Promise<Exclude<ExecutionPlacementCell, {
115
+ role: "forking";
116
+ }>>;
117
+ /** 写事务内按根的当前读(写序律 ① + ③,两句锁定读)→ 读数。行不在 / 委派分区行 ⇒ `undefined`。 */
153
118
  export declare function readPlacementOn(conn: PlacementRowsOn, dialect: SqlDialect, root: string): Promise<ExecutionPlacementReading | undefined>;
119
+ /** 格写只用到的那一面:一句语句、交回受影响行数(驱动的事务连接 `SqlExec` 与会话店交给部署钩的 `SessionTxStatements` 都满足它)。 */
120
+ export interface PlacementCellWriter {
121
+ query(sql: string, params?: unknown[]): Promise<{
122
+ readonly affected: number;
123
+ }>;
124
+ }
125
+ /**
126
+ * 写序律 ④:一句格写(持锁判过「可写」之后;旁表行已在 —— 锁它的那一读 {@link readCellOn} 补的)。格写必中一行,0 行即引擎异常 ⇒ 响亮抛,不折成业务答案。
127
+ */
128
+ export declare function writeCellOn(exec: PlacementCellWriter, dialect: SqlDialect, root: string, statement: PlacementStatement): Promise<void>;
154
129
  /**
155
- * SQL 会话店的执行席(两孪生同一只实现:语句与判官都在这里与 `execution-placement.ts`,方言只在语句文本里)。`db` = 店自己的池包成的 SqlDriver。
156
- * · `read` —— 一次池读;
157
- * · `place` —— 一句自动提交的 UPDATE(认领持有者自己的初始放置写:不读认领、按住客谓词);0 行 ⇒ 按根复读、交判官分类;
158
- * · `markMirror` —— 一只事务:锁根行读认领 → 锁定读这一行 → 判官 → 有认领 ⇒ `active` → UPDATE(持锁下必中一行)。
130
+ * SQL 会话店的执行席(两孪生同一只实现:语句与判官都在这里与 `execution-placement.ts`,方言只在语句文本里)。`db` = 店的池包成的 SqlDriver。
131
+ * · `read` —— 一次池读(根行 LEFT JOIN 旁表行);
132
+ * · `place` —— 一只事务:锁根行读格(写序律 ① ③,不读认领:认领持有者自己的初始放置写;③ 先补行再锁定读)→ 判官 → 格写;
133
+ * · `markMirror` —— 一只事务:锁根行读认领(① ②)→ 补行 + 锁定读这一格(③)→ 判官 → 有认领 ⇒ `active` → 格写。
159
134
  */
160
135
  export declare function createSqlExecutionPlacementSeat(db: Pick<SqlDriver, "dialect" | "query" | "connect">): ExecutionPlacementSeat;
161
136
  //# sourceMappingURL=execution-placement-sql.d.ts.map
@@ -1,29 +1,15 @@
1
1
  import { isExecutionLane } from "../execution-lane-caps.js";
2
+ import { retiredExecutionLaneHintM40 } from "./execution-lane-rename-m40.js";
2
3
  import { sessionFaceFromRow } from "../session-incarnation.js";
3
4
  import { toIso } from "./sql-row-helpers.js";
5
+ import { SESSION_EXT_TABLE, ensureSessionExtRowStatement, ensureSessionExtRowsStatement } from "./session-ext-table.js";
4
6
  import { WRITABLE, assertPlaceableLane, boundDeviceOf, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
5
- export const SESSION_META_PLACEMENT_COLUMNS = [
6
- { name: "execution_role", mysql: "execution_role VARCHAR(16) NULL", pg: `execution_role VARCHAR(16) COLLATE "C"` },
7
- { name: "execution_lane", mysql: "execution_lane VARCHAR(32) NULL", pg: `execution_lane VARCHAR(32) COLLATE "C"` },
8
- { name: "execution_device_id", mysql: "execution_device_id VARCHAR(64) NULL", pg: `execution_device_id VARCHAR(64) COLLATE "C"` },
9
- { name: "execution_device_owner_tenant", mysql: "execution_device_owner_tenant VARCHAR(190) NULL", pg: `execution_device_owner_tenant VARCHAR(190) COLLATE "C"` },
10
- { name: "execution_device_owner_subject", mysql: "execution_device_owner_subject VARCHAR(190) NULL", pg: `execution_device_owner_subject VARCHAR(190) COLLATE "C"` },
11
- { name: "execution_placed_at", mysql: "execution_placed_at DATETIME(3) NULL", pg: "execution_placed_at TIMESTAMPTZ(3)" },
12
- { name: "execution_rev", mysql: "execution_rev BIGINT NULL", pg: "execution_rev BIGINT" },
13
- { name: "fork_key", mysql: "fork_key VARCHAR(255) NULL", pg: `fork_key VARCHAR(255) COLLATE "C"` },
14
- ];
15
- export function sessionMetaPlacementColumnsDdl(dialect) {
16
- return SESSION_META_PLACEMENT_COLUMNS.map((c) => c[dialect]).join(",\n ");
17
- }
18
7
  const WRITER = "writer";
19
8
  const MIRROR = "mirror";
20
9
  const NOT_WRITER = "(execution_role IS NULL OR execution_role <> 'writer')";
21
- export const MIRROR_ON_COMMIT_SET = "execution_role = 'mirror'";
22
- export const MIRROR_ON_COMMIT_GUARD = NOT_WRITER;
23
- export function importedRowCells(forkKey) {
24
- return { columns: "fork_key, execution_role", values: [forkKey, MIRROR] };
25
- }
26
- export const SESSION_META_PLACEMENT_READ_COLUMNS = "owner, incarnation, placement_kind, execution_role, execution_lane, execution_device_id, execution_device_owner_tenant, execution_device_owner_subject, execution_placed_at, execution_rev";
10
+ const EXECUTION_CELL_COLUMNS = "execution_role, execution_lane, execution_device_id, execution_device_owner_tenant, execution_device_owner_subject, execution_placed_at, execution_rev";
11
+ const OCCUPANT_COLUMNS = "owner, incarnation, placement_kind";
12
+ const READING_COLUMNS = `sm.owner, sm.incarnation, sm.placement_kind, ${EXECUTION_CELL_COLUMNS.split(", ").map((c) => `se.${c}`).join(", ")}`;
27
13
  function binder(dialect) {
28
14
  const values = [];
29
15
  return {
@@ -34,38 +20,37 @@ function binder(dialect) {
34
20
  },
35
21
  };
36
22
  }
37
- function occupantPredicate(dialect, p, root, occupant) {
38
- const nullSafeEq = dialect === "pg" ? "IS NOT DISTINCT FROM" : "<=>";
39
- return `session_id = ${p(root)} AND incarnation = ${p(occupant.incarnation)} AND owner ${nullSafeEq} ${p(occupant.owner)} AND placement_kind IS NULL`;
40
- }
41
- export function readPlacementRowStatement(dialect, root, opts) {
23
+ export function readPlacementRowStatement(dialect, root) {
42
24
  const b = binder(dialect);
43
- return { text: `SELECT ${SESSION_META_PLACEMENT_READ_COLUMNS} FROM session_meta WHERE session_id = ${b.p(root)}${opts.lock ? " FOR UPDATE" : ""}`, values: b.values };
25
+ return { text: `SELECT ${READING_COLUMNS} FROM session_meta sm LEFT JOIN ${SESSION_EXT_TABLE} se ON se.session_id = sm.session_id WHERE sm.session_id = ${b.p(root)}`, values: b.values };
44
26
  }
45
- export function placeWriterStatement(dialect, root, occupant, cell, now) {
27
+ export function placeWriterStatement(dialect, root, cell, now) {
46
28
  const b = binder(dialect);
47
29
  const set = `execution_role = '${WRITER}', execution_lane = ${b.p(cell.lane)}, execution_device_id = ${b.p(cell.device?.id ?? null)}, ` +
48
30
  `execution_device_owner_tenant = ${b.p(cell.device?.owner.tenant ?? null)}, execution_device_owner_subject = ${b.p(cell.device?.owner.subject ?? null)}, ` +
49
31
  `execution_placed_at = ${b.p(now)}, execution_rev = 0`;
50
- return { text: `UPDATE session_meta SET ${set} WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role IS NULL`, values: b.values };
32
+ return { text: `UPDATE ${SESSION_EXT_TABLE} SET ${set} WHERE session_id = ${b.p(root)} AND execution_role IS NULL`, values: b.values };
51
33
  }
52
- export function bindDeviceStatement(dialect, root, occupant, device, now) {
34
+ export function bindDeviceStatement(dialect, root, device, now) {
53
35
  const b = binder(dialect);
54
36
  const set = `execution_device_id = ${b.p(device.id)}, execution_device_owner_tenant = ${b.p(device.owner.tenant)}, execution_device_owner_subject = ${b.p(device.owner.subject)}, execution_placed_at = ${b.p(now)}, execution_rev = 0`;
55
37
  return {
56
- text: `UPDATE session_meta SET ${set} WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role = '${WRITER}' AND execution_lane = 'device' AND execution_device_id IS NULL`,
38
+ text: `UPDATE ${SESSION_EXT_TABLE} SET ${set} WHERE session_id = ${b.p(root)} AND execution_role = '${WRITER}' AND execution_lane = 'device' AND execution_device_id IS NULL`,
57
39
  values: b.values,
58
40
  };
59
41
  }
60
- export function rebindDeviceStatement(dialect, root, occupant, owner, expectedRev, targetDeviceId, now) {
42
+ export function rebindDeviceStatement(dialect, root, owner, expectedRev, targetDeviceId, now) {
61
43
  const b = binder(dialect);
62
44
  const set = `execution_device_id = ${b.p(targetDeviceId)}, execution_placed_at = ${b.p(now)}, execution_rev = execution_rev + 1`;
63
- const where = `${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role = '${WRITER}' AND execution_lane = 'device' ` +
45
+ const where = `session_id = ${b.p(root)} AND execution_role = '${WRITER}' AND execution_lane = 'device' ` +
64
46
  `AND execution_device_owner_tenant = ${b.p(owner.tenant)} AND execution_device_owner_subject = ${b.p(owner.subject)} AND execution_rev = ${b.p(expectedRev)}`;
65
- return { text: `UPDATE session_meta SET ${set} WHERE ${where}`, values: b.values };
47
+ return { text: `UPDATE ${SESSION_EXT_TABLE} SET ${set} WHERE ${where}`, values: b.values };
66
48
  }
67
49
  export function deviceCellRowsStatement() {
68
- return { text: `SELECT session_id, ${SESSION_META_PLACEMENT_READ_COLUMNS} FROM session_meta WHERE execution_device_id IS NOT NULL AND placement_kind IS NULL`, values: [] };
50
+ return {
51
+ text: `SELECT se.session_id, ${EXECUTION_CELL_COLUMNS.split(", ").map((c) => `se.${c}`).join(", ")} FROM ${SESSION_EXT_TABLE} se JOIN session_meta sm ON sm.session_id = se.session_id WHERE se.execution_device_id IS NOT NULL AND sm.placement_kind IS NULL`,
52
+ values: [],
53
+ };
69
54
  }
70
55
  export async function countDeviceBindingsOn(conn) {
71
56
  const s = deviceCellRowsStatement();
@@ -75,9 +60,9 @@ export async function countDeviceBindingsOn(conn) {
75
60
  bindings += 1;
76
61
  return bindings;
77
62
  }
78
- export function markMirrorStatement(dialect, root, occupant) {
63
+ export function markMirrorStatement(dialect, root) {
79
64
  const b = binder(dialect);
80
- return { text: `UPDATE session_meta SET execution_role = '${MIRROR}' WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND ${NOT_WRITER}`, values: b.values };
65
+ return { text: `UPDATE ${SESSION_EXT_TABLE} SET execution_role = '${MIRROR}' WHERE session_id = ${b.p(root)} AND ${NOT_WRITER}`, values: b.values };
81
66
  }
82
67
  export function backfillWriterStatement(dialect, lane, now) {
83
68
  const b = binder(dialect);
@@ -105,6 +90,31 @@ export function migrateDeviceBindingsStatement(dialect, sourceTable) {
105
90
  values: [],
106
91
  };
107
92
  }
93
+ export function migrateDeviceBindingsOntoSideTableStatements(dialect, sourceTable) {
94
+ if (dialect === "pg") {
95
+ return [
96
+ ensureSessionExtRowsStatement(dialect, `SELECT sm.session_id FROM ${sourceTable} ds JOIN session_meta sm ON sm.session_id = ds.root_session_id WHERE sm.placement_kind IS NULL`),
97
+ {
98
+ text: `UPDATE ${SESSION_EXT_TABLE} SET execution_role = '${WRITER}', execution_lane = 'device', execution_device_id = ds.device_id, ` +
99
+ "execution_device_owner_tenant = ds.owner_tenant, execution_device_owner_subject = ds.owner_subject, execution_placed_at = ds.bound_at, execution_rev = ds.rev " +
100
+ `FROM ${sourceTable} ds, session_meta sm WHERE ds.root_session_id = ${SESSION_EXT_TABLE}.session_id AND sm.session_id = ${SESSION_EXT_TABLE}.session_id AND sm.placement_kind IS NULL`,
101
+ values: [],
102
+ },
103
+ ];
104
+ }
105
+ const str = (col) => `CONVERT(ds.${col} USING utf8mb4)`;
106
+ const rootKey = `${str("root_session_id")} COLLATE utf8mb4_bin`;
107
+ return [
108
+ ensureSessionExtRowsStatement(dialect, `SELECT sm.session_id FROM ${sourceTable} ds JOIN session_meta sm ON ${rootKey} = sm.session_id WHERE sm.placement_kind IS NULL`),
109
+ {
110
+ text: `UPDATE ${SESSION_EXT_TABLE} se JOIN session_meta sm ON sm.session_id = se.session_id JOIN ${sourceTable} ds ON ${rootKey} = se.session_id ` +
111
+ `SET se.execution_role = '${WRITER}', se.execution_lane = 'device', se.execution_device_id = ${str("device_id")}, ` +
112
+ `se.execution_device_owner_tenant = ${str("owner_tenant")}, se.execution_device_owner_subject = ${str("owner_subject")}, se.execution_placed_at = ds.bound_at, se.execution_rev = ds.rev ` +
113
+ "WHERE sm.placement_kind IS NULL",
114
+ values: [],
115
+ },
116
+ ];
117
+ }
108
118
  export function countOrphanBindingsStatement(dialect, sourceTable) {
109
119
  const rootKey = dialect === "pg" ? "ds.root_session_id" : "CONVERT(ds.root_session_id USING utf8mb4) COLLATE utf8mb4_bin";
110
120
  return {
@@ -135,7 +145,7 @@ export function placementCellFromRow(root, row) {
135
145
  throw new ExecutionPlacementRowError(root, `role ${JSON.stringify(text(role))} is not writer / mirror / NULL`);
136
146
  const lane = isNull(row.execution_lane) ? "" : text(row.execution_lane);
137
147
  if (!isExecutionLane(lane))
138
- throw new ExecutionPlacementRowError(root, `writer lane ${JSON.stringify(lane)} is not an execution lane`);
148
+ throw new ExecutionPlacementRowError(root, `writer lane ${JSON.stringify(lane)} is not an execution lane${retiredExecutionLaneHintM40(lane)}`);
139
149
  if (isNull(row.execution_device_id)) {
140
150
  if (deviceCols.some((v) => !isNull(v)))
141
151
  throw new ExecutionPlacementRowError(root, "device owner without a device");
@@ -164,37 +174,69 @@ export function placementReadingFromRow(root, row) {
164
174
  export function rowsOnSqlExec(exec) {
165
175
  return async (sql, values) => (await exec.query(sql, [...values])).rows;
166
176
  }
167
- export async function lockRootAndReadClaim(conn, dialect, root) {
177
+ async function lockRootOn(conn, dialect, root) {
178
+ const ph = dialect === "pg" ? "$1" : "?";
179
+ return (await conn(`SELECT ${OCCUPANT_COLUMNS} FROM session_meta WHERE session_id = ${ph} FOR UPDATE`, [root]))[0];
180
+ }
181
+ export async function readClaimOn(conn, dialect, root) {
168
182
  const ph = dialect === "pg" ? "$1" : "?";
169
- const row = await conn(`SELECT session_id FROM session_meta WHERE session_id = ${ph} FOR UPDATE`, [root]);
170
- if (row.length === 0)
171
- return { rowPresent: false, activeTaskId: null };
172
183
  const claim = await conn(`SELECT task_id FROM task_active WHERE session_id = ${ph} FOR UPDATE`, [root]);
173
- return { rowPresent: true, activeTaskId: claim[0] === undefined ? null : text(claim[0].task_id) };
184
+ return claim[0] === undefined ? null : text(claim[0].task_id);
185
+ }
186
+ export async function lockRootAndReadClaim(conn, dialect, root) {
187
+ if ((await lockRootOn(conn, dialect, root)) === undefined)
188
+ return { rowPresent: false, activeTaskId: null };
189
+ return { rowPresent: true, activeTaskId: await readClaimOn(conn, dialect, root) };
190
+ }
191
+ export async function readCellOn(conn, dialect, root) {
192
+ const ph = dialect === "pg" ? "$1" : "?";
193
+ const ensure = ensureSessionExtRowStatement(dialect, root);
194
+ await conn(ensure.text, ensure.values);
195
+ const row = (await conn(`SELECT ${EXECUTION_CELL_COLUMNS} FROM ${SESSION_EXT_TABLE} WHERE session_id = ${ph} FOR UPDATE`, [root]))[0];
196
+ return placementCellFromRow(root, row ?? {});
174
197
  }
175
198
  export async function readPlacementOn(conn, dialect, root) {
176
- const s = readPlacementRowStatement(dialect, root, { lock: true });
177
- return placementReadingFromRow(root, (await conn(s.text, s.values))[0]);
199
+ const occupant = await lockRootOn(conn, dialect, root);
200
+ if (occupant === undefined || !isNull(occupant.placement_kind))
201
+ return undefined;
202
+ return { occupant: sessionFaceFromRow(root, isNull(occupant.owner) ? null : text(occupant.owner), occupant.incarnation), cell: await readCellOn(conn, dialect, root) };
203
+ }
204
+ export async function writeCellOn(exec, dialect, root, statement) {
205
+ const res = await exec.query(statement.text, [...statement.values]);
206
+ if (res.affected !== 1)
207
+ throw placementInvariant(root, `a cell write (${statement.text.slice(0, 48)}…)`);
178
208
  }
179
209
  function placementInvariant(root, what) {
180
- return new Error(`execution placement of session ${root}: ${what} matched no row while a re-read finds the cell writable — engine/driver anomaly, refusing to guess`);
210
+ return new Error(`execution placement of session ${root}: ${what} matched no row under a held root-row lock after the judge found the cell writable — engine/driver anomaly, refusing to guess`);
181
211
  }
182
212
  export function createSqlExecutionPlacementSeat(db) {
183
213
  const read = async (root) => {
184
- const s = readPlacementRowStatement(db.dialect, root, { lock: false });
214
+ const s = readPlacementRowStatement(db.dialect, root);
185
215
  return placementReadingFromRow(root, (await db.query(s.text, [...s.values])).rows[0]);
186
216
  };
187
217
  return {
188
218
  read,
189
219
  async place(root, occupant, cell) {
190
220
  assertPlaceableLane(root, cell);
191
- const s = placeWriterStatement(db.dialect, root, occupant, { lane: cell.lane }, new Date());
192
- if ((await db.query(s.text, [...s.values])).affected === 1)
221
+ const conn = await db.connect();
222
+ try {
223
+ await conn.begin();
224
+ const settled = settledPlaceOutcome(root, await readPlacementOn(rowsOnSqlExec(conn), db.dialect, root), occupant, cell);
225
+ if (settled !== WRITABLE) {
226
+ await conn.rollback();
227
+ return settled;
228
+ }
229
+ await writeCellOn(conn, db.dialect, root, placeWriterStatement(db.dialect, root, { lane: cell.lane }, new Date()));
230
+ await conn.commit();
193
231
  return { outcome: "placed" };
194
- const settled = settledPlaceOutcome(root, await read(root), occupant, cell);
195
- if (settled === WRITABLE)
196
- throw placementInvariant(root, "place");
197
- return settled;
232
+ }
233
+ catch (err) {
234
+ await conn.rollback().catch(() => undefined);
235
+ throw err;
236
+ }
237
+ finally {
238
+ conn.release();
239
+ }
198
240
  },
199
241
  async markMirror(root, occupant) {
200
242
  const conn = await db.connect();
@@ -215,9 +257,7 @@ export function createSqlExecutionPlacementSeat(db) {
215
257
  await conn.rollback();
216
258
  return { outcome: "active", activeTaskId: claim.activeTaskId };
217
259
  }
218
- const s = markMirrorStatement(db.dialect, root, occupant);
219
- if ((await conn.query(s.text, [...s.values])).affected !== 1)
220
- throw placementInvariant(root, "markMirror under a held row lock");
260
+ await writeCellOn(conn, db.dialect, root, markMirrorStatement(db.dialect, root));
221
261
  await conn.commit();
222
262
  return { outcome: "mirrored" };
223
263
  }
@@ -8,8 +8,8 @@ import { type SpaceCommitClaim, type SpaceCommitReading, type SpaceCommitState }
8
8
  * `RUN_STORE_STRICT_HYDRATE=on` 下 hydrate 列不出 `runs/` 时的**拒启**类型([ref] 件B;codex 对抗复审
9
9
  * R1-[high] 验真后从裸 `Error` 升成具名类型)。
10
10
  *
11
- * 🔴 **为什么必须是一个类型而不是一句文案**:`openStoreBackendWithFallback` 对**默认推导**的 local
12
- * (`DB_BACKEND` 未设)有一条既有降级臂 —— mkdir / 只读盘那类「这台机器存不了盘」的失败降级到内存继续起。
11
+ * 🔴 **为什么必须是一个类型而不是一句文案**(修前形;降级臂改按数据根状态判之后,`runs/` 在场的根已不会降级,类型仍是拒句的属主):`openStoreBackendWithFallback` 对**默认推导**的 local
12
+ * (`DB_BACKEND` 未设)曾有一条按错误种类判的降级臂 —— mkdir / 只读盘那类「这台机器存不了盘」的失败降级到内存继续起。
13
13
  * 本拒启从 `new LocalBackend(...)` 里冒出来,长得和它们一模一样,不给它一个可判别的身份就会被同一个
14
14
  * catch 吞掉:operator 明确要的 **fail-closed 反而变成 fail-open**,而且丢的正是持久 run 账本(这根旋钮
15
15
  * 存在的全部理由)。判据锚在**类型**上,不在文案上 —— 文案会改,类型不会(同文件族先例:`AdoptionError`
@@ -22,6 +22,13 @@ import { type SpaceCommitClaim, type SpaceCommitReading, type SpaceCommitState }
22
22
  export declare class RunStoreStrictHydrateError extends StoreBootRefusalError {
23
23
  readonly name = "RunStoreStrictHydrateError";
24
24
  }
25
+ /** run 店在数据根下自己铸的目录(构造器与一次性迁移 M39 的名册同读这一只:名册的路径 = 店真实铸的路径)。 */
26
+ export declare function fileRunStoreDirsFor(root: string): {
27
+ readonly runs: string;
28
+ readonly active: string;
29
+ readonly tmp: string;
30
+ readonly spaceCommits: string;
31
+ };
25
32
  export declare class FileRunStore implements InProcessClaimFence {
26
33
  private readonly runsDir;
27
34
  private readonly activeDir;
@@ -96,7 +103,7 @@ export declare class FileRunStore implements InProcessClaimFence {
96
103
  *
97
104
  * `readJsonlRecords` 对 ENOENT 答 `[]`、对其余读错(EACCES / EIO / EISDIR / ENOTDIR)**抛普通 Error**
98
105
  * (亲验 core `dist/stores/file/fs-atomic.js`)。那个普通 Error 从构造器冒出去之后,在**默认
99
- * 推导**的 local 后端上会被 `openStoreBackendWithFallback` 的降级臂吞成 memory —— 也就是说 strict 开着
106
+ * 推导**的 local 后端上修前会被 `openStoreBackendWithFallback` 的降级臂吞成 memory —— 也就是说 strict 开着
100
107
  * 也照样带着残缺账本(或直接换成内存)起动,两处 readdir 门白守。
101
108
  *
102
109
  * ⇒ strict 档:非 ENOENT 的读盘失败一律转成 {@link RunStoreStrictHydrateError}(带路径 + 原文),由
@@ -118,13 +125,21 @@ export declare class FileRunStore implements InProcessClaimFence {
118
125
  */
119
126
  private refuseIfUnreadable;
120
127
  private hydrate;
121
- /** run.json 的整文件原子替换 —— {@link persist} 的落盘那一半,不碰索引。 */
128
+ /** run.json 的整文件原子替换 —— {@link persist} 的落盘那一半,不碰索引(只有 `persist` 调它)。 */
122
129
  private writeRunFile;
123
- /** Commit a RunRow to disk (whole-file atomic replace) AND the index — the single write path for every status change.
124
- * 落盘在前、`runs.set` 在后:落盘抛 ⇒ 索引原样。要「全有或全无」的写口因此不在索引里那只行上原地先改再调 —— 原地改过的行,落盘一抛就是「内存已改、盘上没改」。
125
- * 两种不原地的写法:对**副本**调本口(索引里换成副本:`releaseIdemKey` / `bindIdemBodyFingerprint` / `takeOverClaim`);或副本只走 {@link writeRunFile},
126
- * 落成之后把改动**合回索引里的同一只行**(`fillObjectivePreview`)——后一种用在会撞进收割腿 await 窗的写口上:`reapSuspended` 等收割腿在 probe 的 await 之间手里持着索引里的行对象,
127
- * await 之后的复核读的就是这只对象,索引里的行被换成副本,它们就看不见之后的认领 / 取消了。 */
130
+ /**
131
+ * 索引里的行**只有这一条写律**,每个写口(建行 / 状态迁移 / 心跳 / 取消位 / 幂等绑定 / 接管 / 预览补写)都走它:
132
+ * **对副本判改 → 落盘成功 → 把改动合进索引里的同一只行对象**(索引里没有这一行才 `set` —— 建行)。
133
+ * 写口的形状恒为「在入口守卫读到的行上判,`{ ...r, 改动 }` 铸一份副本,交给本口」;`RunRow` 字段全 `readonly`,原地改一只行编不过。
134
+ *
135
+ * 三条后果都从这一条律拿到,不另设规则:
136
+ * · 落盘抛 ⇒ 索引里的行一字未动 —— 内存与盘同值,入口守卫读到的还是旧状态,重试照常补回。原地先改再落盘的写法,抛的那一拍把内存和盘劈成两半,
137
+ * 而改过的 `status` 还会让「首个终局写者赢」一类守卫把重试挡回 —— 盘上永远落不下那一笔(终局结果丢失,重启后被当孤儿记成 failed);
138
+ * · 落成之后索引里还是**同一只**行对象 —— 跨 await 持着行的收割腿(`reapSuspended` / `failSuspendedWithExpiredCheckpoint` / `reclaimOrphanedAtBoot`)
139
+ * await 之后的复核读的就是手上这只对象,换成副本它们就看不见期间的认领 / 取消 / 接管(再写一遍,把已被别腿落定的行盖掉);
140
+ * · 落盘与合回之间没有让出点(本口同步、体内无 await)⇒ 别的写者与收割腿读到的要么是落成前的行、要么是落成后的行,没有「盘已新、索引未合」的中间态。
141
+ * 在落盘与合回之间插 await 或别的事,就回到了收割写者会撞中间态的形(`test/file-run-store-persist-law.test.ts` 的结构钉守这一格)。
142
+ */
128
143
  private persist;
129
144
  private eventLogFor;
130
145
  /** Create a run iff the session has no active run (the task_active unique-key claim) AND no submission key of this
@@ -143,10 +158,9 @@ export declare class FileRunStore implements InProcessClaimFence {
143
158
  * different claim locks) cannot both pass the check: the loser observes the winner's row. */
144
159
  private commitNewRun;
145
160
  /** run 行预览的写一次补写(SQL 孪生同义:列仍是 null 才写,建行写过 / 已补过的不动;行不在 ⇒ 无事)。
146
- * **副本判改 → 落盘 → 合回**:先把改动落在副本上写盘,落盘抛 ⇒ 内存里的行一字未动(预览仍是 null)、盘上也原样,下一次补写照常再试;
147
- * 落成之后再把预览合回索引里的**同一只**行对象(不换对象:跨 await 持着行的收割腿复核读的是手上这只对象)。
161
+ * 走 {@link persist} 的写律:落盘抛 ⇒ 内存里的行一字未动(预览仍是 null)、盘上也原样,下一次补写照常再试 ——
148
162
  * 原地先改再落盘,抛的那一拍就把内存和盘劈成两半(内存带预览、盘上 null),且「列已有值」的早退让这一分叉再也补不回来。
149
- * 本体同步执行到底,中间没有 await ⇒ 副本落盘与合回之间没有别的写者插进来。 */
163
+ * 预览补写不碰 `updatedAt`(一次预览补写不是一次活性刷新)。 */
150
164
  fillObjectivePreview(taskId: string, preview: string): Promise<void>;
151
165
  requestCancel(taskId: string, owner: string | null): Promise<boolean>;
152
166
  isCancelRequested(taskId: string, owner: string | null): Promise<boolean>;