@sema-agent/server 7.150.0 → 7.151.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 (341) hide show
  1. package/MIGRATION.md +185 -0
  2. package/USAGE.md +53 -25
  3. package/deploy/sema-up/README.md +7 -1
  4. package/deploy/sema-up/chart/Chart.yaml +1 -1
  5. package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
  6. package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
  7. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
  8. package/deploy/sema-up/chart/templates/server.yaml +5 -3
  9. package/deploy/sema-up/chart/values-full-single.yaml +3 -1
  10. package/deploy/sema-up/chart/values.yaml +8 -1
  11. package/deploy/sema-up/cluster-up.sh +1 -0
  12. package/deploy/sema-up/kube-up.sh +1 -1
  13. package/deploy/sema-up/lib/flagshape.sh +1 -1
  14. package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
  15. package/deploy/sema-up/lib/helm-values-kube.py +2 -0
  16. package/dist/adoption/import.js +3 -3
  17. package/dist/approval-ask-machine.d.ts +1 -1
  18. package/dist/approval-card.d.ts +2 -2
  19. package/dist/approval-gate-currency.d.ts +1 -1
  20. package/dist/auth-bridge.d.ts +20 -0
  21. package/dist/auth-bridge.js +9 -0
  22. package/dist/background-session-reap.d.ts +1 -1
  23. package/dist/bake-runner/main.d.ts +4 -2
  24. package/dist/bind-host.d.ts +2 -3
  25. package/dist/bind-host.js +2 -4
  26. package/dist/boot/budget-tracing.js +1 -1
  27. package/dist/boot/engine-lease.d.ts +5 -0
  28. package/dist/boot/engine-lease.js +1 -0
  29. package/dist/boot/execution-env.d.ts +8 -14
  30. package/dist/boot/execution-env.js +54 -17
  31. package/dist/boot/execution-lane.js +32 -5
  32. package/dist/boot/late-binds.d.ts +8 -4
  33. package/dist/boot/late-binds.js +7 -1
  34. package/dist/boot/memory-consolidation.d.ts +3 -2
  35. package/dist/boot/memory-consolidation.js +6 -6
  36. package/dist/boot/reapers.d.ts +5 -0
  37. package/dist/boot/reapers.js +12 -1
  38. package/dist/boot/resolve-spec.d.ts +3 -3
  39. package/dist/boot/resolve-spec.js +31 -22
  40. package/dist/boot/runner-deps.d.ts +9 -2
  41. package/dist/boot/runner-deps.js +3 -3
  42. package/dist/boot/session-age-out.d.ts +36 -0
  43. package/dist/boot/session-age-out.js +101 -0
  44. package/dist/boot/session-faces.d.ts +5 -14
  45. package/dist/boot/session-faces.js +9 -5
  46. package/dist/boot/stage-01-config.js +2 -0
  47. package/dist/boot/stage-05-execution-env.d.ts +5 -3
  48. package/dist/boot/stage-05-execution-env.js +2 -2
  49. package/dist/boot/stage-06-runners.d.ts +7 -4
  50. package/dist/boot/stage-06-runners.js +15 -6
  51. package/dist/boot/stage-07-capability-layer.d.ts +7 -4
  52. package/dist/boot/stage-07-capability-layer.js +2 -1
  53. package/dist/boot/stage-08-reapers.d.ts +7 -4
  54. package/dist/boot/stage-08-reapers.js +4 -2
  55. package/dist/boot/stage-09-leader.d.ts +11 -5
  56. package/dist/boot/stage-09-leader.js +12 -3
  57. package/dist/boot/stage-10-http-server.d.ts +11 -5
  58. package/dist/boot/stage-10-http-server.js +3 -6
  59. package/dist/boot/web-admin.js +3 -1
  60. package/dist/boot/workflow-orchestration.d.ts +7 -2
  61. package/dist/boot/workflow-orchestration.js +24 -7
  62. package/dist/bounded-rotation.d.ts +29 -0
  63. package/dist/bounded-rotation.js +24 -0
  64. package/dist/capability-facts.d.ts +22 -13
  65. package/dist/capability-facts.js +28 -18
  66. package/dist/center-credential.d.ts +25 -6
  67. package/dist/center-credential.js +25 -5
  68. package/dist/config-catalog.d.ts +26 -7
  69. package/dist/config-catalog.js +14 -11
  70. package/dist/config-invariants.d.ts +2 -1
  71. package/dist/config-invariants.js +4 -4
  72. package/dist/config-types.d.ts +10 -4
  73. package/dist/config.d.ts +51 -2
  74. package/dist/config.js +35 -24
  75. package/dist/deployment-shape.d.ts +9 -1
  76. package/dist/deployment-shape.js +2 -1
  77. package/dist/device-protocol.d.ts +5 -4
  78. package/dist/device-protocol.js +1 -1
  79. package/dist/device-ws-hub.js +3 -2
  80. package/dist/device-ws-protocol.d.ts +63 -10
  81. package/dist/device-ws-protocol.js +56 -25
  82. package/dist/engine-identity.d.ts +19 -7
  83. package/dist/engine-identity.js +10 -2
  84. package/dist/entry-mode-clamp-notice.d.ts +2 -1
  85. package/dist/execution-env-refusal.d.ts +23 -0
  86. package/dist/execution-env-refusal.js +5 -0
  87. package/dist/execution-lane-caps.d.ts +3 -1
  88. package/dist/field-conflict.d.ts +1 -1
  89. package/dist/file-history-capture.d.ts +11 -4
  90. package/dist/file-history-capture.js +9 -6
  91. package/dist/file-history-disclosure.d.ts +7 -5
  92. package/dist/file-history-disclosure.js +7 -5
  93. package/dist/fleet/drain-entry.d.ts +4 -0
  94. package/dist/hooks/hook-runner.js +2 -2
  95. package/dist/host-lsp-manager.d.ts +2 -2
  96. package/dist/http/admission.js +17 -2
  97. package/dist/http/approval-sweeps.js +5 -5
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +2 -1
  101. package/dist/http/dispatch.js +34 -27
  102. package/dist/http/durable-run-leg.js +16 -16
  103. package/dist/http/health-self-report.d.ts +9 -1
  104. package/dist/http/health-self-report.js +2 -0
  105. package/dist/http/owner-access.d.ts +31 -11
  106. package/dist/http/owner-access.js +37 -6
  107. package/dist/http/resume-ctx-read.js +25 -3
  108. package/dist/http/resume-legs.d.ts +2 -1
  109. package/dist/http/resume-legs.js +259 -79
  110. package/dist/http/route-ctx.d.ts +18 -8
  111. package/dist/http/route-table.d.ts +1 -1
  112. package/dist/http/routes/admin-memory-optout.d.ts +1 -1
  113. package/dist/http/routes/agents-roster.js +2 -3
  114. package/dist/http/routes/approvals-assistant.js +4 -3
  115. package/dist/http/routes/background.d.ts +3 -3
  116. package/dist/http/routes/capabilities.js +8 -5
  117. package/dist/http/routes/fleet.js +10 -8
  118. package/dist/http/routes/projects.d.ts +3 -2
  119. package/dist/http/routes/projects.js +4 -5
  120. package/dist/http/routes/runs.js +102 -36
  121. package/dist/http/routes/session-sync.d.ts +4 -3
  122. package/dist/http/routes/session-sync.js +172 -77
  123. package/dist/http/routes/sessions-list.js +13 -7
  124. package/dist/http/routes/sessions.js +10 -6
  125. package/dist/http/routes/tasks.js +30 -16
  126. package/dist/http/routes/trace-usage.js +2 -2
  127. package/dist/http/server.d.ts +21 -27
  128. package/dist/http/server.js +2 -2
  129. package/dist/http/submit-ctx.js +5 -4
  130. package/dist/http/wire-types.d.ts +14 -4
  131. package/dist/identity-source.d.ts +20 -3
  132. package/dist/identity-source.js +14 -0
  133. package/dist/index.d.ts +4 -0
  134. package/dist/lane-home-probe.d.ts +3 -7
  135. package/dist/lane-home-probe.js +0 -1
  136. package/dist/leader/leader.d.ts +2 -1
  137. package/dist/leg-facts.d.ts +13 -5
  138. package/dist/leg-facts.js +2 -1
  139. package/dist/memory-scope.d.ts +5 -4
  140. package/dist/memory-scope.js +4 -4
  141. package/dist/memory-sync-client.d.ts +30 -2
  142. package/dist/memory-sync-client.js +16 -9
  143. package/dist/observability/err-text.d.ts +7 -0
  144. package/dist/observability/err-text.js +5 -0
  145. package/dist/observability/fail-open.d.ts +7 -3
  146. package/dist/observability/fail-open.js +7 -3
  147. package/dist/observability/metrics.js +1 -0
  148. package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
  149. package/dist/orchestration/workflow-notify-journal.js +12 -1
  150. package/dist/orchestration/workflow-park-decision.d.ts +70 -18
  151. package/dist/orchestration/workflow-park-decision.js +49 -10
  152. package/dist/orchestration/workflow-park-holder.d.ts +65 -0
  153. package/dist/orchestration/workflow-park-holder.js +98 -0
  154. package/dist/orchestration/workflow-record-fence.d.ts +36 -0
  155. package/dist/orchestration/workflow-record-fence.js +42 -0
  156. package/dist/owner-faces.d.ts +1 -1
  157. package/dist/parked-decide.d.ts +104 -53
  158. package/dist/parked-decide.js +153 -117
  159. package/dist/peer-directory.d.ts +2 -2
  160. package/dist/peer-inbound-face.d.ts +2 -6
  161. package/dist/peer-inbound-face.js +0 -3
  162. package/dist/permission-mode-admission.d.ts +41 -5
  163. package/dist/permission-mode-admission.js +29 -3
  164. package/dist/plugins/adoption-log-sql.d.ts +3 -1
  165. package/dist/plugins/adoption-log-sql.js +7 -1
  166. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  167. package/dist/plugins/approval-ask-store-file.js +8 -0
  168. package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
  169. package/dist/plugins/approval-ask-store-memory.js +28 -9
  170. package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
  171. package/dist/plugins/approval-ask-store-sql.js +34 -5
  172. package/dist/plugins/approval-exemption-store.js +3 -0
  173. package/dist/plugins/approval-nonce-store.js +2 -0
  174. package/dist/plugins/breaker-state-sql.js +2 -0
  175. package/dist/plugins/caching-session-store.d.ts +9 -15
  176. package/dist/plugins/caching-session-store.js +6 -0
  177. package/dist/plugins/device-store-sql.js +6 -0
  178. package/dist/plugins/error-origin.d.ts +18 -0
  179. package/dist/plugins/error-origin.js +10 -0
  180. package/dist/plugins/execution-placement-sql.js +6 -6
  181. package/dist/plugins/image-bake-store-sql.js +6 -0
  182. package/dist/plugins/image-index-sql.js +4 -0
  183. package/dist/plugins/landed-attendants.d.ts +212 -0
  184. package/dist/plugins/landed-attendants.js +161 -0
  185. package/dist/plugins/leader-run-store-sql.js +4 -0
  186. package/dist/plugins/local-session-live-reference.d.ts +40 -0
  187. package/dist/plugins/local-session-live-reference.js +39 -0
  188. package/dist/plugins/local-session-store.d.ts +71 -33
  189. package/dist/plugins/local-session-store.js +129 -27
  190. package/dist/plugins/memory-engine-pg.js +4 -0
  191. package/dist/plugins/memory-engine-sql-apply.js +5 -2
  192. package/dist/plugins/memory-engine-tidb.js +4 -0
  193. package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
  194. package/dist/plugins/memory-sync-store-pg.js +5 -0
  195. package/dist/plugins/memory-sync-store-tidb.js +5 -0
  196. package/dist/plugins/one-time-migrations.d.ts +57 -63
  197. package/dist/plugins/one-time-migrations.js +162 -90
  198. package/dist/plugins/org-delivery-quota-store.js +4 -1
  199. package/dist/plugins/outcome-ledger-sql.js +2 -0
  200. package/dist/plugins/permission-rule-store-sql.js +11 -4
  201. package/dist/plugins/pg-cost-quota.js +4 -0
  202. package/dist/plugins/pg-pool.js +24 -1
  203. package/dist/plugins/pg-rate-limiter.js +2 -0
  204. package/dist/plugins/posix-shell-fs.d.ts +6 -1
  205. package/dist/plugins/posix-shell-fs.js +4 -1
  206. package/dist/plugins/remote-env-adb.d.ts +19 -15
  207. package/dist/plugins/remote-env-adb.js +41 -15
  208. package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
  209. package/dist/plugins/remote-env-connect-seat.js +72 -0
  210. package/dist/plugins/remote-env-device.d.ts +22 -5
  211. package/dist/plugins/remote-env-device.js +106 -8
  212. package/dist/plugins/remote-env-e2b.d.ts +15 -6
  213. package/dist/plugins/remote-env-e2b.js +42 -32
  214. package/dist/plugins/remote-env-file-error.js +6 -2
  215. package/dist/plugins/remote-env-host.d.ts +17 -0
  216. package/dist/plugins/remote-env-host.js +30 -38
  217. package/dist/plugins/remote-env-k8s.d.ts +41 -39
  218. package/dist/plugins/remote-env-k8s.js +53 -30
  219. package/dist/plugins/remote-env-local-docker.d.ts +13 -8
  220. package/dist/plugins/remote-env-local-docker.js +34 -41
  221. package/dist/plugins/remote-env-ssh.d.ts +18 -12
  222. package/dist/plugins/remote-env-ssh.js +60 -59
  223. package/dist/plugins/remote-shell.d.ts +98 -1
  224. package/dist/plugins/remote-shell.js +218 -0
  225. package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
  226. package/dist/plugins/resume-anchor-store-sql.js +12 -5
  227. package/dist/plugins/retention-lane-store-sql.js +4 -0
  228. package/dist/plugins/retention-sql.d.ts +20 -2
  229. package/dist/plugins/retention-sql.js +11 -0
  230. package/dist/plugins/run-store-sql.d.ts +21 -2
  231. package/dist/plugins/run-store-sql.js +31 -7
  232. package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
  233. package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
  234. package/dist/plugins/schema-consistency.d.ts +4 -2
  235. package/dist/plugins/schema-consistency.js +19 -2
  236. package/dist/plugins/send-file-ledger.js +3 -0
  237. package/dist/plugins/serving-requirement.d.ts +26 -7
  238. package/dist/plugins/serving-requirement.js +11 -4
  239. package/dist/plugins/session-birth.d.ts +6 -2
  240. package/dist/plugins/session-capture-record-store-sql.js +2 -0
  241. package/dist/plugins/session-ext-table.d.ts +14 -1
  242. package/dist/plugins/session-ext-table.js +5 -1
  243. package/dist/plugins/session-row-sql.d.ts +49 -2
  244. package/dist/plugins/session-row-sql.js +56 -13
  245. package/dist/plugins/session-store-sql.d.ts +34 -18
  246. package/dist/plugins/session-store-sql.js +98 -17
  247. package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
  248. package/dist/plugins/shared-memory-store-sql.js +6 -2
  249. package/dist/plugins/sql-driver.d.ts +2 -1
  250. package/dist/plugins/sql-driver.js +18 -3
  251. package/dist/plugins/sql-errors.d.ts +11 -6
  252. package/dist/plugins/sql-errors.js +5 -6
  253. package/dist/plugins/stop-then-start-sentence.d.ts +10 -4
  254. package/dist/plugins/stop-then-start-sentence.js +14 -10
  255. package/dist/plugins/stop-then-start.d.ts +109 -0
  256. package/dist/plugins/stop-then-start.js +104 -0
  257. package/dist/plugins/store-backend.d.ts +40 -13
  258. package/dist/plugins/store-backend.js +169 -27
  259. package/dist/plugins/store-boundary.d.ts +57 -0
  260. package/dist/plugins/store-boundary.js +139 -0
  261. package/dist/plugins/store-contracts.d.ts +17 -0
  262. package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
  263. package/dist/plugins/store-fault-sentence-m53.js +25 -0
  264. package/dist/plugins/store-fault.d.ts +26 -7
  265. package/dist/plugins/store-fault.js +18 -2
  266. package/dist/plugins/task-attachment-store.js +9 -0
  267. package/dist/plugins/task-list-store-sql.d.ts +2 -2
  268. package/dist/plugins/task-list-store-sql.js +10 -8
  269. package/dist/plugins/tidb-cost-quota.js +4 -0
  270. package/dist/plugins/tidb-pool.js +2 -1
  271. package/dist/plugins/tidb-rate-limiter.js +4 -0
  272. package/dist/plugins/user-input-ledger-sql.js +4 -0
  273. package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
  274. package/dist/plugins/workflow-run-store-sql.js +64 -4
  275. package/dist/plugins/workspace-fill.d.ts +4 -7
  276. package/dist/plugins/workspace-fill.js +2 -1
  277. package/dist/plugins/write-behind-counter.js +5 -0
  278. package/dist/project-context.d.ts +0 -2
  279. package/dist/project-context.js +4 -8
  280. package/dist/project-registry.d.ts +46 -0
  281. package/dist/project-registry.js +44 -0
  282. package/dist/request-key-closure.js +1 -1
  283. package/dist/request-value-closure.d.ts +4 -0
  284. package/dist/request-value-closure.js +10 -0
  285. package/dist/run-acceptance.d.ts +3 -2
  286. package/dist/run-acceptance.js +4 -6
  287. package/dist/run-local.js +1 -0
  288. package/dist/runs.d.ts +37 -8
  289. package/dist/runs.js +53 -12
  290. package/dist/runtime-governance.d.ts +5 -4
  291. package/dist/security.d.ts +30 -32
  292. package/dist/security.js +41 -7
  293. package/dist/server-secret-env.d.ts +12 -10
  294. package/dist/server-secret-env.js +24 -15
  295. package/dist/session-bindings.d.ts +82 -0
  296. package/dist/session-bindings.js +18 -0
  297. package/dist/session-children-retire.d.ts +2 -2
  298. package/dist/session-children-retire.js +2 -6
  299. package/dist/session-deletion.d.ts +16 -1
  300. package/dist/session-error-wire.d.ts +3 -3
  301. package/dist/session-error-wire.js +3 -3
  302. package/dist/session-incarnation.d.ts +10 -0
  303. package/dist/session-incarnation.js +10 -1
  304. package/dist/session-kind.d.ts +57 -0
  305. package/dist/session-kind.js +22 -0
  306. package/dist/session-row.d.ts +21 -1
  307. package/dist/session-row.js +7 -1
  308. package/dist/session-sync-kernel.d.ts +24 -14
  309. package/dist/session-sync-kernel.js +3 -6
  310. package/dist/session-sync.d.ts +4 -98
  311. package/dist/session-sync.js +1 -71
  312. package/dist/session-titler.d.ts +10 -3
  313. package/dist/session-titler.js +19 -6
  314. package/dist/ssh-host-key.d.ts +5 -0
  315. package/dist/ssh-host-key.js +3 -0
  316. package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
  317. package/dist/stores-sql/file-history-store-sql.js +108 -70
  318. package/dist/stores-sql/pg-session-storage.d.ts +34 -7
  319. package/dist/stores-sql/pg-session-storage.js +37 -13
  320. package/dist/stores-sql/retention-store-sql.d.ts +1 -9
  321. package/dist/stores-sql/retention-store-sql.js +2 -10
  322. package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
  323. package/dist/stores-sql/session-policy-store-sql.js +61 -44
  324. package/dist/stores-sql/session-write-hooks.d.ts +11 -0
  325. package/dist/stores-sql/tidb-session-store.d.ts +58 -7
  326. package/dist/stores-sql/tidb-session-store.js +37 -13
  327. package/dist/submission-exit.d.ts +3 -2
  328. package/dist/submission-exit.js +9 -1
  329. package/dist/task-cwd.d.ts +2 -2
  330. package/dist/task-settings.d.ts +6 -5
  331. package/dist/terminal.d.ts +33 -2
  332. package/dist/terminal.js +139 -9
  333. package/dist/tool-approval.d.ts +140 -6
  334. package/dist/tool-approval.js +165 -72
  335. package/dist/trace/self-minted-wire.d.ts +290 -27
  336. package/dist/trace/self-minted-wire.js +101 -13
  337. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +78 -27
  338. package/package.json +2 -2
  339. package/wire-changes/server@7.151.0.json +762 -0
  340. package/dist/http/session-inherit-env.d.ts +0 -36
  341. package/dist/http/session-inherit-env.js +0 -6
@@ -5,6 +5,8 @@ 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
7
  import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextExpr, readOpaqueJsonValueColumn, } from "./sql-json-column.js";
8
+ import { rewrittenBackgroundRecordM53, rewrittenToolEndDataM53 } from "./store-fault-sentence-m53.js";
9
+ import { pgSafeJsonStringify } from "./pg-safe-json.js";
8
10
  import { incarnationFromCell, sessionFaceFromRow } from "../session-incarnation.js";
9
11
  import { StoreBootRefusalError } from "./store-contracts.js";
10
12
  import { decodeRunResultCellM44 } from "./run-store-sql.js";
@@ -31,8 +33,9 @@ import { IDENTITY_COLUMN_NAME, SCENARIO_PREFIX_LIKE, SCENARIO_PREFIX_MARKER_ID,
31
33
  import { MIGRATION_MARKER_TABLE, SKILL_EXEMPTION_CUTOFF_ID, markIfAbsentSql, migrationMarkerDdl } from "./one-time-migration-marker.js";
32
34
  import { describeUpgradeDriftTail, expectedTableShapes } from "./schema-consistency.js";
33
35
  import { olderReplicaEvidenceSentence, stopThenStartAction } from "./stop-then-start-sentence.js";
34
- import { buildThisReplicaRegistration, knowsCapability, readReplicaRegistrations, registerReplica } from "./replica-registration.js";
35
- import { assertReplicaTablesPresent, assertServingRequirementsMet } from "./serving-requirement.js";
36
+ import { buildThisReplicaRegistration, registerReplica } from "./replica-registration.js";
37
+ import { assertReplicaTablesPresent, assertServingRequirementsMet, recordServingRequirement } from "./serving-requirement.js";
38
+ import { assertStopThenStartDeclarations, guardOwedStopThenStart, readOlderReplicaEvidence } from "./stop-then-start.js";
36
39
  const RETIRED_SESSION_RULE_BUCKETS = ["commandAllow", "commandDeny"];
37
40
  export function buildMigrationReceipts() {
38
41
  return {
@@ -67,6 +70,7 @@ export function buildMigrationReceipts() {
67
70
  scenarioPrefixSourcesScanned: 0,
68
71
  withdrawMarkWritten: 0,
69
72
  sessionDeletingSwitchMarked: 0,
73
+ storeFaultSentencesRewritten: 0,
70
74
  runErrorCodeColumnRewritten: 0,
71
75
  runObjectivePreviewRewritten: 0,
72
76
  shellSentinelOwnersCleared: 0,
@@ -129,6 +133,7 @@ function changedAnything(r) {
129
133
  r.sessionMetaColumnsDropped > 0 ||
130
134
  r.withdrawMarkWritten > 0 ||
131
135
  r.sessionDeletingSwitchMarked > 0 ||
136
+ r.storeFaultSentencesRewritten > 0 ||
132
137
  r.runErrorCodeColumnRewritten > 0 ||
133
138
  r.runObjectivePreviewRewritten > 0 ||
134
139
  r.shellSentinelOwnersCleared > 0 ||
@@ -211,6 +216,7 @@ export const ONE_TIME_MIGRATIONS = [
211
216
  "(`test/config-lkg.test.ts`、`test/config-hot/batch1-version-ordinal.test.ts` 改写成 3;`test/models-dropped-entries-s1295.test.ts` 的旧 LKG 迁移格改钉 2 读回不可用)",
212
217
  afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 2 答「unknown formatVersion 2」):2 读回 ⇒ 不可用,中心可达照拉(整拉,读腿铸出完整载体,新 LKG 按 3 覆盖)、" +
213
218
  "断着则 env 兜底,下一份干净候选重写 —— 与回滚到 7.122.0 读 3 同一种处置",
219
+ stopThenStart: null,
214
220
  reportKeys: [],
215
221
  },
216
222
  {
@@ -224,6 +230,7 @@ export const ONE_TIME_MIGRATIONS = [
224
230
  "`test/ss11-stored-migrations.test.ts` 的 M15 组(4 原样读回那一格留)",
225
231
  afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 3 答「unknown formatVersion 3」):旧载荷的 LKG 读回 ⇒ 不可用,中心可达照拉(整拉,新 LKG 按 4 覆盖)、" +
226
232
  "断着则 env 兜底 —— 与回滚到 7.123.0 读 4 同一种处置",
233
+ stopThenStart: null,
227
234
  reportKeys: [],
228
235
  },
229
236
  {
@@ -235,6 +242,7 @@ export const ONE_TIME_MIGRATIONS = [
235
242
  "`test/ss11-stored-migrations.test.ts` 的 M16 组;部署文档里「起服自动迁 config.d」那一句(运维仍可手跑契约命令行,直到契约删族)",
236
243
  afterRemoval: "契约读腿的墓碑(永久):config.d 里还带旧形的域文档 —— `runtime.json` 带事实 ⇒ `RetiredDomainError` 整份读不出;`atModelAllowlist` / `models.default` ⇒ " +
237
244
  "models 域降级、rosters 整份读不出 —— 拒句点名迁移 id;出路是运维手跑契约命令行(契约删族之后只剩手改)",
245
+ stopThenStart: null,
238
246
  reportKeys: [],
239
247
  },
240
248
  {
@@ -253,6 +261,7 @@ export const ONE_TIME_MIGRATIONS = [
253
261
  "`config.schema_mismatch`(缺 `rule_entries`、多那一列)—— " +
254
262
  "出路是先在一只带本条的版本上起服一次;② 行里仍带退役命令桶:SQL 店读回照旧答 `corrupt`(引擎的形读法点名那只桶,与本条在场时同一个结局),只是起服日志不再逐行点名 —— " +
255
263
  "运维在库里把那一行改写成引擎文法或删掉它(删会话即删规则);③ 跳过本条直接升的库同样没有改名那一拍的条目判读 —— 由 ① 的拒启接住(出路同 ①)",
264
+ stopThenStart: null,
256
265
  reportKeys: ["sessionRuleColumn", "sessionRuleRowsWithRetiredBuckets", "sessionRuleRowsWithOldGrammarEntries", "sessionRuleRowsWithUnreadableDeny"],
257
266
  shapeChanges: [{ kind: "rename", table: SESSION_POLICY_TABLE, from: [RETIRED_SESSION_RULES_COLUMN, OLD_GRAMMAR_SESSION_RULES_COLUMN], to: SESSION_RULES_COLUMN.name }],
258
267
  },
@@ -271,6 +280,7 @@ export const ONE_TIME_MIGRATIONS = [
271
280
  "幂等重入走 park、回决口按要人;仍带它们的待决停驻行(把 `APPROVAL_TERMINAL_BACKSTOP_MS` 调长的部署、local 车道引擎 File 店里窗关仍未决的 —— 停驻行从来不由本服务改写," +
272
281
  "窗内靠读侧改写读,窗关后读侧改写删)由本服务的读面按坏行处置:运维队列行缺那一位 + `server.approval-card.closed-word-out-of-set` 计数、耐久 `/decide` 的 remember 按强制(只放行这一次)。" +
273
282
  "引擎续跑不受影响:续跑入口读行摘要、不按今日闭集重判停驻行上的这两座,行字节引擎怎么写的还是怎么在",
283
+ stopThenStart: null,
274
284
  reportKeys: ["retiredCardWordAskRowsRewritten", "retiredCardWordRowsUnreadable"],
275
285
  },
276
286
  {
@@ -298,6 +308,7 @@ export const ONE_TIME_MIGRATIONS = [
298
308
  "批准后撞 `resume.tool_unavailable`(拒绝照常可出)—— 出路是拒绝那张卡,用改好的 `toolAxes` 重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、" +
299
309
  "调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行(头注 M19 段)。A2A 一面同:存量体里 `a2aPeers[].toolAxes` 判不过的对端续跑整条不挂," +
300
310
  "运维 warn `a2a_injection_dropped` 点名,停驻在它 skill 上的审批批准后撞 `resume.tool_unavailable` —— 出路同上:拒绝那张卡,改好声明重新提交",
311
+ stopThenStart: null,
301
312
  reportKeys: [],
302
313
  },
303
314
  {
@@ -315,6 +326,7 @@ export const ONE_TIME_MIGRATIONS = [
315
326
  afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里带这两形的 `settings.hooks`,在会采纳 hooks 的车道上续跑被拒 —— " +
316
327
  "「请求成员读不出」判据(`src/unread-request-members.ts`)答 409 `resume_blocked_by_policy`(`blockedBy: request.field_invalid`,`field: settings.hooks`),卡仍待决、行不动(7.147.0 起;此前是整份不采纳照跑)—— " +
317
328
  "出路是取消这类停驻任务、删掉那个未知字段 / `__proto__` 键后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行(头注 M20 段)",
329
+ stopThenStart: null,
318
330
  reportKeys: [],
319
331
  },
320
332
  {
@@ -330,6 +342,7 @@ export const ONE_TIME_MIGRATIONS = [
330
342
  "队列行不带卡词(壳显示通用审批卡),到期走 SLA 拒扫腿(模型读到「拒」),决断口仍按工具名认问答(`approve` 必须带 `answer`,否则 400);出路 = 升级前答完或拒掉;" +
331
343
  "② `gate_kind` 为 NULL 的停驻行:既有响亮臂 —— 运维待批队列(`listPending`)不列它、计 `server.approvals.queue-row-gate-kind-missing`(决断口按门型绑定取行,本来就不认它);" +
332
344
  "出路是运维把那一行置 expired 或删掉会话(终局收割照旧在 `terminal_at_ms` 收它)",
345
+ stopThenStart: null,
333
346
  reportKeys: ["parkedQuestionCardsWritten", "parkedQuestionRowsLeftAsWritten", "parkedRowGateKindsBackfilled", "parkedRowsUnresumableExpired"],
334
347
  },
335
348
  {
@@ -342,6 +355,7 @@ export const ONE_TIME_MIGRATIONS = [
342
355
  "文件头注 formatVersion 5 一段里「4 的载荷 …」那两句;`test/center-artifact-v3-lane.test.ts` 的两格 4 读回(5 原样读回那一格留)",
343
356
  afterRemoval: "既有响亮臂 `config_lkg_unusable`(`loadLkg` 对 4 答「unknown formatVersion 4」):4 读回 ⇒ 不可用,中心可达照拉(整拉,新 LKG 按 5 覆盖)、断着则 env 兜底 —— " +
344
357
  "与回滚到 7.131.x 读 5 同一种处置",
358
+ stopThenStart: null,
345
359
  reportKeys: [],
346
360
  },
347
361
  {
@@ -360,6 +374,7 @@ export const ONE_TIME_MIGRATIONS = [
360
374
  "其余调用方答 404(与别人的行同体)—— 出路是升级时先过一次带本条的版本,或运维按 `placement_root_session_id`(缺则 `placement_parent_session_id`)那一行的属主手工回填、" +
361
375
  "推不出的写一个任何主体都不匹配的值;停驻挂点只比对宿主行属主与引擎交来的属主(复活腿交那一行自己的属主),不写无主行;" +
362
376
  "建行那一笔记 `placement.owner` 不随本条删(那是常设的写法 —— core 盒义务 7,不是迁移)",
377
+ stopThenStart: null,
363
378
  reportKeys: ["placedSessionOwnersInherited", "placedSessionOwnersOrphaned", "placedSessionOwnersRemaining"],
364
379
  },
365
380
  {
@@ -374,6 +389,7 @@ export const ONE_TIME_MIGRATIONS = [
374
389
  "`test/one-time-migrations.test.ts` 的 M24 组与 `test/one-time-migrations-db-integration.test.ts` 的 M24 格。**不随本条删**:属主表、行主键里的令牌与店的写面 / 读面墙(那是常设的数据形)",
375
390
  afterRemoval: "起服对账一族的列集探针接住(`schema-consistency.ts` 的 `assertSchemaMatchesCode`,常设):跳过了带本条的版本直接升的库,`workflow_journal` 上仍带 scope 列、没有 incarnation 列 = 库与代码列集不符 ⇒ " +
376
391
  "响亮拒启(`config.schema_mismatch`),拒句点名两列;出路是先在带本条的版本(7.133.x)起服一次让它走完,再升",
392
+ stopThenStart: null,
377
393
  reportKeys: ["workflowJournalScopeColumn", "workflowJournalOwnersBackfilled", "workflowJournalForeignScopeRowsRemoved"],
378
394
  shapeChanges: [{ kind: "rebuild", table: WORKFLOW_JOURNAL_TABLE, whilePresent: RETIRED_JOURNAL_SCOPE_COLUMN, supplies: [WORKFLOW_JOURNAL_SUPPLIED_COLUMN] }],
379
395
  },
@@ -387,6 +403,7 @@ export const ONE_TIME_MIGRATIONS = [
387
403
  "`test/memory-project-layer-fold.test.ts` 的 ① 格与 `test/memory-project-layer-fold-db-integration.test.ts` 整份、" +
388
404
  "`test/project-identity.test.ts` 的 `repo:` 金样格(公式金样改钉 `p` 档摘要)",
389
405
  afterRemoval: "没有新的臂(如实):窗内一次都没跑过任务的项目,它的 `repo:` 层留在库里、不再被任何读列表包含(不读、不删;起服拒只管运维点名这一族);出路 = 升级时先过一次带本条的版本并在每个项目跑一次任务",
406
+ stopThenStart: null,
390
407
  reportKeys: [],
391
408
  },
392
409
  {
@@ -399,6 +416,7 @@ export const ONE_TIME_MIGRATIONS = [
399
416
  "**不随本条删**:键的铸点 `breakerKeyOf` / `routeDigestOf`(常设的键形)与新表 `breaker_state`",
400
417
  afterRemoval: "没有新的臂(如实):跳过了带本条的版本直接升的库,退役表 `circuit_breaker` 留在库里、没有读者也没有写者(断路器只读写 `breaker_state`)—— " +
401
418
  "出路 = 升级时先过一次带本条的版本(它起服删掉退役表);不先过也无害 —— 退役表没有读者也没有写者,留在库里只占空间(建议性状态,无数据价值)",
419
+ stopThenStart: null,
402
420
  reportKeys: ["retiredBreakerTablesDropped"],
403
421
  },
404
422
  {
@@ -411,6 +429,7 @@ export const ONE_TIME_MIGRATIONS = [
411
429
  "`MAILBOX_KEYED_TABLES` / `mailboxKeyDeleteSql`(`drop` 的常设语句)",
412
430
  afterRemoval: "没有新的臂(如实):跳过了带本条的版本直接升的库,判官拒的键的行留在库里,任何入口对它都按「箱不在」答(不读、不投、不清)—— 出路 = 升级时先过一次带本条的版本," +
413
431
  "或照 `docs/DEPLOY-PREREQS.md` 7.133.0 SQL 节的查法按键手工删",
432
+ stopThenStart: null,
414
433
  reportKeys: ["mailboxRejectedKeyRowsRemoved"],
415
434
  },
416
435
  {
@@ -428,6 +447,7 @@ export const ONE_TIME_MIGRATIONS = [
428
447
  "**不随本条删**:凭据位密封的常设规则(封口 / 开口 / 闭集 / 读口第四态 / 409 码 / 装配期拒启)",
429
448
  afterRemoval: "读口对凭据位上明文的常设拒臂接住:跳过了带本条的版本直接升的库,带明文凭据的停驻行续跑答 409 `conflict.checkpoint_ctx_unsealable`(`reason:\"plaintext\"`)," +
430
449
  "行字节不动 —— 出路 = 作为新任务重提(仍停驻的先取消),或升级时先过一次带本条的版本",
450
+ stopThenStart: null,
431
451
  reportKeys: [],
432
452
  },
433
453
  {
@@ -440,6 +460,7 @@ export const ONE_TIME_MIGRATIONS = [
440
460
  "`test/checkpoint-index-columns-db-integration.test.ts` 的 M29 格。**不随本条删**:`approval_card` 列、写口的派生、到期 facet 的生成谓词(常设)",
441
461
  afterRemoval: "没有新的臂(如实):跳过了带本条的版本直接升的库,升级前停驻、升级后仍挂着的行卡列是 NULL —— 生成谓词读门型的 facet(审批门 ⇒ SLA 拒扫腿),问答行到期被拒扫" +
442
462
  "(引擎对问答拒收 `sla_timeout`,拒扫腿每拍一条失败,由 `terminal_at_ms` 兜底收割);运维队列行不带卡词。出路 = 升级前答完 / 拒掉停驻的问答,或升级时先过一次带本条的版本",
463
+ stopThenStart: null,
443
464
  reportKeys: ["parkedApprovalCardsBackfilled", "parkedApprovalCardRowsUnreadable", "parkedApprovalCardRowsTooLong"],
444
465
  },
445
466
  {
@@ -468,6 +489,7 @@ export const ONE_TIME_MIGRATIONS = [
468
489
  "**不随本条删**:续跑判据的第四问(退役请求键闭集 `RETIRED_REQUEST_KEYS` / `retiredRequestKeyOf` / `retiredRequestKeyRefusal`,常设)",
469
490
  afterRemoval: "续跑判据第四问的常设拒臂接住:跳过了带本条的版本直接升的库(或窗内从没起过本版的部署),体里仍带 `promptProfile` 的停驻行续跑在五个问点上答 409 `checkpoint.unsupported_version`" +
470
491
  "(判据 `retired_request_key`,`http/stored-body-replay.ts`)、行字节不动 —— 出路 = 作为新任务重提(`promptLean` 代替 `promptProfile`;仍停驻的先取消),或升级时先过一次带本条的版本并在窗内起服",
492
+ stopThenStart: null,
471
493
  reportKeys: [],
472
494
  },
473
495
  {
@@ -481,6 +503,7 @@ export const ONE_TIME_MIGRATIONS = [
481
503
  "`test/memory-sync-client.test.ts` 里标题以 `🔴 M31:` 或 `M31 ` 起头的格、对 `relocateSyncCursorsM31` 的引入与常量 `LEGACY_PEER_DIR`,常量 `CURSOR_FILE` 的行注改成「没有读者」(用它的格随删后无人用它 ⇒ 常量一起删)",
482
504
  afterRemoval: "没有新的臂(如实):跳过了带本条的版本直接升的数据根,升级前的游标还在 `sync-cursors/central/` 与 `sync-cursor-central.json`,没有读者 ⇒ 那几层以空基线首轮同步" +
483
505
  "(两边都改过的条目按冲突上报,上一轮之后在一侧删掉的条目从另一侧回来)。出路 = 升级时先过一次带本条的版本并在窗内跑一轮同步,或升级前在旧版上跑完一轮同步、再删掉这些文件",
506
+ stopThenStart: null,
484
507
  reportKeys: [],
485
508
  },
486
509
  {
@@ -502,6 +525,7 @@ export const ONE_TIME_MIGRATIONS = [
502
525
  "**不随本条删**:执行放置八列、会话店的执行席、设备店的首绑 / 换绑两只写(常设的数据形)",
503
526
  afterRemoval: "起服对账一族的跳版库拒启臂接住:删码同版把 `device_session` / `device_session_retired` 两张表加进退役 schema 判据(`schema-consistency.ts` 的跳版库探针,先于建表)⇒ " +
504
527
  "跳过了带本条的版本直接升的库响亮拒启,出路 = 先在带本条的版本起服一次让切换走完,再升;缺执行放置列的库由列集对账缺列拒启(`config.schema_mismatch`,拒句给 ALTER),同一出路",
528
+ stopThenStart: null,
505
529
  reportKeys: ["executionPlacementColumnsAdded", "deviceBindingsMoved", "deviceBindingsOrphaned", "deviceBindingTablesDropped", "executionPlacementsBackfilled", "askExecutionLanesBackfilled", "executionPlacementSessionsUnreadable"],
506
530
  shapeChanges: [{ kind: "add", table: APPROVAL_ASK_TABLE, column: ASK_EXECUTION_LANE_COLUMN, whilePresent: { table: "session_meta", anyOf: SESSION_EXT_MOVED_COLUMNS.map((c) => c.name) } }],
507
531
  },
@@ -523,6 +547,7 @@ export const ONE_TIME_MIGRATIONS = [
523
547
  "`session-error-wire.ts` 的 `StampedSessionArchiveError` / `STAMPED_SESSION_ARCHIVE_SENTENCE` / `sessionErrorWireOf` 与本店带戳判的唯一读口 `assertArchiveAnonymous`(fork 的源、改日志推送的落点、会话同步导入第一阶段的早拒三处问它)、行上缺建档时刻的 F 类读法(`server.local-session-row.created-at-absent`)",
524
548
  afterRemoval: "三只常设臂接住:① 数据根下的快照目录非空 ⇒ 拒启(`config.local_archive_snapshot_present`,点名文件与两条出路);② 跳过了带本条的版本直接升的数据根(或本条留着的存档),带属主戳的存档被 fork、改日志的推送与会话同步导入第一阶段对 fork / stale 的早拒同一只具名拒(409 `conflict.session_archive_stamped` + 固定句,零写 / 行格净零写)," +
525
549
  "读 / 续跑 / 拉取照常 —— 出路 = 删掉会话后重推,或升级时先过一次带本条的版本并在窗内起服;③ 行上没有建档时刻的格(同一类数据根)⇒ 列表 / 会话视图 / 交出的元数据读纪元(F 类 tag 计数)",
550
+ stopThenStart: null,
526
551
  reportKeys: ["localArchiveRowFactsBackfilled", "localArchivesAnonymized", "localArchivesKeptStamped", "localArchivesUnreadable", "localArchiveSnapshotsRecovered"],
527
552
  },
528
553
  {
@@ -539,6 +564,7 @@ export const ONE_TIME_MIGRATIONS = [
539
564
  afterRemoval: "两只常设臂接住:① SQL —— 删码同版把 `approval_ask.denied_reason` 加进退役 schema 判据(`schema-consistency.ts` 的跳版库探针,先于建表)⇒ 跳过了带本条的版本直接升的库响亮拒启," +
540
565
  "出路 = 先在带本条的版本起服一次让迁移走完,再升(列册对账见幽灵列 / 缺列同样拒启,但它的拒句给的是加列,不是这一出路 —— 所以退役判据要在删码同版加上);" +
541
566
  "② local —— 信封号 1 的日志记录走 File 问账店「认不出的信封号」拒启(`unknown envelope version`,点名文件与出路:用写下它的那一版起服一次,或移开文件)",
567
+ stopThenStart: null,
542
568
  reportKeys: ["askCauseColumnsChanged", "askParkTriggersMoved", "askJournalRecordsUpgraded"],
543
569
  shapeChanges: [
544
570
  { kind: "rename", table: APPROVAL_ASK_TABLE, from: [RETIRED_ASK_CAUSE_COLUMN], to: APPROVAL_ASK_ENDING_CAUSE_COLUMN.name },
@@ -555,6 +581,7 @@ export const ONE_TIME_MIGRATIONS = [
555
581
  "**不随本条删**:模型面闸 `env-model-table-gate.ts`、`LocalConfigProvider` 读决定(etag 覆盖)、目录信任判官的根级文档名单(常设)",
556
582
  afterRemoval: "常设的模型面闸接住:跳过了带本条的版本直接升的本地根(或运行记录在场而决定缺席的根),决定缺席读作 kept ⇒ env 模型表已声明时管模型面的本地目录被组拒 " +
557
583
  "`config.catalog_replaces_env_models`,拒句点名被顶替的 env 模型与两条出路(在配置根写 `config.env-models.json` = `{\"envModels\":\"replaced\"}`,或撤掉 MODEL_ID / MODEL_CHEAP_ID)",
584
+ stopThenStart: null,
558
585
  reportKeys: [],
559
586
  },
560
587
  {
@@ -572,6 +599,7 @@ export const ONE_TIME_MIGRATIONS = [
572
599
  "**不随本条删**:`SqlDurableRulePartitionProvider` 收「方言 + 执行口」的那一形(`BucketSql`;常设,池照旧满足它)",
573
600
  afterRemoval: "没有响亮臂(如实):删码之后,跳过了带本条的版本直接升的库(或回滚到引擎 7.61.0 之前、在其上又导入过的库),存量的这种全局 `/x` allow 照旧在每个项目根放行,本服务不再点名 —— " +
574
601
  "出路 = 升级时先过一次带本条的版本并起服;或运维列出桶里出处为导入(`adds[].origin` = `imported-cc`)、scope 为全局、从 `/` 写起的 allow(`GET /v1/rules`)并撤掉(`DELETE /v1/rules`,撤的是这条规则的全部批准)",
602
+ stopThenStart: null,
575
603
  reportKeys: ["importedLayerRootAllowsRetired", "importedLayerRootBucketsSkipped", "importedLayerRootBucketsUnnamed"],
576
604
  },
577
605
  {
@@ -584,6 +612,7 @@ export const ONE_TIME_MIGRATIONS = [
584
612
  "`test/approval-card-source-key-m37.test.ts` 整只文件、本文件头注 M37 段",
585
613
  afterRemoval: "既有坏行臂接住(响亮,不再改名):仍带旧键的存卡(跳过了带本条的版本直接升的库、窗关之后还在写的 7.142.x 副本)整卡读不出 —— 断连重放跳过 + 一次 warn、" +
586
614
  "幂等重入走 park、回决口按要人",
615
+ stopThenStart: null,
587
616
  reportKeys: [],
588
617
  },
589
618
  {
@@ -598,6 +627,7 @@ export const ONE_TIME_MIGRATIONS = [
598
627
  "`test/settings-env-credential.test.ts` 里窗开的三格(剥键照续 / 剥空登记空表 / 预判窗开不拦)与「窗关当天仍开」那格、`withM38Window` 助手(窗关的几格改成不注入窗直接断言拒)、M38 登记行那一格;本文件头注 M38 段",
599
628
  afterRemoval: "没有新的臂,接住它的是窗关之后的读法(删码零行为变化:窗关那一刻起剥键臂已经不生效):存体 `settings.env` 里仍带凭据条目的停驻行,续跑前的预判拒(409 `resume_blocked_by_policy`,`blockedBy: request.env_credential_refused`,机读位点名键与命中类),卡仍 pending —— " +
600
629
  "出路是取消那条 run,把凭据挪进引擎进程环境后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行、窗内续跑过又停驻到窗外的任务(头注 M38 段)",
630
+ stopThenStart: null,
601
631
  reportKeys: [],
602
632
  },
603
633
  {
@@ -611,6 +641,7 @@ export const ONE_TIME_MIGRATIONS = [
611
641
  "**不随本条删**:数据根建目录只经 `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 不被复用)",
612
642
  afterRemoval: "没有响亮臂(如实,也无需):删码之后,跳过了带本条的版本直接升的数据根,升级前铸成的 0755 目录原样留着(新建的目录一律 0700,不受影响;里面的文件多半仍是 0600),本服务不再点名 —— " +
613
643
  "出路 = 运维一次 `chmod 700` 数据根下 `runs`(及其子目录)· `space-commits` · `checkpoint-ctx` · `.tmp` · `attachments` · `scratchpad`(及其子目录)· `plugin-cache`(及其子目录)· `<记忆引擎根>/sync-cursors`,或升级时先过一次带本条的版本并起服",
644
+ stopThenStart: null,
614
645
  reportKeys: [],
615
646
  },
616
647
  {
@@ -618,7 +649,7 @@ export const ONE_TIME_MIGRATIONS = [
618
649
  introducedIn: "7.147.0",
619
650
  releasedOn: "2026-11-07",
620
651
  removableAfter: "2026-12-07",
621
- removal: "删 `src/plugins/execution-lane-rename-m40.ts` 整只文件;本文件的 M40 步(`renameRetiredExecutionLaneWordsM40` / `refuseWhileRunsHeartbeat`〔读证据的那一处调用;证据读法 `readOlderReplicaEvidence` 与判活窗输入归 M52 行〕/ `ExecutionLaneRenameRefusedError` / " +
652
+ removal: "删 `src/plugins/execution-lane-rename-m40.ts` 整只文件;本文件的 M40 步(`renameRetiredExecutionLaneWordsM40` / `refuseExecutionLaneRenameM40`〔本行 `stopThenStart.refuse`;证据读法 `readOlderReplicaEvidence`、判活窗输入与守卫段归 `stop-then-start.ts`,常设,不随本行删〕/ `ExecutionLaneRenameRefusedError` / " +
622
653
  "`EXECUTION_LANE_RENAME_REFUSED_CODE`,`migrateStepsOn` 第一步那一行)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `executionLaneWordsRenamed`、头注 M40 段;" +
623
654
  "`store-backend.ts` 的 `LocalBackend.ensureSchema` 第一步与 `executionLaneWordsRenamed` 字段;" +
624
655
  "`local-session-store.ts` 的内核 `renameRetiredExecutionLaneWordsInCellsM40` 与数据根准入的具名读者 `readSessionMetaSidecarInWindow`(在内存里套的那一步;准入的解码点改用 `readSessionMetaSidecar`);" +
@@ -629,6 +660,8 @@ export const ONE_TIME_MIGRATIONS = [
629
660
  stopThenStart: {
630
661
  reason: "renames the retired execution-lane word \"in-process\" to \"none\" on stored approval asks and sessions; a replica of an earlier version would keep writing the old word after the rename",
631
662
  owedOn: owesExecutionLaneRenameM40,
663
+ refuse: refuseExecutionLaneRenameM40,
664
+ fence: { kind: "accepted", residual: "an earlier replica keeps writing the old word after the rename; this version's readers refuse such a row loudly, and the next boot inside the window renames it again" },
632
665
  },
633
666
  reportKeys: ["executionLaneWordsRenamed"],
634
667
  },
@@ -637,8 +670,8 @@ export const ONE_TIME_MIGRATIONS = [
637
670
  introducedIn: "7.147.0",
638
671
  releasedOn: "2026-11-07",
639
672
  removableAfter: "2026-12-07",
640
- removal: "删本文件的 M41 步(`moveSessionCellsToSideTable` / `refuseSessionExtMoveWhileRunsHeartbeat` / `deploymentColumnsOnMeta` / `SessionExtMoveRefusedError` / `SESSION_EXT_MOVE_REFUSED_CODE` / " +
641
- "`SessionExtMoveRefusalReason` / `sameCell` / `SESSION_EXT_MOVE_PAGE`,`migrateStepsOn` 里那两行)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `sessionExtRowsMoved` / `sessionMetaColumnsDropped`、头注 M41 段;" +
673
+ removal: "删本文件的 M41 步(`moveSessionCellsToSideTable` / `refuseSessionExtMoveM41`〔本行 `stopThenStart.refuse`〕/ `deploymentColumnsOnMeta` / `SessionExtMoveRefusedError` / `SESSION_EXT_MOVE_REFUSED_CODE` / " +
674
+ "`SessionExtMoveRefusalReason` / `sameCell` / `SESSION_EXT_MOVE_PAGE`,`migrateStepsOn` 里那一行)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `sessionExtRowsMoved` / `sessionMetaColumnsDropped`、头注 M41 段;" +
642
675
  "M32 读形里的 `cellsOnSideTable` 判(它读本条的 `deploymentColumnsOnMeta`):M32 还在时改成恒旁表形 —— 删 M32 写 `session_meta` 八列的那些臂(② ④ ④b ⑥ 与 ③ 的 `migrateDeviceBindingsStatement` 臂)," +
643
676
  "留 ③ 的旁表臂(`migrateDeviceBindingsOntoSideTableStatements`:围栏拒句的「采旧」在任何库上仍要能落到执行格);M32 先于本条删码时这些随 M32 一并删;`test/session-ext-move-db-integration.test.ts` 整只文件。能力名单 `REPLICA_CAPABILITIES` 里的 `M41` 不删(只增)",
644
677
  afterRemoval: "跳过了带本条的版本直接升上来的库:`session_meta` 上仍带部署格列 ⇒ 起服列册对账拒启(`config.schema_mismatch`,那几列是本树之外的多余列,拒句不给 ALTER —— 出路是带本条的版本);" +
@@ -646,6 +679,8 @@ export const ONE_TIME_MIGRATIONS = [
646
679
  stopThenStart: {
647
680
  reason: "moves the session deployment cells from session_meta to session_ext and then retires the old columns; a cell an earlier replica writes between the copy and the retirement is lost, and every cell write it makes afterwards fails",
648
681
  owedOn: owesSessionExtMoveM41,
682
+ refuse: refuseSessionExtMoveM41,
683
+ fence: { kind: "accepted", residual: "a cell an earlier replica writes between the copy and the column drop is lost; after the drop every cell write it makes fails" },
649
684
  },
650
685
  reportKeys: ["sessionExtRowsMoved", "sessionMetaColumnsDropped"],
651
686
  shapeChanges: [{ kind: "drop", table: "session_meta", columns: SESSION_EXT_MOVED_COLUMNS.map((c) => c.name) }],
@@ -655,7 +690,7 @@ export const ONE_TIME_MIGRATIONS = [
655
690
  introducedIn: "7.148.0",
656
691
  releasedOn: "2026-11-08",
657
692
  removableAfter: "2026-12-08",
658
- removal: "删 `src/plugins/scenario-prefix-switch-m42.ts` 整只文件;本文件的 M42 步(`switchScenarioPrefixM42` / `scannedIdentityColumnsM42`,`migrateStepsOn` 第一步那一行)、报告 / 收据 / 日志的 `scenarioPrefixSourcesScanned`、头注 M42 段;" +
693
+ removal: "删 `src/plugins/scenario-prefix-switch-m42.ts` 整只文件;本文件的 M42 步(`switchScenarioPrefixM42` / `scannedIdentityColumnsM42` / `refuseScenarioPrefixSwitchM42`〔本行 `stopThenStart.refuse`〕,`migrateStepsOn` 第一步那一行)、报告 / 收据 / 日志的 `scenarioPrefixSourcesScanned`、头注 M42 段;" +
659
694
  "`store-backend.ts` 的 `LocalBackend.runOneTimeMigrations` 里 M42 那一腿(`switchLocalScenarioPrefixM42`)与四只 File 店的 `scenarioPrefixOwnerRowsM42`(run / 会话 / 问 / 附件);`test/scenario-prefix-switch-m42.test.ts` 与 `test/scenario-prefix-switch-db-integration.test.ts` 两只文件。" +
660
695
  "标记表 `one-time-migration-marker.ts` 不随删(常设:中央建表语句建它,`Skill` 豁免截点那一行的家);库里留下的 `M42` 标记行 / 数据根里的标记文件无人再读,不删",
661
696
  afterRemoval: "没有响亮臂(如实):跳过了带本条的版本直接升的库,存量里以 `_scn:` 开头的身份被恢复管线读作某个人的场景(拆不出合文法的场景与人的 ⇒ `owner.malformed_subject` 响亮)。" +
@@ -663,6 +698,8 @@ export const ONE_TIME_MIGRATIONS = [
663
698
  stopThenStart: {
664
699
  reason: "scans stored identities for the reserved scenario prefix and records a marker; an earlier replica does not reserve the prefix and would keep accepting such identities after the scan",
665
700
  owedOn: owesScenarioPrefixSwitchM42,
701
+ refuse: refuseScenarioPrefixSwitchM42,
702
+ fence: { kind: "accepted", residual: "an earlier replica keeps accepting identities that start with the reserved scenario prefix after the scan; this version reads such an identity as someone else's scenario" },
666
703
  },
667
704
  reportKeys: ["scenarioPrefixSourcesScanned"],
668
705
  },
@@ -676,6 +713,7 @@ export const ONE_TIME_MIGRATIONS = [
676
713
  "local 会话元数据格「没有 `clientWrites` 位 ⇒ upgrade_pending」那一臂;库里留下的标记行无人再读,不删(与 M42 同一张标记表)",
677
714
  afterRemoval: "窗 30 天之后更早版本的副本早已不在;标记行不再被任何读者读(留着无害)。跳过了带本条的版本直接升上来的库:撤回出口从第一次起服就启用 —— " +
678
715
  "跨两版以上的混跑本就不被支持(7.147.0 起的停-迁-起)。出路 = 升级时先过一次带本条的版本并起服",
716
+ stopThenStart: null,
679
717
  reportKeys: ["withdrawMarkWritten"],
680
718
  },
681
719
  {
@@ -689,6 +727,7 @@ export const ONE_TIME_MIGRATIONS = [
689
727
  afterRemoval: "没有响亮臂(如实):跳过了带本条的版本直接升上来的库,旧行按列答 —— 停驻两口在列建立之前写下的 `needs_review` 行 `errorCode` 缺席(`review.pending` 仍在 `result` 里)、" +
690
728
  "列里是没折过的原码的旧行答原码;`result.terminal.code` 仍在 `result` 里。调长了停驻终局兜底(`APPROVAL_TERMINAL_BACKSTOP_MS` > 30 天)的部署,窗关时还可能有更早版本写下的停驻没结清,读法同上。" +
691
729
  "出路 = 升级时先过一次带本条的版本并起服",
730
+ stopThenStart: null,
692
731
  reportKeys: ["runErrorCodeColumnRewritten"],
693
732
  },
694
733
  {
@@ -701,6 +740,7 @@ export const ONE_TIME_MIGRATIONS = [
701
740
  "`test/run-preview-m45.test.ts` 与 `test/helpers/run-preview-m45-rows.ts` 整个文件、`test/session-preview-single-source-db-integration.test.ts` 的 M45 组",
702
741
  afterRemoval: "没有响亮臂(如实):跳过了带本条的版本直接升上来的库,7.124.0 之前写下的、以注入块起头的预览照旧按列答 —— 会话列表 / `GET /v1/tasks` / 助手任务行名 / 收件箱上显示那段块文本。" +
703
742
  "出路 = 升级时先过一次带本条的版本并起服",
743
+ stopThenStart: null,
704
744
  reportKeys: ["runObjectivePreviewRewritten"],
705
745
  },
706
746
  {
@@ -720,6 +760,7 @@ export const ONE_TIME_MIGRATIONS = [
720
760
  "SQL 在 `ensureSchema()` 建表之前(`LegacySkillExemptionsPresentError`,与退役 schema 探针同一处),local 在数据根准入(读数种别 `skipped-required-migration`)。" +
721
761
  "出路 = 先用一个仍带本条、不低于 7.148.0(local 还要不低于拒句里已记的引擎高水位)的版本对这只库完整起一次,再升;SQL 另可手工 `DELETE FROM approval_exemption WHERE tool_name = 'Skill'`。" +
722
762
  "T0 由常设的一步落(SQL:`ensureSchema()` 建表之后的种子行;local:准入的锁内一步):判过、缺席才落 —— 删码之后全新的库照样有 T0,不会被自己写下的合法行拒",
763
+ stopThenStart: null,
723
764
  reportKeys: ["legacySkillExemptionsDropped"],
724
765
  },
725
766
  {
@@ -736,6 +777,7 @@ export const ONE_TIME_MIGRATIONS = [
736
777
  afterRemoval: "跳版拒启臂接住(常设,数据根准入的一格:只读、本进程第一笔写之前、引擎高水位抬之前):(打开之前的引擎高水位早于 7.148.0 或缺席 ∨ 数据根里的收令牌欠账标记还在 —— 带本条的版本打开过、没收完)" +
737
778
  "∧ 根里有存档在而会话元数据格缺的会话 ⇒ 拒启 `config.schema_mismatch`(读数种别 `skipped-required-migration`,点名会话数与版本),零写、不抬高水位 —— " +
738
779
  "出路 = 先用一个仍带本条、不低于 7.148.0 且不低于已记高水位的版本(服务或 run-local)完整起一次,再升",
780
+ stopThenStart: null,
739
781
  reportKeys: ["localArchiveTokensAdopted", "localArchiveTokensMinted"],
740
782
  },
741
783
  {
@@ -750,6 +792,7 @@ export const ONE_TIME_MIGRATIONS = [
750
792
  afterRemoval: "没有响亮臂(如实):没有身份边界的部署跳过了带本条的版本直接升上来,哨兵属主的存量行仍记着那个字面量,属主面表已不认它 —— 不带主体的本地单用户读不到、续不上那些会话与 run(404)," +
751
793
  "带同一个头的请求仍读得到(属主相同)。出路 = 升级时先过一次带本条的版本并起服(或运维手工 `UPDATE <表> SET owner = NULL WHERE owner = 'anon:shell-live'`,三张表见删法)。" +
752
794
  "有身份边界的部署本条本来就零改写,删码前后同答(哨兵是一位普通的具名属主)",
795
+ stopThenStart: null,
753
796
  reportKeys: ["shellSentinelOwnersCleared", "shellSentinelOwnerRowsRetained"],
754
797
  },
755
798
  {
@@ -763,6 +806,7 @@ export const ONE_TIME_MIGRATIONS = [
763
806
  afterRemoval: "没有响亮臂(如实):跳过了带本条的版本直接升上来的库,升级前停下、升级后仍挂着的卡归属为空 —— 按 run 找卡的读口找不到它:取消那条 run 不作废它、" +
764
807
  "两条停驻收割把那条 run 当成「卡已不在」收掉(`approval.expired`)、决断答 409 `conflict.resume_context_unavailable`,卡由终局兜底收割收走。出路 = 升级前答完 / 拒掉停驻的卡," +
765
808
  "或升级时先过一次带本条的版本并起服(或运维手工 `UPDATE checkpoint SET host_session_id = session_id WHERE host_session_id IS NULL`)",
809
+ stopThenStart: null,
766
810
  reportKeys: ["checkpointHostSessionsBackfilled"],
767
811
  },
768
812
  {
@@ -775,6 +819,7 @@ export const ONE_TIME_MIGRATIONS = [
775
819
  "`approval-ask-store-memory.ts` 的 `deleteByTaskM50`;`test/approval-ask-session-cascade.test.ts` 的 M50 组(窗内 / 窗关当天 / 窗关次日 / 坏记录四格与 `withM50Window` 助手);本文件头注 M50 段",
776
820
  afterRemoval: "既有的词表外动词拒启(`approval-ask-store-file.ts` 重放的动词闭集判,拒句点名账本路径,出路 = 用写下它的那一版起服一次或把文件挪开)—— 与窗关之后本条自己的拒启同一个结局,删码零行为变化。" +
777
821
  "会撞上它的是窗内一次都没在带本条的版本上起过服的 local 数据根(跳过本版直接升的),账本里又恰好留着一条嵌入方写下的这只词",
822
+ stopThenStart: null,
778
823
  reportKeys: [],
779
824
  },
780
825
  {
@@ -782,28 +827,29 @@ export const ONE_TIME_MIGRATIONS = [
782
827
  introducedIn: "7.148.0",
783
828
  releasedOn: "2026-11-08",
784
829
  removableAfter: "2026-12-08",
785
- removal: "删本文件的 M51 步(`switchSessionDeletingM51` / `SessionDeletingSwitchRefusedError` / `SESSION_DELETING_SWITCH_REFUSED_CODE` / `SESSION_DELETING_MARK_ID`,`migrateStepsOn` 里那一行)、" +
830
+ removal: "删本文件的 M51 步(`switchSessionDeletingM51` / `refuseSessionDeletingSwitchM51`〔本行 `stopThenStart.refuse`〕/ `SessionDeletingSwitchRefusedError` / `SESSION_DELETING_SWITCH_REFUSED_CODE` / `SESSION_DELETING_MARK_ID`,`migrateStepsOn` 里那一行)、" +
786
831
  "报告 / 收据 / `changedAnything` / `readingFields` / `NO_SQL_MIGRATIONS` 的 `sessionDeletingSwitchMarked`、头注 M51 段;`test/session-deleting-state-db-integration.test.ts` 的 M51 组;" +
787
832
  "库里留下的 `M51` 标记行无人再读,不删。**不随本条删**:在删态本身(列、围栏、规则 L 的在删半、终刀判令牌 —— 常设)、标记表 `one-time-migration-marker.ts`(常设)",
788
833
  afterRemoval: "没有响亮臂(也无需):窗 30 天之后更早版本的副本早已不在;跳过了带本条的版本直接升上来的库 —— 跨两版以上的混跑本就不被支持(7.147.0 起的停-迁-起)。出路 = 升级时先过一次带本条的版本并起服",
789
834
  stopThenStart: {
790
835
  reason: "switches session deletion to a deleting state that every replica must read; an earlier replica does not read it and would accept a submission on a session this version is deleting",
791
836
  owedOn: owesSessionDeletingSwitchM51,
837
+ refuse: refuseSessionDeletingSwitchM51,
838
+ fence: { kind: "accepted", residual: "an earlier replica does not read the deleting state and may accept a submission on a session this version is deleting" },
792
839
  },
793
840
  reportKeys: ["sessionDeletingSwitchMarked"],
794
841
  },
795
842
  {
796
- id: "M52",
797
- introducedIn: "7.150.0",
798
- releasedOn: "2026-11-09",
799
- removableAfter: "2026-12-09",
800
- removal: "删 `readOlderReplicaEvidence` 的心跳一半(按 `instance_id` 分组读 `task_run` 心跳的那一句、减去登记为知道该迁移的持有者那一步)与它的判活窗输入:`LiveRunWindowInput`、`SqlOneTimeMigrationInput` 里的那一半、" +
801
- "`store-backend.ts` 的 `buildSqlOneTimeMigrationInput` 读 `runStaleSec` 的那一半、夹具 `test/helpers/db-schema-boot.ts` 的 `liveRunWindowMs`;停-迁-起守卫若已全部删码(M40 / M41 / M42 / M43 / M51 的窗都早于本条关)," +
802
- "连同 `readOlderReplicaEvidence` 与 `stop-then-start-sentence.ts` 的 `OlderReplicaEvidence` / `OlderReplicaHolder` / `olderReplicaEvidenceSentence` 一并删;" +
803
- "`test/run-liveness-single-source-gate.test.ts` ② 的 `one-time-migrations.ts` 白名单行与 ③ `store-backend.ts` 理由里「一次性迁移守卫」半句;`test/older-replica-evidence-db-integration.test.ts` 的证据组;头注 M52 段。" +
804
- "**不随本条删**:副本能力登记(`replica-registration.ts`,能力名单只增)与起服准入(`serving-requirement.ts`)—— 常设",
805
- afterRemoval: "没有响亮臂(如实):早于副本登记的副本不登记,删码之后它们对停-迁-起守卫完全不可见,忙与闲同 —— 发布日之后的天数不证明旧进程不在;出路只有运维:起带停-迁-起迁移的版本之前停光早于本条的副本",
806
- reportKeys: [],
843
+ id: "M53",
844
+ introducedIn: "7.151.0",
845
+ releasedOn: "2026-11-10",
846
+ removableAfter: "2026-12-10",
847
+ removal: "删本文件的 M53 步(`rewriteStoreFaultSentencesM53` / `rewriteBackgroundAgentRowsM53` / `rewriteToolEndEventsM53` / `M53_PAGE` / `M53_BACKGROUND_AGENT_TABLE`,`migrateStepsOn` 里那两行)、整只 `store-fault-sentence-m53.ts`、" +
848
+ "报告 / 收据 / `changedAnything` / `readingFields` / `NO_SQL_MIGRATIONS` 的 `storeFaultSentencesRewritten`、头注 M53 段;`test/store-fault-sentence-m53.test.ts` 与 `test/store-fault-sentence-m53-db-integration.test.ts`(`scripts/db-integration-files.json` 里它那一行同删)。" +
849
+ "**不随本条删**:店边界本身(`terminal.ts` 的 `storeBoundaryFault`、`store-boundary.ts` 的封口 —— 常设)",
850
+ afterRemoval: "没有响亮臂(如实):跳过了带本条的版本直接升的库上,换句之前落盘的后台子代行与 run 事件卡格仍带库驱动的原句(回读 / 回放照原样渲染);出路只有运维:按留存清理,或手删那些行",
851
+ stopThenStart: null,
852
+ reportKeys: ["storeFaultSentencesRewritten"],
807
853
  },
808
854
  ];
809
855
  export function migrationWindowOpen(window, today) {
@@ -821,7 +867,7 @@ export function migrationCarried(id) {
821
867
  return ONE_TIME_MIGRATIONS.some((w) => w.id === id);
822
868
  }
823
869
  export function stopThenStartMigrations(registry = ONE_TIME_MIGRATIONS) {
824
- return registry.flatMap((row) => (row.stopThenStart === undefined ? [] : [{ id: row.id, ...row.stopThenStart }]));
870
+ return registry.flatMap((row) => row.stopThenStart === null ? [] : [{ id: row.id, introducedIn: row.introducedIn, ...(row.shapeChanges !== undefined ? { shapeChanges: row.shapeChanges } : {}), ...row.stopThenStart }]);
825
871
  }
826
872
  export const NO_SQL_MIGRATIONS = {
827
873
  sessionRuleColumn: "file-store",
@@ -853,6 +899,7 @@ export const NO_SQL_MIGRATIONS = {
853
899
  sessionMetaColumnsDropped: 0,
854
900
  withdrawMarkWritten: 0,
855
901
  sessionDeletingSwitchMarked: 0,
902
+ storeFaultSentencesRewritten: 0,
856
903
  askCauseColumnsChanged: 0,
857
904
  askParkTriggersMoved: 0,
858
905
  };
@@ -912,6 +959,8 @@ export function createSqlImportedLayerRootSource(exec) {
912
959
  return { candidateOwners: async () => ({ owners: await listRuleBucketOwners(exec), unnamedBuckets: 0 }), durable: new SqlDurableRulePartitionProvider(exec), isWholeBucketRefusal: isSqlBucketRefusal, isVanishedBucket: isSqlBucketVanished };
913
960
  }
914
961
  export async function runSqlOneTimeMigrations(dialect, pool, input, me = buildThisReplicaRegistration()) {
962
+ const stopThenStart = stopThenStartMigrations();
963
+ assertStopThenStartDeclarations(stopThenStart);
915
964
  const head = async (db) => {
916
965
  await assertReplicaTablesPresent(db, dialect, me);
917
966
  await registerReplica(db, dialect, me, Date.now());
@@ -919,6 +968,7 @@ export async function runSqlOneTimeMigrations(dialect, pool, input, me = buildTh
919
968
  };
920
969
  const locked = async (db) => {
921
970
  await assertServingRequirementsMet(db, me);
971
+ await guardOwedStopThenStart(db, dialect, input, stopThenStart);
922
972
  return migrateOn(db, dialect, input);
923
973
  };
924
974
  return dialect === "mysql" ? withMysqlLock(pool, head, locked) : withPgLock(pool, head, locked);
@@ -966,10 +1016,9 @@ async function migrateOn(db, dialect, input) {
966
1016
  return completedReport(receipts);
967
1017
  }
968
1018
  async function migrateStepsOn(db, dialect, receipts, input) {
969
- await switchScenarioPrefixM42(db, dialect, receipts, input);
970
- await renameRetiredExecutionLaneWordsM40(db, dialect, receipts, input);
971
- await refuseSessionExtMoveWhileRunsHeartbeat(db, dialect, input);
972
- await switchSessionDeletingM51(db, dialect, receipts, input);
1019
+ await switchScenarioPrefixM42(db, dialect, receipts);
1020
+ await renameRetiredExecutionLaneWordsM40(db, dialect, receipts);
1021
+ await switchSessionDeletingM51(db, dialect, receipts);
973
1022
  await rewriteRunErrorCodeColumnM44(db, dialect, receipts);
974
1023
  await rewriteRunObjectivePreviewM45(db, dialect, receipts);
975
1024
  await clearShellSentinelOwnersM48(db, dialect, receipts, input);
@@ -992,6 +1041,7 @@ async function migrateStepsOn(db, dialect, receipts, input) {
992
1041
  await moveSessionCellsToSideTable(db, dialect, receipts);
993
1042
  await markWithdrawEnabledM43(db, dialect, receipts, input);
994
1043
  await dropLegacySkillExemptionsM46(db, dialect, receipts);
1044
+ await rewriteStoreFaultSentencesM53(db, dialect, receipts);
995
1045
  }
996
1046
  const M44_PAGE = 500;
997
1047
  async function rewriteRunErrorCodeColumnM44(db, dialect, receipts) {
@@ -1035,6 +1085,71 @@ async function rewriteOneRunErrorCodeM44(db, dialect, row) {
1035
1085
  ? "UPDATE task_run SET error_code = ? WHERE task_id = ? AND status = ? AND updated_at = ? AND error_code <=> ?"
1036
1086
  : "UPDATE task_run SET error_code = $1 WHERE task_id = $2 AND status = $3 AND updated_at = $4 AND error_code IS NOT DISTINCT FROM $5", [target, taskId, String(row["status"]), row["updated_at"], storedCode]);
1037
1087
  }
1088
+ const M53_PAGE = 500;
1089
+ const M53_BACKGROUND_AGENT_TABLE = "background_agent";
1090
+ async function rewriteStoreFaultSentencesM53(db, dialect, receipts) {
1091
+ if (await tableExists(db, dialect, M53_BACKGROUND_AGENT_TABLE))
1092
+ await rewriteBackgroundAgentRowsM53(db, dialect, receipts);
1093
+ if (await tableExists(db, dialect, "task_event"))
1094
+ await rewriteToolEndEventsM53(db, dialect, receipts);
1095
+ }
1096
+ async function rewriteBackgroundAgentRowsM53(db, dialect, receipts) {
1097
+ let after;
1098
+ for (;;) {
1099
+ const cursor = after === undefined ? "" : dialect === "mysql" ? " AND (scope_key, handle) > (?, ?)" : " AND (scope_key, handle) > ($1, $2)";
1100
+ const page = await db.rows(`SELECT scope_key, handle, rev, ${jsonColumnText(dialect, "record_json")} FROM ${M53_BACKGROUND_AGENT_TABLE} WHERE ${jsonColumnTextExpr(dialect, "record_json")} LIKE '%"errorCode"%'${cursor} ORDER BY scope_key, handle LIMIT ${M53_PAGE}`, after === undefined ? [] : [after.scope, after.handle]);
1101
+ for (const row of page) {
1102
+ let record;
1103
+ try {
1104
+ record = decodeJsonColumn(row["record_json"], readJsonObjectColumn, `${M53_BACKGROUND_AGENT_TABLE}.record_json for handle ${String(row["handle"])}`);
1105
+ }
1106
+ catch (err) {
1107
+ if (isJsonColumnBodyProblem(err))
1108
+ continue;
1109
+ throw err;
1110
+ }
1111
+ const rewritten = rewrittenBackgroundRecordM53(record);
1112
+ if (rewritten === undefined)
1113
+ continue;
1114
+ const rev = Number(row["rev"]);
1115
+ const next = { ...rewritten, rev: rev + 1 };
1116
+ receipts.storeFaultSentencesRewritten += await db.exec(dialect === "mysql"
1117
+ ? `UPDATE ${M53_BACKGROUND_AGENT_TABLE} SET record_json = ?, rev = ? WHERE scope_key = ? AND handle = ? AND rev = ?`
1118
+ : `UPDATE ${M53_BACKGROUND_AGENT_TABLE} SET record_json = $1, rev = $2 WHERE scope_key = $3 AND handle = $4 AND rev = $5`, [dialect === "mysql" ? JSON.stringify(next) : pgSafeJsonStringify(next), rev + 1, row["scope_key"], row["handle"], row["rev"]]);
1119
+ }
1120
+ if (page.length < M53_PAGE)
1121
+ return;
1122
+ const last = page[page.length - 1];
1123
+ after = { scope: last["scope_key"], handle: last["handle"] };
1124
+ }
1125
+ }
1126
+ async function rewriteToolEndEventsM53(db, dialect, receipts) {
1127
+ const carded = dialect === "mysql" ? "JSON_EXTRACT(data, '$.structured.errorCode') IS NOT NULL" : "data->'structured'->>'errorCode' IS NOT NULL";
1128
+ let after;
1129
+ for (;;) {
1130
+ const cursor = after === undefined ? "" : dialect === "mysql" ? " AND (task_id, seq) > (?, ?)" : " AND (task_id, seq) > ($1, $2)";
1131
+ const page = await db.rows(`SELECT task_id, seq, ${jsonColumnText(dialect, "data")} FROM task_event WHERE type = 'tool_end' AND ${carded}${cursor} ORDER BY task_id, seq LIMIT ${M53_PAGE}`, after === undefined ? [] : [after.taskId, after.seq]);
1132
+ for (const row of page) {
1133
+ let data;
1134
+ try {
1135
+ data = decodeJsonColumn(row["data"], readJsonObjectColumn, `task_event.data for task ${String(row["task_id"])} seq ${String(row["seq"])}`);
1136
+ }
1137
+ catch (err) {
1138
+ if (isJsonColumnBodyProblem(err))
1139
+ continue;
1140
+ throw err;
1141
+ }
1142
+ const next = rewrittenToolEndDataM53(data);
1143
+ if (next === undefined)
1144
+ continue;
1145
+ receipts.storeFaultSentencesRewritten += await db.exec(dialect === "mysql" ? "UPDATE task_event SET data = ? WHERE task_id = ? AND seq = ?" : "UPDATE task_event SET data = $1::jsonb WHERE task_id = $2 AND seq = $3", [dialect === "mysql" ? JSON.stringify(next) : pgSafeJsonStringify(next), String(row["task_id"]), row["seq"]]);
1146
+ }
1147
+ if (page.length < M53_PAGE)
1148
+ return;
1149
+ const last = page[page.length - 1];
1150
+ after = { taskId: String(last["task_id"]), seq: last["seq"] };
1151
+ }
1152
+ }
1038
1153
  const M45_PAGE = 500;
1039
1154
  async function rewriteRunObjectivePreviewM45(db, dialect, receipts) {
1040
1155
  if (!(await tableExists(db, dialect, "task_run")))
@@ -1157,14 +1272,15 @@ export class SessionDeletingSwitchRefusedError extends StoreBootRefusalError {
1157
1272
  this.name = "SessionDeletingSwitchRefusedError";
1158
1273
  }
1159
1274
  }
1160
- async function switchSessionDeletingM51(db, dialect, receipts, input) {
1275
+ async function refuseSessionDeletingSwitchM51(evidence, read, dialect) {
1276
+ return new SessionDeletingSwitchRefusedError(evidence, await describeUpgradeDriftTail(read, dialect));
1277
+ }
1278
+ async function switchSessionDeletingM51(db, dialect, receipts) {
1161
1279
  if (!(await tableExists(db, dialect, MIGRATION_MARKER_TABLE)))
1162
1280
  await execUntil(db, migrationMarkerDdl(dialect), () => tableExists(db, dialect, MIGRATION_MARKER_TABLE));
1163
1281
  if (!(await owesSessionDeletingSwitchM51((sql) => db.rows(sql), dialect)))
1164
1282
  return;
1165
- const evidence = await readOlderReplicaEvidence(db, dialect, input, "M51");
1166
- if (evidence !== undefined)
1167
- throw new SessionDeletingSwitchRefusedError(evidence, await describeUpgradeDriftTail((sql) => db.rows(sql), dialect));
1283
+ await recordServingRequirement(db, dialect, "M51", Date.now());
1168
1284
  receipts.sessionDeletingSwitchMarked += await db.exec(markIfAbsentSql(dialect), [SESSION_DELETING_MARK_ID, Date.now()]);
1169
1285
  }
1170
1286
  export async function scannedIdentityColumnsM42(db, dialect) {
@@ -1181,9 +1297,10 @@ export async function scannedIdentityColumnsM42(db, dialect) {
1181
1297
  }
1182
1298
  return out.sort((a, b) => `${a.table}.${a.column}`.localeCompare(`${b.table}.${b.column}`));
1183
1299
  }
1184
- export async function switchScenarioPrefixM42(db, dialect, receipts, input) {
1185
- if (!(await tableExists(db, dialect, MIGRATION_MARKER_TABLE)))
1186
- await execUntil(db, migrationMarkerDdl(dialect), () => tableExists(db, dialect, MIGRATION_MARKER_TABLE));
1300
+ async function refuseScenarioPrefixSwitchM42(evidence, read, dialect) {
1301
+ return new ScenarioPrefixSwitchRefusedError("older_replica", [], evidence, await describeUpgradeDriftTail(read, dialect));
1302
+ }
1303
+ export async function switchScenarioPrefixM42(db, dialect, receipts) {
1187
1304
  if (!(await owesScenarioPrefixSwitchM42((sql) => db.rows(sql), dialect)))
1188
1305
  return;
1189
1306
  const columns = await scannedIdentityColumnsM42(db, dialect);
@@ -1197,9 +1314,9 @@ export async function switchScenarioPrefixM42(db, dialect, receipts, input) {
1197
1314
  receipts.scenarioPrefixSourcesScanned += columns.length;
1198
1315
  if (hits.length > 0)
1199
1316
  throw new ScenarioPrefixSwitchRefusedError("stored_prefix", hits, undefined, await describeUpgradeDriftTail((sql) => db.rows(sql), dialect));
1200
- const evidence = await readOlderReplicaEvidence(db, dialect, input, "M42");
1201
- if (evidence !== undefined)
1202
- throw new ScenarioPrefixSwitchRefusedError("older_replica", [], evidence, await describeUpgradeDriftTail((sql) => db.rows(sql), dialect));
1317
+ if (!(await tableExists(db, dialect, MIGRATION_MARKER_TABLE)))
1318
+ await execUntil(db, migrationMarkerDdl(dialect), () => tableExists(db, dialect, MIGRATION_MARKER_TABLE));
1319
+ await recordServingRequirement(db, dialect, "M42", Date.now());
1203
1320
  await db.exec(markIfAbsentSql(dialect), [SCENARIO_PREFIX_MARKER_ID, Date.now()]);
1204
1321
  }
1205
1322
  export const EXECUTION_LANE_RENAME_REFUSED_CODE = "config.execution_lane_rename_refused";
@@ -1244,64 +1361,22 @@ async function executionLaneWordRowsM40(read, dialect) {
1244
1361
  }
1245
1362
  return { tables, pendingRows };
1246
1363
  }
1247
- export async function renameRetiredExecutionLaneWordsM40(db, dialect, receipts, input) {
1364
+ export async function renameRetiredExecutionLaneWordsM40(db, dialect, receipts) {
1248
1365
  const { tables, pendingRows } = await executionLaneWordRowsM40((sql) => db.rows(sql), dialect);
1249
1366
  if (pendingRows === 0)
1250
1367
  return;
1251
- await refuseWhileRunsHeartbeat(db, dialect, pendingRows, input);
1368
+ await recordServingRequirement(db, dialect, "M40", Date.now());
1252
1369
  for (const table of tables) {
1253
1370
  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]);
1254
1371
  }
1255
1372
  }
1256
- export async function readOlderReplicaEvidence(db, dialect, input, migrationId) {
1257
- if (!(await tableExists(db, dialect, "task_run")))
1258
- return undefined;
1259
- const since = new Date(Date.now() - input.liveRunWindowMs);
1260
- const groups = (await db.rows(`SELECT instance_id, COUNT(*) AS n, MAX(updated_at) AS newest FROM task_run WHERE status = 'running' AND updated_at >= ${dialect === "mysql" ? "?" : "$1"} GROUP BY instance_id`, [since]))
1261
- .map((r) => ({ holder: typeof r["instance_id"] === "string" ? r["instance_id"] : undefined, runs: Number(r["n"] ?? 0), newest: r["newest"] }))
1262
- .filter((g) => g.runs > 0);
1263
- if (groups.length === 0)
1264
- return undefined;
1265
- const registered = await readReplicaRegistrations({ query: async (sql, params) => ({ rows: await db.rows(sql, params), affected: 0 }) }, dialect, groups.flatMap((g) => (g.holder === undefined ? [] : [g.holder])));
1266
- let unregisteredRuns = 0;
1267
- let newestMs = Number.NaN;
1268
- const registeredWithout = [];
1269
- for (const g of groups) {
1270
- const r = g.holder === undefined ? undefined : registered.get(g.holder);
1271
- if (r !== undefined && knowsCapability(r.capabilities, migrationId))
1272
- continue;
1273
- if (r === undefined)
1274
- unregisteredRuns += g.runs;
1275
- else
1276
- registeredWithout.push({ instanceId: r.instanceId, releaseLine: r.releaseLine, hostName: r.hostName, pid: r.pid, runs: g.runs });
1277
- const cell = g.newest instanceof Date ? g.newest : new Date(String(g.newest));
1278
- if (!Number.isNaN(cell.getTime()) && !(cell.getTime() <= newestMs))
1279
- newestMs = cell.getTime();
1280
- }
1281
- const liveRuns = unregisteredRuns + registeredWithout.reduce((n, h) => n + h.runs, 0);
1282
- if (liveRuns === 0)
1283
- return undefined;
1284
- const now = Date.now();
1285
- const restartAtMs = (Number.isNaN(newestMs) ? now : newestMs) + input.liveRunWindowMs;
1286
- return {
1287
- migration: migrationId,
1288
- liveRuns,
1289
- windowSec: Math.round(input.liveRunWindowMs / 1000),
1290
- restartAfter: new Date(restartAtMs).toISOString(),
1291
- after: `${new Date(restartAtMs).toISOString()} (${Math.max(0, Math.ceil((restartAtMs - now) / 1000))}s from now)`,
1292
- unregisteredRuns,
1293
- registeredWithout: registeredWithout.sort((a, b) => a.instanceId.localeCompare(b.instanceId)),
1294
- };
1295
- }
1296
- async function refuseWhileRunsHeartbeat(db, dialect, pendingRows, input) {
1297
- const evidence = await readOlderReplicaEvidence(db, dialect, input, "M40");
1298
- if (evidence === undefined)
1299
- return;
1300
- throw new ExecutionLaneRenameRefusedError(pendingRows, evidence.liveRuns, evidence.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, ` +
1373
+ async function refuseExecutionLaneRenameM40(evidence, read, dialect) {
1374
+ const { pendingRows } = await executionLaneWordRowsM40(read, dialect);
1375
+ return new ExecutionLaneRenameRefusedError(pendingRows, evidence.liveRuns, evidence.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, ` +
1301
1376
  `which this version calls "${RENAMED_EXECUTION_LANE_M40}" and does not read; this boot renames them (one-time migration M40), but ${olderReplicaEvidenceSentence(evidence)} ` +
1302
1377
  `— a replica of an earlier version may still be serving, and it would keep writing the old word after the rename. No stored row was changed. ` +
1303
1378
  stopThenStartAction(evidence.after) +
1304
- (await describeUpgradeDriftTail((sql) => db.rows(sql), dialect)));
1379
+ (await describeUpgradeDriftTail(read, dialect)));
1305
1380
  }
1306
1381
  export const SESSION_EXT_MOVE_REFUSED_CODE = "config.session_ext_move_refused";
1307
1382
  export class SessionExtMoveRefusedError extends StoreBootRefusalError {
@@ -1327,23 +1402,19 @@ const sameCell = (dialect, column) => {
1327
1402
  return dialect === "mysql" ? `sm.${column.name} <=> ${side}` : `sm.${column.name} IS NOT DISTINCT FROM ${side}`;
1328
1403
  };
1329
1404
  const SESSION_EXT_MOVE_PAGE = 500;
1330
- export async function refuseSessionExtMoveWhileRunsHeartbeat(db, dialect, input) {
1331
- const columns = await deploymentColumnsOnMeta(db, dialect);
1332
- if (columns.length === 0)
1333
- return;
1334
- const evidence = await readOlderReplicaEvidence(db, dialect, input, "M41");
1335
- if (evidence === undefined)
1336
- return;
1337
- throw new SessionExtMoveRefusedError("older_replica", `${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 ` +
1405
+ async function refuseSessionExtMoveM41(evidence, read, dialect) {
1406
+ const columns = await deploymentColumnsOnMeta({ rows: read }, dialect);
1407
+ return new SessionExtMoveRefusedError("older_replica", `${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 ` +
1338
1408
  `(one-time migration M41: copy, verify, then drop the columns), but ${olderReplicaEvidenceSentence(evidence)} — ` +
1339
1409
  "a replica of an earlier version may still be 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. " +
1340
1410
  stopThenStartAction(evidence.after) +
1341
- (await describeUpgradeDriftTail((sql) => db.rows(sql), dialect)));
1411
+ (await describeUpgradeDriftTail(read, dialect)));
1342
1412
  }
1343
1413
  export async function moveSessionCellsToSideTable(db, dialect, receipts) {
1344
1414
  const columns = await deploymentColumnsOnMeta(db, dialect);
1345
1415
  if (columns.length === 0)
1346
1416
  return;
1417
+ await recordServingRequirement(db, dialect, "M41", Date.now());
1347
1418
  const names = columns.map((c) => c.name);
1348
1419
  const p = dialect === "mysql" ? () => "?" : (n) => `$${n}`;
1349
1420
  const upsert = `INSERT INTO ${SESSION_EXT_TABLE} (session_id, ${names.join(", ")}) SELECT session_id, ${names.join(", ")} FROM session_meta ` +
@@ -2214,6 +2285,7 @@ function readingFields(r) {
2214
2285
  scenarioPrefixSourcesScanned: r.scenarioPrefixSourcesScanned,
2215
2286
  withdrawMarkWritten: r.withdrawMarkWritten,
2216
2287
  sessionDeletingSwitchMarked: r.sessionDeletingSwitchMarked,
2288
+ storeFaultSentencesRewritten: r.storeFaultSentencesRewritten,
2217
2289
  runErrorCodeColumnRewritten: r.runErrorCodeColumnRewritten,
2218
2290
  runObjectivePreviewRewritten: r.runObjectivePreviewRewritten,
2219
2291
  shellSentinelOwnersCleared: r.shellSentinelOwnersCleared,