@sema-agent/server 7.43.0 → 7.44.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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
package/USAGE.md CHANGED
@@ -805,6 +805,24 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
805
805
  > 今天仍有效的预防旋钮 = **在开放 `settings.hooks.PreToolUse` 的部署上不依赖后台子代的 durable 审批**
806
806
  > (或用 `REQUIRE_PRINCIPAL` 关掉该面),同样只对**此后**新铸的行生效。
807
807
  >
808
+ > **🔧 升级到 7.44.0 前:`PERMISSION_RULES_ENABLED=true` 的部署必须删库重建 `permission_rule_approval`
809
+ > 表(BREAKING)。** #340 的卡编辑面(自由文本规则臂)给这张表新增 `command`/`edited_json` 两列,而三条
810
+ > 记录语句(`get`/`create`/`cas`)无条件引用它们。本服务的 schema 契约 = 启动 DDL 是唯一真源、不发
811
+ > `ALTER` 增量 seam,建表语句是 `CREATE TABLE IF NOT EXISTS` ⇒ 对已存在的旧表**一字不改**:新两列不在,
812
+ > 升级后**整条审批记录面**(包括修前就有的候选臂)在这台机器上是坏的——不是「少一个新功能」。
813
+ > 为此本版加了一道**拒启**探针(不是 warn):启动时对该表探两个新列,可证缺列 ⇒ 拒绝启动,错误文案
814
+ > 逐字指路 `DROP TABLE permission_rule_approval;`,重启后 schema 在 boot 时按新 DDL 重建。代价有界:
815
+ > 这张表只装**审批记录**(谁在何时同意了哪条候选的审计事实),丢的是在飞的 CC 导入预览(可重新跑一遍)
816
+ > 与历史记录轨迹;真正持久化的规则本身在 `permission_rule` 表,不受影响、不需要重建。
817
+ > 探针只在规则车道真被装配时跑(`PERMISSION_RULES_ENABLED=false` 的部署一次都不碰这张表,不受影响)。
818
+ >
819
+ > **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(#342,BREAKING)。** #273 过渡窗的别名层
820
+ > (`SCENARIO_ALIASES`)整体撤销:请求体里显式传 `scenario:"team"` 从「静默归一到 `discuss`」变成
821
+ > **未知名响亮 400**(`scenario_unknown`);指派/部署配置里悬空写着的旧名走**现有**
822
+ > unknown→default 回退(与任何其他未知名同待遇,不是新逻辑)。prompts 里写死旧工具名 `run_team` 的
823
+ > 部署 ⇒ 模型看到「工具不存在」。滚版前把请求面/配置面/prompts 里的旧名换成现役主名
824
+ > `discuss` / `run_discussion`。
825
+ >
808
826
  > **🔧 升级到 7.10.0 前:必须删库重建(BREAKING,两条同窗)。** ① pg 侧 memory 两表的
809
827
  > `agent_memory_engine_entry.scope/slug` 与 `agent_memory_engine_cursor.scope` 由 `text` 收窄为
810
828
  > `varchar(190)` / `varchar(512)`(与 MySQL-protocol 方言同宽);② 七个 epoch-BIGINT 列补 `_ms` 后缀,
@@ -1,46 +1,5 @@
1
- /**
2
- * design/183 §4.3 —— **form b 收编的迁移计划表**(server 半场细设计;183 §1 明写「表级细设计归 server」)。
3
- *
4
- * 这张表是本车的**单一真源**:phase 0 的目的地冲突 preflight、phase 3 的行迁/桶迁、回执里的 `legs`
5
- * 三者全部由它派生 —— 三处各抄一份表正是「preflight 说没冲突、迁移腿却撞 UNIQUE」这类分家的温床。
6
- *
7
- * ── 判定轴(183 §4.1 身份轴三形,落到本仓真 schema)────────────────────────────────────────────────
8
- * · **形Ⅰ 行迁**(`row-rewrite`):数据里有 owner/principal 结构位,且该位**不在任何唯一键里**
9
- * ⇒ 一条 `UPDATE … SET col = :to WHERE col = :from` 即可,重跑改到同值 = 零行变化(幂等),
10
- * 且**不可能撞唯一键**(`residualKey: undefined` 就是这句话的机器形)。
11
- * · **形Ⅱ 桶迁**(`bucket-rebind`):键是 scope,而本部署的 scope 词表把身份编进去(server durable
12
- * scope ≡ principal 的恒等涟漪)。scope 若**在**唯一键里,重绑就可能与目的地已有的同逻辑键撞
13
- * ⇒ `residualKey` 记下「唯一键去掉身份轴之后剩下的列」,preflight 按它查重叠。
14
- * · **形Ⅲ 无身份轴**:键=session/ref/内容寻址(session_event / task_event / task_active /
15
- * checkpoint_ctx / tool_result / snapshot_manifest / snapshot_blob / approval_batch /
16
- * task_list_* / workflow_completion_inbox 的 session_id 那一半 …)⇒ **零改写**:归属随它挂靠的
17
- * 资产(会话 / run / 内容)一起走,本表不列它们,也不该列 —— 列了就是给「跟着 session_id 走的行」
18
- * 再造一个第二真源。
19
- *
20
- * ── 一张表两根身份轴的处置(agent_roster / background_agent)────────────────────────────────────
21
- * 逐轴各一条腿,各带**自己**的 `residualKey`(= 该表唯一键去掉这根轴)。两条腿在**同一个事务**里
22
- * 顺序跑;preflight 是按迁移**前**的库态逐轴算的,理论上仍存在一个跨轴的组合形撞唯一键
23
- * (A 行只动 owner、B 行只动 scope 后同键)—— 那一形由 phase 3 事务里的唯一键违例**响亮拒 + 整体
24
- * 回滚**兜住(两侧字节零变更),而不是靠 preflight 声称自己算全了。fail-closed,不 fail-open。
25
- *
26
- * ── 为什么腿的集合比小票逐字点名的多(**如实登记,候属主裁**)────────────────────────────────────
27
- * 小票 §行迁/桶迁 点名的是 `session_meta.owner` / `session_policy` / checkpoint / mailbox / workflow /
28
- * background-agent / snapshot / memory。本表在此之外还迁了 `task_run.owner`、`resume_anchor.owner`、
29
- * `sendfile_link.scope`、`task_attachment.owner`、`approval_ask.owner`、`agent_roster` 两轴、
30
- * `workflow_completion_inbox.owner` / `workflow_inbox_fence.owner` / `workflow_notify_journal.principal`、
31
- * `sandbox_image_index.tenant_id`。判据不是「多迁点保险」,是小票自己写的那条**类判据**:
32
- * 「形Ⅰ = owner/principal 结构位」「形Ⅱ = scope ≡ principal 分区面」—— 上面每一列都逐字落在这两句里,
33
- * 且每一列都是**活的授权/可见性轴**(不是史实字段):`session_meta.owner` 迁了而 `task_run.owner` 没迁,
34
- * 结果就是「会话看得见、它的 run 历史读不到」= 183 §6 明令要防的第一病形「owner 变了调用方够不着」。
35
- * 反向的「不该迁」一律进 {@link NOT_MIGRATED_BY_DESIGN}(D1/D2 裁决号在案),不进本表。
36
- */
37
1
  import { formatUserScope, encodeScopeSegment, SCOPE_SEGMENT_MAX_ENCODED } from "@sema-agent/core";
38
- /**
39
- * 全部重绑腿。**次序即执行序**(同一事务内顺序跑):同表两轴相邻,workflow 家族相邻
40
- * (183 §4.3「run + journal 同事务」在本车是「**全部腿同一个事务**」的更强形)。
41
- */
42
2
  export const REBIND_LEGS = [
43
- // ── 形Ⅰ:owner/principal 结构位,不在任何唯一键里 ⇒ 结构性无冲突 ────────────────────────────
44
3
  {
45
4
  leg: "session_meta#owner",
46
5
  table: "session_meta",
@@ -49,15 +8,10 @@ export const REBIND_LEGS = [
49
8
  columns: ["owner"],
50
9
  matchColumn: "owner",
51
10
  encoding: "verbatim",
52
- residualKey: undefined, // PK(session_id) 不含 owner
11
+ residualKey: undefined,
53
12
  why: "会话归属主列(183 §4.3 形Ⅰ 首项;存量 `anon:shell-live` 哨兵行同走此腿——它只是一个 principal 字面量,不需要 ad hoc 分支)",
54
13
  },
55
14
  {
56
- // #292(core 5.48 placement 分区,身份轴普查门在合并树首跑即逮到——门的存在理由):core 契约明写
57
- // placement.scope = "Tenant key, same source as the durable agent row's scope" ⇒ 形Ⅱ(scope ≡
58
- // principal 分区面),与 background_agent.scope 同判。**活的可见性轴**:listPlaced 按它过滤
59
- // (pg/tidb 店的 WHERE placement_kind+placement_scope),不迁则收编后子代转录对新身份枚举不到、
60
- // 对旧身份被 owner 门拒 = 双盲。placement_scope 只在 placed 行非 NULL,谓词天然只碰 placement 分区。
61
15
  leg: "session_meta#placement_scope",
62
16
  table: "session_meta",
63
17
  kind: "bulk-rebind",
@@ -65,7 +19,7 @@ export const REBIND_LEGS = [
65
19
  columns: ["placement_scope"],
66
20
  matchColumn: "placement_scope",
67
21
  encoding: "verbatim",
68
- residualKey: undefined, // PK(session_id) 不含它;idx_session_meta_placement 非唯一
22
+ residualKey: undefined,
69
23
  why: "placement 分区键(core 5.48 SessionPlacement.scope=tenant key,与 durable agent 行 scope 同源)——子代转录的枚举可见性轴,不迁=收编后 listPlaced 对新旧身份双盲",
70
24
  },
71
25
  {
@@ -76,7 +30,7 @@ export const REBIND_LEGS = [
76
30
  columns: ["owner"],
77
31
  matchColumn: "owner",
78
32
  encoding: "verbatim",
79
- residualKey: undefined, // PK(task_id)
33
+ residualKey: undefined,
80
34
  why: "run 列表/属主门的过滤列(GET /v1/runs 与 trace 属主门读它)——不迁 = 会话迁了它的 run 历史读不到",
81
35
  },
82
36
  {
@@ -87,7 +41,7 @@ export const REBIND_LEGS = [
87
41
  columns: ["owner"],
88
42
  matchColumn: "owner",
89
43
  encoding: "verbatim",
90
- residualKey: undefined, // PK(session_id, event_id)
44
+ residualKey: undefined,
91
45
  why: "E18 anchor 的单库舰队纵深防御列(与 session 同生命周期)",
92
46
  },
93
47
  {
@@ -98,7 +52,7 @@ export const REBIND_LEGS = [
98
52
  columns: ["scope"],
99
53
  matchColumn: "scope",
100
54
  encoding: "verbatim",
101
- residualKey: undefined, // PK(id)
55
+ residualKey: undefined,
102
56
  why: "SendUserFile 台账的租户列(DDL 行内注:scope = VERIFIED principal verbatim)= 逐租户 list/revoke 句柄",
103
57
  },
104
58
  {
@@ -109,7 +63,7 @@ export const REBIND_LEGS = [
109
63
  columns: ["owner"],
110
64
  matchColumn: "owner",
111
65
  encoding: "verbatim",
112
- residualKey: undefined, // PK(id)
66
+ residualKey: undefined,
113
67
  why: "D-1 附件面的属主列(auth principal ?? default)",
114
68
  },
115
69
  {
@@ -120,7 +74,7 @@ export const REBIND_LEGS = [
120
74
  columns: ["owner"],
121
75
  matchColumn: "owner",
122
76
  encoding: "verbatim",
123
- residualKey: undefined, // PK(ask_id);UNIQUE(task_id, idempotency_key) 不含 owner
77
+ residualKey: undefined,
124
78
  why: "#151 流内审批 ask 行的租户列(读面按它过滤)。**decision_actor / decision_note 不动** —— 那是史实(D2)",
125
79
  },
126
80
  {
@@ -131,17 +85,10 @@ export const REBIND_LEGS = [
131
85
  columns: ["tenant_id"],
132
86
  matchColumn: "tenant_id",
133
87
  encoding: "verbatim",
134
- residualKey: undefined, // PK(id);UNIQUE(repo, digest) 不含 tenant_id
88
+ residualKey: undefined,
135
89
  why: "镜像可见性的租户列(tenant-scoped 镜像只对该 principal 可见)—— 活的可见性轴,不是史实",
136
90
  },
137
91
  {
138
- // #193 车4 件1 同批:`leader_run` 这张表一落地,收编的身份轴普查门(adoption-db-integration)当场逮到
139
- // 它的 `owner` 列未表态 —— 正是那道门的存在理由。判定=**活的可见性轴**(GET /v1/leader/:id 的 404
140
- // 属主门读它),与 `task_run#owner` 同形,故真迁而不是登记进 declared。
141
- // 🔴 `idem_key` 不随迁(也无腿):它是 `sha256(owner \x1f 调用方原始键)`,重绑后旧行的哈希对新身份
142
- // 不再命中 ⇒ 提交幂等窗在收编那一刻失效(重投会起一条新 run)。这是**有意的**:收编按 183 I1 要求
143
- // 停机进行,窗内没有在飞的重投;而反过来去重算哈希会把「这行当初是用哪个键提交的」这条史实改写,
144
- // 且新旧哈希不等 ⇒ 结构上也撞不了 UNIQUE(故 residualKey 仍是 undefined)。
145
92
  leg: "leader_run#owner",
146
93
  table: "leader_run",
147
94
  kind: "bulk-rebind",
@@ -149,7 +96,7 @@ export const REBIND_LEGS = [
149
96
  columns: ["owner"],
150
97
  matchColumn: "owner",
151
98
  encoding: "verbatim",
152
- residualKey: undefined, // PK(leader_run_id);UNIQUE(idem_key) 不含 owner
99
+ residualKey: undefined,
153
100
  why: "leader run 的属主列(GET /v1/leader/:id 的 404 属主门读它)—— 不迁 = 收编后这些 run 对新身份全部读作「不存在」",
154
101
  },
155
102
  {
@@ -160,7 +107,7 @@ export const REBIND_LEGS = [
160
107
  columns: ["owner"],
161
108
  matchColumn: "owner",
162
109
  encoding: "verbatim",
163
- residualKey: undefined, // PK(session_id, run_id)
110
+ residualKey: undefined,
164
111
  why: "workflow 完成收件箱的属主列(投递门读它)",
165
112
  },
166
113
  {
@@ -171,7 +118,7 @@ export const REBIND_LEGS = [
171
118
  columns: ["owner"],
172
119
  matchColumn: "owner",
173
120
  encoding: "verbatim",
174
- residualKey: undefined, // PK(session_id, kind, run_id)
121
+ residualKey: undefined,
175
122
  why: "收件箱围栏行的属主列(与 inbox 同轴,漏迁 ⇒ 围栏对新身份失效)",
176
123
  },
177
124
  {
@@ -182,13 +129,10 @@ export const REBIND_LEGS = [
182
129
  columns: ["principal"],
183
130
  matchColumn: "principal",
184
131
  encoding: "verbatim",
185
- residualKey: undefined, // PK(run_id)
132
+ residualKey: undefined,
186
133
  why: "notify 日志的投递身份列(未 ack 的通知按它重投)",
187
134
  },
188
135
  {
189
- // 🔴 这条腿是**普查门逮出来的**(2026-08-10 建门当天):本表有 `principal` 与 `scope` **两根**身份轴,
190
- // 初版只迁了 principal。漏掉 scope 的后果是恢复扫描按 `WHERE scope = <新身份>` 一条都捞不到,
191
- // 未 ack 的通知对新身份永久不可见 —— 而 principal 那半迁了,黑盒上看起来「迁过了」。
192
136
  leg: "workflow_notify_journal#scope",
193
137
  table: "workflow_notify_journal",
194
138
  kind: "bulk-rebind",
@@ -196,11 +140,10 @@ export const REBIND_LEGS = [
196
140
  columns: ["scope"],
197
141
  matchColumn: "scope",
198
142
  encoding: "verbatim",
199
- residualKey: undefined, // PK(run_id) 不含 scope
143
+ residualKey: undefined,
200
144
  why: "notify 日志的 scope 分区(与 workflow run/journal 同轴同事务;principal 是投递身份、scope 是租户分区,两根轴各迁各的)",
201
145
  },
202
146
  {
203
- // 两根轴(scope_key / owner)都可能带旧身份 ⇒ 匹配面必须是它们的**并**,否则只在 owner 上带旧值的行找不到。
204
147
  leg: "background_agent#record_json",
205
148
  table: "background_agent",
206
149
  kind: "blob-rewrite",
@@ -220,11 +163,10 @@ export const REBIND_LEGS = [
220
163
  columns: ["owner"],
221
164
  matchColumn: "owner",
222
165
  encoding: "verbatim",
223
- residualKey: undefined, // PK(scope_key, handle) 不含 owner
166
+ residualKey: undefined,
224
167
  why: "后台代理行的 default-deny 谓词两根轴之一(owner);另一根 scope 见下面的桶迁腿",
225
168
  },
226
169
  {
227
- // 🔴 载荷腿排在同表的列腿**之前**:改写要按**旧**身份值找行,列一旦换掉就再也找不到了。
228
170
  leg: "checkpoint#checkpoint_blob",
229
171
  table: "checkpoint",
230
172
  kind: "blob-rewrite",
@@ -232,7 +174,7 @@ export const REBIND_LEGS = [
232
174
  columns: ["scope"],
233
175
  matchColumn: "scope",
234
176
  encoding: "verbatim",
235
- residualKey: undefined, // 逐行按 PK 回写,不可能撞唯一键
177
+ residualKey: undefined,
236
178
  blob: { column: "checkpoint", pk: ["token"], fields: ["scope"], revColumn: "rev" },
237
179
  why: "core `Checkpoint.scope`(类型注:Multi-tenant isolation key)住在 blob 里 —— 只迁 `scope` 列会让载荷里的隔离键留在旧身份",
238
180
  },
@@ -244,7 +186,7 @@ export const REBIND_LEGS = [
244
186
  columns: ["scope"],
245
187
  matchColumn: "scope",
246
188
  encoding: "verbatim",
247
- residualKey: undefined, // PK(token) 不含 scope ⇒ 桶迁语义、结构性无冲突
189
+ residualKey: undefined,
248
190
  why: "durable HITL 检查点的租户分区列(scope = `principal ?? \"_\"`)——不迁 ⇒ 新身份 resume 不了自己的挂起任务",
249
191
  },
250
192
  {
@@ -267,7 +209,7 @@ export const REBIND_LEGS = [
267
209
  columns: ["scope"],
268
210
  matchColumn: "scope",
269
211
  encoding: "verbatim",
270
- residualKey: undefined, // PK(id)
212
+ residualKey: undefined,
271
213
  why: "workflow run 行的 scope 分区(scope = `principal ?? \"default\"`)。183 §4.2 D4:不变量属 update 动词面,存储层迁移豁免",
272
214
  },
273
215
  {
@@ -278,10 +220,9 @@ export const REBIND_LEGS = [
278
220
  columns: ["scope"],
279
221
  matchColumn: "scope",
280
222
  encoding: "verbatim",
281
- residualKey: undefined, // PK(run_id, ordinal)
223
+ residualKey: undefined,
282
224
  why: "resume 重放日志的 scope 守卫列 —— 与 run 行**同事务**(错配会让 load 守卫拒掉整条 resume)",
283
225
  },
284
- // ── 形Ⅱ:身份轴**在**唯一键里 ⇒ residualKey 非 undefined,preflight 必须逐族查重叠 ──────────
285
226
  {
286
227
  leg: "workflow_resume_claim#scope",
287
228
  table: "workflow_resume_claim",
@@ -290,7 +231,7 @@ export const REBIND_LEGS = [
290
231
  columns: ["scope"],
291
232
  matchColumn: "scope",
292
233
  encoding: "verbatim",
293
- residualKey: ["source_run_id"], // PK(source_run_id, scope)
234
+ residualKey: ["source_run_id"],
294
235
  why: "跨副本 resume 准入租约(与 run/journal 同族同事务)",
295
236
  },
296
237
  {
@@ -301,7 +242,7 @@ export const REBIND_LEGS = [
301
242
  columns: ["scope_key", "scope"],
302
243
  matchColumn: "scope_key",
303
244
  encoding: "verbatim",
304
- residualKey: ["handle"], // PK(scope_key, handle)
245
+ residualKey: ["handle"],
305
246
  why: "信箱行的 scope 分区。`scope_key`(字节列)与 `scope`(可读列)存的是同一个值,必须一起改",
306
247
  },
307
248
  {
@@ -312,7 +253,7 @@ export const REBIND_LEGS = [
312
253
  columns: ["scope_key"],
313
254
  matchColumn: "scope_key",
314
255
  encoding: "verbatim",
315
- residualKey: ["handle", "seq"], // PK(scope_key, handle, seq)
256
+ residualKey: ["handle", "seq"],
316
257
  why: "未投递通知 = 183 [3265] A 类资产(消息体随信箱同迁;本表无 `scope` 可读列)",
317
258
  },
318
259
  {
@@ -323,7 +264,7 @@ export const REBIND_LEGS = [
323
264
  columns: ["scope_key", "scope"],
324
265
  matchColumn: "scope_key",
325
266
  encoding: "verbatim",
326
- residualKey: ["handle"], // PK(scope_key, handle)
267
+ residualKey: ["handle"],
327
268
  why: "后台代理行的 scope 分区(default-deny 谓词的第二根轴)",
328
269
  },
329
270
  {
@@ -334,7 +275,7 @@ export const REBIND_LEGS = [
334
275
  columns: ["owner_key", "owner"],
335
276
  matchColumn: "owner_key",
336
277
  encoding: "verbatim",
337
- residualKey: ["name_key", "scope_key"], // PK(name_key, owner_key, scope_key)
278
+ residualKey: ["name_key", "scope_key"],
338
279
  why: "花名册行的 owner 轴(core 1.365 起 owner/scope 双轴必填 = default-deny 谓词的两根轴)",
339
280
  },
340
281
  {
@@ -345,10 +286,9 @@ export const REBIND_LEGS = [
345
286
  columns: ["scope_key", "scope"],
346
287
  matchColumn: "scope_key",
347
288
  encoding: "verbatim",
348
- residualKey: ["name_key", "owner_key"], // PK(name_key, owner_key, scope_key)
289
+ residualKey: ["name_key", "owner_key"],
349
290
  why: "花名册行的 scope 轴(与 owner 轴同表两根轴,逐轴各一条腿——见顶注「一张表两根身份轴」段)",
350
291
  },
351
- // ── 形Ⅱ(memory 族):scope 是 core 的 v2 typed 键,身份在**段**里 ⇒ 值对由 DISTINCT 扫描解出 ──
352
292
  {
353
293
  leg: "agent_memory_engine_entry#scope",
354
294
  table: "agent_memory_engine_entry",
@@ -357,7 +297,7 @@ export const REBIND_LEGS = [
357
297
  columns: ["scope"],
358
298
  matchColumn: "scope",
359
299
  encoding: "memory-scope",
360
- residualKey: ["slug"], // UNIQUE(scope, slug)
300
+ residualKey: ["slug"],
361
301
  why: "记忆条目盘(user:/proj:/userproj: 三形键的 tenant 段 = principal)",
362
302
  },
363
303
  {
@@ -368,7 +308,7 @@ export const REBIND_LEGS = [
368
308
  columns: ["scope"],
369
309
  matchColumn: "scope",
370
310
  encoding: "memory-scope",
371
- residualKey: [], // PK(scope) —— 唯一键**就是**身份轴:两侧各有一行即冲突
311
+ residualKey: [],
372
312
  why: "记忆盘游标(每 scope 一行)",
373
313
  },
374
314
  {
@@ -379,7 +319,7 @@ export const REBIND_LEGS = [
379
319
  columns: ["scope"],
380
320
  matchColumn: "scope",
381
321
  encoding: "memory-scope",
382
- residualKey: ["peer"], // PK(scope, peer)
322
+ residualKey: ["peer"],
383
323
  why: "记忆同步的 per-peer 游标",
384
324
  },
385
325
  {
@@ -390,7 +330,7 @@ export const REBIND_LEGS = [
390
330
  columns: ["scope"],
391
331
  matchColumn: "scope",
392
332
  encoding: "memory-scope",
393
- residualKey: undefined, // PK(id)
333
+ residualKey: undefined,
394
334
  why: "记忆推送队列(未推完的条目随盘走)",
395
335
  },
396
336
  {
@@ -401,64 +341,34 @@ export const REBIND_LEGS = [
401
341
  columns: ["scope"],
402
342
  matchColumn: "scope",
403
343
  encoding: "memory-scope",
404
- residualKey: undefined, // PK(id, rev)
344
+ residualKey: undefined,
405
345
  why: "记忆修订史的 scope 列(随盘走;`op`/`snapshot` 是内容不是身份)",
406
346
  },
407
- // ── 唯一一条要逐行重算主键的腿 ────────────────────────────────────────────────────────────────
408
347
  {
409
348
  leg: "session_policy#principal",
410
349
  table: "session_policy",
411
350
  kind: "session-policy-rekey",
412
351
  action: "row-rewrite",
413
- columns: ["principal"], // policy_key 由 (session_id, principal) 重算,不是等值搬运
352
+ columns: ["principal"],
414
353
  matchColumn: "principal",
415
354
  encoding: "verbatim",
416
- residualKey: ["session_id"], // policy_key = sha256([session_id, principal]) ⇒ 同 session 的目的地行即冲突
355
+ residualKey: ["session_id"],
417
356
  why: "E6 逐会话逐 principal 的工具收紧规则:身份在**主键的 hash 原像**里,等值 UPDATE 够不着 ⇒ 逐行读出→重算 policy_key→改写",
418
357
  },
419
- // ── 规则桶的整桶改键(A-010.4 / A-010.16 / A-010.22)────────────────────────────────────────────
420
358
  {
421
359
  leg: "permission_rule#owner_key",
422
360
  table: "permission_rule",
423
361
  kind: "rule-owner-rekey",
424
362
  action: "bucket-rebind",
425
- // 两列同腿:`owner_key` 是**派生**的桶键、`principal` 是它的明文原像列(店按前者寻址、运维按后者读)。
426
- // 只改一列 = 库里两份身份说法不一致,而寻址用的那份与人读的那份互相矛盾。
427
363
  columns: ["owner_key", "principal"],
428
364
  matchColumn: "owner_key",
429
365
  encoding: "rule-owner-key",
430
- // PK(owner_key) ⇒ 唯一键**就是**这根轴本身 ⇒ 目的地已有桶即冲突(退化成两侧各有任意一行)。
431
- // 与 agent_memory_engine_cursor 的 PK(scope) 同形,故 `[]` 而不是 undefined。
432
- //
433
- // 🔴 连带的一条真语义(真双库上实测出来的,不是推的):**迟到桶清扫不掉,而这是对的**。
434
- // 终态之后旧身份下若又长出一只桶(= 部署配置没随迁,还在按旧身份导规则),改键式清扫会撞上新身份
435
- // 已经占住的那把 PK ⇒ 腿抛 ⇒ `sweepLate` 捕获 + 响亮 warn,残留读数继续报非零。
436
- // 不给它加「合并」臂:两只桶合并是一次 **CRDT 语义 join**(core `applySyncJoin` 的管辖面),
437
- // 收编弧自己发明一个 join 就是在放行面上编语义 —— 本车的既定口径是「不做逐表 merge,拒比合安全
438
- // 且可重来」。运维侧的可行动信号是三处同时指向同一个真因:一行 warn + 不肯归零的
439
- // `residualSourceRows` + 非空的 `outstandingConfigs`。
440
366
  residualKey: [],
441
367
  why: "#154 车二的**常驻放行桶** —— 一条 allow 规则就是「这个人对这条命令的持久同意」,是本仓最直白的**活授权轴**。" +
442
368
  "core 5.23.0 form a 把 permission rules 列为身份轴重绑腿之首(A-010.4:server form b 此前与它有实质分歧);" +
443
369
  "#203 把 `PERMISSION_RULES_ENABLED` 默认翻 ON 之后,不迁 = 收编完成而**旧身份名下仍躺着一桶活着的放行规则**",
444
370
  },
445
371
  ];
446
- /**
447
- * server 侧**按设计不迁**的面(183 §12 裁决号在案)。这份清单进回执的 `notMigratedByDesign`,
448
- * 让「按设计不迁」与「漏了没迁」在黑盒上可分(183 §10.5)。
449
- *
450
- * 🔴 D1 三员为什么同族:它们全是**按对齐窗口分桶的治理瞬态计数**(usage_window / cost_quota /
451
- * rate_limit),窗口语义与身份绑在一起搬过去要跨窗口对齐,复杂度与价值不成比例;清零重计的代价是
452
- * 新身份在当前窗口拿到一份新额度 —— 一次性部署级动作里这是可接受的,而且**方向偏松不偏紧**,
453
- * 不会把人锁在门外。
454
- * 🔴 D2 三员为什么不改:它们是**谁在什么时候做了什么**的史实字段。改写史实 = 伪造审计。
455
- */
456
- /**
457
- * 🔴 本数组的成员集合必须与 {@link NOT_MIGRATED_FACES}(冻结词表)**全等** —— 词表说有而这里没有
458
- * 就是一条下游按名写了断言、回执里却永远不出现的幽灵成员。等式由 `test/adoption-arc.test.ts` 钉住。
459
- * (建表时真发生过:`outcome-ledger` 一度只进了词表 —— 它压根没有身份轴,属于「无可迁」而不是
460
- * 「按设计不迁」,两者混在一起会让 D2 的语义从「史实不改写」滑成「凡是没迁的都算设计」。)
461
- */
462
372
  export const NOT_MIGRATED_BY_DESIGN = [
463
373
  { face: "usage-window", ruling: "D1", reason: "deployment token-governance window (usage_window): a windowed governance transient — not carried, recounted from zero" },
464
374
  { face: "cost-quota", ruling: "D1", reason: "per-window cumulative spend counter (cost_quota.scope_key) — same windowed-transient class as usage-window" },
@@ -466,12 +376,6 @@ export const NOT_MIGRATED_BY_DESIGN = [
466
376
  { face: "approval-exemption-grantor", ruling: "D2", reason: "approval_exemption.granted_by is a historical fact (who granted this exemption), not a live authorization axis" },
467
377
  { face: "approval-ask-decision-actor", ruling: "D2", reason: "approval_ask.decision_actor / decision_note record who decided — historical audit fields, never rewritten" },
468
378
  { face: "image-bake-requested-by", ruling: "D2", reason: "image_bake.requested_by records who requested the build — historical audit field" },
469
- // 🔴 A-010.22:规则店族的**分家表态**。同一族三张表,三个不同的结局,回执必须逐个说清楚:
470
- // · `permission_rule`(活桶) → **真迁**,见 REBIND_LEGS 的 `permission_rule#owner_key` 腿;
471
- // · `permission_rule_approval`(审批史)→ D2,下面这一员;
472
- // · `permission_rule_ticket`(导入票) → D1,下面那一员。
473
- // 判据与既有 D1/D2 六员逐字同源,不是为这一族新造的标准:改写「谁批准过哪条规则」= 伪造审计;
474
- // 分钟级 TTL 的一次性导入票是治理瞬态,过期即死,搬过去没有任何可兑付的价值。
475
379
  {
476
380
  face: "permission-rule-approval-history",
477
381
  ruling: "D2",
@@ -490,29 +394,11 @@ export const AFFECTED_CONFIG_TEMPLATES = [
490
394
  { deployment: "env", key: "REQUIRE_PRINCIPAL", requiredValue: "true (multi-tenant posture must stay on)" },
491
395
  { deployment: "core-decl", key: "localOwnerRules", requiredValue: "(withdraw — the declaration resolves to the adopted principal after adoption)" },
492
396
  ];
493
- /**
494
- * 目的地身份值的**列宽约束**(codex 对抗复审 R1-F3,亲核属实)。
495
- *
496
- * 🔴 病:请求面收 ≤190 字符的 principal,而迁移腿要把它写进**最窄**只有 `VARCHAR(64)` 的列
497
- * (`sandbox_image_index.tenant_id`),两根键列还是 `VARBINARY(190)`(**字节**,不是字符),memory scope
498
- * 更是在 principal 外面再套前缀 + 百分号编码(非 ASCII 膨胀约 9 倍)。于是一个**合法**请求可以跑到腿的
499
- * 中途才被库以截断/超宽打回 —— 那时 phase 停在 INTENT,每次重发与每次 boot 扫描都会再撞一次同样的墙。
500
- *
501
- * 🔴 姿势:在**铸行之前**按最窄真列校验并 typed 拒(fail-closed)。**不**在这里加自动截断——截断身份
502
- * 就是把 A 的数据交给 B。
503
- *
504
- * 另一条可选解是把 `tenant_id` 拓宽到与其它身份列同宽(183/codex 的「统一身份契约」建议)。本车不动
505
- * 别的特性的 schema:那是一次跨车道的列变更,该由镜像面属主拍。此处如实登记这条不对称。
506
- */
507
397
  export const IDENTITY_WIDTH_LIMITS = [
508
398
  { what: "sandbox_image_index.tenant_id VARCHAR(64)", unit: "chars", max: 64 },
509
399
  { what: "owner/principal/scope columns VARCHAR(190)", unit: "chars", max: 190 },
510
400
  { what: "mailbox/background_agent/agent_roster key columns VARBINARY(190) (MySQL: bytes, not characters)", unit: "bytes", max: 190 },
511
401
  ];
512
- /**
513
- * core 的段编码器对超长/不可编码的段是**抛错**而不是返回。这里把那个异常**转成调用方的拒绝理由**
514
- * (错误本身被带出去,不是吞掉换个默认值)—— 拒绝理由要能指着具体是哪一侧、为什么装不下。
515
- */
516
402
  function scopeSegmentRejection(label, principal) {
517
403
  let encoded;
518
404
  try {
@@ -525,30 +411,7 @@ function scopeSegmentRejection(label, principal) {
525
411
  ? `${label} does not fit core's memory scope-segment limit (${SCOPE_SEGMENT_MAX_ENCODED} encoded characters; percent-encoding expands non-ASCII ~9x)`
526
412
  : undefined;
527
413
  }
528
- /** memory scope 列宽(两方言 VARCHAR(190) 同宽;段编码后才是真长度)。 */
529
414
  export const MEMORY_SCOPE_MAX_CHARS = 190;
530
- /**
531
- * 收编身份的**键空间卫生门**(A-010.15,验真后修)。纯函数、零 I/O ⇒ 在铸任何行之前判。
532
- *
533
- * ── 病 ──────────────────────────────────────────────────────────────────────────────────────────
534
- * `adoption_log.from_principal` 上的 UNIQUE 是 183 D7「同一个源不许被收编两次」的**数据库强形**,
535
- * 而这张表的 MySQL 臂是 `VARCHAR(190) … COLLATE utf8mb4_bin`。`utf8mb4_bin` 虽然是 binary collation,
536
- * **却仍然是 PAD SPACE 的**(MySQL 只有 `utf8mb4_0900_*` 族是 NO PAD)—— 等值比较与唯一键检查会把两侧
537
- * 右填充到等长。于是 `"user:a"` 与 `"user:a "` 在 MySQL 腿上是**同一个键**,在 PG 腿(`COLLATE "C"`,
538
- * 无填充)上是**两个键**。同一份代码、同一个请求,两个后端上结局不同:
539
- * · MySQL:第二次收编被 `adoption.source_already_bound` 拒 —— 一个运维完全看不懂的 409;
540
- * · PG:落第二行,弧照跑,却**一行都迁不到**(别的表里没有任何列存着带尾空格的那个身份)。
541
- *
542
- * ── 为什么修在边界而不是把列改成 VARBINARY(本仓已有的字节孪生形)─────────────────────────────
543
- * 本仓对**同一族**问题已经裁过一次,逐字在 `plugins/approval-ask-store-sql.ts` 的
544
- * `assertIdempotencyKeyShape` 头注里:「在边界上 fail-loud 拒掉,方言分歧面整个消失(比把列改
545
- * VARBINARY 更窄、且对三形同时成立)」。这里的判据比那次还硬一层 —— **带首尾空白的 principal 在本仓
546
- * 根本不可能是一个真身份**:`security.ts` 的 `principalFrom` 读头时逐字 `.trim()`,所以没有任何
547
- * owner/scope 列里存得下带尾空格的值。收编一个这样的源身份,能迁到的行恒为零。
548
- * 拒 = 把一个注定空转(或注定撞出误导性 409)的请求在**入库之前**变成一句说得清的 400。
549
- * 改列则要动 schema + 基线 + 双库,却只把 MySQL 腿对齐到 PG 腿,对「这个值本来就不是合法身份」这件事
550
- * 一个字都没说 —— 那才是治标。
551
- */
552
415
  export function checkAdoptionPrincipalShape(fromPrincipal, toPrincipal) {
553
416
  for (const [label, p] of [["fromPrincipal", fromPrincipal], ["toPrincipal", toPrincipal]]) {
554
417
  if (p.trim() !== p) {
@@ -557,52 +420,28 @@ export function checkAdoptionPrincipalShape(fromPrincipal, toPrincipal) {
557
420
  }
558
421
  return undefined;
559
422
  }
560
- /**
561
- * 目的地身份能不能被每一根被写列容纳?返回**人可读的拒绝理由**,`undefined` = 可以。
562
- * 纯函数(零 I/O),所以校验发生在任何库动作之前。
563
- */
564
423
  export function checkAdoptionWidths(fromPrincipal, toPrincipal) {
565
- // 🔴 判据的**次序**有讲究(不是随手排):先查「写进列装不装得下」,再查「core 的段编码上限」。
566
- // 反过来排的话,段上限(64 个**编码后**字符)对任何带 `:` 的 principal 都比 `tenant_id VARCHAR(64)`
567
- // 更早触发 ⇒ 列宽那条臂**永远走不到**(一条摆着好看的死支;本仓有过「变异打在不可达死支上」的判例)。
568
424
  const bytes = Buffer.byteLength(toPrincipal, "utf8");
569
425
  for (const limit of IDENTITY_WIDTH_LIMITS) {
570
426
  const got = limit.unit === "bytes" ? bytes : toPrincipal.length;
571
427
  if (got > limit.max)
572
428
  return `toPrincipal is ${got} ${limit.unit}, but ${limit.what} caps it at ${limit.max}`;
573
429
  }
574
- // **两侧都要查**(本车自测逮到,与 codex R3-F3 同族):`from` 不写进任何列,但它要经
575
- // `encodeScopeSegment` 铸出 memory 扫描的前缀,而 core 对**段**的上限是 64 个编码后字符且是**抛错**。
576
- // 只查 `to` 的话,一个超长的源身份会让弧在 phase 0 的扫描里抛一个未分类异常:意图行已经落库、
577
- // phase 停在 INTENT,此后每次重发与每次 boot 续跑都确定性地再抛一次 —— 一条永远跑不完的弧。
578
430
  for (const [label, p] of [["fromPrincipal", fromPrincipal], ["toPrincipal", toPrincipal]]) {
579
431
  const rejection = scopeSegmentRejection(label, p);
580
432
  if (rejection !== undefined)
581
433
  return rejection;
582
434
  }
583
- // 派生的 memory scope 键(前缀 + 段编码)也要能装下 —— 非 ASCII 在这里膨胀最猛。
584
435
  const derived = formatUserScope(toPrincipal);
585
436
  if (derived.length > MEMORY_SCOPE_MAX_CHARS) {
586
437
  return `the derived memory scope key for toPrincipal is ${derived.length} characters (percent-encoding expands non-ASCII ~9x), but the scope column caps it at ${MEMORY_SCOPE_MAX_CHARS}`;
587
438
  }
588
439
  return undefined;
589
440
  }
590
- /** `proj:` / `userproj:` 键的 tenant 段前缀(值对由 DISTINCT 扫描按前缀解出)。 */
591
441
  export const MEMORY_PROJECT_SCOPE_PREFIXES = ["proj:", "userproj:"];
592
- /** `verbatim` 轴的值对(纯数据,零 I/O)。 */
593
442
  export function buildVerbatimPair(fromPrincipal, toPrincipal) {
594
443
  return { from: fromPrincipal, to: toPrincipal };
595
444
  }
596
- /**
597
- * memory scope 键的重写(纯函数,给扫描出来的每个 scope 值算新值)。
598
- * · `user:<enc(from)>` ⇒ `user:<enc(to)>`(逐字等值);
599
- * · `proj:<enc(from)>/…` / `userproj:<enc(from)>/…` ⇒ 只换 tenant 段,项目段原样;
600
- * · 其它(`org:` 组织盘、无前缀的旧不透明键)⇒ `undefined` = **不属这个 principal,不动**。
601
- *
602
- * 🔴 为什么不用 `LIKE '<prefix>%'`:段是**百分号编码**的(`user%3Aweb-demo`),`%` 在 LIKE 里是通配符,
603
- * 不逐字符转义就会误命中一大片。用「DISTINCT 扫 + 纯函数判前缀」把这道坑整个绕开:判定在 JS 里做,
604
- * 落到 SQL 的永远只有**等值**谓词。
605
- */
606
445
  export function rewriteMemoryScope(scope, fromPrincipal, toPrincipal) {
607
446
  const fromUser = formatUserScope(fromPrincipal);
608
447
  if (scope === fromUser)