@sema-agent/server 7.145.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 (526) hide show
  1. package/MIGRATION.md +876 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +374 -99
  5. package/deploy/facts/CHANGES.json +25 -0
  6. package/deploy/facts/README.md +57 -0
  7. package/deploy/facts/lanes.json +61 -0
  8. package/deploy/facts/lanes.schema.json +57 -0
  9. package/deploy/facts/sandbox-rbac.json +113 -0
  10. package/deploy/facts/sandbox-rbac.schema.json +74 -0
  11. package/deploy/facts/sealed-key.json +9 -0
  12. package/deploy/facts/sealed-key.schema.json +52 -0
  13. package/deploy/sema-up/README.md +2 -2
  14. package/deploy/sema-up/chart/templates/_helpers.tpl +41 -0
  15. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
  16. package/deploy/sema-up/chart/templates/server-v4pro.yaml +9 -4
  17. package/deploy/sema-up/chart/templates/server.yaml +9 -4
  18. package/deploy/sema-up/chart/values.yaml +8 -1
  19. package/deploy/sema-up/cluster-up.sh +2 -1
  20. package/deploy/sema-up/lib/flagshape.sh +4 -0
  21. package/deploy/sema-up/lib/lanes.json +2 -5
  22. package/deploy/sema-up/sema-up.sh +12 -9
  23. package/deploy/sema-up/stack/compose.yaml +5 -4
  24. package/deploy/sema-up/stack/sema-stack-cluster.yaml +34 -17
  25. package/dist/adoption/import-limits.d.ts +6 -0
  26. package/dist/adoption/import-limits.js +7 -0
  27. package/dist/adoption/import.d.ts +62 -0
  28. package/dist/adoption/import.js +107 -0
  29. package/dist/adoption/wire.d.ts +52 -0
  30. package/dist/adoption/wire.js +9 -0
  31. package/dist/approval-ask-machine.d.ts +2 -2
  32. package/dist/approval-card.d.ts +7 -16
  33. package/dist/approval-card.js +0 -4
  34. package/dist/approval-gate-currency.d.ts +1 -1
  35. package/dist/approval-gate-currency.js +1 -1
  36. package/dist/approval-reconciler.d.ts +1 -1
  37. package/dist/approval.d.ts +3 -3
  38. package/dist/audit.js +2 -2
  39. package/dist/bench/s1/live-deps.js +3 -1
  40. package/dist/boot/crash-last.js +2 -1
  41. package/dist/boot/deployment-mods.d.ts +11 -1
  42. package/dist/boot/deployment-mods.js +15 -2
  43. package/dist/boot/device-lane.js +7 -5
  44. package/dist/boot/engine-lease.d.ts +195 -22
  45. package/dist/boot/engine-lease.js +146 -20
  46. package/dist/boot/execution-env.d.ts +5 -0
  47. package/dist/boot/execution-env.js +57 -33
  48. package/dist/boot/execution-lane.d.ts +37 -9
  49. package/dist/boot/execution-lane.js +74 -20
  50. package/dist/boot/exit-boundary.d.ts +50 -0
  51. package/dist/boot/exit-boundary.js +32 -0
  52. package/dist/boot/exit-last.d.ts +20 -4
  53. package/dist/boot/exit-last.js +23 -5
  54. package/dist/boot/governance-seams.d.ts +2 -2
  55. package/dist/boot/governance-seams.js +1 -0
  56. package/dist/boot/leader.js +1 -1
  57. package/dist/boot/memory-boundary.d.ts +11 -11
  58. package/dist/boot/memory-boundary.js +3 -3
  59. package/dist/boot/memory-consolidation.d.ts +42 -41
  60. package/dist/boot/memory-consolidation.js +26 -28
  61. package/dist/boot/org-memory.js +3 -2
  62. package/dist/boot/parked-revive-gate.d.ts +1 -1
  63. package/dist/boot/parked-revive-gate.js +1 -1
  64. package/dist/boot/reapers.d.ts +12 -4
  65. package/dist/boot/reapers.js +40 -16
  66. package/dist/boot/resolve-spec.d.ts +2 -0
  67. package/dist/boot/resolve-spec.js +126 -123
  68. package/dist/boot/retention-lane.d.ts +4 -42
  69. package/dist/boot/runner-deps.js +1 -1
  70. package/dist/boot/session-faces.d.ts +1 -0
  71. package/dist/boot/session-faces.js +11 -3
  72. package/dist/boot/shutdown.js +27 -9
  73. package/dist/boot/stage-01-config.d.ts +1 -1
  74. package/dist/boot/stage-01-config.js +7 -3
  75. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
  76. package/dist/boot/stage-02-web-admin-pull.js +10 -8
  77. package/dist/boot/stage-03-store-backend.d.ts +2 -1
  78. package/dist/boot/stage-03-store-backend.js +2 -2
  79. package/dist/boot/stage-04-stores.d.ts +2 -1
  80. package/dist/boot/stage-05-execution-env.d.ts +4 -1
  81. package/dist/boot/stage-05-execution-env.js +6 -6
  82. package/dist/boot/stage-06-runners.d.ts +5 -2
  83. package/dist/boot/stage-06-runners.js +12 -13
  84. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  85. package/dist/boot/stage-07-capability-layer.js +7 -7
  86. package/dist/boot/stage-08-reapers.d.ts +5 -2
  87. package/dist/boot/stage-08-reapers.js +3 -3
  88. package/dist/boot/stage-09-leader.d.ts +6 -2
  89. package/dist/boot/stage-10-http-server.d.ts +6 -2
  90. package/dist/boot/stage-10-http-server.js +5 -3
  91. package/dist/boot/stores.d.ts +10 -0
  92. package/dist/boot/stores.js +53 -26
  93. package/dist/boot/web-admin.js +3 -3
  94. package/dist/boot/workflow-orchestration.d.ts +1 -1
  95. package/dist/boot/workflow-orchestration.js +2 -1
  96. package/dist/capabilities/center-plugins.d.ts +2 -0
  97. package/dist/capabilities/center-plugins.js +6 -2
  98. package/dist/capabilities/composite-members.d.ts +4 -1
  99. package/dist/capabilities/composite-members.js +4 -1
  100. package/dist/capabilities/execution-lane.d.ts +7 -7
  101. package/dist/capabilities/execution-lane.js +2 -2
  102. package/dist/capabilities/hands-lane.d.ts +1 -1
  103. package/dist/capabilities/scenarios.d.ts +34 -20
  104. package/dist/capabilities/scenarios.js +13 -9
  105. package/dist/capabilities/settings-contract.d.ts +29 -0
  106. package/dist/capabilities/settings-contract.js +14 -0
  107. package/dist/capabilities/skills.d.ts +5 -5
  108. package/dist/capability-facts.d.ts +56 -1
  109. package/dist/capability-facts.js +59 -8
  110. package/dist/center-credential-renewer.js +3 -2
  111. package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
  112. package/dist/config-catalog.js +19 -9
  113. package/dist/config-invariants.d.ts +18 -3
  114. package/dist/config-invariants.js +36 -19
  115. package/dist/config-lkg.d.ts +1 -1
  116. package/dist/config-lkg.js +5 -6
  117. package/dist/config-provider.d.ts +2 -2
  118. package/dist/config-provider.js +2 -0
  119. package/dist/config-types.d.ts +228 -185
  120. package/dist/config.d.ts +74 -6
  121. package/dist/config.js +146 -64
  122. package/dist/degenerate-instrument.js +1 -1
  123. package/dist/deleted-session-ids.d.ts +19 -0
  124. package/dist/deleted-session-ids.js +26 -0
  125. package/dist/deploy-facts.d.ts +155 -0
  126. package/dist/deploy-facts.js +95 -0
  127. package/dist/deployment-shape.d.ts +21 -12
  128. package/dist/deployment-shape.js +24 -6
  129. package/dist/device-ws-hub.d.ts +48 -3
  130. package/dist/device-ws-hub.js +48 -2
  131. package/dist/env-facts.d.ts +7 -5
  132. package/dist/env-facts.js +10 -6
  133. package/dist/error-code-family.d.ts +35 -0
  134. package/dist/error-code-family.js +26 -0
  135. package/dist/execution-config-root.d.ts +5 -5
  136. package/dist/execution-config-root.js +5 -5
  137. package/dist/execution-env-purpose.d.ts +14 -0
  138. package/dist/execution-env-purpose.js +29 -0
  139. package/dist/execution-lane-caps.d.ts +49 -86
  140. package/dist/execution-lane-caps.js +52 -62
  141. package/dist/execution-placement.d.ts +18 -2
  142. package/dist/execution-placement.js +19 -0
  143. package/dist/file-history-disclosure.d.ts +2 -2
  144. package/dist/fleet/drain-entry.d.ts +46 -3
  145. package/dist/fleet/drain-entry.js +6 -0
  146. package/dist/fleet/fleet-bus.d.ts +2 -1
  147. package/dist/fleet/serving-lease.js +3 -4
  148. package/dist/history-anchor.d.ts +22 -0
  149. package/dist/history-anchor.js +14 -0
  150. package/dist/hooks/branch-transcript.d.ts +1 -1
  151. package/dist/hooks/branch-transcript.js +1 -0
  152. package/dist/hooks/classic-stations.d.ts +25 -8
  153. package/dist/hooks/classic-stations.js +29 -3
  154. package/dist/hooks/command-spawn.d.ts +1 -4
  155. package/dist/hooks/command-spawn.js +0 -11
  156. package/dist/hooks/hook-runner.d.ts +85 -24
  157. package/dist/hooks/hook-runner.js +326 -109
  158. package/dist/hooks/hook-source.d.ts +14 -2
  159. package/dist/hooks/hook-source.js +8 -0
  160. package/dist/http/active-run-conflict.d.ts +1 -1
  161. package/dist/http/active-run-conflict.js +1 -1
  162. package/dist/http/admission.js +11 -1
  163. package/dist/http/approval-sweeps.d.ts +2 -5
  164. package/dist/http/approval-sweeps.js +3 -2
  165. package/dist/http/checkpoint-ctx.d.ts +76 -71
  166. package/dist/http/checkpoint-ctx.js +40 -40
  167. package/dist/http/claim-latency.d.ts +10 -0
  168. package/dist/http/claim-latency.js +12 -0
  169. package/dist/http/dispatch.js +19 -3
  170. package/dist/http/durable-run-leg.d.ts +5 -0
  171. package/dist/http/durable-run-leg.js +3 -1
  172. package/dist/http/execution-context.d.ts +7 -1
  173. package/dist/http/execution-context.js +13 -0
  174. package/dist/http/health-self-report.d.ts +6 -2
  175. package/dist/http/health-self-report.js +3 -2
  176. package/dist/http/memory-compliance-failure-reply.d.ts +2 -2
  177. package/dist/http/memory-compliance-failure-reply.js +5 -15
  178. package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
  179. package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
  180. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  181. package/dist/http/memory-store-refusal-reply.js +3 -0
  182. package/dist/http/owner-access.d.ts +11 -0
  183. package/dist/http/owner-access.js +7 -4
  184. package/dist/http/principal-gate.d.ts +28 -16
  185. package/dist/http/principal-gate.js +10 -19
  186. package/dist/http/request-guards.d.ts +2 -2
  187. package/dist/http/request-guards.js +5 -5
  188. package/dist/http/resume-legs.d.ts +29 -10
  189. package/dist/http/resume-legs.js +94 -56
  190. package/dist/http/rewind-files-reply.d.ts +34 -0
  191. package/dist/http/rewind-files-reply.js +36 -0
  192. package/dist/http/route-ctx.d.ts +43 -26
  193. package/dist/http/route-table.js +4 -0
  194. package/dist/http/routes/adoption.d.ts +11 -0
  195. package/dist/http/routes/adoption.js +80 -6
  196. package/dist/http/routes/agents-roster.js +1 -0
  197. package/dist/http/routes/approvals-assistant.js +7 -26
  198. package/dist/http/routes/background.js +1 -0
  199. package/dist/http/routes/capabilities.js +12 -3
  200. package/dist/http/routes/devices.js +1 -1
  201. package/dist/http/routes/diagnostics.d.ts +9 -14
  202. package/dist/http/routes/diagnostics.js +10 -20
  203. package/dist/http/routes/engine.d.ts +31 -0
  204. package/dist/http/routes/engine.js +81 -0
  205. package/dist/http/routes/memory-bundle.js +3 -0
  206. package/dist/http/routes/memory-consolidation.js +3 -5
  207. package/dist/http/routes/memory-origin.d.ts +2 -2
  208. package/dist/http/routes/memory-origin.js +2 -4
  209. package/dist/http/routes/memory-policy.js +5 -1
  210. package/dist/http/routes/notify-wake.js +2 -1
  211. package/dist/http/routes/observability.js +3 -6
  212. package/dist/http/routes/projects.d.ts +70 -0
  213. package/dist/http/routes/projects.js +86 -0
  214. package/dist/http/routes/rules.js +30 -2
  215. package/dist/http/routes/runs.js +15 -11
  216. package/dist/http/routes/session-sync.js +39 -35
  217. package/dist/http/routes/sessions-list.d.ts +1 -1
  218. package/dist/http/routes/sessions.d.ts +7 -1
  219. package/dist/http/routes/sessions.js +108 -2
  220. package/dist/http/routes/tasks.js +32 -8
  221. package/dist/http/routes/trace-usage.js +1 -1
  222. package/dist/http/run-concurrency-cap.d.ts +86 -0
  223. package/dist/http/run-concurrency-cap.js +54 -0
  224. package/dist/http/server.d.ts +31 -14
  225. package/dist/http/server.js +29 -3
  226. package/dist/http/stored-body-replay.d.ts +12 -7
  227. package/dist/http/stored-body-replay.js +28 -5
  228. package/dist/http/wire-types.d.ts +27 -13
  229. package/dist/index.d.ts +2 -2
  230. package/dist/index.js +1 -1
  231. package/dist/instruction-source-notice.d.ts +1 -1
  232. package/dist/json-body-limit.d.ts +8 -0
  233. package/dist/json-body-limit.js +2 -0
  234. package/dist/leader/diffout.d.ts +1 -1
  235. package/dist/local-data-root.d.ts +2 -2
  236. package/dist/memory-embedder-reembed.d.ts +38 -0
  237. package/dist/memory-embedder-reembed.js +20 -0
  238. package/dist/memory-incident-sink.d.ts +0 -28
  239. package/dist/memory-incident-sink.js +5 -0
  240. package/dist/memory-operator-faces.d.ts +9 -4
  241. package/dist/memory-scope-stores.d.ts +9 -7
  242. package/dist/memory-scope-stores.js +3 -14
  243. package/dist/memory-scope.d.ts +38 -2
  244. package/dist/memory-scope.js +33 -2
  245. package/dist/memory-store-refusal.d.ts +17 -4
  246. package/dist/memory-store-refusal.js +1 -1
  247. package/dist/memory-sync-client.d.ts +2 -0
  248. package/dist/memory-sync-client.js +13 -11
  249. package/dist/memory-sync.js +1 -1
  250. package/dist/model-compat.d.ts +35 -21
  251. package/dist/model-compat.js +49 -21
  252. package/dist/model-entry-refusal.d.ts +11 -1
  253. package/dist/model-entry-refusal.js +7 -0
  254. package/dist/namespaced-rule-name.d.ts +18 -1
  255. package/dist/namespaced-rule-name.js +12 -0
  256. package/dist/net-address.d.ts +53 -0
  257. package/dist/net-address.js +111 -0
  258. package/dist/observability/fail-open.d.ts +29 -17
  259. package/dist/observability/fail-open.js +29 -17
  260. package/dist/observability/metrics.d.ts +2 -0
  261. package/dist/observability/metrics.js +2 -0
  262. package/dist/observability/secret-env-scrub.d.ts +30 -4
  263. package/dist/observability/secret-env-scrub.js +15 -3
  264. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  265. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  266. package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
  267. package/dist/orchestration/workflow-completion-inbox.js +63 -45
  268. package/dist/orchestration/workflow-notify-journal.js +2 -3
  269. package/dist/org-delivery-gate.js +1 -1
  270. package/dist/org-memory-admission.d.ts +5 -2
  271. package/dist/org-memory-admission.js +25 -10
  272. package/dist/parked-decide.d.ts +33 -26
  273. package/dist/parked-decide.js +36 -44
  274. package/dist/peer-directory.d.ts +6 -12
  275. package/dist/peer-directory.js +5 -1
  276. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  277. package/dist/plugins/approval-ask-store-file.js +37 -4
  278. package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
  279. package/dist/plugins/approval-ask-store-memory.js +11 -2
  280. package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
  281. package/dist/plugins/approval-ask-store-sql.js +3 -1
  282. package/dist/plugins/blob-backend.d.ts +38 -195
  283. package/dist/plugins/blob-backend.js +33 -229
  284. package/dist/plugins/caching-session-store.d.ts +12 -5
  285. package/dist/plugins/caching-session-store.js +3 -0
  286. package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
  287. package/dist/plugins/checkpoint-index-columns.js +3 -2
  288. package/dist/plugins/checkpoint-store-face.d.ts +426 -0
  289. package/dist/plugins/checkpoint-store-face.js +152 -0
  290. package/dist/plugins/device-store-sql.d.ts +2 -2
  291. package/dist/plugins/device-store-sql.js +5 -10
  292. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  293. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  294. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  295. package/dist/plugins/execution-placement-sql.js +94 -54
  296. package/dist/plugins/file-run-store.d.ts +26 -18
  297. package/dist/plugins/file-run-store.js +64 -75
  298. package/dist/plugins/fork-routing-session-store.d.ts +6 -2
  299. package/dist/plugins/fork-routing-session-store.js +2 -2
  300. package/dist/plugins/imported-layer-root-retirement.d.ts +22 -11
  301. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  302. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  303. package/dist/plugins/inbox-payload-column.js +13 -0
  304. package/dist/plugins/local-checkpoint-store.d.ts +7 -2
  305. package/dist/plugins/local-checkpoint-store.js +13 -8
  306. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  307. package/dist/plugins/local-data-dir-modes-m39.js +182 -0
  308. package/dist/plugins/local-engine-high-water.d.ts +11 -0
  309. package/dist/plugins/local-engine-high-water.js +59 -0
  310. package/dist/plugins/local-session-store.d.ts +119 -30
  311. package/dist/plugins/local-session-store.js +156 -53
  312. package/dist/plugins/local-task-attachment-store.d.ts +2 -0
  313. package/dist/plugins/local-task-attachment-store.js +10 -11
  314. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  315. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  316. package/dist/plugins/memory-embedder.d.ts +1 -1
  317. package/dist/plugins/memory-embedder.js +2 -2
  318. package/dist/plugins/memory-engine-pg.d.ts +30 -6
  319. package/dist/plugins/memory-engine-pg.js +96 -214
  320. package/dist/plugins/memory-engine-sql-apply.d.ts +130 -0
  321. package/dist/plugins/memory-engine-sql-apply.js +358 -0
  322. package/dist/plugins/memory-engine-tidb.d.ts +38 -9
  323. package/dist/plugins/memory-engine-tidb.js +99 -230
  324. package/dist/plugins/memory-key-guards.d.ts +1 -1
  325. package/dist/plugins/memory-origin-law.d.ts +3 -3
  326. package/dist/plugins/one-time-migrations.d.ts +169 -83
  327. package/dist/plugins/one-time-migrations.js +295 -235
  328. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  329. package/dist/plugins/owner-only-file-mode.js +141 -0
  330. package/dist/plugins/permission-rule-store-file.d.ts +17 -3
  331. package/dist/plugins/permission-rule-store-file.js +20 -1
  332. package/dist/plugins/pg-pool.d.ts +2 -1
  333. package/dist/plugins/pg-pool.js +12 -5
  334. package/dist/plugins/release-version.d.ts +20 -0
  335. package/dist/plugins/release-version.js +28 -0
  336. package/dist/plugins/remote-env-adb.d.ts +20 -4
  337. package/dist/plugins/remote-env-adb.js +39 -11
  338. package/dist/plugins/remote-env-e2b.d.ts +12 -3
  339. package/dist/plugins/remote-env-e2b.js +6 -4
  340. package/dist/plugins/remote-env-host.d.ts +9 -1
  341. package/dist/plugins/remote-env-host.js +6 -5
  342. package/dist/plugins/remote-env-k8s.d.ts +5 -2
  343. package/dist/plugins/remote-env-k8s.js +15 -4
  344. package/dist/plugins/remote-env-local-docker.d.ts +4 -1
  345. package/dist/plugins/remote-env-local-docker.js +3 -3
  346. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  347. package/dist/plugins/remote-env-ssh.js +26 -24
  348. package/dist/plugins/remote-shell.d.ts +59 -30
  349. package/dist/plugins/remote-shell.js +175 -62
  350. package/dist/plugins/retention-declaration.d.ts +37 -0
  351. package/dist/plugins/retention-declaration.js +9 -0
  352. package/dist/plugins/retention-deployment.d.ts +126 -0
  353. package/dist/plugins/retention-deployment.js +88 -0
  354. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  355. package/dist/plugins/retention-lane-store-sql.js +103 -1
  356. package/dist/plugins/retention-sql.d.ts +29 -0
  357. package/dist/plugins/retention-sql.js +8 -0
  358. package/dist/plugins/run-store-sql.js +2 -2
  359. package/dist/plugins/schema-consistency.d.ts +0 -6
  360. package/dist/plugins/schema-consistency.js +1 -2
  361. package/dist/plugins/schema-ensure-legs.d.ts +1 -1
  362. package/dist/plugins/schema-ensure-legs.js +13 -3
  363. package/dist/plugins/session-ext-table.d.ts +74 -0
  364. package/dist/plugins/session-ext-table.js +121 -0
  365. package/dist/plugins/session-fork.d.ts +31 -9
  366. package/dist/plugins/session-fork.js +3 -2
  367. package/dist/plugins/session-placement.d.ts +4 -2
  368. package/dist/plugins/session-row-sql.d.ts +8 -7
  369. package/dist/plugins/session-row-sql.js +15 -7
  370. package/dist/plugins/session-store-sql.d.ts +131 -0
  371. package/dist/plugins/session-store-sql.js +298 -0
  372. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  373. package/dist/plugins/session-workspace-cell.js +2 -2
  374. package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
  375. package/dist/plugins/shared-memory-store-sql.js +13 -13
  376. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  377. package/dist/plugins/sql-driver.d.ts +23 -7
  378. package/dist/plugins/sql-driver.js +41 -8
  379. package/dist/plugins/sql-errors.d.ts +5 -2
  380. package/dist/plugins/sql-errors.js +4 -0
  381. package/dist/plugins/sql-json-column.d.ts +5 -1
  382. package/dist/plugins/store-backend.d.ts +46 -16
  383. package/dist/plugins/store-backend.js +192 -69
  384. package/dist/plugins/store-contracts.d.ts +10 -13
  385. package/dist/plugins/store-contracts.js +1 -9
  386. package/dist/plugins/store-sweep.d.ts +63 -0
  387. package/dist/plugins/store-sweep.js +48 -0
  388. package/dist/plugins/task-attachment-store.d.ts +6 -8
  389. package/dist/plugins/task-attachment-store.js +15 -10
  390. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  391. package/dist/plugins/text-column-capacity.d.ts +21 -0
  392. package/dist/plugins/text-column-capacity.js +5 -0
  393. package/dist/plugins/tidb-pool.d.ts +3 -2
  394. package/dist/plugins/tidb-pool.js +20 -32
  395. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  396. package/dist/prompt-screen.d.ts +6 -9
  397. package/dist/prompt-screen.js +2 -5
  398. package/dist/read-face-posture.d.ts +6 -6
  399. package/dist/read-face-posture.js +2 -2
  400. package/dist/request-value-closure.d.ts +5 -5
  401. package/dist/request-value-closure.js +1 -1
  402. package/dist/rules-consent.d.ts +48 -27
  403. package/dist/rules-consent.js +147 -23
  404. package/dist/run-cancel-context.d.ts +4 -1
  405. package/dist/run-cancel-context.js +2 -1
  406. package/dist/run-local.js +17 -13
  407. package/dist/runs.d.ts +48 -30
  408. package/dist/runs.js +27 -17
  409. package/dist/runtime-caps-resolver.d.ts +15 -8
  410. package/dist/runtime-caps-resolver.js +14 -10
  411. package/dist/sandbox-egress-policy.js +6 -11
  412. package/dist/sandbox-rbac-rules.d.ts +88 -0
  413. package/dist/sandbox-rbac-rules.js +80 -0
  414. package/dist/sealed-key-layout.d.ts +16 -0
  415. package/dist/sealed-key-layout.js +3 -0
  416. package/dist/sealed-key.js +8 -6
  417. package/dist/security.d.ts +43 -9
  418. package/dist/security.js +9 -2
  419. package/dist/sema-config-dir.js +2 -2
  420. package/dist/server-secret-env.d.ts +2 -0
  421. package/dist/service-version.d.ts +2 -1
  422. package/dist/service-version.js +2 -2
  423. package/dist/session-error-wire.d.ts +1 -1
  424. package/dist/session-incarnation.d.ts +8 -5
  425. package/dist/session-notify-door.d.ts +5 -3
  426. package/dist/session-resident-background.d.ts +110 -0
  427. package/dist/session-resident-background.js +88 -0
  428. package/dist/session-row.d.ts +1 -1
  429. package/dist/session-sync-kernel.d.ts +53 -14
  430. package/dist/session-sync-kernel.js +70 -1
  431. package/dist/session-sync.d.ts +5 -3
  432. package/dist/session-workspace.d.ts +12 -2
  433. package/dist/session-workspace.js +6 -0
  434. package/dist/sighup-idle.d.ts +9 -7
  435. package/dist/sighup-idle.js +4 -5
  436. package/dist/spec-fields.d.ts +16 -0
  437. package/dist/spec-fields.js +7 -0
  438. package/dist/store-posture.d.ts +2 -1
  439. package/dist/stored-request-env-credentials-m38.d.ts +3 -0
  440. package/dist/stored-request-env-credentials-m38.js +8 -0
  441. package/dist/stored-request-hooks-m20.d.ts +3 -2
  442. package/dist/stored-request-hooks-m20.js +19 -14
  443. package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
  444. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  445. package/dist/stores-sql/blob-index-sql.js +154 -0
  446. package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +14 -428
  447. package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +43 -220
  448. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  449. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  450. package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
  451. package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
  452. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +27 -67
  453. package/dist/{plugins → stores-sql}/pg-session-storage.js +82 -207
  454. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
  455. package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
  456. package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
  457. package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
  458. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  459. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  460. package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
  461. package/dist/stores-sql/session-write-hooks.d.ts +59 -0
  462. package/dist/stores-sql/session-write-hooks.js +2 -0
  463. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
  464. package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
  465. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +35 -112
  466. package/dist/{plugins → stores-sql}/tidb-session-store.js +73 -169
  467. package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +3 -3
  468. package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
  469. package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
  470. package/dist/stores-sql/windowed-session-storage.js +61 -0
  471. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
  472. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
  473. package/dist/task-a2a.d.ts +7 -1
  474. package/dist/task-a2a.js +15 -14
  475. package/dist/task-cwd.d.ts +11 -3
  476. package/dist/task-cwd.js +17 -1
  477. package/dist/task-mcp.d.ts +10 -2
  478. package/dist/task-mcp.js +13 -16
  479. package/dist/task-settings.d.ts +68 -8
  480. package/dist/task-settings.js +63 -6
  481. package/dist/tool-approval.d.ts +23 -10
  482. package/dist/tool-approval.js +10 -6
  483. package/dist/trace/core-keyset-guard.d.ts +17 -10
  484. package/dist/trace/engine-notice-wire.d.ts +1 -1
  485. package/dist/trace/engine-notice-wire.js +4 -0
  486. package/dist/trace/exit-seat.d.ts +29 -1
  487. package/dist/trace/exit-seat.js +4 -0
  488. package/dist/trace/project.d.ts +33 -16
  489. package/dist/trace/project.js +21 -9
  490. package/dist/trace/redact.d.ts +13 -2
  491. package/dist/trace/redact.js +347 -78
  492. package/dist/trace/self-minted-wire.d.ts +587 -16
  493. package/dist/trace/self-minted-wire.js +215 -9
  494. package/dist/trace/sema-provenance.d.ts +45 -41
  495. package/dist/trace/sema-provenance.js +81 -10
  496. package/dist/trace/task-notification-facets.d.ts +70 -2
  497. package/dist/trace/task-notification-facets.js +71 -1
  498. package/dist/trace/wire-projection-faces.d.ts +39 -3
  499. package/dist/trace/wire-projection-faces.js +27 -2
  500. package/dist/unread-request-members.d.ts +66 -0
  501. package/dist/unread-request-members.js +41 -0
  502. package/dist/usage-analytics.d.ts +4 -0
  503. package/dist/user-input-ledger.d.ts +2 -2
  504. package/dist/web-admin-client/apply-effective.js +22 -26
  505. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  506. package/dist/web-admin-client/candidate-gate.js +53 -5
  507. package/dist/web-admin-client/center-request.d.ts +4 -1
  508. package/dist/web-admin-client/center-request.js +2 -1
  509. package/dist/web-admin-client/effective-keys.js +0 -1
  510. package/dist/web-admin-client/facade.d.ts +1 -1
  511. package/dist/web-admin-client/http-client.d.ts +1 -0
  512. package/dist/web-admin-client/http-client.js +17 -9
  513. package/dist/web-admin-client/restart-signal.js +5 -2
  514. package/dist/web-admin-client/skills-mcp.d.ts +20 -6
  515. package/dist/web-admin-client/skills-mcp.js +26 -21
  516. package/dist/web-admin-client/types.d.ts +34 -49
  517. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
  518. package/package.json +22 -13
  519. package/scripts/drain-check-local.mjs +8 -55
  520. package/scripts/drain-check.mysql.sql +3 -40
  521. package/scripts/drain-check.pg.sql +3 -40
  522. package/skills/update-config.md +1 -1
  523. package/wire-changes/server@7.146.0.json +3221 -0
  524. package/wire-changes/server@7.147.0.json +813 -0
  525. package/dist/approval-batch-census.d.ts +0 -27
  526. package/dist/approval-batch-census.js +0 -27
@@ -4,29 +4,25 @@ import { judgeCheckpointIndexCells } from "./checkpoint-index-columns.js";
4
4
  import { dialectProtocolJsonEncoder } from "./sql-driver.js";
5
5
  import { SCHEMA_STATEMENTS, acquireEnsureSchemaLock, releaseEnsureSchemaLock } from "./tidb-pool.js";
6
6
  import { PG_SCHEMA_STATEMENTS, acquirePgEnsureSchemaLock, releasePgEnsureSchemaLock } from "./pg-pool.js";
7
- import { CENTRAL_SCHEMA_INDEXES } from "./schema-indexes.js";
8
- import { ensureIndexes } from "./ensure-index.js";
9
- import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextExpr, readNonNullJsonValueColumn, readOpaqueJsonValueColumn, } from "./sql-json-column.js";
7
+ import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextExpr, readOpaqueJsonValueColumn, } from "./sql-json-column.js";
10
8
  import { incarnationFromCell, sessionFaceFromRow } from "../session-incarnation.js";
11
- import { RUN_HOLDER_DEATH_CODES, RUN_ORPHANED_ERROR, RUN_STALLED_ERROR, StoreBootRefusalError } from "./store-contracts.js";
9
+ import { StoreBootRefusalError } from "./store-contracts.js";
12
10
  import { PERMISSION_RULE_TABLE, SqlDurableRulePartitionProvider, isSqlBucketRefusal, isSqlBucketVanished, listRuleBucketOwners, purgeUndurableScopeRows } from "./permission-rule-store-sql.js";
13
11
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
14
12
  import { readJsonObjectColumn } from "./sql-json-column.js";
15
13
  import { retireStoredCardWords } from "../retired-card-words.js";
16
14
  import { redactSecrets } from "../trace/redact.js";
17
15
  import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, isOrphanSessionOwner, judgeDelegatedOwner } from "../delegated-session-owner.js";
18
- import { MAILBOX_KEYED_TABLES, mailboxKeyDeleteSql, storedMailboxKeyless } from "./mailbox-store-sql.js";
19
- import { SESSION_META_PLACEMENT_COLUMNS, backfillWriterStatement, countOrphanBindingsStatement, migrateDeviceBindingsStatement } from "./execution-placement-sql.js";
16
+ import { MAILBOX_KEYED_TABLES, mailboxKeyDeleteSql, storedMailboxKeyless } from "../stores-sql/mailbox-store-sql.js";
17
+ import { backfillWriterStatement, countOrphanBindingsStatement, migrateDeviceBindingsOntoSideTableStatements, migrateDeviceBindingsStatement } from "./execution-placement-sql.js";
18
+ import { EXECUTION_PLACEMENT_COLUMNS, SESSION_EXT_COLUMNS, SESSION_EXT_TABLE } from "./session-ext-table.js";
20
19
  import { APPROVAL_ASK_ENDING_CAUSE_COLUMN, APPROVAL_ASK_EXECUTION_LANE_COLUMN, APPROVAL_ASK_PARK_TRIGGER_COLUMN, APPROVAL_ASK_TABLE } from "./approval-ask-store-sql.js";
20
+ import { RENAMED_EXECUTION_LANE_M40, RETIRED_EXECUTION_LANE_WORD_M40, retiredExecutionLaneHintM40 } from "./execution-lane-rename-m40.js";
21
21
  import { retireImportedLayerRootAllows } from "./imported-layer-root-retirement.js";
22
22
  import { isUnusableArchive } from "../execution-placement.js";
23
23
  const RETIRED_SESSION_RULE_BUCKETS = ["commandAllow", "commandDeny"];
24
24
  export function buildMigrationReceipts() {
25
25
  return {
26
- holderDeathCodeRowsBackfilled: 0,
27
- allowRuleShadowRowsMigrated: 0,
28
- allowRuleShadowRowsUnreadable: 0,
29
- workflowParkIndexRowsCarried: 0,
30
26
  undurableScopeRuleRows: [],
31
27
  undurableScopeRuleBucketsDeferred: 0,
32
28
  undurableScopeRuleBucketsUnreadable: [],
@@ -55,6 +51,9 @@ export function buildMigrationReceipts() {
55
51
  deviceBindingTablesDropped: 0,
56
52
  executionPlacementsBackfilled: 0,
57
53
  askExecutionLanesBackfilled: 0,
54
+ executionLaneWordsRenamed: 0,
55
+ sessionExtRowsMoved: 0,
56
+ sessionMetaColumnsDropped: 0,
58
57
  executionPlacementSessionsUnreadable: [],
59
58
  localArchiveRowFactsBackfilled: 0,
60
59
  localArchivesAnonymized: 0,
@@ -66,22 +65,18 @@ export function buildMigrationReceipts() {
66
65
  askJournalRecordsUpgraded: 0,
67
66
  importedLayerRootAllowsRetired: [],
68
67
  importedLayerRootBucketsSkipped: [],
68
+ importedLayerRootBucketsUnnamed: 0,
69
69
  };
70
70
  }
71
71
  function completedReport(r) {
72
- const { workflowParkIndexKey, sessionRuleColumn, workflowJournalScopeColumn } = r;
73
- if (workflowParkIndexKey === undefined || sessionRuleColumn === undefined || workflowJournalScopeColumn === undefined) {
74
- throw new Error(`one-time migrations finished without settling a shape reading (M9 ${String(workflowParkIndexKey)} / M17 ${String(sessionRuleColumn)} / M24 ${String(workflowJournalScopeColumn)}) — a step did not record it`);
72
+ const { sessionRuleColumn, workflowJournalScopeColumn } = r;
73
+ if (sessionRuleColumn === undefined || workflowJournalScopeColumn === undefined) {
74
+ throw new Error(`one-time migrations finished without settling a shape reading (M17 ${String(sessionRuleColumn)} / M24 ${String(workflowJournalScopeColumn)}) — a step did not record it`);
75
75
  }
76
- return { ...r, workflowParkIndexKey, sessionRuleColumn, workflowJournalScopeColumn };
76
+ return { ...r, sessionRuleColumn, workflowJournalScopeColumn };
77
77
  }
78
78
  function changedAnything(r) {
79
- return (r.holderDeathCodeRowsBackfilled > 0 ||
80
- r.allowRuleShadowRowsMigrated > 0 ||
81
- r.allowRuleShadowRowsUnreadable > 0 ||
82
- r.workflowParkIndexKey === "reshaped" ||
83
- r.workflowParkIndexRowsCarried > 0 ||
84
- r.undurableScopeRuleRows.length > 0 ||
79
+ return (r.undurableScopeRuleRows.length > 0 ||
85
80
  r.undurableScopeRuleBucketsDeferred > 0 ||
86
81
  r.undurableScopeRuleBucketsUnreadable.length > 0 ||
87
82
  r.sessionRuleColumn === "renamed" ||
@@ -111,6 +106,9 @@ function changedAnything(r) {
111
106
  r.deviceBindingTablesDropped > 0 ||
112
107
  r.executionPlacementsBackfilled > 0 ||
113
108
  r.askExecutionLanesBackfilled > 0 ||
109
+ r.executionLaneWordsRenamed > 0 ||
110
+ r.sessionExtRowsMoved > 0 ||
111
+ r.sessionMetaColumnsDropped > 0 ||
114
112
  r.executionPlacementSessionsUnreadable.length > 0 ||
115
113
  r.localArchiveRowFactsBackfilled > 0 ||
116
114
  r.localArchivesAnonymized > 0 ||
@@ -121,7 +119,8 @@ function changedAnything(r) {
121
119
  r.askParkTriggersMoved > 0 ||
122
120
  r.askJournalRecordsUpgraded > 0 ||
123
121
  r.importedLayerRootAllowsRetired.length > 0 ||
124
- r.importedLayerRootBucketsSkipped.length > 0);
122
+ r.importedLayerRootBucketsSkipped.length > 0 ||
123
+ r.importedLayerRootBucketsUnnamed > 0);
125
124
  }
126
125
  function readingsSnapshot(r) {
127
126
  return {
@@ -163,60 +162,6 @@ export class OneTimeMigrationInterruptedError extends Error {
163
162
  }
164
163
  }
165
164
  export const ONE_TIME_MIGRATIONS = [
166
- {
167
- id: "M6",
168
- introducedIn: "7.116.0",
169
- releasedOn: "2026-10-05",
170
- removableAfter: "2026-11-04",
171
- removal: "删 migrateOn 的 M6 步(`task_run` 两条 `UPDATE … SET error_code`)与 local 腿 `FileRunStore.migrateLegacyHolderDeathCodes`(及 " +
172
- "`LocalBackend.runOneTimeMigrations` 里那一格);报告与日志行的 `holderDeathCodeRowsBackfilled`;**连同读侧半场一起删**:" +
173
- "`store-contracts.ts` 的 `readLegacyHolderDeathCode` 与 SQL 行映射 `run-store-sql.ts#rowToRun` 里对它的调用(码列改回原样读)",
174
- afterRemoval: "没有响亮臂:窗关删码之后仍只有句子、码列为空的行(跳过了所有带 M6 的版本直接升的库,或窗关之后还在写的 ≤7.115.x 副本)不再被判成持有者死亡 —— " +
175
- "用户输入受理账本的收敛判词(`user-input-ledger.ts` `judgeOpen`)对这种行落到「其余」臂 `leg_over`:补落 `settled{leg_end}`、不告知,那条 run 上" +
176
- "丢掉的 steer 不再告诉用户;`GET /v1/runs/:id` 上 `errorCode` 缺席、只剩 `error` 句子",
177
- reportKeys: ["holderDeathCodeRowsBackfilled"],
178
- },
179
- {
180
- id: "M7",
181
- introducedIn: "7.117.0",
182
- releasedOn: "2026-10-06",
183
- removableAfter: "2026-11-05",
184
- removal: "删 `http/checkpoint-ctx.ts` 的读法记号判据与拒臂(`allowListReadingUnstated` / `allowListReadingEraOf` / `allowListReadingRefusal` / " +
185
- "`PERMISSIONS_ALLOW_READINGS` / `ALLOW_LIST_READING_ERAS`、`buildCheckpointCtx` 里的记号写点)、`CheckpointCtx.permissionsAllowReading`、" +
186
- "`resume-legs.ts` 两个判点与 `approval-sweeps.ts` 的跳过臂、随包排空判据 `permissions-allow-list`(两份 SQL + 本地脚本那一条)",
187
- afterRemoval: "没有响亮臂:删码之后仍停驻、体里带非空 allow 的旧读法行(把 `APPROVAL_TERMINAL_BACKSTOP_MS` 调长的部署,或跳过了带本条的版本直接升的库)会被按" +
188
- "当前读法静默重放(名单时代的行:名单外的工具放开;表态时代的行:普通问不再问)—— 升级前按 MIGRATION 7.117.0 节跑排空判据清零",
189
- reportKeys: [],
190
- },
191
- {
192
- id: "M8",
193
- introducedIn: "7.117.0",
194
- releasedOn: "2026-10-06",
195
- removableAfter: "2026-11-05",
196
- removal: "删 migrateOn 的 M8 步(`checkpoint.risk_descriptor` 的旧键扫描 + CAS 回写)与 `migrateShadowedRuleKey` / `migrateShadowedRuleDescriptor`;" +
197
- "报告、日志行与 `NO_SQL_MIGRATIONS` 的 `allowRuleShadowRowsMigrated` / `allowRuleShadowRowsUnreadable`",
198
- afterRemoval: "没有响亮臂:删码之后仍带旧键的停驻行(跳过了带本条的版本直接升的库)在运维待批队列上只缺结构化披露 `allowRuleShadowed`;旧键的文本仍经脱敏叶表" +
199
- "「认不得的键按内容脱敏」一条脱敏后原样显示,卡上 `message` 里引擎写好的那句话不受影响",
200
- reportKeys: ["allowRuleShadowRowsMigrated", "allowRuleShadowRowsUnreadable"],
201
- },
202
- {
203
- id: "M9",
204
- introducedIn: "7.118.0",
205
- releasedOn: "2026-10-07",
206
- removableAfter: "2026-11-06",
207
- removal: "删 migrateOn 的 M9 步(`reshapeWorkflowParkIndex` / `execAtomically` / `finishParkIndexShape`:收尾段、主键宽度探针、暂存表复制、换名、删旧表、索引补建、主键约束名)与 " +
208
- "`primaryKeyWidth` / `WORKFLOW_PARK_INDEX_TABLE` / `WORKFLOW_PARK_INDEX_STAGING` / `WORKFLOW_PARK_INDEX_RETIRED`;报告、日志行与 `NO_SQL_MIGRATIONS` 的 " +
209
- "`workflowParkIndexKey` / `workflowParkIndexRowsCarried`;**连同读侧半场一起删**(都是「引擎 7.43 及以前写下的 workflow 子代停驻记录」这同一件事,同窗同删,不另起登记行):" +
210
- "`orchestration/workflow-agent-session-index.ts` 的 `older_engine` 读臂(`routeWorkflowPark` 规则 ③、`WorkflowParkRoute` 与 `parked-decide.ts` `ParkOrigin` 上 `cause` 里的这一词," +
211
- "`denyWorkflowParksUnreadable` 的对应分支;`unreadable` 成为唯一成因)与随包排空判据 `workflow-park-holder`(`scripts/drain-check.mysql.sql` / `scripts/drain-check.pg.sql` 的那一段" +
212
- "与文件头判据清单里的那一行、`scripts/drain-check-local.mjs` 的 `DRAIN_CRITERIA` 那一条与 `readParkIndex`,以及 `MIGRATION.md` / `docs/DEPLOY-PREREQS.md` 里点名它的说明)",
213
- afterRemoval: "没有响亮臂:删码之后仍是旧主键(只有 `session_id`)的库(跳过了带本条的版本直接升)上,同一个子会话只能有一枚指针 —— 续跑链 / 分叉里" +
214
- "后写的那条记录覆盖先写的,判决只看得到一条记录:分叉判不出、链尾可能被更早的记录盖掉(决定投给那一条)。运维手工照中央 DDL 重建这张表即可" +
215
- "(它是派生投影,丢了只丢升级那一刻还停驻着的旧卡的指针 —— 那些卡改答诚实 miss)。读侧半场删后:升级那一刻还停驻着、点名它的条目没有持有者位的存量卡" +
216
- "(跳过了带本条的版本直接升的库)路由判 `none` ⇒ 落 legacy 腿,答 `409 conflict.resume_context_unavailable`(响亮,但成因是子会话没有续跑上下文,不再是 " +
217
- "`decide.workflow_parks_unreadable`);出路不变(续跑发起它的那条 workflow run);随包排空判据没了,换钉前没有机器读数可数这些卡",
218
- reportKeys: ["workflowParkIndexKey", "workflowParkIndexRowsCarried"],
219
- },
220
165
  {
221
166
  id: "M10",
222
167
  introducedIn: "7.119.0",
@@ -349,6 +294,9 @@ export const ONE_TIME_MIGRATIONS = [
349
294
  "`test/task-a2a.test.ts` 里的克隆钉(标题去掉开头的 `🔴 ` 后以 `克隆钉:` 开头的各格,整格删;按这个精确前缀删,不按标题里的「M19 重读」之类泛词删 —— 那会误删同文件里的通用不变式钉),以及三格不带前缀、删码后要改写的钉:`需要「读」的两个键…` 那格的 `readRequestToolAxes` 调用方名单(`normalizeA2aPeer` 只在 M19 重读臂里调它,删后名单只剩 `mintPeerSnapshot`,标题里「与读器里 M19 重读产物的那一次」半句同删)、`读器与门的差别只有三格…` 改成两格(删 ③ 的注释行与标题里的 ③)、`请求入口快照的形…` 里 `snapshotRequestA2a(…, { reread: false })` 的第二实参(删 `reread` 形参后改成单参);" +
350
295
  "`test/one-time-migration-window-gate.test.ts` 里 M19 一节中与克隆有关的三格(声明抽取器正负控 · 删法条与克隆名字两向对账 · 克隆钉标题前缀对账)与它们共用的帮手 `declaredNames`;" +
351
296
  "`test/mcp-tool-axes-m19.test.ts`(窗关那一格 —— 存量坏形整条不进、warn 与通告点名 `field: toolAxes` —— 改钉到 `test/task-mcp.test.ts`);" +
297
+ "「请求成员读不出」一问上的重读座走线:`http/stored-body-replay.ts` 里递给 `unreadRequestMembers` 的 `storedToolAxes: storedToolAxesRereadM19(today)` 与它的 import," +
298
+ "`unread-request-members.ts` 的 `StoredToolAxesRereadM19` 型 import、`UnreadMembersLeg` 续跑臂的 `storedToolAxes` 字段与 `unreadRequestMembers` 里取它、递给两只谓词的那几处," +
299
+ "`task-mcp.ts` 的 `requestMcpTransportUnread` 与 `task-a2a.ts` 的 `requestA2aHeadersUnread` 的 `storedToolAxes` 形参(连同它们递给 `normalizeServer` / `normalizeA2aPeer` 的那一实参);" +
352
300
  "本文件的 `migrationWindowOpen`(若再无别的读者)",
353
301
  afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里引擎判官判不过的 `toolAxes`,续跑重放时这一条 server 整条不进," +
354
302
  "运维 warn `mcp_injection_dropped`(`reason: malformed_entry`、`field: toolAxes`)+ 用户面通告 `mcp.injection_dropped`,兄弟照挂;停驻在这台 server 工具审批上的任务" +
@@ -362,14 +310,16 @@ export const ONE_TIME_MIGRATIONS = [
362
310
  introducedIn: "7.131.0",
363
311
  releasedOn: "2026-10-16",
364
312
  removableAfter: "2026-11-15",
365
- removal: "删 `src/stored-request-hooks-m20.ts` 整只文件(`storedHooksRereadM20` / `rereadStoredHooksM20` / `StoredHooksRereadM20` / `ENTRY_FIELD_NAMES` / `MATCHER_FIELD_NAMES` / `nodeAt` / `cloneDoc` / `removeKeyAt`);" +
313
+ removal: "删 `src/stored-request-hooks-m20.ts` 整只文件(`storedHooksRereadM20` / `rereadStoredHooksM20` / `StoredHooksRereadM20` / `MATCHER_FIELD_NAMES` / `nodeAt` / `cloneDoc` / `removeKeyAt`);" +
366
314
  "`boot/resolve-spec.ts` 里递给 `parseTaskSettings` 的重读座实参(`opts?.leg === \"fresh\" || !taskHooksHonored(config) ? {} : { storedHooksReread: storedHooksRereadM20(…) }`)与它的 import、" +
367
315
  "`hooks_read_as_before_upgrade` 那一段 warn;`task-settings.ts` 的 `ParseTaskSettingsSeats` / `ParseTaskSettingsResult.hooksReread` / `parseTaskSettings` 第二形参与 hooks 臂里的重读那一支;" +
368
316
  "`hooks/hook-runner.ts` 的 `ParseHooksConfigResult.pieces` / `HooksRefusalPiece`(若再无别的读者)与 `hooksCoordinate` 的 export;" +
369
- "`test/hooks-stored-reread-m20.test.ts`(窗关那一格 —— 整份不采纳 + `server.hooks.resume-reader-refused` 计一笔 —— 改钉到 `test/hooks-entry-admission.test.ts` 的续跑腿组)",
370
- afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里带这两形的 `settings.hooks`,续跑重放时整份不采纳(其中真在拦的 `PreToolUse` 钩子也不再拦)," +
371
- "运维 warn `task_settings_deferred`(字段 `hooks`)+ fail-open 登记 `server.hooks.resume-reader-refused`(计数 + 探针 + 一次性 warn,census 第 140 行),任务照常续跑 —— " +
372
- "出路是把这类停驻任务决掉、删掉那个未知字段 / `__proto__` 键后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行(头注 M20 段)",
317
+ "「请求成员读不出」一问上的重读座走线:`unread-request-members.ts` 的 `storedHooksRereadM20` import 与 `unreadRequestMembers` 里 `hooksReread` 那一行(递给 `parseTaskSettings` 的 `storedHooksReread` 座)、" +
318
+ "`UnreadMembersLeg` 续跑臂的 `today` 字段(只为 M20 的窗问它)与 `http/stored-body-replay.ts`、`boot/resolve-spec.ts` 递 `today` 的那两处(`stored-body-replay.ts` 的 `today` 局部若 M19 已删再无读者一并删);" +
319
+ "`test/hooks-stored-reread-m20.test.ts`(窗关那一格 —— 续跑拒 409 `resume_blocked_by_policy` —— 改钉到 `test/hooks-entry-admission.test.ts` 的续跑腿组)",
320
+ afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里带这两形的 `settings.hooks`,在会采纳 hooks 的车道上续跑被拒 —— " +
321
+ "「请求成员读不出」判据(`src/unread-request-members.ts`)答 409 `resume_blocked_by_policy`(`blockedBy: request.field_invalid`,`field: settings.hooks`),卡仍待决、行不动(7.147.0 起;此前是整份不采纳照跑)—— " +
322
+ "出路是取消这类停驻任务、删掉那个未知字段 / `__proto__` 键后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行(头注 M20 段)",
373
323
  reportKeys: [],
374
324
  },
375
325
  {
@@ -411,7 +361,7 @@ export const ONE_TIME_MIGRATIONS = [
411
361
  "`test/one-time-migrations.test.ts` 的 M23 组与 `test/one-time-migrations-db-integration.test.ts` 的 M23 格。" +
412
362
  "**不随本条删**:`ORPHAN_SESSION_OWNER_PREFIX` 与它在 `security.ts` 保留主体前缀里的登记(已落库的行可能带哨兵,永久保留);" +
413
363
  "属主为 NULL 的 placed 行只对 fleet 调用方放行(`security.ts` 的 `ownerlessRowOpen` / `sessionOwnerCellOf` 与各门读 `placementOf` 的那一次)—— 那是常设的读面律",
414
- afterRemoval: "会话面的读面律(常设,不随本条删)接住:跳过了带本条的版本直接升的库里,属主为 NULL 的存量 placed 子代行只对 fleet 调用方放行(出示 service 凭据、不报主体;dev-open)," +
364
+ afterRemoval: "会话面的读面律(常设,不随本条删)接住:跳过了带本条的版本直接升的库里,属主为 NULL 的存量 placed 子代行只对 fleet 调用方放行(出示 service 凭据、不报主体 —— 除非部署声明 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false`,那时这一形是无身份调用方、不在其内;或 dev-open 部署上的任何调用方)," +
415
365
  "其余调用方答 404(与别人的行同体)—— 出路是升级时先过一次带本条的版本,或运维按 `placement_root_session_id`(缺则 `placement_parent_session_id`)那一行的属主手工回填、" +
416
366
  "推不出的写一个任何主体都不匹配的值;停驻挂点只比对宿主行属主与引擎交来的属主(复活腿交那一行自己的属主),不写无主行;" +
417
367
  "建行那一笔记 `placement.owner` 不随本条删(那是常设的写法 —— core 盒义务 7,不是迁移)",
@@ -472,13 +422,13 @@ export const ONE_TIME_MIGRATIONS = [
472
422
  introducedIn: "7.134.0",
473
423
  releasedOn: "2026-10-21",
474
424
  removableAfter: "2026-11-20",
475
- removal: "删 `src/checkpoint-ctx-seal-m28.ts` 整个文件(`sealCheckpointCtxCredentialsM28`)与起服第 6 段(`src/boot/stage-06-runners.ts`)顶部对 `sealCheckpointCtxCredentialsM28` 的 import 行与对它的那一处调用(连同起服 info 行 `checkpoint_ctx_credentials_sealed`);两孪生共有的 ctx 行改写店腿(`src/plugins/checkpoint-store-sql.ts` 与 `src/plugins/local-checkpoint-store.ts` 里的 `rewriteCtxRows` / `CtxRowsRewrite` / `CtxRowRewrite` / `CheckpointCtxRowsRewriter` / 扫描页 `CTX_REWRITE_PAGE`)只在本表再无别的条目用它时(今天 M30 也用)一起删;店腿删时 `test/checkpoint-ctx-sealed-brand.test.ts` 里 `rewriteCtxRows` 的断言(`@ts-expect-error` 与 `void local.rewriteCtxRows(` 两行)一起删 —— 店腿不在后那一行的 tsc 错变成「属性不存在」,`@ts-expect-error` 照样满足,断言静默变空 —— 同文件 `putCtx` 的常设断言保留;**店腿格不随本条删**:`test/checkpoint-ctx-rows-rewrite-db-integration.test.ts`(店腿自己的 CAS / 分页 / 一行装不下三格)与 `test/checkpoint-ctx-rows-rewrite-local.test.ts`(读不出的档 / 信封 updatedAt 不动两格)用中性的行改写、不借任何一条迁移的函数,店腿还有用户时它们要活着;店腿删时两个文件整文件随店腿删,`scripts/db-integration-files.json` 里 db 那个文件的行同删;店腿删时的同族注释(店腿是两条迁移共用的,谁后删谁做;两条 removal 都点名,逐条同字面):`src/http/checkpoint-ctx.ts`:「与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)」删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 ctx 行改写回调(M28 / M30 共用)」删、「,M28 行改写回调交回裸对象 —— 三处都是 tsc 错」改成「 —— 两处都是 tsc 错」;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
425
+ removal: "删 `src/checkpoint-ctx-seal-m28.ts` 整个文件(`sealCheckpointCtxCredentialsM28`)与起服第 6 段(`src/boot/stage-06-runners.ts`)顶部对 `sealCheckpointCtxCredentialsM28` 的 import 行与对它的那一处调用(连同起服 info 行 `checkpoint_ctx_credentials_sealed`);两孪生共有的 ctx 行改写店腿(`src/stores-sql/checkpoint-store-sql.ts`、`src/plugins/checkpoint-store-face.ts` 与 `src/plugins/local-checkpoint-store.ts` 里的 `rewriteCtxRows` / `CtxRowsRewrite` / `CtxRowRewrite` / `CheckpointCtxRowsRewriter` / 扫描页 `CTX_REWRITE_PAGE`)只在本表再无别的条目用它时(今天 M30 也用)一起删;店腿删时 `test/checkpoint-ctx-sealed-brand.test.ts` 里 `rewriteCtxRows` 的断言(`@ts-expect-error` 与 `void local.rewriteCtxRows(` 两行)一起删 —— 店腿不在后那一行的 tsc 错变成「属性不存在」,`@ts-expect-error` 照样满足,断言静默变空 —— 同文件 `putCtx` 的常设断言保留;**店腿格不随本条删**:`test/checkpoint-ctx-rows-rewrite-db-integration.test.ts`(店腿自己的 CAS / 分页 / 一行装不下三格)与 `test/checkpoint-ctx-rows-rewrite-local.test.ts`(读不出的档 / 信封 updatedAt 不动两格)用中性的行改写、不借任何一条迁移的函数,店腿还有用户时它们要活着;店腿删时两个文件整文件随店腿删,`scripts/db-integration-files.json` 里 db 那个文件的行同删;店腿删时的同族注释(店腿是两条迁移共用的,谁后删谁做;两条 removal 都点名,逐条同字面):`src/http/checkpoint-ctx.ts`:「与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)」删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 ctx 行改写回调(M28 / M30 共用)」删、「,M28 行改写回调交回裸对象 —— 三处都是 tsc 错」改成「 —— 两处都是 tsc 错」;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
476
426
  "`http/checkpoint-ctx.ts` 的 `sealStoredCheckpointCtxM28`;`test/checkpoint-ctx-seal-m28.test.ts` 整个文件与 `test/checkpoint-ctx-credential-seal-db-integration.test.ts` 里的「[M28] 明文旧行」那一格(它调 `sealStoredCheckpointCtxM28`);" +
477
427
  "`test/doc-claims-upgrade-notes.test.ts` ㊵ 组里读这处调用与日志行的三行断言随删(两格本身与其余断言留着;不删就是删码后这两格红):「锚(嵌入方段的每件事实读实现)」格的 `callSites(\"sealCheckpointCtxCredentialsM28\")` 一行;" +
478
428
  "「锚(恢复句的每件事实读实现)」格里 guarded 上对 `sealCheckpointCtxCredentialsM28` 调用的断言与对 `logger.info(\"checkpoint_ctx_credentials_sealed\"` 日志行的断言两行;" +
479
429
  "`test/stored-request-prompt-profile-m30.test.ts` 里钉起服第 6 段调用次序的那一格(`describe` 名含「起服接线」;M30 删码时才删它,M28 的窗先关,先删 M28 时它要活着)里读 M28 调用位置的 `const m28` 那一行(它用 indexOf 找 `sealCheckpointCtxCredentialsM28` 的调用)与「M28 在那一臂里」「M30 在那一臂里、M28 之后」两条断言,改成只相对 `guard` 比较 M30 的位置;" +
480
430
  "依赖本行的格(行一删就红):doc-claims ㊵ 的「嵌入方一段分清两只迁移的窗」格(读 M28 / M30 两行的窗末日;哪一行先删就先删这一格,MIGRATION 7.137.0 节的文字是已发布的历史、留着不动),`test/stored-request-prompt-profile-m30.test.ts` 里「M28 登记行」组整组随删(组内三格都读 M28 行的 `removal`:「M28 的 removal 同样点名」「M28 删法条补四类漏项」「同一注释半句只归一条 removal」)、同文件 SHARED 表里 rows 含 M28 的五行改成只剩 M30 并入 SITES(M30 的登记行组已先删时该表已不在,这一句无事可做);" +
481
- "同族注释(每个半句只归本条或 M30 条之一,不重复改;标〔另一条的〕的几处住在另一条迁移的文件 / 头注段里,那条已先删则宿主已不在,跳过):`src/http/checkpoint-ctx.ts`:「一次性迁移 M28 对存量行用同一只走法」删、「({@link sealStoredCheckpointCtxM28})」删、「、{@link sealStoredCheckpointCtxM28}」删、「(开口与 M28 同一只)」删、「(更早版本写下、M28 还没扫到)」改成「(更早版本写下)」、「读写两侧与一次性迁移 M28 共用」改成「读写两侧共用」、「已落盘的行由 M28 补封」改成「已落盘的行不补封(要补封须另起迁移)」、「M28 封的是」起到「窗外加位要另起迁移)。」止整段删、「(读写两侧与 M28 共用这一只)」改成「(读写两侧共用这一只)」、「(开口与 M28 共用这一只判词,闭集三词)」改成「(开口用这一只判词,闭集三词)」、「开口去开它;M28 不动它;」改成「开口去开它;」、「M28 把它封好;」删、「M28 不动它(留给读口响亮拒)」改成「(留给读口响亮拒)」、「封口 / 开口 / M28 共用的那一只」改成「封口 / 开口共用的那一只」;`src/sealed-key.ts`:「起服的一次性迁移 M28 会用它把还读得懂的明文行换成谁都开不开的密文」改成「新写的停驻行会被封成谁都开不开的密文」;`src/boot/web-admin.ts`:「与一次性迁移 M28(stage-06)」删、「M28 会拿它毁掉还读得懂的行」改成「新写的停驻行会被封成谁都开不开的密文」;`src/boot/stage-06-runners.ts`:「也是一次性迁移 M28 要它的地方。M28 在任何路由装配之前跑(窗外不扫;读数一行 info)。」删、「排在 M28 之后」删;`src/plugins/checkpoint-store-sql.ts`:「M28(凭据位明文封好)」删;`src/stored-request-prompt-profile-m30.ts`:〔另一条的〕「,与 M28 同一只」删、〔另一条的〕「本条与 M28 同在起服第 6 段」改成「本条在起服第 6 段」、〔另一条的〕「(M28 用同一只)」删;`src/plugins/one-time-migrations.ts`:〔另一条的〕「起服第 6 段 M28 之后调一次」改成「起服第 6 段调一次」、〔另一条的〕「店腿是两孪生共有的 `rewriteCtxRows`,与 M28 同一只」改成「店腿是两孪生共有的 `rewriteCtxRows`」;`test/owner-gate-single-owner.test.ts`:「两只封口 `sealCheckpointCtx` / `sealStoredCheckpointCtxM28` 经它」改成「封口 `sealCheckpointCtx` 经它」;`test/drain-check-local.test.ts`:「封口 / 开口 / M28 共用的凭据位遍历器」改成「封口 / 开口共用的凭据位遍历器」;`test/checkpoint-ctx-credential-seal.test.ts`:「(否则 M28 会把可用的明文行换成谁都开不开的密文)」改成「(否则新写的停驻行会被封成谁都开不开的密文)」;`test/helpers/ctx-sealing.ts`:「 / M28 的行改写」删;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「/ `checkpoint-ctx-seal-m28.test.ts`」删、「窗与读数那一层在 local 半场(`checkpoint-ctx-seal-m28.test.ts`)钉」起到「两半场共用同一只行改写 `sealStoredCheckpointCtxM28`。」止整段删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 M28 的行改写(`sealStoredCheckpointCtxM28`)」删;" +
431
+ "同族注释(每个半句只归本条或 M30 条之一,不重复改;标〔另一条的〕的几处住在另一条迁移的文件 / 头注段里,那条已先删则宿主已不在,跳过):`src/http/checkpoint-ctx.ts`:「一次性迁移 M28 对存量行用同一只走法」删、「({@link sealStoredCheckpointCtxM28})」删、「、{@link sealStoredCheckpointCtxM28}」删、「(开口与 M28 同一只)」删、「(更早版本写下、M28 还没扫到)」改成「(更早版本写下)」、「读写两侧与一次性迁移 M28 共用」改成「读写两侧共用」、「已落盘的行由 M28 补封」改成「已落盘的行不补封(要补封须另起迁移)」、「M28 封的是」起到「窗外加位要另起迁移)。」止整段删、「(读写两侧与 M28 共用这一只)」改成「(读写两侧共用这一只)」、「(开口与 M28 共用这一只判词,闭集三词)」改成「(开口用这一只判词,闭集三词)」、「开口去开它;M28 不动它;」改成「开口去开它;」、「M28 把它封好;」删、「M28 不动它(留给读口响亮拒)」改成「(留给读口响亮拒)」、「封口 / 开口 / M28 共用的那一只」改成「封口 / 开口共用的那一只」;`src/sealed-key.ts`:「起服的一次性迁移 M28 会用它把还读得懂的明文行换成谁都开不开的密文」改成「新写的停驻行会被封成谁都开不开的密文」;`src/boot/web-admin.ts`:「与一次性迁移 M28(stage-06)」删、「M28 会拿它毁掉还读得懂的行」改成「新写的停驻行会被封成谁都开不开的密文」;`src/boot/stage-06-runners.ts`:「也是一次性迁移 M28 要它的地方。M28 在任何路由装配之前跑(窗外不扫;读数一行 info)。」删、「排在 M28 之后」删;`src/plugins/checkpoint-store-face.ts`:「M28(凭据位明文封好)」删;`src/stored-request-prompt-profile-m30.ts`:〔另一条的〕「,与 M28 同一只」删、〔另一条的〕「本条与 M28 同在起服第 6 段」改成「本条在起服第 6 段」、〔另一条的〕「(M28 用同一只)」删;`src/plugins/one-time-migrations.ts`:〔另一条的〕「起服第 6 段 M28 之后调一次」改成「起服第 6 段调一次」、〔另一条的〕「店腿是两孪生共有的 `rewriteCtxRows`,与 M28 同一只」改成「店腿是两孪生共有的 `rewriteCtxRows`」;`test/owner-gate-single-owner.test.ts`:「两只封口 `sealCheckpointCtx` / `sealStoredCheckpointCtxM28` 经它」改成「封口 `sealCheckpointCtx` 经它」;`test/drain-check-local.test.ts`:「封口 / 开口 / M28 共用的凭据位遍历器」改成「封口 / 开口共用的凭据位遍历器」;`test/checkpoint-ctx-credential-seal.test.ts`:「(否则 M28 会把可用的明文行换成谁都开不开的密文)」改成「(否则新写的停驻行会被封成谁都开不开的密文)」;`test/helpers/ctx-sealing.ts`:「 / M28 的行改写」删;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「/ `checkpoint-ctx-seal-m28.test.ts`」删、「窗与读数那一层在 local 半场(`checkpoint-ctx-seal-m28.test.ts`)钉」起到「两半场共用同一只行改写 `sealStoredCheckpointCtxM28`。」止整段删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 M28 的行改写(`sealStoredCheckpointCtxM28`)」删;" +
482
432
  "**不随本条删**:凭据位密封的常设规则(封口 / 开口 / 闭集 / 读口第四态 / 409 码 / 装配期拒启)",
483
433
  afterRemoval: "读口对凭据位上明文的常设拒臂接住:跳过了带本条的版本直接升的库,带明文凭据的停驻行续跑答 409 `conflict.checkpoint_ctx_unsealable`(`reason:\"plaintext\"`)," +
484
434
  "行字节不动 —— 出路 = 作为新任务重提(仍停驻的先取消),或升级时先过一次带本条的版本",
@@ -502,13 +452,13 @@ export const ONE_TIME_MIGRATIONS = [
502
452
  releasedOn: "2026-10-25",
503
453
  removableAfter: "2026-11-24",
504
454
  removal: "删 `src/stored-request-prompt-profile-m30.ts` 整个文件(`retirePromptProfileM30` / `retirePromptProfileRowM30` / `PromptProfileRetireM30Report`)与起服第 6 段(`src/boot/stage-06-runners.ts`)顶部对 `retirePromptProfileM30` 的 import 行与对它的那一处调用(连同起服 info 行 `checkpoint_ctx_prompt_profile_rewritten`);" +
505
- "`http/checkpoint-ctx.ts` 的 `rewriteStoredBodyKeys` 与 `CREDENTIAL_SLOT_ROOTS`(若再无别的调用方);两孪生共有的 ctx 行改写店腿(`src/plugins/checkpoint-store-sql.ts` 与 `src/plugins/local-checkpoint-store.ts` 里的 `rewriteCtxRows` / `CtxRowsRewrite` / `CtxRowRewrite` / `CheckpointCtxRowsRewriter` / `CTX_REWRITE_PAGE`)只在本表再无别的条目用它时一起删(今天 M28 也用);店腿删时 `test/checkpoint-ctx-sealed-brand.test.ts` 里 `rewriteCtxRows` 的断言(`@ts-expect-error` 与 `void local.rewriteCtxRows(` 两行)一起删 —— 店腿不在后那一行的 tsc 错变成「属性不存在」,`@ts-expect-error` 照样满足,断言静默变空 —— 同文件 `putCtx` 的常设断言保留;**店腿格不随本条删**:`test/checkpoint-ctx-rows-rewrite-db-integration.test.ts`(店腿自己的 CAS / 分页 / 一行装不下三格)与 `test/checkpoint-ctx-rows-rewrite-local.test.ts`(读不出的档 / 信封 updatedAt 不动两格)用中性的行改写、不借任何一条迁移的函数,店腿还有用户时它们要活着;店腿删时两个文件整文件随店腿删,`scripts/db-integration-files.json` 里 db 那个文件的行同删;店腿删时的同族注释(店腿是两条迁移共用的,谁后删谁做;两条 removal 都点名,逐条同字面):`src/http/checkpoint-ctx.ts`:「与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)」删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 ctx 行改写回调(M28 / M30 共用)」删、「,M28 行改写回调交回裸对象 —— 三处都是 tsc 错」改成「 —— 两处都是 tsc 错」;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
455
+ "`http/checkpoint-ctx.ts` 的 `rewriteStoredBodyKeys` 与 `CREDENTIAL_SLOT_ROOTS`(若再无别的调用方);两孪生共有的 ctx 行改写店腿(`src/stores-sql/checkpoint-store-sql.ts`、`src/plugins/checkpoint-store-face.ts` 与 `src/plugins/local-checkpoint-store.ts` 里的 `rewriteCtxRows` / `CtxRowsRewrite` / `CtxRowRewrite` / `CheckpointCtxRowsRewriter` / `CTX_REWRITE_PAGE`)只在本表再无别的条目用它时一起删(今天 M28 也用);店腿删时 `test/checkpoint-ctx-sealed-brand.test.ts` 里 `rewriteCtxRows` 的断言(`@ts-expect-error` 与 `void local.rewriteCtxRows(` 两行)一起删 —— 店腿不在后那一行的 tsc 错变成「属性不存在」,`@ts-expect-error` 照样满足,断言静默变空 —— 同文件 `putCtx` 的常设断言保留;**店腿格不随本条删**:`test/checkpoint-ctx-rows-rewrite-db-integration.test.ts`(店腿自己的 CAS / 分页 / 一行装不下三格)与 `test/checkpoint-ctx-rows-rewrite-local.test.ts`(读不出的档 / 信封 updatedAt 不动两格)用中性的行改写、不借任何一条迁移的函数,店腿还有用户时它们要活着;店腿删时两个文件整文件随店腿删,`scripts/db-integration-files.json` 里 db 那个文件的行同删;店腿删时的同族注释(店腿是两条迁移共用的,谁后删谁做;两条 removal 都点名,逐条同字面):`src/http/checkpoint-ctx.ts`:「与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)」删;`test/checkpoint-ctx-sealed-brand.test.ts`:「与 ctx 行改写回调(M28 / M30 共用)」删、「,M28 行改写回调交回裸对象 —— 三处都是 tsc 错」改成「 —— 两处都是 tsc 错」;`test/checkpoint-ctx-credential-seal-db-integration.test.ts`:「另有各随一条一次性迁移的格」起到「别的文件的行一个都不碰);」止整段删;" +
506
456
  "`http/checkpoint-ctx.ts` 的拒句铸点 `retiredRequestKeyRefusal`:模板串里从「If whoever operates this deployment restarts this server on or before 〈窗末日〉 (UTC), the stored request is rewritten in place and the same request can be sent again; otherwise submit」起的整段换成大写开头的「Submit」(窗关后重启不再改写;只删半句会留下句首小写、悬空的 otherwise),模板串变回整句双引号字面 —— 删后整句(逐字;冻结表那一行搬回字面表时也是这一句):「this task was suspended by an earlier server version and its stored request still carries promptProfile, a request key this version no longer reads (it was replaced by the boolean promptLean: classic is promptLean: false, simple is leaving promptLean out), so continuing would run the task with a prompt form it was not submitted with. Nothing was decided and nothing was resumed. Submit the work again as a new task with promptLean in place of promptProfile, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel).」," +
507
457
  "它的第二参数 `rewriteWindowEnd` 同删(定义处与私有的 `storedBodyReplayArmAnswer` 里 `retired_request_key` 臂上的那一处调用),`StoredBodyReplayRefusal` 的 `retired_request_key` 臂上的必填字段 `rewriteWindowEnd` 同删;" +
508
458
  "`src/trace/self-minted-wire.ts` 的 `refusalSentenceForm` 里「409 checkpoint.unsupported_version#retired_request_key」那一行(自铸句型登记):骨架 attr 尾上的「submit the work again」改成「Submit the work again」(删后整句的形),since 改写成删码所在的版本(该表的律:改 attr 的同时改 since);" +
509
459
  "`http/checkpoint-ctx.ts` 的日志字段铸点 `storedBodyReplayLogFields` 里 `retired_request_key` 臂的 `note` 句改成常设拒臂的句子(原句里「the one-time rewrite at startup did not reach it」与「or a restart of this version inside the window rewrites the row」两半删码后成假话,与本行 afterRemoval 的「出路 = 重提」矛盾;" +
510
460
  "改后句:「this session's stored request still carries a request key this version retired and nothing rewrites it any more; it is not replayed — the task stays suspended until someone cancels it and resubmits it as a new task (promptLean in place of promptProfile)」);" +
511
- "同族注释(每个半句只归本条或 M28 条之一,不重复改;标〔另一条的〕的几处住在另一条迁移的文件 / 头注段里,那条已先删则宿主已不在,跳过):`src/http/checkpoint-ctx.ts`:「两条出路(运维在一次性迁移的窗内重启本服务 ⇒ 起服改写存体、同一请求可再发;否则作为新任务重提,仍停驻的先取消)」改成「出路(作为新任务重提,仍停驻的先取消)」、「**窗的日期不手写**」起到「删法见登记表 M30 的」止整段删、「存体里的旧键由一次性迁移在起服时原地改写」改成「存体里的旧键没有迁移改写」、「(M30,`stored-request-prompt-profile-m30.ts`,行改写经本模块的 {@link rewriteStoredBodyKeys})。改写不到的残留行」改成「仍带旧键的残留行」、「;存体由 M30 起服改写」删、「与 {@link rewriteStoredBodyKeys}」删、「(存量行非凭据顶层体键的改写,凭据位与读法记号原样随行)」删;`src/http/wire-types.ts`:「由一次性迁移 M30 在起服时按同一张映射原地改写」改成「没有迁移改写」、「(`stored-request-prompt-profile-m30.ts`;M30 · 窗口见 `plugins/one-time-migrations.ts` 登记表)」删;`src/spec-fields.ts`:「由一次性迁移 M30 在起服时改写成 `promptLean`(`stored-request-prompt-profile-m30.ts`),」改成「不改写,」;`src/boot/stage-06-runners.ts`:「M30(存体里的退役请求键 `promptProfile` 原地改写)同在这里、」删、「(同一只 ctx 行改写店腿;不要封签器),同样早于任何续跑入口。」删;`src/plugins/checkpoint-store-sql.ts`:「、M30(退役请求键改写)」删;`src/plugins/one-time-migrations.ts`:〔另一条的〕「M30 用同一只;」删;`src/checkpoint-ctx-seal-m28.ts`:〔另一条的〕「(M30 用同一只)」删;`test/task-agents-876.test.ts`:「体由一次性迁移 M30 在起服时原地改写」改成「体没有迁移改写」;`test/allow-list-reading-park-refusal.test.ts`:「一次性迁移 M30 没改写到的残留」改成「没有迁移改写的残留」;`test/api-error-text-freeze.test.ts`:「一次性迁移 M30 没改写到的残留」改成「没有迁移改写的残留」、「洞 = 窗的最后一天(绝对日期)」起到「不手写。」止整段删;" +
461
+ "同族注释(每个半句只归本条或 M28 条之一,不重复改;标〔另一条的〕的几处住在另一条迁移的文件 / 头注段里,那条已先删则宿主已不在,跳过):`src/http/checkpoint-ctx.ts`:「两条出路(运维在一次性迁移的窗内重启本服务 ⇒ 起服改写存体、同一请求可再发;否则作为新任务重提,仍停驻的先取消)」改成「出路(作为新任务重提,仍停驻的先取消)」、「**窗的日期不手写**」起到「删法见登记表 M30 的」止整段删、「存体里的旧键由一次性迁移在起服时原地改写」改成「存体里的旧键没有迁移改写」、「(M30,`stored-request-prompt-profile-m30.ts`,行改写经本模块的 {@link rewriteStoredBodyKeys})。改写不到的残留行」改成「仍带旧键的残留行」、「;存体由 M30 起服改写」删、「与 {@link rewriteStoredBodyKeys}」删、「(存量行非凭据顶层体键的改写,凭据位与读法记号原样随行)」删;`src/http/wire-types.ts`:「由一次性迁移 M30 在起服时按同一张映射原地改写」改成「没有迁移改写」、「(`stored-request-prompt-profile-m30.ts`;M30 · 窗口见 `plugins/one-time-migrations.ts` 登记表)」删;`src/spec-fields.ts`:「由一次性迁移 M30 在起服时改写成 `promptLean`(`stored-request-prompt-profile-m30.ts`),」改成「不改写,」;`src/boot/stage-06-runners.ts`:「M30(存体里的退役请求键 `promptProfile` 原地改写)同在这里、」删、「(同一只 ctx 行改写店腿;不要封签器),同样早于任何续跑入口。」删;`src/plugins/checkpoint-store-face.ts`:「、M30(退役请求键改写)」删;`src/plugins/one-time-migrations.ts`:〔另一条的〕「M30 用同一只;」删;`src/checkpoint-ctx-seal-m28.ts`:〔另一条的〕「(M30 用同一只)」删;`test/task-agents-876.test.ts`:「体由一次性迁移 M30 在起服时原地改写」改成「体没有迁移改写」;`test/allow-list-reading-park-refusal.test.ts`:「一次性迁移 M30 没改写到的残留」改成「没有迁移改写的残留」;`test/api-error-text-freeze.test.ts`:「一次性迁移 M30 没改写到的残留」改成「没有迁移改写的残留」、「洞 = 窗的最后一天(绝对日期)」起到「不手写。」止整段删;" +
512
462
  "`http/stored-body-replay.ts` 里 `RETIRED_KEY_REWRITE_MIGRATION` 的 JSDoc 随表删;" +
513
463
  "`http/stored-body-replay.ts` 的 `RETIRED_KEY_REWRITE_MIGRATION`(`satisfies Record<ReplayRefusedRetiredKey, string>`:M30 删后 `promptProfile` 仍是退役键,这张表只能整张删)、" +
514
464
  "判据里 `rewriteWindowEnd: migrationWindowEnd(…)` 那一项与 `migrationWindowEnd` 的引入行;`plugins/one-time-migrations.ts` 导出的 `migrationWindowEnd` 在无别的读者时一起删;" +
@@ -547,7 +497,7 @@ export const ONE_TIME_MIGRATIONS = [
547
497
  "`RETIRED_DEVICE_BINDING_TABLE` / `PARKED_DEVICE_BINDING_TABLE` / `FENCE_ROOTS_SHOWN`、`ExecutionPlacementSwitchRefusedError` / `EXECUTION_PLACEMENT_SWITCH_REFUSED_CODE` / `ExecutionPlacementSwitchRefusalReason`)" +
548
498
  "与 DDL 前探针(`assertExecutionPlacementSwitchAdmissible` / `assertExecutionPlacementSwitchAdmissibleOnPool`,`store-backend.ts` 的 `withRetiredSchemaProbe` 里调它的那一行);" +
549
499
  "`runSqlOneTimeMigrations` 的第三参 `ExecutionPlacementSwitchInput`(若再无别的条目用它)与它在两只 SQL 后端 / 测试夹具上的实参;local 腿(`LocalBackend.runOneTimeMigrations` 里的 `backfillLocalExecutionPlacements` 与 `FileRunStore.sessionIdsWithRuns`);" +
550
- "`execution-placement-sql.ts` 的 `backfillWriterStatement` / `migrateDeviceBindingsStatement` / `countOrphanBindingsStatement`;报告、日志行与 `NO_SQL_MIGRATIONS` 的 `executionPlacementColumnsAdded` / `deviceBindingsMoved` / " +
500
+ "`execution-placement-sql.ts` 的 `backfillWriterStatement` / `migrateDeviceBindingsStatement` / `migrateDeviceBindingsOntoSideTableStatements` / `countOrphanBindingsStatement`;报告、日志行与 `NO_SQL_MIGRATIONS` 的 `executionPlacementColumnsAdded` / `deviceBindingsMoved` / " +
551
501
  "`deviceBindingsOrphaned` / `deviceBindingTablesDropped` / `executionPlacementsBackfilled` / `executionPlacementSessionsUnreadable` 与 `device_bindings_orphaned` / `execution_placement_session_unreadable` 两行 warn;`test/execution-placement-switch-db-integration.test.ts` 整份与 `test/one-time-migrations.test.ts` 的 M32 组。" +
552
502
  "**local 腿的回填席与它的点名词一并删**:`LocalSessionStore` 的 `executionPlacementBackfill`(属性与构造里的那一臂)、`execution-placement.ts` 的 `LocalPlacementBackfillSeat` / `UnusableArchive` / `UnusableArchiveWord`(含它的 `storage` 词 —— 读存档文件时抛了别的故障、`errno` 随带,该词只有回填点名用)/ `isUnusableArchive`、" +
553
503
  "`ExecutionPlacementSessionUnreadable`;`local-session-store.ts` 里 `placementRead` 并回 `readExecutionPlacement`、`metaRead` 的 unusable 臂与 `forkingOf` 的 `storage` 臂改成就地上抛 `probe.fault`(`archiveFaultWord` / `unusableArchiveOf` 随词删,探针只留「不在」与已知在而用不了的读法);" +
@@ -611,14 +561,14 @@ export const ONE_TIME_MIGRATIONS = [
611
561
  removal: "**删码前提**:本服务已不支持回滚到引擎 7.61.0 之前的版本(更早的引擎的导入门会重新铸出这种 add,本条要在每次起服把它们收掉)—— 窗关时仍支持这种回滚 ⇒ 本行两格顺延(改日期,并在本注里写明为什么),不删。" +
612
562
  "删什么:`src/plugins/imported-layer-root-retirement.ts` 整个文件;migrateStepsOn 的 M36 步(规则表与 `grammar` 列的在场探针 + 对 `retireImportedLayerRootAllows` 的调用)与握锁连接上的 `query` 口" +
613
563
  "(`LockedMigrationDb` 与 `mysqlDb` / `pgDb` 里的那一员,若届时再无别的步骤用它);`permission-rule-store-sql.ts` 的 `listRuleBucketOwners` / `isSqlBucketRefusal` / `isSqlBucketVanished` / `RuleBucketVanishedError`(取点那一句的具名类,届时若再无别的读者按类判它,收回成裸错)/ `IMPORT_RECORD_KIND`;" +
614
- "`permission-rule-store-file.ts` 的 `FileRuleApprovalRecordStore.importRecordOwners`;`store-backend.ts` 的 `LocalBackend.runOneTimeMigrations` 里 M36 那一腿(规则目录在场判与那一处调用)与报告两键的那两行;" +
615
- "本文件的 `createSqlImportedLayerRootSource` 与 `importedLayerRootAllowsRetired` / `importedLayerRootBucketsSkipped`(报告 / 收据 / `changedAnything` / `readingsSnapshot` / `readingFields` / `receiptSentence` / `NO_SQL_MIGRATIONS` 的排除表)、" +
616
- "`imported_layer_root_allow_retired` / `imported_layer_root_bucket_skipped` 两行 warn 与 `importedLayerRootSkipNote`、读数行 note 里的 M36 句与头注 M36 段;" +
564
+ "`permission-rule-store-file.ts` 的 `FileRuleApprovalRecordStore.importRecordOwners` / `fileRuleBucketOwners` / `isFileBucketDamaged`;`store-backend.ts` 的 `LocalBackend.runOneTimeMigrations` 里 M36 那一腿(规则目录在场判与那一处调用)与报告两键的那两行;" +
565
+ "本文件的 `createSqlImportedLayerRootSource` 与 `importedLayerRootAllowsRetired` / `importedLayerRootBucketsSkipped` / `importedLayerRootBucketsUnnamed`(报告 / 收据 / `changedAnything` / `readingsSnapshot` / `readingFields` / `receiptSentence` / `NO_SQL_MIGRATIONS` 的排除表)、" +
566
+ "`imported_layer_root_allow_retired` / `imported_layer_root_bucket_skipped` / `imported_layer_root_buckets_unnamed` 三行 warn 与 `importedLayerRootSkipNote`、读数行 note 里的 M36 句与头注 M36 段;" +
617
567
  "`test/imported-layer-root-retirement.test.ts`、`test/imported-layer-root-retirement-db-integration.test.ts`(连同 `scripts/db-integration-files.json` 里它那一行)与 `test/helpers/imported-layer-root-fixture.ts` 整文件。" +
618
568
  "**不随本条删**:`SqlDurableRulePartitionProvider` 收「方言 + 执行口」的那一形(`BucketSql`;常设,池照旧满足它)",
619
569
  afterRemoval: "没有响亮臂(如实):删码之后,跳过了带本条的版本直接升的库(或回滚到引擎 7.61.0 之前、在其上又导入过的库),存量的这种全局 `/x` allow 照旧在每个项目根放行,本服务不再点名 —— " +
620
570
  "出路 = 升级时先过一次带本条的版本并起服;或运维列出桶里出处为导入(`adds[].origin` = `imported-cc`)、scope 为全局、从 `/` 写起的 allow(`GET /v1/rules`)并撤掉(`DELETE /v1/rules`,撤的是这条规则的全部批准)",
621
- reportKeys: ["importedLayerRootAllowsRetired", "importedLayerRootBucketsSkipped"],
571
+ reportKeys: ["importedLayerRootAllowsRetired", "importedLayerRootBucketsSkipped", "importedLayerRootBucketsUnnamed"],
622
572
  },
623
573
  {
624
574
  id: "M37",
@@ -632,6 +582,60 @@ export const ONE_TIME_MIGRATIONS = [
632
582
  "幂等重入走 park、回决口按要人",
633
583
  reportKeys: [],
634
584
  },
585
+ {
586
+ id: "M38",
587
+ introducedIn: "7.146.0",
588
+ releasedOn: "2026-11-06",
589
+ removableAfter: "2026-12-06",
590
+ removal: "删 `src/stored-request-env-credentials-m38.ts` 整只文件(`storedEnvCredentialsDropM38`);" +
591
+ "`boot/resolve-spec.ts` 里凭据判据那一段的重放腿分支(`opts?.leg !== \"fresh\" && storedEnvCredentialsDropM38(…)` 那一臂、`withoutKeys` 那一行、`settings_env_credentials_dropped` 那一行 warn)与 `storedEnvCredentialsDropM38` / `withoutKeys` 的 import —— 判据命中改为两腿一律抛,`settingsEnv` 改回 `const`;" +
592
+ "`task-settings.ts` 的 `withoutKeys`(`readSettingsEnv` 一只读法、`ParseTaskSettingsSeats.settingsEnv` 座与 `resolveSpec` 两腿递它**不删**:判据与装配读同一份是常设的,不随窗走;删后 `reaching` 不再出现 `{}`,`SettingsEnvLayer` 文档里那半句一起删);" +
593
+ "`http/stored-body-replay.ts` 预判那一臂里的窗问(`storedEnvCredentialsDropM38(…)`,删后命中即拒)与它的 import;" +
594
+ "`test/settings-env-credential.test.ts` 里窗开的三格(剥键照续 / 剥空登记空表 / 预判窗开不拦)与「窗关当天仍开」那格、`withM38Window` 助手(窗关的几格改成不注入窗直接断言拒)、M38 登记行那一格;本文件头注 M38 段",
595
+ afterRemoval: "没有新的臂,接住它的是窗关之后的读法(删码零行为变化:窗关那一刻起剥键臂已经不生效):存体 `settings.env` 里仍带凭据条目的停驻行,续跑前的预判拒(409 `resume_blocked_by_policy`,`blockedBy: request.env_credential_refused`,机读位点名键与命中类),卡仍 pending —— " +
596
+ "出路是取消那条 run,把凭据挪进引擎进程环境后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行、窗内续跑过又停驻到窗外的任务(头注 M38 段)",
597
+ reportKeys: [],
598
+ },
599
+ {
600
+ id: "M39",
601
+ introducedIn: "7.147.0",
602
+ releasedOn: "2026-11-07",
603
+ removableAfter: "2026-12-07",
604
+ removal: "删 `src/plugins/local-data-dir-modes-m39.ts` 整只文件;`store-backend.ts` 里 `LocalBackend.runOneTimeMigrations` 开头调它的那一段(两只日志座的接线)、`dataDirLayout` 字段与它的赋值、顶部的 import(含 `memoryEngineRootFor`),以及 `runOneTimeMigrations` 与 `discloseWarn` 注释里点名 M39 的那两句;" +
605
+ "各店为名册导出的目录路径函数(`fileRunStoreDirsFor` / `localCheckpointStoreDirsFor` / `attachmentStoreDirFor` / `scratchpadRootFor` / `memorySyncCursorDirFor`)只剩本店自己读的去掉 `export` 与注释里的 M39 一句(`pluginCacheRootFor` 组合根仍读,留);" +
606
+ "`test/data-dir-modes-upgrade-m39.test.ts` 整只文件;`test/data-dir-law-census.test.ts` 例外名册里 `plugins/local-data-dir-modes-m39.ts` 那一行、模式值例外名册里它那一行、「M39 名册零字面路径」一格与「字面量 = ensureDir 铸出的模式」一格里的迁移半场;本文件头注 M39 段;重生成 `docs/core-consumption/READ-TABLE.json`(迁移读 core 的 `physicalPathOf` 那一行随之去掉)。" +
607
+ "**不随本条删**:数据根建目录只经 `ensureDir` 这条律本身(常设)、`plugins/owner-only-file-mode.ts`(本服务文件的 `0600` 唯一值与原子写的两个出口:异步出口常设,同步出口按它自己的退役条件 —— core 的原子写改成独占新建 tmp —— 走,不随本条)及它们的钉 —— `test/data-dir-law-census.test.ts`(`src/**` 零裸 mkdir、零手写 0o700、文件模式只认一个名字、tmp 只在一只写口里铸)与 `test/data-dir-modes.test.ts`(每只 local 店首建目录 0700、旧 tmp 不被复用)",
608
+ afterRemoval: "没有响亮臂(如实,也无需):删码之后,跳过了带本条的版本直接升的数据根,升级前铸成的 0755 目录原样留着(新建的目录一律 0700,不受影响;里面的文件多半仍是 0600),本服务不再点名 —— " +
609
+ "出路 = 运维一次 `chmod 700` 数据根下 `runs`(及其子目录)· `space-commits` · `checkpoint-ctx` · `.tmp` · `attachments` · `scratchpad`(及其子目录)· `plugin-cache`(及其子目录)· `<记忆引擎根>/sync-cursors`,或升级时先过一次带本条的版本并起服",
610
+ reportKeys: [],
611
+ },
612
+ {
613
+ id: "M40",
614
+ introducedIn: "7.147.0",
615
+ releasedOn: "2026-11-07",
616
+ removableAfter: "2026-12-07",
617
+ removal: "删 `src/plugins/execution-lane-rename-m40.ts` 整只文件;本文件的 M40 步(`renameRetiredExecutionLaneWordsM40` / `refuseWhileRunsHeartbeat`〔它读的 `readLiveRunHeartbeat` / `LiveRunHeartbeat` 由 M41 共读,M41 也已删码时随之删〕/ `ExecutionLaneRenameRefusedError` / " +
618
+ "`EXECUTION_LANE_RENAME_REFUSED_CODE` / `ExecutionLaneRenameInput`,`migrateStepsOn` 第一步那一行与 `SqlOneTimeMigrationInput` 里的那一半)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `executionLaneWordsRenamed`、头注 M40 段;" +
619
+ "`store-backend.ts` 的 `LocalBackend.ensureSchema` 第一步与 `executionLaneWordsRenamed` 字段、两只 SQL 后端递 `liveRunWindowMs` 的那一半;" +
620
+ "`LocalSessionStore.renameRetiredExecutionLaneWordM40`、`FileApprovalAskStore.renameRetiredExecutionLaneWordM40` 与内核的 `renameExecutionLaneWord`;" +
621
+ "`approval-ask-store-sql.ts` / `execution-placement-sql.ts` 拒句里的 `retiredExecutionLaneHintM40(…)` 补句;`test/execution-lane-rename-m40.test.ts` 整只文件与 `test/execution-placement-switch-db-integration.test.ts` 的 M40 组",
622
+ afterRemoval: "既有读口的拒臂接住(响亮,不再改名):仍带旧词的行 —— 跳过了带本条的版本直接升的库、窗关之后还在写的更早版本副本 —— SQL 问行读口拒、会话执行格读口拒(`ExecutionPlacementRowError`)," +
623
+ "local 会话元数据边档整只严格拒启(`config.schema_mismatch`),local 问账日志由 M32 回填的词表外拒臂拒启(M32 删码之后:那只问行原样装回)。出路 = 升级时先过一次带本条的版本并起服",
624
+ reportKeys: ["executionLaneWordsRenamed"],
625
+ },
626
+ {
627
+ id: "M41",
628
+ introducedIn: "7.147.0",
629
+ releasedOn: "2026-11-07",
630
+ removableAfter: "2026-12-07",
631
+ removal: "删本文件的 M41 步(`moveSessionCellsToSideTable` / `refuseSessionExtMoveWhileRunsHeartbeat` / `deploymentColumnsOnMeta` / `SessionExtMoveRefusedError` / `SESSION_EXT_MOVE_REFUSED_CODE` / " +
632
+ "`SessionExtMoveRefusalReason` / `sameCell` / `SESSION_EXT_MOVE_PAGE`,`migrateStepsOn` 里那两行)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `sessionExtRowsMoved` / `sessionMetaColumnsDropped`、头注 M41 段;" +
633
+ "M32 读形里的 `cellsOnSideTable` 判(它读本条的 `deploymentColumnsOnMeta`):M32 还在时改成恒旁表形 —— 删 M32 写 `session_meta` 八列的那些臂(② ④ ④b ⑥ 与 ③ 的 `migrateDeviceBindingsStatement` 臂)," +
634
+ "留 ③ 的旁表臂(`migrateDeviceBindingsOntoSideTableStatements`:围栏拒句的「采旧」在任何库上仍要能落到执行格);M32 先于本条删码时这些随 M32 一并删;`readLiveRunHeartbeat` 在 M40 也已删码时随之删;`test/session-ext-move-db-integration.test.ts` 整只文件",
635
+ afterRemoval: "跳过了带本条的版本直接升上来的库:`session_meta` 上仍带部署格列 ⇒ 起服列册对账拒启(`config.schema_mismatch`,那几列是本树之外的多余列,拒句不给 ALTER —— 出路是带本条的版本);" +
636
+ "部署格从旁表读,旧列上的值不被任何读者读。出路 = 升级时先过一次带本条的版本并起服",
637
+ reportKeys: ["sessionExtRowsMoved", "sessionMetaColumnsDropped"],
638
+ },
635
639
  ];
636
640
  export function migrationWindowOpen(window, today) {
637
641
  return window.removableAfter === null || today <= window.removableAfter;
@@ -645,10 +649,6 @@ export function migrationWindowEnd(id) {
645
649
  return row.removableAfter;
646
650
  }
647
651
  export const NO_SQL_MIGRATIONS = {
648
- allowRuleShadowRowsMigrated: 0,
649
- allowRuleShadowRowsUnreadable: 0,
650
- workflowParkIndexKey: "file-ledger",
651
- workflowParkIndexRowsCarried: 0,
652
652
  undurableScopeRuleRows: [],
653
653
  undurableScopeRuleBucketsDeferred: 0,
654
654
  undurableScopeRuleBucketsUnreadable: [],
@@ -677,6 +677,8 @@ export const NO_SQL_MIGRATIONS = {
677
677
  deviceBindingsMoved: 0,
678
678
  deviceBindingsOrphaned: 0,
679
679
  deviceBindingTablesDropped: 0,
680
+ sessionExtRowsMoved: 0,
681
+ sessionMetaColumnsDropped: 0,
680
682
  askCauseColumnsChanged: 0,
681
683
  askParkTriggersMoved: 0,
682
684
  };
@@ -733,7 +735,7 @@ async function columnExists(db, dialect, table, column) {
733
735
  return (await db.rows(sql)).length > 0;
734
736
  }
735
737
  export function createSqlImportedLayerRootSource(exec) {
736
- return { candidateOwners: () => listRuleBucketOwners(exec), durable: new SqlDurableRulePartitionProvider(exec), isWholeBucketRefusal: isSqlBucketRefusal, isVanishedBucket: isSqlBucketVanished };
738
+ return { candidateOwners: async () => ({ owners: await listRuleBucketOwners(exec), unnamedBuckets: 0 }), durable: new SqlDurableRulePartitionProvider(exec), isWholeBucketRefusal: isSqlBucketRefusal, isVanishedBucket: isSqlBucketVanished };
737
739
  }
738
740
  export async function runSqlOneTimeMigrations(dialect, pool, input) {
739
741
  return dialect === "mysql" ? withMysqlLock(pool, (db) => migrateOn(db, "mysql", input)) : withPgLock(pool, (db) => migrateOn(db, "pg", input));
@@ -777,16 +779,10 @@ async function migrateOn(db, dialect, input) {
777
779
  return completedReport(receipts);
778
780
  }
779
781
  async function migrateStepsOn(db, dialect, receipts, input) {
780
- receipts.holderDeathCodeRowsBackfilled += await db.exec(dialect === "mysql"
781
- ? "UPDATE task_run SET error_code = ? WHERE status = 'failed' AND error_code IS NULL AND error = ?"
782
- : "UPDATE task_run SET error_code = $1 WHERE status = 'failed' AND error_code IS NULL AND error = $2", [RUN_HOLDER_DEATH_CODES.stalled, RUN_STALLED_ERROR]);
783
- receipts.holderDeathCodeRowsBackfilled += await db.exec(dialect === "mysql"
784
- ? "UPDATE task_run SET error_code = ? WHERE status = 'failed' AND error_code IS NULL AND (error = ? OR error LIKE ?)"
785
- : "UPDATE task_run SET error_code = $1 WHERE status = 'failed' AND error_code IS NULL AND (error = $2 OR error LIKE $3)", [RUN_HOLDER_DEATH_CODES.orphaned, RUN_ORPHANED_ERROR, `${RUN_ORPHANED_ERROR} (%`]);
786
- await migrateShadowedRuleKey(db, dialect, receipts);
782
+ await renameRetiredExecutionLaneWordsM40(db, dialect, receipts, input);
783
+ await refuseSessionExtMoveWhileRunsHeartbeat(db, dialect, input);
787
784
  await migrateRetiredCardWords(db, dialect, receipts);
788
785
  await splitApprovalAskCauseColumns(db, dialect, receipts);
789
- await reshapeWorkflowParkIndex(db, dialect, receipts);
790
786
  await migrateSessionRuleGrammar(db, dialect, receipts);
791
787
  await repairParkedRows(db, dialect, receipts);
792
788
  await backfillParkedApprovalCards(db, dialect, receipts);
@@ -802,6 +798,149 @@ async function migrateStepsOn(db, dialect, receipts, input) {
802
798
  await dropRetiredBreakerTable(db, dialect, receipts);
803
799
  await dropRejectedMailboxKeys(db, dialect, receipts);
804
800
  await switchExecutionPlacement(db, dialect, receipts, input);
801
+ await moveSessionCellsToSideTable(db, dialect, receipts);
802
+ }
803
+ export const EXECUTION_LANE_RENAME_REFUSED_CODE = "config.execution_lane_rename_refused";
804
+ export class ExecutionLaneRenameRefusedError extends StoreBootRefusalError {
805
+ pendingRows;
806
+ liveRuns;
807
+ restartAfter;
808
+ code = EXECUTION_LANE_RENAME_REFUSED_CODE;
809
+ constructor(pendingRows, liveRuns, restartAfter, detail) {
810
+ super(`${EXECUTION_LANE_RENAME_REFUSED_CODE}: ${detail}`);
811
+ this.pendingRows = pendingRows;
812
+ this.liveRuns = liveRuns;
813
+ this.restartAfter = restartAfter;
814
+ this.name = "ExecutionLaneRenameRefusedError";
815
+ }
816
+ }
817
+ export class ExecutionLaneRenameWriteRefusedError extends StoreBootRefusalError {
818
+ file;
819
+ code = EXECUTION_LANE_RENAME_REFUSED_CODE;
820
+ errno;
821
+ constructor(file, cause) {
822
+ const errno = typeof cause?.code === "string" ? cause.code : undefined;
823
+ const fault = errno ?? (cause instanceof Error ? cause.message : String(cause));
824
+ super(`${EXECUTION_LANE_RENAME_REFUSED_CODE}: one-time migration M40 could not rewrite ${file} (${fault}) — it still records the retired execution-lane word ` +
825
+ `"${RETIRED_EXECUTION_LANE_WORD_M40}" (now "${RENAMED_EXECUTION_LANE_M40}"), which this build refuses to read, so it refuses to start rather than run without that data. ` +
826
+ `The file is unchanged. Restore write access to the file and its directory (clear an immutable flag, remount read-write, free disk space), then restart: the rename runs again on the next start.`, { cause });
827
+ this.file = file;
828
+ this.name = "ExecutionLaneRenameWriteRefusedError";
829
+ this.errno = errno;
830
+ }
831
+ }
832
+ const EXECUTION_LANE_TABLES = [APPROVAL_ASK_TABLE, "session_meta", SESSION_EXT_TABLE];
833
+ export async function renameRetiredExecutionLaneWordsM40(db, dialect, receipts, input) {
834
+ const tables = [];
835
+ for (const table of EXECUTION_LANE_TABLES)
836
+ if (await columnExists(db, dialect, table, "execution_lane"))
837
+ tables.push(table);
838
+ let pendingRows = 0;
839
+ for (const table of tables) {
840
+ const [row] = await db.rows(`SELECT COUNT(*) AS n FROM ${table} WHERE execution_lane = ${dialect === "mysql" ? "?" : "$1"}`, [RETIRED_EXECUTION_LANE_WORD_M40]);
841
+ pendingRows += Number(row?.["n"] ?? 0);
842
+ }
843
+ if (pendingRows === 0)
844
+ return;
845
+ await refuseWhileRunsHeartbeat(db, dialect, pendingRows, input);
846
+ for (const table of tables) {
847
+ receipts.executionLaneWordsRenamed += await db.exec(dialect === "mysql" ? `UPDATE ${table} SET execution_lane = ? WHERE execution_lane = ?` : `UPDATE ${table} SET execution_lane = $1 WHERE execution_lane = $2`, [RENAMED_EXECUTION_LANE_M40, RETIRED_EXECUTION_LANE_WORD_M40]);
848
+ }
849
+ }
850
+ async function readLiveRunHeartbeat(db, dialect, input) {
851
+ if (!(await tableExists(db, dialect, "task_run")))
852
+ return undefined;
853
+ const since = new Date(Date.now() - input.liveRunWindowMs);
854
+ const [row] = await db.rows(`SELECT COUNT(*) AS n, MAX(updated_at) AS newest FROM task_run WHERE status = 'running' AND updated_at >= ${dialect === "mysql" ? "?" : "$1"}`, [since]);
855
+ const liveRuns = Number(row?.["n"] ?? 0);
856
+ if (liveRuns === 0)
857
+ return undefined;
858
+ const newestCell = row?.["newest"];
859
+ const newest = newestCell instanceof Date ? newestCell : new Date(String(newestCell));
860
+ const now = Date.now();
861
+ const restartAtMs = (Number.isNaN(newest.getTime()) ? now : newest.getTime()) + input.liveRunWindowMs;
862
+ return {
863
+ liveRuns,
864
+ windowSec: Math.round(input.liveRunWindowMs / 1000),
865
+ restartAfter: new Date(restartAtMs).toISOString(),
866
+ after: `${new Date(restartAtMs).toISOString()} (${Math.max(0, Math.ceil((restartAtMs - now) / 1000))}s from now)`,
867
+ };
868
+ }
869
+ async function refuseWhileRunsHeartbeat(db, dialect, pendingRows, input) {
870
+ const heartbeat = await readLiveRunHeartbeat(db, dialect, input);
871
+ if (heartbeat === undefined)
872
+ return;
873
+ const { liveRuns, windowSec, restartAfter, after } = heartbeat;
874
+ throw new ExecutionLaneRenameRefusedError(pendingRows, liveRuns, restartAfter, `${pendingRows} stored rows (approval asks / sessions) carry the execution lane word "${RETIRED_EXECUTION_LANE_WORD_M40}", the name an earlier version gave the lane with no execution environment, ` +
875
+ `which this version calls "${RENAMED_EXECUTION_LANE_M40}" and does not read; this boot renames them (one-time migration M40), but ${liveRuns} running runs on this database heartbeat within the last ${windowSec}s ` +
876
+ `(REAP_RUN_STALE_SEC) — a replica of an earlier version is still serving, and it would keep writing the old word after the rename. No stored row was changed. ` +
877
+ "Stop every replica of the earlier version, then start this version (stop, migrate, start — this release cannot be rolled across replicas of both versions). " +
878
+ `If every replica is already stopped, those are the runs they left behind: start this version again after ${after}`);
879
+ }
880
+ export const SESSION_EXT_MOVE_REFUSED_CODE = "config.session_ext_move_refused";
881
+ export class SessionExtMoveRefusedError extends StoreBootRefusalError {
882
+ reason;
883
+ code = SESSION_EXT_MOVE_REFUSED_CODE;
884
+ constructor(reason, detail) {
885
+ super(`${SESSION_EXT_MOVE_REFUSED_CODE} (reason: ${reason}): ${detail}`);
886
+ this.reason = reason;
887
+ this.name = "SessionExtMoveRefusedError";
888
+ }
889
+ }
890
+ async function deploymentColumnsOnMeta(db, dialect) {
891
+ if (!(await tableExists(db, dialect, "session_meta")))
892
+ return [];
893
+ const present = [];
894
+ for (const column of SESSION_EXT_COLUMNS)
895
+ if (await columnExists(db, dialect, "session_meta", column.name))
896
+ present.push(column);
897
+ return present;
898
+ }
899
+ const sameCell = (dialect, column) => {
900
+ const side = column.name === "pinned" ? "COALESCE(se.pinned, FALSE)" : `se.${column.name}`;
901
+ return dialect === "mysql" ? `sm.${column.name} <=> ${side}` : `sm.${column.name} IS NOT DISTINCT FROM ${side}`;
902
+ };
903
+ const SESSION_EXT_MOVE_PAGE = 500;
904
+ export async function refuseSessionExtMoveWhileRunsHeartbeat(db, dialect, input) {
905
+ const columns = await deploymentColumnsOnMeta(db, dialect);
906
+ if (columns.length === 0)
907
+ return;
908
+ const heartbeat = await readLiveRunHeartbeat(db, dialect, input);
909
+ if (heartbeat === undefined)
910
+ return;
911
+ throw new SessionExtMoveRefusedError("live_runs", `${columns.length} session cells (${columns.map((c) => c.name).join(", ")}) still live on session_meta; this version keeps them on the side table ${SESSION_EXT_TABLE} and moves them on this boot ` +
912
+ `(one-time migration M41: copy, verify, then drop the columns), but ${heartbeat.liveRuns} running runs on this database heartbeat within the last ${heartbeat.windowSec}s (REAP_RUN_STALE_SEC) — ` +
913
+ "a replica of an earlier version is still serving; a cell it writes between the copy and the drop would be lost, and every cell write it makes after the drop fails. Nothing was changed. " +
914
+ "Stop every replica of the earlier version, then start this version (stop, migrate, start — this release cannot be rolled across replicas of both versions). " +
915
+ `If every replica is already stopped, those are the runs they left behind: start this version again after ${heartbeat.after}`);
916
+ }
917
+ export async function moveSessionCellsToSideTable(db, dialect, receipts) {
918
+ const columns = await deploymentColumnsOnMeta(db, dialect);
919
+ if (columns.length === 0)
920
+ return;
921
+ const names = columns.map((c) => c.name);
922
+ const p = dialect === "mysql" ? () => "?" : (n) => `$${n}`;
923
+ const upsert = `INSERT INTO ${SESSION_EXT_TABLE} (session_id, ${names.join(", ")}) SELECT session_id, ${names.join(", ")} FROM session_meta ` +
924
+ `WHERE session_id > ${p(1)} AND session_id <= ${p(2)} ` +
925
+ (dialect === "mysql" ? `ON DUPLICATE KEY UPDATE ${names.map((n) => `${SESSION_EXT_TABLE}.${n} = VALUES(${n})`).join(", ")}` : `ON CONFLICT (session_id) DO UPDATE SET ${names.map((n) => `${n} = EXCLUDED.${n}`).join(", ")}`);
926
+ let after = "";
927
+ for (;;) {
928
+ const page = await db.rows(`SELECT session_id FROM session_meta WHERE session_id > ${p(1)} ORDER BY session_id LIMIT ${SESSION_EXT_MOVE_PAGE}`, [after]);
929
+ if (page.length === 0)
930
+ break;
931
+ const last = String(page[page.length - 1]["session_id"]);
932
+ await db.exec(upsert, [after, last]);
933
+ receipts.sessionExtRowsMoved += page.length;
934
+ after = last;
935
+ }
936
+ const [mismatch] = await db.rows(`SELECT sm.session_id FROM session_meta sm LEFT JOIN ${SESSION_EXT_TABLE} se ON se.session_id = sm.session_id WHERE NOT (${columns.map((c) => sameCell(dialect, c)).join(" AND ")}) LIMIT 1`);
937
+ if (mismatch !== undefined) {
938
+ throw new SessionExtMoveRefusedError("copy_mismatch", `after copying the session cells (${names.join(", ")}) from session_meta to ${SESSION_EXT_TABLE}, session ${String(mismatch["session_id"])} does not hold the same values on both tables — ` +
939
+ "a writer changed it while this boot copied (an earlier-version replica still serving), or the copy did not land. No column was dropped. " +
940
+ "Stop every replica of the earlier version and start this version again: the copy runs again from the start");
941
+ }
942
+ await execUntil(db, `ALTER TABLE session_meta ${names.map((n) => `DROP COLUMN ${n}`).join(", ")}`, async () => (await deploymentColumnsOnMeta(db, dialect)).length === 0);
943
+ receipts.sessionMetaColumnsDropped += columns.length;
805
944
  }
806
945
  const RETIRED_ASK_CAUSE_COLUMN = "denied_reason";
807
946
  function renameAskCauseColumnSql(dialect) {
@@ -843,12 +982,13 @@ async function readSwitchShape(db, dialect) {
843
982
  const sessionMeta = await tableExists(db, dialect, "session_meta");
844
983
  const placementColumns = new Set();
845
984
  if (sessionMeta)
846
- for (const c of SESSION_META_PLACEMENT_COLUMNS)
985
+ for (const c of EXECUTION_PLACEMENT_COLUMNS)
847
986
  if (await columnExists(db, dialect, "session_meta", c.name))
848
987
  placementColumns.add(c.name);
849
988
  return {
850
989
  sessionMeta,
851
990
  placementColumns,
991
+ cellsOnSideTable: sessionMeta && (await deploymentColumnsOnMeta(db, dialect)).length === 0,
852
992
  bindingTable: await tableExists(db, dialect, RETIRED_DEVICE_BINDING_TABLE),
853
993
  parkedTable: await tableExists(db, dialect, PARKED_DEVICE_BINDING_TABLE),
854
994
  taskRun: sessionMeta && (await tableExists(db, dialect, "task_run")),
@@ -860,11 +1000,11 @@ async function askTableShape(db, dialect) {
860
1000
  return { askTable, askLaneColumn: askTable && (await columnExists(db, dialect, APPROVAL_ASK_TABLE, "execution_lane")) };
861
1001
  }
862
1002
  function switchStarted(s) {
863
- return s.placementColumns.has("execution_role") || s.parkedTable;
1003
+ return s.placementColumns.has("execution_role") || s.parkedTable || s.cellsOnSideTable;
864
1004
  }
865
1005
  const SWITCH_COMPLETION_COLUMN = "fork_key";
866
1006
  function switchComplete(s) {
867
- return s.placementColumns.size === SESSION_META_PLACEMENT_COLUMNS.length && (!s.askTable || s.askLaneColumn) && !s.bindingTable && !s.parkedTable;
1007
+ return (s.placementColumns.size === EXECUTION_PLACEMENT_COLUMNS.length || s.cellsOnSideTable) && (!s.askTable || s.askLaneColumn) && !s.bindingTable && !s.parkedTable;
868
1008
  }
869
1009
  function askLaneBackfillSql(dialect) {
870
1010
  return dialect === "mysql"
@@ -915,7 +1055,7 @@ const EXECUTION_PLACEMENT_BACKFILL_NEEDS = [
915
1055
  { what: "approval ask rows that do not record the execution lane they were raised on (they would record their root session's lane, or the only declared lane)", holds: askRowsAwaitingLane },
916
1056
  ];
917
1057
  async function refuseOnAmbiguousBackfill(read, dialect, shape, lanes) {
918
- if (!shape.sessionMeta || switchComplete(shape) || lanes.length === 1)
1058
+ if (!shape.sessionMeta || shape.cellsOnSideTable || switchComplete(shape) || lanes.length === 1)
919
1059
  return;
920
1060
  const pending = [];
921
1061
  for (const need of EXECUTION_PLACEMENT_BACKFILL_NEEDS)
@@ -968,7 +1108,7 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
968
1108
  id: "① park the retired binding table",
969
1109
  run: async ({ db, dialect, shape }) => {
970
1110
  const s = await shape();
971
- if (!s.bindingTable || s.placementColumns.has("execution_role"))
1111
+ if (!s.bindingTable || s.placementColumns.has("execution_role") || s.cellsOnSideTable)
972
1112
  return;
973
1113
  const sql = dialect === "mysql" ? `RENAME TABLE ${RETIRED_DEVICE_BINDING_TABLE} TO ${PARKED_DEVICE_BINDING_TABLE}` : `ALTER TABLE ${RETIRED_DEVICE_BINDING_TABLE} RENAME TO ${PARKED_DEVICE_BINDING_TABLE}`;
974
1114
  await execUntil(db, sql, async () => {
@@ -992,7 +1132,9 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
992
1132
  {
993
1133
  id: "② add the execution placement columns",
994
1134
  run: async ({ db, dialect, receipts, shape }) => {
995
- for (const column of SESSION_META_PLACEMENT_COLUMNS) {
1135
+ if ((await shape()).cellsOnSideTable)
1136
+ return;
1137
+ for (const column of EXECUTION_PLACEMENT_COLUMNS) {
996
1138
  if (column.name === SWITCH_COMPLETION_COLUMN)
997
1139
  continue;
998
1140
  if ((await shape()).placementColumns.has(column.name))
@@ -1010,10 +1152,17 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
1010
1152
  {
1011
1153
  id: "③ move the bindings onto the root session rows",
1012
1154
  run: async ({ db, dialect, receipts, shape }) => {
1013
- if (!(await shape()).parkedTable)
1155
+ const s = await shape();
1156
+ if (!s.parkedTable)
1014
1157
  return;
1015
1158
  const orphans = countOrphanBindingsStatement(dialect, PARKED_DEVICE_BINDING_TABLE);
1016
1159
  receipts.deviceBindingsOrphaned += Number((await db.rows(orphans.text, orphans.values))[0]?.["orphans"] ?? 0);
1160
+ if (s.cellsOnSideTable) {
1161
+ const [ensure, move] = migrateDeviceBindingsOntoSideTableStatements(dialect, PARKED_DEVICE_BINDING_TABLE);
1162
+ await db.exec(ensure.text, ensure.values);
1163
+ receipts.deviceBindingsMoved += await db.exec(move.text, move.values);
1164
+ return;
1165
+ }
1017
1166
  const move = migrateDeviceBindingsStatement(dialect, PARKED_DEVICE_BINDING_TABLE);
1018
1167
  receipts.deviceBindingsMoved += await db.exec(move.text, move.values);
1019
1168
  },
@@ -1022,6 +1171,8 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
1022
1171
  id: "④ backfill placements of sessions with runs",
1023
1172
  run: async ({ db, dialect, input, receipts, shape }) => {
1024
1173
  const s = await shape();
1174
+ if (s.cellsOnSideTable)
1175
+ return;
1025
1176
  if (input.lanes.length !== 1) {
1026
1177
  await refuseOnAmbiguousBackfill((sql) => db.rows(sql), dialect, s, input.lanes);
1027
1178
  return;
@@ -1035,7 +1186,7 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
1035
1186
  id: "④b backfill the execution lane of approval ask rows",
1036
1187
  run: async ({ db, dialect, input, receipts, shape }) => {
1037
1188
  const s = await shape();
1038
- if (!s.askTable || !s.askLaneColumn)
1189
+ if (!s.askTable || !s.askLaneColumn || s.cellsOnSideTable)
1039
1190
  return;
1040
1191
  if (input.lanes.length !== 1) {
1041
1192
  await refuseOnAmbiguousBackfill((sql) => db.rows(sql), dialect, s, input.lanes);
@@ -1056,9 +1207,10 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
1056
1207
  {
1057
1208
  id: "⑥ add the completion marker column",
1058
1209
  run: async ({ db, dialect, receipts, shape }) => {
1059
- if ((await shape()).placementColumns.has(SWITCH_COMPLETION_COLUMN))
1210
+ const s = await shape();
1211
+ if (s.placementColumns.has(SWITCH_COMPLETION_COLUMN) || s.cellsOnSideTable)
1060
1212
  return;
1061
- const marker = SESSION_META_PLACEMENT_COLUMNS.find((c) => c.name === SWITCH_COMPLETION_COLUMN);
1213
+ const marker = EXECUTION_PLACEMENT_COLUMNS.find((c) => c.name === SWITCH_COMPLETION_COLUMN);
1062
1214
  await execUntil(db, `ALTER TABLE session_meta ADD COLUMN ${marker[dialect]}`, async () => (await shape()).placementColumns.has(SWITCH_COMPLETION_COLUMN));
1063
1215
  receipts.executionPlacementColumnsAdded += 1;
1064
1216
  },
@@ -1100,9 +1252,17 @@ export async function backfillLocalExecutionPlacements(sessionIds, seat, input)
1100
1252
  }
1101
1253
  return { backfilled: placed, unreadable };
1102
1254
  }
1255
+ const UNKNOWN_LANE_ASKS_SHOWN = 5;
1103
1256
  export function backfillLocalAskExecutionLanes(store, input) {
1104
- return store.backfillExecutionLanes(input.lanes, (pending) => {
1105
- throw new ExecutionPlacementSwitchRefusedError("backfill_ambiguous", `the local approval ask journal holds asks that do not record the execution lane they were raised on (${pending}), and the deployment declares ${input.lanes.length === 0 ? "no lane" : `${input.lanes.length} lanes (${input.lanes.join(", ")})`} — ` +
1257
+ return store.backfillExecutionLanes(input.lanes, (refusal) => {
1258
+ if (refusal.kind === "unknown_lane") {
1259
+ const shown = refusal.asks.slice(0, UNKNOWN_LANE_ASKS_SHOWN).map((a) => `${a.askId} = ${JSON.stringify(a.word)}${retiredExecutionLaneHintM40(a.word) === "" ? "" : " (the retired name of none)"}`);
1260
+ const more = refusal.asks.length > shown.length ? ` and ${refusal.asks.length - shown.length} more` : "";
1261
+ throw new ExecutionPlacementSwitchRefusedError("unknown_lane", `the local approval ask journal holds ${refusal.asks.length} asks whose recorded execution lane is not a lane this version knows (${shown.join(", ")}${more}) — ` +
1262
+ "recording the deployment's lane over it would rewrite the lane the ask was raised on, and this server will not guess one. Nothing was changed. " +
1263
+ "The journal was written by a newer version or altered by hand: start the version that wrote it, or restore the journal from a copy");
1264
+ }
1265
+ throw new ExecutionPlacementSwitchRefusedError("backfill_ambiguous", `the local approval ask journal holds asks that do not record the execution lane they were raised on (${refusal.pending}), and the deployment declares ${input.lanes.length === 0 ? "no lane" : `${input.lanes.length} lanes (${input.lanes.join(", ")})`} — ` +
1106
1266
  "with more than one lane there is no single right answer, and this server will not guess one. Nothing was changed. " +
1107
1267
  "Start this version once with a single declared lane (REMOTE_EXEC set to one lane), then declare the additional lanes");
1108
1268
  });
@@ -1331,55 +1491,6 @@ function renameSessionRulesColumnSql(dialect, from, to) {
1331
1491
  const definition = SESSION_RULES_COLUMN.mysql.slice(SESSION_RULES_COLUMN.name.length);
1332
1492
  return dialect === "mysql" ? `ALTER TABLE ${SESSION_POLICY_TABLE} CHANGE COLUMN ${from} ${to}${definition}` : `ALTER TABLE ${SESSION_POLICY_TABLE} RENAME COLUMN ${from} TO ${to}`;
1333
1493
  }
1334
- const WORKFLOW_PARK_INDEX_TABLE = "workflow_agent_session";
1335
- async function primaryKeyWidth(db, dialect, table) {
1336
- const sql = dialect === "mysql"
1337
- ? `SELECT COUNT(*) AS n FROM information_schema.key_column_usage WHERE table_schema = DATABASE() AND table_name = '${table}' AND constraint_name = 'PRIMARY'`
1338
- : `SELECT COUNT(*) AS n FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ` +
1339
- `ON kcu.constraint_name = tc.constraint_name AND kcu.table_schema = tc.table_schema AND kcu.table_name = tc.table_name ` +
1340
- `WHERE tc.table_schema = current_schema() AND tc.table_name = '${table}' AND tc.constraint_type = 'PRIMARY KEY'`;
1341
- return Number((await db.rows(sql))[0]?.n ?? 0);
1342
- }
1343
- const WORKFLOW_PARK_INDEX_STAGING = `${WORKFLOW_PARK_INDEX_TABLE}_m9`;
1344
- const WORKFLOW_PARK_INDEX_RETIRED = `${WORKFLOW_PARK_INDEX_TABLE}_m9_old`;
1345
- async function reshapeWorkflowParkIndex(db, dialect, receipts) {
1346
- const T = WORKFLOW_PARK_INDEX_TABLE;
1347
- const S = WORKFLOW_PARK_INDEX_STAGING;
1348
- const O = WORKFLOW_PARK_INDEX_RETIRED;
1349
- const exists = (t) => tableExists(db, dialect, t);
1350
- const ddl = (dialect === "mysql" ? SCHEMA_STATEMENTS : PG_SCHEMA_STATEMENTS).find((stmt) => stmt.includes(`CREATE TABLE IF NOT EXISTS ${T} (`));
1351
- if (ddl === undefined)
1352
- throw new Error(`one-time migration M9: the central schema carries no definition of ${T} — refusing to reshape a table it cannot rebuild`);
1353
- const copyPointers = (to, from) => db.exec(dialect === "mysql"
1354
- ? `INSERT IGNORE INTO ${to} (session_id, run_id, agent_key, scope) SELECT session_id, run_id, agent_key, scope FROM ${from}`
1355
- : `INSERT INTO ${to} (session_id, run_id, agent_key, scope) SELECT session_id, run_id, agent_key, scope FROM ${from} ON CONFLICT DO NOTHING`);
1356
- const resumed = await exists(S);
1357
- if (resumed) {
1358
- await db.exec(ddl);
1359
- receipts.workflowParkIndexRowsCarried += await copyPointers(T, S);
1360
- await execUntil(db, `DROP TABLE ${S}`, async () => !(await exists(S)));
1361
- }
1362
- if (await exists(O))
1363
- await execUntil(db, `DROP TABLE ${O}`, async () => !(await exists(O)));
1364
- if ((await primaryKeyWidth(db, dialect, T)) !== 1) {
1365
- if (await exists(T))
1366
- await finishParkIndexShape(db, dialect);
1367
- receipts.workflowParkIndexKey = resumed ? "reshaped" : "current";
1368
- return;
1369
- }
1370
- await db.exec(ddl.replace(`CREATE TABLE IF NOT EXISTS ${T} (`, `CREATE TABLE IF NOT EXISTS ${S} (`));
1371
- receipts.workflowParkIndexRowsCarried += await copyPointers(S, T);
1372
- const swapped = async () => !(await exists(S)) && (await exists(T)) && (await primaryKeyWidth(db, dialect, T)) !== 1;
1373
- if (dialect === "mysql") {
1374
- await execUntil(db, `RENAME TABLE ${T} TO ${O}, ${S} TO ${T}`, swapped);
1375
- await execUntil(db, `DROP TABLE IF EXISTS ${O}`, async () => !(await exists(O)));
1376
- }
1377
- else {
1378
- await execAtomically(db, [`ALTER TABLE ${T} RENAME TO ${O}`, `ALTER TABLE ${S} RENAME TO ${T}`, `DROP TABLE ${O}`], async () => (await swapped()) && !(await exists(O)));
1379
- }
1380
- await finishParkIndexShape(db, dialect);
1381
- receipts.workflowParkIndexKey = "reshaped";
1382
- }
1383
1494
  async function execAtomically(db, statements, postcondition) {
1384
1495
  try {
1385
1496
  await db.exec("BEGIN");
@@ -1399,64 +1510,6 @@ async function execAtomically(db, statements, postcondition) {
1399
1510
  throw e;
1400
1511
  }
1401
1512
  }
1402
- async function finishParkIndexShape(db, dialect) {
1403
- await ensureIndexes(async (sql, params) => (dialect === "mysql" ? [await db.rows(sql, params)] : { rows: await db.rows(sql, params) }), dialect, CENTRAL_SCHEMA_INDEXES.filter((spec) => spec.table === WORKFLOW_PARK_INDEX_TABLE));
1404
- if (dialect !== "pg")
1405
- return;
1406
- const conventional = `${WORKFLOW_PARK_INDEX_TABLE}_pkey`;
1407
- const pk = await db.rows(`SELECT constraint_name FROM information_schema.table_constraints WHERE table_schema = current_schema() AND table_name = '${WORKFLOW_PARK_INDEX_TABLE}' AND constraint_type = 'PRIMARY KEY'`);
1408
- const name = pk[0]?.constraint_name;
1409
- if (typeof name === "string" && name !== conventional)
1410
- await db.exec(`ALTER TABLE ${WORKFLOW_PARK_INDEX_TABLE} RENAME CONSTRAINT ${name} TO ${conventional}`);
1411
- }
1412
- const RETIRED_SHADOWED_RULE_KEY = "shadowedRule";
1413
- export function migrateShadowedRuleDescriptor(text) {
1414
- let parsed;
1415
- try {
1416
- parsed = decodeJsonColumn(text, readNonNullJsonValueColumn, "checkpoint.risk_descriptor (M8 rewrite)");
1417
- }
1418
- catch (error) {
1419
- if (isJsonColumnBodyProblem(error))
1420
- return { kind: "unreadable", error };
1421
- throw error;
1422
- }
1423
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
1424
- return { kind: "untouched" };
1425
- const { [RETIRED_SHADOWED_RULE_KEY]: old, ...rest } = parsed;
1426
- if (typeof old !== "string")
1427
- return { kind: "untouched" };
1428
- const next = Object.hasOwn(rest, "allowRuleShadowed") ? rest : { ...rest, allowRuleShadowed: [{ member: "persisted", entries: [old] }] };
1429
- return { kind: "rewrite", text: JSON.stringify(next) };
1430
- }
1431
- const SHADOWED_RULE_BATCH = 500;
1432
- async function migrateShadowedRuleKey(db, dialect, receipts) {
1433
- const like = `%"${RETIRED_SHADOWED_RULE_KEY}"%`;
1434
- let after = "";
1435
- for (;;) {
1436
- const rows = await db.rows(dialect === "mysql"
1437
- ? `SELECT token, ${jsonColumnText(dialect, "risk_descriptor")} FROM checkpoint WHERE ${jsonColumnTextExpr(dialect, "risk_descriptor")} LIKE ? AND token > ? ORDER BY token LIMIT ${SHADOWED_RULE_BATCH}`
1438
- : `SELECT token, ${jsonColumnText(dialect, "risk_descriptor")} FROM checkpoint WHERE ${jsonColumnTextExpr(dialect, "risk_descriptor")} LIKE $1 AND token > $2 ORDER BY token LIMIT ${SHADOWED_RULE_BATCH}`, [like, after]);
1439
- for (const r of rows) {
1440
- const token = String(r.token);
1441
- after = token;
1442
- const text = r.risk_descriptor;
1443
- if (typeof text !== "string")
1444
- continue;
1445
- const next = migrateShadowedRuleDescriptor(text);
1446
- if (next.kind === "unreadable") {
1447
- receipts.allowRuleShadowRowsUnreadable += 1;
1448
- continue;
1449
- }
1450
- if (next.kind === "untouched")
1451
- continue;
1452
- receipts.allowRuleShadowRowsMigrated += await db.exec(dialect === "mysql"
1453
- ? `UPDATE checkpoint SET risk_descriptor = ? WHERE token = ? AND ${jsonColumnTextExpr(dialect, "risk_descriptor")} = ?`
1454
- : `UPDATE checkpoint SET risk_descriptor = $1 WHERE token = $2 AND ${jsonColumnTextExpr(dialect, "risk_descriptor")} = $3`, [next.text, token, text]);
1455
- }
1456
- if (rows.length < SHADOWED_RULE_BATCH)
1457
- return;
1458
- }
1459
- }
1460
1513
  const RETIRED_CARD_WORD_PATTERNS = ['%"shadowed"%', '%"governance_unavailable"%'];
1461
1514
  const RETIRED_CARD_WORD_BATCH = 500;
1462
1515
  function decodeForM18(text, where) {
@@ -1706,11 +1759,6 @@ export async function inheritPlacedSessionOwners(db, dialect, receipts) {
1706
1759
  }
1707
1760
  function readingFields(r) {
1708
1761
  return {
1709
- holderDeathCodeRowsBackfilled: r.holderDeathCodeRowsBackfilled,
1710
- allowRuleShadowRowsMigrated: r.allowRuleShadowRowsMigrated,
1711
- allowRuleShadowRowsUnreadable: r.allowRuleShadowRowsUnreadable,
1712
- ...(r.workflowParkIndexKey !== undefined ? { workflowParkIndexKey: r.workflowParkIndexKey } : {}),
1713
- workflowParkIndexRowsCarried: r.workflowParkIndexRowsCarried,
1714
1762
  undurableScopeRuleRowsRemoved: r.undurableScopeRuleRows.length,
1715
1763
  undurableScopeRuleBucketsDeferred: r.undurableScopeRuleBucketsDeferred,
1716
1764
  undurableScopeRuleBucketsUnreadable: r.undurableScopeRuleBucketsUnreadable.length,
@@ -1741,6 +1789,9 @@ function readingFields(r) {
1741
1789
  deviceBindingTablesDropped: r.deviceBindingTablesDropped,
1742
1790
  executionPlacementsBackfilled: r.executionPlacementsBackfilled,
1743
1791
  askExecutionLanesBackfilled: r.askExecutionLanesBackfilled,
1792
+ executionLaneWordsRenamed: r.executionLaneWordsRenamed,
1793
+ sessionExtRowsMoved: r.sessionExtRowsMoved,
1794
+ sessionMetaColumnsDropped: r.sessionMetaColumnsDropped,
1744
1795
  executionPlacementSessionsUnreadable: r.executionPlacementSessionsUnreadable.length,
1745
1796
  localArchiveRowFactsBackfilled: r.localArchiveRowFactsBackfilled,
1746
1797
  localArchivesAnonymized: r.localArchivesAnonymized,
@@ -1752,6 +1803,7 @@ function readingFields(r) {
1752
1803
  askJournalRecordsUpgraded: r.askJournalRecordsUpgraded,
1753
1804
  importedLayerRootAllowsRetired: r.importedLayerRootAllowsRetired.length,
1754
1805
  importedLayerRootBucketsSkipped: r.importedLayerRootBucketsSkipped.length,
1806
+ importedLayerRootBucketsUnnamed: r.importedLayerRootBucketsUnnamed,
1755
1807
  };
1756
1808
  }
1757
1809
  export function logOneTimeMigrations(log, backend, report) {
@@ -1759,12 +1811,7 @@ export function logOneTimeMigrations(log, backend, report) {
1759
1811
  backend,
1760
1812
  ...readingFields(report),
1761
1813
  removableAfter: Object.fromEntries(ONE_TIME_MIGRATIONS.map((w) => [w.id, w.removableAfter])),
1762
- note: "one-time persisted-data migrations (M6: run rows failed by a " +
1763
- "holder-death verdict written by an earlier version with only the sentence got the machine-readable code run.stalled / " +
1764
- "run.orphaned; M7 writes nothing — it is the window of the refusal that keeps a suspended task whose stored allow list was written " +
1765
- "under an earlier reading from resuming under the current one; M8: parked rows' allow-layer disclosure written by an earlier engine " +
1766
- "under its old key shadowedRule rewritten to allowRuleShadowed; M9: the workflow park pointer index rebuilt with the key (session, run), " +
1767
- "its rows carried over; M10 writes nothing at boot — on the local backend, while its window is open, the first task submitted with a principal " +
1814
+ note: "one-time persisted-data migrations (M10 writes nothing at boot — on the local backend, while its window is open, the first task submitted with a principal " +
1768
1815
  "on a session written by an earlier version (which recorded no owner) claims that session for the principal, one session_owner_claimed line per claim; " +
1769
1816
  "M11: persisted permission rules whose project scope root is not a complete absolute path — accepted by earlier versions, dropped on every read " +
1770
1817
  "by the current engine and not removable through DELETE /v1/rules — removed, one permission_rule_row_removed line per row; a bucket the read path " +
@@ -1813,7 +1860,10 @@ export function logOneTimeMigrations(log, backend, report) {
1813
1860
  "M36: allow approvals that an import before engine 7.61.0 stored as global rules from a settings file's user-layer path written from `/` — which the user layer reads at its " +
1814
1861
  "configuration directory and a global rule reads at every task's project root — retired by tombstones naming exactly those approvals (a rule that also holds an approval a person " +
1815
1862
  "gave directly stays in effect; deny and ask rules are never retired), one imported_layer_root_allow_retired line per rule; a bucket that could not be read or written, or that another replica deleted while it was being written, is named by an " +
1816
- "imported_layer_root_bucket_skipped line on every boot). Each step " +
1863
+ "imported_layer_root_bucket_skipped line on every boot; " +
1864
+ "M40: the stored execution lane word in-process — the name earlier versions gave the lane with no execution environment, which this version calls none and does not read — " +
1865
+ "rewritten as none on approval ask rows and session rows (on the local backend: in the session metadata file and the approval ask journal); a boot that finds such rows while " +
1866
+ "runs on the same database still heartbeat refuses with nothing changed, because a replica of an earlier version is still serving and would keep writing the old word). Each step " +
1817
1867
  "runs on every boot until its window closes (the release that introduced it + 30 days; the per-step date is in " +
1818
1868
  "removableAfter, null while that release is not published yet) and is deleted in the first minor after that — start a " +
1819
1869
  "version that still carries a step at least once before upgrading past its deletion.",
@@ -1933,6 +1983,16 @@ function logReceiptRows(log, backend, r) {
1933
1983
  for (const bucket of r.importedLayerRootBucketsSkipped) {
1934
1984
  log.warn("imported_layer_root_bucket_skipped", { backend, ownerKey: bucket.ownerKey, reason: bucket.reason, why: bucket.why, note: importedLayerRootSkipNote(bucket.reason) });
1935
1985
  }
1986
+ if (r.importedLayerRootBucketsUnnamed > 0) {
1987
+ log.warn("imported_layer_root_buckets_unnamed", {
1988
+ backend,
1989
+ count: r.importedLayerRootBucketsUnnamed,
1990
+ note: "this many permission-rule bucket files are present in the rules directory but neither the directory (the local owner's bucket, the identities its adoption marker names) " +
1991
+ "nor this store's import records name their owner (a bucket file is named by a one-way digest of its owner), so the one-time retirement of imported allow rules written from `/` " +
1992
+ "did not read them: any such rule in them stays in effect. This is a gap in the census, not a statement that they are empty. Each boot reports it again; " +
1993
+ "list the rules of every owner you know (`GET /v1/rules`) and remove what should not stand",
1994
+ });
1995
+ }
1936
1996
  for (const bucket of r.undurableScopeRuleBucketsUnreadable) {
1937
1997
  log.warn("permission_rule_bucket_unreadable", {
1938
1998
  backend,
@@ -1993,7 +2053,7 @@ function logOneTimeMigrationsInterrupted(log, backend, error) {
1993
2053
  ...readingFields(error.receipts),
1994
2054
  note: "the one-time migrations failed after committing changes: the readings on this line and the lines that follow are what this boot already committed — a retried " +
1995
2055
  "boot finds that work done and does not report it again, so these lines are the only record of them, written once by this boot — save them. The failure itself follows " +
1996
- "as this boot's refusal. A shape reading absent from this line (workflowParkIndexKey / sessionRuleColumn / workflowJournalScopeColumn) belongs to a step that did not " +
2056
+ "as this boot's refusal. A shape reading absent from this line (sessionRuleColumn / workflowJournalScopeColumn) belongs to a step that did not " +
1997
2057
  "finish; sessionRuleColumn absent after session_rule_row_old_grammar_entries lines means the column still carries its in-progress name — the next boot rewrites the " +
1998
2058
  "remaining rows (rows already rewritten are left alone) and publishes the final name",
1999
2059
  });