@sema-agent/server 7.142.0 → 7.143.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 (258) hide show
  1. package/MIGRATION.md +442 -0
  2. package/USAGE.md +41 -24
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +7 -5
  5. package/dist/approval-ask-audit-store.js +12 -6
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +124 -25
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-queue-kind.d.ts +27 -0
  11. package/dist/approval-queue-kind.js +6 -0
  12. package/dist/approval-reconciler.d.ts +8 -8
  13. package/dist/approval-reconciler.js +3 -3
  14. package/dist/audit.js +4 -2
  15. package/dist/boot/budget-tracing.d.ts +1 -1
  16. package/dist/boot/governance-seams.d.ts +26 -41
  17. package/dist/boot/governance-seams.js +61 -28
  18. package/dist/boot/late-binds.d.ts +7 -4
  19. package/dist/boot/late-binds.js +3 -1
  20. package/dist/boot/leader.js +2 -4
  21. package/dist/boot/memory-consolidation.d.ts +0 -8
  22. package/dist/boot/memory-consolidation.js +3 -3
  23. package/dist/boot/reapers.js +5 -4
  24. package/dist/boot/resolve-spec.d.ts +1 -1
  25. package/dist/boot/resolve-spec.js +8 -8
  26. package/dist/boot/runner-deps.d.ts +10 -7
  27. package/dist/boot/runner-deps.js +5 -17
  28. package/dist/boot/shutdown.d.ts +9 -0
  29. package/dist/boot/shutdown.js +16 -2
  30. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  31. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  32. package/dist/boot/stage-03-store-backend.js +1 -2
  33. package/dist/boot/stage-04-stores.d.ts +5 -2
  34. package/dist/boot/stage-04-stores.js +3 -5
  35. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  36. package/dist/boot/stage-06-runners.d.ts +5 -2
  37. package/dist/boot/stage-06-runners.js +1 -3
  38. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  39. package/dist/boot/stage-07-capability-layer.js +5 -1
  40. package/dist/boot/stage-08-reapers.d.ts +5 -2
  41. package/dist/boot/stage-09-leader.d.ts +5 -2
  42. package/dist/boot/stage-10-http-server.d.ts +5 -2
  43. package/dist/boot/stage-10-http-server.js +9 -1
  44. package/dist/boot/stores.d.ts +5 -2
  45. package/dist/boot/stores.js +7 -7
  46. package/dist/boot/web-admin.d.ts +5 -1
  47. package/dist/boot/web-admin.js +31 -19
  48. package/dist/capabilities/hands-lane.d.ts +9 -39
  49. package/dist/capabilities/hands-lane.js +8 -13
  50. package/dist/capabilities/mcp-probe.d.ts +3 -2
  51. package/dist/capabilities/scenarios.d.ts +11 -1
  52. package/dist/capabilities/scenarios.js +1 -0
  53. package/dist/capabilities/skills.d.ts +1 -17
  54. package/dist/capabilities/skills.js +0 -8
  55. package/dist/capability-facts.d.ts +8 -8
  56. package/dist/capability-facts.js +4 -4
  57. package/dist/card-void.d.ts +6 -9
  58. package/dist/config-catalog.js +1 -1
  59. package/dist/config-dir-trust.js +58 -29
  60. package/dist/config-invariants.d.ts +7 -1
  61. package/dist/config-invariants.js +7 -0
  62. package/dist/config-provider.d.ts +10 -0
  63. package/dist/config-provider.js +15 -4
  64. package/dist/config-root-migration.d.ts +13 -0
  65. package/dist/config-root-migration.js +99 -0
  66. package/dist/config-types.d.ts +5 -5
  67. package/dist/config.d.ts +4 -3
  68. package/dist/config.js +1 -1
  69. package/dist/delegated-session-owner.d.ts +5 -3
  70. package/dist/delegated-session-owner.js +1 -1
  71. package/dist/device-ws-protocol.d.ts +1 -0
  72. package/dist/device-ws-protocol.js +2 -1
  73. package/dist/env-model-table-gate.d.ts +53 -0
  74. package/dist/env-model-table-gate.js +53 -0
  75. package/dist/fleet/fleet-bus.d.ts +28 -52
  76. package/dist/fleet/fleet-bus.js +35 -100
  77. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  78. package/dist/fleet/fleet-reconciler.js +1 -1
  79. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  80. package/dist/fleet/subagent-tail-bus.js +15 -28
  81. package/dist/hooks/hook-runner.js +28 -7
  82. package/dist/host-decision.d.ts +54 -20
  83. package/dist/host-decision.js +42 -14
  84. package/dist/http/admission.d.ts +1 -1
  85. package/dist/http/admission.js +7 -3
  86. package/dist/http/approval-sweeps.js +11 -8
  87. package/dist/http/caller-trust.d.ts +1 -1
  88. package/dist/http/durable-run-leg.js +2 -2
  89. package/dist/http/engine-refusal.d.ts +16 -0
  90. package/dist/http/engine-refusal.js +17 -0
  91. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  92. package/dist/http/memory-compliance-failure-reply.js +4 -6
  93. package/dist/http/owner-access.d.ts +11 -0
  94. package/dist/http/owner-access.js +13 -0
  95. package/dist/http/resume-legs.d.ts +21 -7
  96. package/dist/http/resume-legs.js +71 -49
  97. package/dist/http/resume-restatement.d.ts +39 -0
  98. package/dist/http/resume-restatement.js +25 -0
  99. package/dist/http/route-ctx.d.ts +35 -10
  100. package/dist/http/route-ctx.js +1 -1
  101. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  102. package/dist/http/routes/approvals-assistant.js +142 -31
  103. package/dist/http/routes/capabilities.js +11 -2
  104. package/dist/http/routes/memory-policy.d.ts +2 -2
  105. package/dist/http/routes/memory-policy.js +1 -1
  106. package/dist/http/routes/notify-wake.js +23 -25
  107. package/dist/http/routes/rules.js +48 -13
  108. package/dist/http/routes/runs.js +9 -8
  109. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  110. package/dist/http/routes/session-memory-sync.js +27 -10
  111. package/dist/http/routes/session-sync.js +13 -20
  112. package/dist/http/routes/sessions.d.ts +2 -2
  113. package/dist/http/routes/sessions.js +22 -23
  114. package/dist/http/routes/tasks.js +27 -49
  115. package/dist/http/server.d.ts +13 -7
  116. package/dist/http/server.js +2 -2
  117. package/dist/http/stored-body-replay.js +15 -4
  118. package/dist/http/wire-types.d.ts +17 -7
  119. package/dist/leader/leader.js +5 -2
  120. package/dist/leader/wire.d.ts +7 -1
  121. package/dist/leader/wire.js +6 -5
  122. package/dist/leg-facts.d.ts +37 -0
  123. package/dist/leg-facts.js +15 -0
  124. package/dist/memory-bundle-engine.d.ts +1 -4
  125. package/dist/memory-bundle-engine.js +2 -2
  126. package/dist/memory-operator-faces.d.ts +30 -66
  127. package/dist/memory-operator-faces.js +17 -88
  128. package/dist/memory-scope-stores.d.ts +38 -40
  129. package/dist/memory-scope-stores.js +51 -36
  130. package/dist/memory-scope.d.ts +0 -9
  131. package/dist/memory-scope.js +0 -9
  132. package/dist/memory-sync-client.js +14 -3
  133. package/dist/memory-sync.d.ts +20 -4
  134. package/dist/memory-sync.js +23 -4
  135. package/dist/model-plane-face.d.ts +13 -7
  136. package/dist/model-plane-face.js +2 -1
  137. package/dist/observability/fail-open.d.ts +20 -4
  138. package/dist/observability/fail-open.js +17 -4
  139. package/dist/observability/run-terminal.js +4 -0
  140. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  141. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  142. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  143. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  144. package/dist/orchestration/workflow-park-decision.js +5 -3
  145. package/dist/org-delivery-gate.d.ts +11 -0
  146. package/dist/org-delivery-gate.js +19 -1
  147. package/dist/parked-decide.d.ts +13 -9
  148. package/dist/parked-decide.js +7 -7
  149. package/dist/parked-revive-surface.js +1 -1
  150. package/dist/peer-inbound-face.d.ts +127 -0
  151. package/dist/peer-inbound-face.js +158 -0
  152. package/dist/permission-rule-list.d.ts +13 -9
  153. package/dist/permission-rule-list.js +7 -3
  154. package/dist/plugins/approval-ask-store-file.js +18 -6
  155. package/dist/plugins/approval-ask-store-memory.js +8 -6
  156. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  157. package/dist/plugins/approval-ask-store-sql.js +30 -11
  158. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  159. package/dist/plugins/checkpoint-store-sql.js +9 -7
  160. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  161. package/dist/plugins/file-history-store-sql.js +24 -19
  162. package/dist/plugins/file-run-store.d.ts +29 -0
  163. package/dist/plugins/file-run-store.js +97 -8
  164. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  165. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  166. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  167. package/dist/plugins/local-checkpoint-store.js +2 -2
  168. package/dist/plugins/local-session-store.d.ts +6 -2
  169. package/dist/plugins/local-session-store.js +8 -2
  170. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  171. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  172. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  173. package/dist/plugins/mailbox-store-sql.js +76 -7
  174. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  175. package/dist/plugins/memory-sync-store-pg.js +21 -0
  176. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  177. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  178. package/dist/plugins/one-time-migrations.d.ts +32 -2
  179. package/dist/plugins/one-time-migrations.js +106 -3
  180. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  181. package/dist/plugins/org-delivery-quota-store.js +2 -12
  182. package/dist/plugins/park-commit-watch.d.ts +1 -1
  183. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  184. package/dist/plugins/permission-rule-store-file.js +15 -2
  185. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  186. package/dist/plugins/permission-rule-store-sql.js +35 -2
  187. package/dist/plugins/remote-env-device.d.ts +8 -11
  188. package/dist/plugins/remote-env-device.js +6 -3
  189. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  190. package/dist/plugins/remote-env-k8s.js +50 -17
  191. package/dist/plugins/retention-store-sql.d.ts +2 -2
  192. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  193. package/dist/plugins/sandbox-read-floor.js +2 -7
  194. package/dist/plugins/sql-errors.d.ts +10 -1
  195. package/dist/plugins/sql-errors.js +13 -0
  196. package/dist/plugins/store-backend.d.ts +23 -28
  197. package/dist/plugins/store-backend.js +9 -1
  198. package/dist/plugins/store-contracts.d.ts +29 -5
  199. package/dist/plugins/store-contracts.js +4 -1
  200. package/dist/plugins/task-attachment-store.d.ts +2 -2
  201. package/dist/plugins/task-attachment-store.js +37 -52
  202. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  203. package/dist/plugins/tool-result-store-sql.js +23 -2
  204. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  205. package/dist/plugins/wiring-section-codec.js +55 -0
  206. package/dist/plugins/workspace-fill.d.ts +1 -1
  207. package/dist/rule-entry-list.d.ts +3 -2
  208. package/dist/rule-entry-list.js +5 -1
  209. package/dist/rules-consent.d.ts +28 -5
  210. package/dist/rules-consent.js +40 -28
  211. package/dist/run-local.js +9 -5
  212. package/dist/runs.d.ts +12 -8
  213. package/dist/runs.js +6 -7
  214. package/dist/runtime-governance.d.ts +1 -1
  215. package/dist/server-secret-env.d.ts +4 -0
  216. package/dist/session-error-wire.d.ts +12 -0
  217. package/dist/session-error-wire.js +6 -0
  218. package/dist/session-incarnation.d.ts +1 -1
  219. package/dist/session-notify-door.d.ts +106 -0
  220. package/dist/session-notify-door.js +93 -0
  221. package/dist/session-sync.d.ts +9 -14
  222. package/dist/session-sync.js +5 -5
  223. package/dist/session-workspace.d.ts +3 -13
  224. package/dist/session-workspace.js +0 -9
  225. package/dist/standing-answer-grant.d.ts +5 -0
  226. package/dist/standing-answer-grant.js +10 -0
  227. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  228. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  229. package/dist/task-mcp.d.ts +1 -1
  230. package/dist/task-settings.d.ts +11 -8
  231. package/dist/task-settings.js +31 -11
  232. package/dist/tool-approval.d.ts +52 -34
  233. package/dist/tool-approval.js +66 -57
  234. package/dist/trace/core-keyset-guard.d.ts +11 -8
  235. package/dist/trace/engine-notice-wire.d.ts +1 -1
  236. package/dist/trace/engine-notice-wire.js +3 -1
  237. package/dist/trace/exit-seat.d.ts +7 -1
  238. package/dist/trace/exit-seat.js +1 -0
  239. package/dist/trace/ledger-sink.d.ts +2 -2
  240. package/dist/trace/model-segment.d.ts +55 -25
  241. package/dist/trace/model-segment.js +101 -33
  242. package/dist/trace/project.d.ts +7 -4
  243. package/dist/trace/project.js +6 -0
  244. package/dist/trace/redact.d.ts +61 -9
  245. package/dist/trace/redact.js +1490 -468
  246. package/dist/trace/self-minted-wire.d.ts +510 -38
  247. package/dist/trace/self-minted-wire.js +165 -17
  248. package/dist/trace/wire-projection-faces.d.ts +1 -1
  249. package/dist/trace/wire-projection-faces.js +1 -1
  250. package/dist/verifier-pause-card.d.ts +33 -5
  251. package/dist/verifier-pause-card.js +15 -10
  252. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  253. package/dist/web-admin-client/apply-effective.js +90 -10
  254. package/dist/workspace-space.d.ts +19 -3
  255. package/dist/workspace-space.js +8 -4
  256. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  257. package/package.json +3 -2
  258. package/wire-changes/server@7.143.0.json +1167 -0
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import { mkdir, readFile, readdir, rename, rmdir, stat, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
- import { syncMemoryScope, } from "@sema-agent/core";
4
+ import { carriesCredential, syncMemoryScope, } from "@sema-agent/core";
5
5
  import { replicationCovers } from "./memory-scope.js";
6
6
  import { parseProjectLayerKey } from "./project-layer-key.js";
7
7
  import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
@@ -46,7 +46,7 @@ function scopeDigest(scope) {
46
46
  export function memorySyncCursorPath(memoryEngineDir, scope) {
47
47
  return join(memoryEngineDir, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
48
48
  }
49
- const CURSOR_AXES = { scope: true, peer: true, store: true };
49
+ const CURSOR_AXES = { scope: true, peer: true, store: true, incarnation: true };
50
50
  const CURSOR_FOUND = { absent: true, other: true };
51
51
  function wayOutSentence(wayOut, cursorPath) {
52
52
  switch (wayOut) {
@@ -106,7 +106,18 @@ function cursorOf(parsed) {
106
106
  const updatedAtMs = c["updatedAtMs"];
107
107
  if (typeof updatedAtMs !== "number" || !Number.isFinite(updatedAtMs))
108
108
  return "updatedAtMs is not a finite number";
109
- return { scope: c["scope"], peer: c["peer"], store: c["store"], baseRevs: { ...baseRevs }, updatedAtMs };
109
+ const peerIncarnation = c["peerIncarnation"];
110
+ if (peerIncarnation !== undefined && (typeof peerIncarnation !== "string" || peerIncarnation.trim().length === 0 || carriesCredential(peerIncarnation))) {
111
+ return "peerIncarnation is not a non-blank string free of credentials";
112
+ }
113
+ return {
114
+ scope: c["scope"],
115
+ peer: c["peer"],
116
+ store: c["store"],
117
+ ...(peerIncarnation !== undefined ? { peerIncarnation } : {}),
118
+ baseRevs: { ...baseRevs },
119
+ updatedAtMs,
120
+ };
110
121
  }
111
122
  async function loadLayerBaseline(path, scope) {
112
123
  let raw;
@@ -3,6 +3,9 @@ import type { MemorySyncStore } from "./plugins/memory-sync-store-pg.js";
3
3
  import { type MemorySyncIdentityFlaw } from "./memory-sync-identity.js";
4
4
  /** 一轮同步请求(验型后的干净形状——`parseMemorySyncRequest` 是唯一铸造点)。 */
5
5
  export interface MemorySyncRequest {
6
+ /** 主张:客户端游标的 `peerIncarnation` 原样(这份基线是对着本中心哪一份状态取的);缺席 = 没有主张(首轮,或 7.61.0 之前写下的游标)。
7
+ * ⓪ 在任何读写之前与分区化身比。 */
8
+ peerIncarnation?: string;
6
9
  /** 对端标识(`CentralSyncCursor.peer`,per-(scope,peer) 游标分区键)= 客户端到达本中心用的同步身份(规范化基址)。验型时已过同步身份的唯一铸点
7
10
  * (`memory-sync-identity.ts`,「声明的身份」座位:必须已是规范形、不带凭据);路由随后与本中心声明的身份按串比(不等 ⇒ 409,零写)。 */
8
11
  peer: string;
@@ -37,15 +40,24 @@ export interface MemorySyncResponse {
37
40
  id: string;
38
41
  baseRev: string;
39
42
  }>;
40
- /** 本轮落库的新基线(草案偏离②)——客户端应用完 pull 半场后把它持久化为自己的游标。 */
43
+ /** 本轮落库的新基线(草案偏离②)——客户端应用完 pull 半场后把它持久化为自己的游标。`incarnation` = 答这一轮的分区化身(⓪;
44
+ * 有主张时恒等于主张),客户端存成游标的 `peerIncarnation`。 */
41
45
  cursor: {
42
46
  peer: string;
47
+ incarnation: string;
43
48
  baseRevs: Record<string, string>;
44
49
  updatedAtMs: number;
45
50
  };
46
51
  /** S2.5 分批腿:serverEntries 被 pull.limit 截断时为 true(缺席=未截断)。客户端续轮直到缺席。 */
47
52
  pullTruncated?: true;
48
53
  }
54
+ /** ⓪ 拒答:请求的主张不是本分区的化身,或带非空基线却没有主张 —— 答本分区的化身,什么都没读、什么都没写。路由按 200 原样发(引擎读它成游标拒)。 */
55
+ export interface MemorySyncRefusal {
56
+ refused: "baseline_incarnation";
57
+ incarnation: string;
58
+ }
59
+ /** 一轮的答:结果或拒答(两者都是 2xx 体)。 */
60
+ export type MemorySyncAnswer = MemorySyncResponse | MemorySyncRefusal;
49
61
  /** 一轮 push 的条目数上限 = core REMOTE_HARVEST_MAX_FILES 同款口径(一个 scope 的文件面上限 500;
50
62
  * 同步一轮打满全集也在此内)。deletes 上限同基数放宽(全集删除也要能一轮表达)。 */
51
63
  export declare const MEMORY_SYNC_MAX_ENTRIES = 500;
@@ -65,10 +77,14 @@ type ParseResult = {
65
77
  * 多余键绝不透传进数据面)。`scope` = 路径上已过 owner 门的 scope——每个条目的 scope 必须逐字
66
78
  * 等于它(否则一个 owner-gated 路由就成了跨租户写门)。 */
67
79
  export declare function parseMemorySyncRequest(body: unknown, scope: string): ParseResult;
80
+ /** ⓪ 栅栏收口:写完之后分区化身已不是开头那一只 —— 分区在这一轮中被重置。路由按 500 答(客户端零本地写)。 */
81
+ export declare class MemorySyncPartitionReset extends Error {
82
+ constructor(scope: string);
83
+ }
68
84
  /**
69
- * One full sync round for one (scope, peer). `backend` = 中心侧 entry plane(DB 权威),`cursors` =
70
- * per-(scope,peer) 游标面(sync_cursors)。纯数据参数进(路由已完成鉴权/owner 门/验型),响应形状出。
85
+ * One full sync round for one (scope, peer). `backend` = 中心侧 entry plane(DB 权威),`sync` = 同步店
86
+ * (分区化身 + per-(scope,peer) 游标面)。纯数据参数进(路由已完成鉴权/owner 门/验型),答出(结果或 ⓪ 拒答)。
71
87
  */
72
- export declare function performMemorySync(backend: MemoryBackend, cursors: Pick<MemorySyncStore, "putCursor">, scope: string, req: MemorySyncRequest, now?: () => number): Promise<MemorySyncResponse>;
88
+ export declare function performMemorySync(backend: MemoryBackend, sync: Pick<MemorySyncStore, "putCursor" | "partitionIncarnation">, scope: string, req: MemorySyncRequest, now?: () => number): Promise<MemorySyncAnswer>;
73
89
  export {};
74
90
  //# sourceMappingURL=memory-sync.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { computeEntryRev, MEMORY_ORIGIN_CAUSES, nextSyncBaseline, reconcileMemoryEntries, screenInboundEntries, } from "@sema-agent/core";
2
2
  import { exportMemoryScope } from "./memory-export.js";
3
3
  import { declaredMemorySyncIdentityFlaw } from "./memory-sync-identity.js";
4
+ const MEMORY_SYNC_INCARNATION_MAX = 128;
4
5
  export const MEMORY_SYNC_MAX_ENTRIES = 500;
5
6
  export const MEMORY_SYNC_MAX_DELETES = 2000;
6
7
  export function parseMemorySyncRequest(body, scope) {
@@ -13,6 +14,10 @@ export function parseMemorySyncRequest(body, scope) {
13
14
  const peerFlaw = declaredMemorySyncIdentityFlaw(peer);
14
15
  if (peerFlaw !== undefined)
15
16
  return { ok: false, peerFlaw };
17
+ const rawClaim = b["peerIncarnation"];
18
+ if (rawClaim !== undefined && (typeof rawClaim !== "string" || rawClaim.length === 0 || rawClaim.length > MEMORY_SYNC_INCARNATION_MAX)) {
19
+ return { ok: false, error: `peerIncarnation must be a non-empty string of at most ${MEMORY_SYNC_INCARNATION_MAX} characters when present — the incarnation this center answered the cursor's last round with, carried back verbatim` };
20
+ }
16
21
  const rawBase = b["baseRevs"];
17
22
  if (rawBase === null || typeof rawBase !== "object" || Array.isArray(rawBase))
18
23
  return { ok: false, error: "baseRevs must be an object mapping entry id → rev (may be empty on the first round)" };
@@ -153,7 +158,7 @@ export function parseMemorySyncRequest(body, scope) {
153
158
  }
154
159
  pull = { limit: lim };
155
160
  }
156
- return { ok: true, value: { peer, baseRevs, entries, deletes, ...(pull !== undefined ? { pull } : {}) } };
161
+ return { ok: true, value: { peer, ...(rawClaim !== undefined ? { peerIncarnation: rawClaim } : {}), baseRevs, entries, deletes, ...(pull !== undefined ? { pull } : {}) } };
157
162
  }
158
163
  function baselineStub(id, rev, scope, slug) {
159
164
  return { id, rev, scope, slug: slug ?? `sync-baseline-stub/${id}`, frontmatter: {}, body: "" };
@@ -162,7 +167,19 @@ function malformedEntryReason(err) {
162
167
  const msg = err instanceof Error ? err.message : String(err);
163
168
  return `malformed_entry: core cannot read this entry's frontmatter shape (${msg.slice(0, 200)}) — the entry is refused and never stored; fix the frontmatter (a present \`distilled\` block needs planId/at/carrierRev/inputs[]) and resend`;
164
169
  }
165
- export async function performMemorySync(backend, cursors, scope, req, now = Date.now) {
170
+ function servesClaim(req, incarnation) {
171
+ return req.peerIncarnation !== undefined ? req.peerIncarnation === incarnation : Object.keys(req.baseRevs).length === 0;
172
+ }
173
+ export class MemorySyncPartitionReset extends Error {
174
+ constructor(scope) {
175
+ super(`memory sync: the partition of scope ${JSON.stringify(scope)} was reset during this round (its incarnation changed between the comparison and the last write) — no result is answered for a state that is no longer the one compared`);
176
+ this.name = "MemorySyncPartitionReset";
177
+ }
178
+ }
179
+ export async function performMemorySync(backend, sync, scope, req, now = Date.now) {
180
+ const incarnation = await sync.partitionIncarnation(scope);
181
+ if (!servesClaim(req, incarnation))
182
+ return { refused: "baseline_incarnation", incarnation };
166
183
  const conflicts = [];
167
184
  const rejected = new Map();
168
185
  const malformed = new Set();
@@ -258,13 +275,15 @@ export async function performMemorySync(backend, cursors, scope, req, now = Date
258
275
  }
259
276
  }
260
277
  const updatedAtMs = now();
261
- await cursors.putCursor({ scope, peer: req.peer, baseRevs: nextBase, updatedAtMs });
278
+ await sync.putCursor({ scope, peer: req.peer, baseRevs: nextBase, updatedAtMs });
279
+ if ((await sync.partitionIncarnation(scope)) !== incarnation)
280
+ throw new MemorySyncPartitionReset(scope);
262
281
  return {
263
282
  applied: report.applied,
264
283
  conflicts,
265
284
  serverEntries,
266
285
  serverDeletes: plan.deleteRemote,
267
- cursor: { peer: req.peer, baseRevs: nextBase, updatedAtMs },
286
+ cursor: { peer: req.peer, incarnation, baseRevs: nextBase, updatedAtMs },
268
287
  ...(pullTruncated ? { pullTruncated: true } : {}),
269
288
  };
270
289
  }
@@ -5,24 +5,28 @@
5
5
  * 判官的输入是服务进程的事实:采纳之后 `config.model` 是不是还是那只 env 模型(对象同一性)、anthropic 路由装没装(凭据在场性)、名册布防(env 表是占位)。
6
6
  * 壳拼不出这些,只能镜像两道判(目录条目逐条合形、车道 / 端点可解析)—— 镜像会随时间与真判官漂开(同一问两份读法)。所以结论只有 server 说,壳读它。
7
7
  *
8
- * ## 四个字段,两根轴
8
+ * ## 五个字段,两根轴
9
9
  * · `servedTable` —— **谁在服**:{@link servedModelTableLeg}(此刻服务 env 表还是目录表;同一只读法,不另记旗子)。
10
- * · `catalog` —— **最近一份目录候选的结局**({@link CATALOG_STANCES}):
11
- * `refused` —— 候选被整拒:候选判官整拒(`candidateRejectionState` 座),或模型面组拒(`modelRefusalsState().plane` 在场;各码见
12
- * settings-schema `MODEL_PLANE_REFUSAL_CODES`)。没有 LKG 时服务的是 env 表,有 LKG(或上一代已采纳)时仍服目录表 —— 谁在服归 `servedTable` 答,
10
+ * · `catalog` —— **最新到达的那份目录候选的结局**({@link CATALOG_STANCES}):
11
+ * `refused` —— 候选被整拒:候选判官整拒(`candidateRejectionState` 座),或模型面组拒(`latestPlaneRefusalState()` 在场 —— 落下组拒信号的那次 apply
12
+ * 就是最新一次到达;各码见 settings-schema `MODEL_PLANE_REFUSAL_CODES`)。没有 LKG 时服务的是 env 表,有 LKG(或上一代已采纳)时仍服目录表 —— 谁在服归 `servedTable` 答,
13
13
  * 不在这一格里合并(合并 = 一个字段两个意思);逐条被拒(条目不可用,`entries`)不是整拒,那些条目之外的目录照服;
14
14
  * `adopted` —— 没有整拒、且服务的是目录表;
15
15
  * `absent` —— 没有整拒、且服务的是 env 表:没配目录源 / 配了但没有启用条目(候选不管模型面,不是拒)/ 纯 env 部署。
16
16
  * 两个座(候选判官的结论座与模型面组拒的信号座)是候选在两道判官各自留下的事实,本格只读它们,不另算第二份。
17
17
  * **不在内**:候选过了两道判官、却在装配期被引擎整份拒收(`applyEffective` 里引擎拒收 `apiKeyEnv` 声明 / Runner 拒收换装;活配置零触碰,只有日志与 `/health` 世代账的 target≠applied)——
18
18
  * 这一形今天没有座,`catalog` 仍按上一代读;`adopted` 说的是「没有被判官整拒」,不是「最新候选已落地」(那一问归世代账)。要覆盖它得先给装配期拒收立一个座(另案设计)。
19
+ * · `planeRefusalCode` —— `catalog:"refused"` 的模型面那一半的**码**(与上一格同取 `latestPlaneRefusal` 一份事实,不另算):在场 ⇔ 最新到达的目录候选被模型面组拒。
20
+ * `catalog:"refused"` 而本键缺席 ⇔ 最新到达的候选被候选判官整拒(看 `candidateRejected`)。与 `/health.modelRefusals.plane` 分两个主语:那一键说「最近一份**被 stage 判过**的
21
+ * 模型面的组拒判决」,不随更新的到达撤;本键随每一次更新的到达撤(更新的候选过了门、却在 apply 落账之前失败 ⇒ 本键缺席,那份失败归世代账)。
22
+ * 同一只路由判官的码可能同时出现在本键与 `refusalCode`:本键指最新到达的候选目录,`refusalCode` 指此刻在服的那张表。
19
23
  * · `refusalCode` / `gap` —— **起服路由判官对当前 config 的判决**:`judgeServedModelRoute` 同一只函数(装配第 2 段与 `run-local` 调的就是它),通过 ⇒ 两键缺席。
20
24
  * 进程既然起来了,正常是缺席;测试夹具 / 自建装配 / 不拒启的入口可能在场 —— 如实投,不替它们编造通过。**不带句子**(拒因句只走日志与起服错误)。
21
25
  *
22
26
  * ## 只在起服后可答
23
27
  * 读面挂在运行中的进程上;判官必须在目录应用之后调(`judgeServedModelRoute` 头注),起服前没有「将要服务的表」,所以没有起服前的预判口。
24
28
  */
25
- import type { ModelRefusalsSignal } from "@sema-agent/settings-schema";
29
+ import type { ModelPlaneRefusalCode } from "@sema-agent/settings-schema";
26
30
  import { type ModelRouteEndpointVerdict, type ModelRouteGap, type ServedModelRouteView } from "./model-route-endpoint.js";
27
31
  import { type ModelTableLeg } from "./model-select.js";
28
32
  import type { CandidateRejection } from "./web-admin-client/candidate-gate.js";
@@ -37,6 +41,8 @@ export interface ModelPlaneFace {
37
41
  readonly refusalCode?: ModelRouteEndpointVerdict["code"];
38
42
  /** 同一次判决的出路轴(缺口两词)。 */
39
43
  readonly gap?: ModelRouteGap;
44
+ /** 最新到达的目录候选被模型面组拒的码(见文件头;缺席 ⇔ 最新到达的候选没有被模型面组拒)。 */
45
+ readonly planeRefusalCode?: ModelPlaneRefusalCode;
40
46
  }
41
47
  /** {@link buildModelPlaneFace} 读的进程事实(都由调用方从各自的座取,本函数不碰任何全局)。 */
42
48
  export interface ModelPlaneFacts {
@@ -44,8 +50,8 @@ export interface ModelPlaneFacts {
44
50
  readonly envTableIsPlaceholder: boolean;
45
51
  /** 候选判官最近一次整拒的结论座(`WebAdminRuntime.candidateRejectionState()`);座空 / 没有配置管道 ⇒ `undefined`。 */
46
52
  readonly candidateRejection: CandidateRejection | undefined;
47
- /** 模型拒因信号(`WebAdminRuntime.modelRefusalsState()`);`plane` 在场 ⇒ 最近一份候选的模型面被整组拒。 */
48
- readonly modelRefusals: Pick<ModelRefusalsSignal, "plane"> | undefined;
53
+ /** 最新到达的目录候选被模型面组拒的码(`WebAdminRuntime.latestPlaneRefusalState()`);没有 / 没有配置管道 ⇒ `undefined`。 */
54
+ readonly latestPlaneRefusal: ModelPlaneRefusalCode | undefined;
49
55
  }
50
56
  export declare function buildModelPlaneFace(config: ServedModelRouteView, facts: ModelPlaneFacts): ModelPlaneFace;
51
57
  //# sourceMappingURL=model-plane-face.d.ts.map
@@ -13,12 +13,13 @@ function catalogStanceOf(served, wholeRefused) {
13
13
  }
14
14
  export function buildModelPlaneFace(config, facts) {
15
15
  const servedTable = servedModelTableLeg(config);
16
- const wholeRefused = facts.candidateRejection !== undefined || facts.modelRefusals?.plane !== undefined;
16
+ const wholeRefused = facts.candidateRejection !== undefined || facts.latestPlaneRefusal !== undefined;
17
17
  const verdict = judgeServedModelRoute(config, { envTableIsPlaceholder: facts.envTableIsPlaceholder });
18
18
  return {
19
19
  servedTable,
20
20
  catalog: catalogStanceOf(servedTable, wholeRefused),
21
21
  ...(verdict !== undefined ? { refusalCode: verdict.code, gap: verdict.gap } : {}),
22
+ ...(facts.latestPlaneRefusal !== undefined ? { planeRefusalCode: facts.latestPlaneRefusal } : {}),
22
23
  };
23
24
  }
24
25
  //# sourceMappingURL=model-plane-face.js.map
@@ -207,6 +207,10 @@ export declare const FAIL_OPEN_TAGS: {
207
207
  readonly cls: "F";
208
208
  readonly note: "7.133.0(`checkpoint-identity.ts` 的 `durableRowIdentity`;两只店的 `listPending` + `/v1/assistant/inbox` handler):三处 durable 读面(运维队列行 `GET /v1/approvals` 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的行)读到一行停驻门**没有可用的 `checkpointId`** ⇒ 这一行**照列、`checkpointId` 键缺席**(消费方按「这一行没有可 join 的身份」处理)。detail = `face=<approvals-queue|assistant-inbox> why=<absent|untrusted>`:`absent` = 行上没有这一格(core 5.42.0 之前停驻的老行 —— core 明写不回填);`untrusted` = 在场却没过信任边界(非串 / 空串 / 超长 / 含 resume token —— 数据污染;没过边界的值不上 wire)。放行(列、键缺席)的最坏后果 = 消费方对这一行做不了跨面 join(三面对账少一行)—— 行本身、决断口(`/v1/approvals/:sessionId/decide`,按会话)、到期收割都照常。不放行有两种形:整只拒的代价是一行老 / 坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧);把这一行藏起来的代价更糟 —— 人看不到要批的东西(审批面上的 fail-open,方向反了)。故 F 类;必须留痕(两种成因从此可分)。计数非零 = 有老 / 坏行在飞(列着、没身份);`/stream` 每 3s 一拍重读,同一行每拍计一次(量值 = 行 × 拍数,在场才是信号)。 7.137.0 起第四面:park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`;唯一构造器 `trace/project.ts` 的 `parkEventData`,写时判一次,detail `face=run-events-park`)—— 暂停因由上的门身份没过信任边界 ⇒ 行照写、`checkpointId` 键缺席(决断方回显不了这道门,退回不带键的决断或从 run 结果取原值;行本身、门、待批调用身份照常)。 第五面:回决门族的 200 体(`/decide` / `/resume` / `/plan_review` 执行之前从选中的那一行捕获一次,`http/resume-legs.ts` 三处;detail `face=decision-receipt`)—— 选中那一行的门身份没过信任边界 ⇒ 结清证据照铸、体上 `checkpointId` 键缺席(决断方只是对不上身份;位、决定、续跑都照常)。每次走到捕获点的决断计一次(计的是选中那一行的事实,与这次决断随后走哪条车道无关)。";
209
209
  };
210
+ readonly "server.peer-inbound.notice-coordinates-unreadable": {
211
+ readonly cls: "F";
212
+ readonly note: "S-1370(`peer-inbound-face.ts` 的 `observe`,部署级通告转发席喂它):core 的 `peer.inbound_disposition{disposition:\"held\"}` / `peer.held_settled{heldCount:0}` 通告的 `detail` 读不出坐标(`sessionId` 不是串或不合 core 的会话地址文法 / `box` 既不是那只会话的箱也不是 principal 收件箱)⇒ 这一条不进(或不出)收信方决断面的坐标表。按构造不可达:core 的两只铸点恒给这两位。放行(忽略这一条)的最坏后果:进表那一形 = 那张被扣的信不出现在 `GET /v1/approvals` 的 `pending[]` 上 —— 它仍在 core 的队列里,按 `dialogExpiry` 带 `expired` 回执结清(core 的安全默认),也仍可经 core 自己的通告面看到;出表那一形 = 坐标表多留一行(读时多查一次空队列)。不放行(抛)会把异常回抛进引擎的通告投递(转发席的隔离契约禁止)。故 F 类;计数非零 = core 改了这两条通告的 detail 形,对 core 的 d.ts 重核坐标读法。";
213
+ };
210
214
  readonly "server.approvals.queue-row-gate-kind-missing": {
211
215
  readonly cls: "F";
212
216
  readonly note: "S-1681(`plugins/checkpoint-store-sql.ts` 的 `queueRowGateKind`,7.132.0;SQL 与 local 两只孪生的 `listPending` 同调这一只判官):运维待批队列(`GET /v1/approvals` / `/stream`)读到一行 pending 的门型(SQL 孪生:`checkpoint.gate_kind` 列;local 孪生:快照上的门)是 NULL 或不是 core 登记的门型 ⇒ 这一行**不列**。按构造不可达:每次停驻都把门型反范式进这一列,一次性迁移 M21 把没有它的存量行从门子树补齐(读不出登记门型的置 expired)。放行(不列、队列其余照常)的最坏后果 = 一行谁也决不了的行不出现在队列上 —— 它本来就不可决:每一条决断口按门型绑定取行(`gate_kind IN (…)`),证不出门型就不当它是这道门;不放行(整只拒)的代价是一行坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧)。也不按缺键投出去:消费方按 `gateKind` 路由、缺键即 fail-closed,队列行的契约是 `gateKind` 恒在场。故 F 类;计数非零 = 写腿或手改出了错,`SELECT session_id FROM checkpoint WHERE status = 'pending' AND gate_kind IS NULL` 查那一行(置 expired 或删会话)。";
@@ -365,7 +369,7 @@ export declare const FAIL_OPEN_TAGS: {
365
369
  };
366
370
  readonly "server.run-terminal.redact-threw": {
367
371
  readonly cls: "F";
368
- readonly note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形由 S-219 的 atLeast 铸点闭掉;JSON 秘密字段臂的转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉,本臂留作结构性兜底,计数恒零是证据、不归零即新形)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。";
372
+ readonly note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛)+ 活面放行窗(`trace/model-segment.ts` 的 `createLiveModelText`,probe 前缀 `live-hold`:逐块放行口抛 ⇒ 这一拍不放、字节留在窗里;段末整段读法抛 ⇒ 余下的字节整段换同一枚占位):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形由 S-219 的 atLeast 铸点闭掉;JSON 秘密字段臂的转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉,本臂留作结构性兜底,计数恒零是证据、不归零即新形)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。";
369
373
  };
370
374
  readonly "server.run-terminal.provider-echo-shaped": {
371
375
  readonly cls: "F";
@@ -395,14 +399,26 @@ export declare const FAIL_OPEN_TAGS: {
395
399
  readonly cls: "F";
396
400
  readonly note: "S-107(全量门 silent-degradation 棘轮抓出的五处静默 `catch {}`):run 终局链里**账本腿**(flush 缓冲 text/reasoning、append `failed` 行)与终态翻转(setTerminal)已不共命运——账本 INSERT 抛不再让 run 卡 running 到 reaper;但那条抛掉的账本行之前是**静默**吞的。detail `arm=` 四臂(flush-clean-cancel / append-clean-cancel / flush-catch / append-thrown-cancel / append-failed);S-947 起续跑腿(`http/resume-legs.ts`)两座同 tag:resume-append-resume-aborted / resume-append-thrown-cancel(取消抛出两臂的 `failed` 行,修前那两臂根本不落账本终局行):缺的是 run_events 尾巴(缓冲文本或 failed 事件),run 行本身仍是 durable truth,读面/reaper 容忍缺失,故 F 类;计数=多少次终局账本写失败。";
397
401
  };
402
+ readonly "server.live-text.hold-diverged": {
403
+ readonly cls: "P-DEBT";
404
+ readonly note: "live 面模型文本的流式口(`trace/model-segment.ts` 的 `createLiveModelText`,三条 live 腿共用;流式口 = `trace/redact.ts` 的 `createRedactionStream`:喂入 / 取已判定前缀 / 收尾冲刷):一段冲刷时,已取出去的逐块字节与整段读法在那一截上的渲染对不上,或有一条整段读法的跨度横跨放行位 —— 流式口的律被打破(某条臂的判定在脱敏器读出的未决尾上界 `PENDING_TAIL_UNITS` 之外落定,而它没有把那一支标成未决尾)。处置:余下的字节照整段读法出(横跨放行位的跨度整枚换记号,不再出它的原文字节),段末帧照发整段脱敏的正文、消费端按它整段替换;但**已经出门**的那几块收不回 —— 其中可能有整段读法会遮的字节(这就是 P 族的理由)。债:未决尾上界是候选池成员判定距离的最大值,正则臂从正则源读出,手写扫描器的那一项是成员处的陈述(它起头要读全的字面锚),不是从扫描器的代码机械读出;已知一形是多遮方向(方案词锚的头值在文本末尾被读成「收在行尾」,其后隔着长过上界的行内空白又跟了词 ⇒ 整段读法不遮、活流已出记号)。钉 = 基线语料逐条按块流过流式口拼接 = 整段脱敏 + 判定距离最远的几形逐码元。detail `family=<text|reasoning> len=<段长>`;计数 = 多少段的律被打破(期望恒 0)。";
405
+ };
398
406
  readonly "server.redact.ppk-eat-to-end": {
399
407
  readonly cls: "F";
400
408
  readonly note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;出口座有界行的 head 读法(只扫一段头)上「窗内无合格终止」是终止可能在窗外的未决 —— 照样遮到窗尾,但不是兜底,不计;计数=多少次整段吃尾。";
401
409
  };
410
+ readonly "server.redact.forensic-level-cap": {
411
+ readonly cls: "F";
412
+ readonly note: "脱敏器取证相(redact.ts armPool 第二相)跑满级数界 FORENSIC_LEVEL_MAX 仍没收敛(证据链里层被认出、外层才认得出,刻意交替嵌套的私钥块 / URL 每层多一级,深过 31 层才够得着;现役语料最深 3 级)⇒ 下一级本会重跑的取证臂各自从它读证据的起点遮到窗尾(fail-closed:多遮可见、不少遮),不再升级;每次调用至多一条;影子扫不计;detail from=/len= 是视图坐标。";
413
+ };
402
414
  readonly "server.approval-card.closed-word-out-of-set": {
403
415
  readonly cls: "F";
404
416
  readonly note: "S-249(`approval-card.ts` 的边界窄读族;今天四个成员:`readRuleOffersAbsence`、S-934 ② 起的 `readAskClassPair`(core 7.37.0 #1214 的类对 `askClass` / `classes`,活卡帧 / 呈卡帧的卡 / 运维队列行同一只,判法 = core 屏 `screenAskClassMembers`(S-955 ⑥ / core 7.38.0 #1249,本仓零自写对判),两键同在同缺,半对 / 集外 / 首员不符 / 重复员 = 屏拒 = 读不出;detail 前缀 `askClass=` / `classes=`),与 S-721 起的 `readClosedWordSeat` —— 一座一只 core 成员筛,座是参数:`mandate`(core 7.32.0 #1092,活卡帧 / 活挂起行 / 运维队列行同一只,闭集筛 = core `isPersistedRuleMandate`)与 S-755 起的 `origin`(运维队列行读 park 行孪生座,闭集筛 = core `isAskOrigin`,与 inbox 行 core 投影处同一只筛)、S-1170 起的 `classifierNoRuling`(core 7.43.0 #1353 的成因词,活卡帧读 `AskRequest.classifierNoRuling`,闭集筛 = core `isAutoModeFallbackCause`)与 `differs`(core 7.44.0 #1374,载体是 `runs.ts` 的 409 `steering.duplicate_input_id` 体读的引擎拒错 `detail.differs`,闭集筛 = core `STEER_REPLAY_FACETS` 的非空子集;集外成员 / 空 / 非数组 ⇒ 不铸 `detail`,码仍是契约);detail 前缀 `ruleOffersAbsence=` / `mandate=` / `origin=` / `classifierNoRuling=` / `differs=` 判别是哪一座;第四个成员是 `approval-content-kind.ts` 的 `readApprovalCard` —— 卡词 `approvalCard` 自己的闭集窄读,活 ask / LOCAL 店停驻门 / SQL 店读出的门子树三个载体共用一只,闭集 = core `TOOL_APPROVAL_CARDS`,detail 前缀 `approvalCard=`):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。";
405
417
  };
418
+ readonly "server.approval-card.retired-card-key-read": {
419
+ readonly cls: "F";
420
+ readonly note: "一次性迁移 M37 的读侧(`approval-card.ts` 的 `renameRetiredSourceKey`,存卡的唯一读者入口 `readStoredCardEnvelope` 先调它;窗与删法见 `plugins/one-time-migrations.ts` 登记表 M37 —— 窗关随 M37 删本 tag):存下的审批卡信封(`approval_ask.card_json` / local 问账的同一份信封)上,委派出处键还是引擎 7.61.0 之前的旧名 `card.sourceTaskId` ⇒ 读时改名 `sourceSessionId`(值不变),整卡照读。这不是放行:改名前后是同一个值、同一个事实,只是不改名 `.strict()` 的卡 schema 就整卡读不出(断连重放跳过、回决口按要人、幂等重入走 park)。计数非零 = 有本版之前铸的存卡还在被读(滚动升级窗里旧副本新写的、升级前已结仍被幂等重入 / 兑付读的);窗关前应归零。";
421
+ };
406
422
  readonly "server.approval-card.retired-card-word-read": {
407
423
  readonly cls: "F";
408
424
  readonly note: "S-1562(core 7.51.0 #1515 / #1702;一次性迁移 M18 的**读侧半场**,`approval-card.ts` 的 `readStoredCardEnvelope` / `currentParkArm`,窗与删法见 `plugins/one-time-migrations.ts` 登记表 M18 —— 窗关随 M18 删本 tag):存下的审批卡(`approval_ask.card_json`)或停驻行(`pendingAction` 的 `tool_approval` 臂)上带着引擎 7.51.0 退役的词 —— `ruleOffersAbsence: \"shadowed\"` 或类对里的 `governance_unavailable` —— 读时按 M18 那一只改写读(前者删键 = 合法缺席,后者换成 `rule_unreadable`、类集去重按引擎序)。每改写一次记一次;detail = 载体(`card_json` / `parked row`)与座名,不带值。放行的最坏后果 = 无:这一读与存卡的起服改写对同一份字节给同一个答案(一只函数);它不是兜底,它登记在这里是为了**响亮** —— 载体 `card_json` 计数非零 = 有起服改写够不着的卡(滚动升级窗里 7.128.x 副本新写的卡、local 文件 ask 账);载体 `parked row` 计数非零 = 读到了带旧词的停驻行(引擎写的文档,本服务从不改写它 —— 行摘要覆盖类对 `askClass` / `classes`,改写 = 续跑判 `damaged`;所以这一格在窗内随每次读到那样的行而涨,直到那些行被决掉 / 收割),运维据它判断旧副本是否都已换下、窗关删码前是否还有这种行在飞。反方向(不改写)= 修前:整张存卡读不出(断连重放跳过、幂等重入走 park、回决口按要人)、停驻行的 `shadowed` 读成「读不出 ⇒ 按强制」(拿不到「本会话放行」)。";
@@ -421,7 +437,7 @@ export declare const FAIL_OPEN_TAGS: {
421
437
  };
422
438
  readonly "server.run-store.corrupt-record-skipped": {
423
439
  readonly cls: "F";
424
- readonly note: "S-582(`plugins/file-run-store.ts`,local 车道的 File 形 run 店;三个盘面同一只座):盘上一条记录读不出形 —— 一行 `42` / 缺本店必填键的对象 / 中间的半截 JSON(撕尾不走本 tag,那是崩溃的正常落盘形)⇒ 跳过它继续。三个盘面各自的最坏后果:`run.json` 坏 ⇒ 那条 run 读成**缺席**(`GET /v1/runs/:id` 404,listRuns 少一行;它若占着 claim,EEXIST 路按「行不在」判残骸接管);`events.jsonl` 一行坏 ⇒ 回放少一个事件(其余照读、按 seq 去重不变);claim 记录坏 ⇒ 走既有的撕裂 claim 路(有非终局行就收编,否则隔离出 activeDir)。都是**可用性**损失,不开任何门 ⇒ F。修前三面全零披露(店自己的 `typeof … continue` 与 core 的静默跳过);计数让「这台机器的 run 账在腐」在遥测里显形。⚠️ `RUN_STORE_STRICT_HYDRATE=on` 的射程是「文件读不出」,不含「文件里有坏行」—— strict 档下坏行照走本 tag(未改 strict 语义,见普查表第 96 行)。";
440
+ readonly note: "S-582(`plugins/file-run-store.ts`,local 车道的 File 形 run 店;四个盘面同一只座):盘上一条记录读不出形 —— 一行 `42` / 缺本店必填键的对象 / 中间的半截 JSON(撕尾不走本 tag,那是崩溃的正常落盘形)⇒ 跳过它继续。前三个盘面各自的最坏后果:`run.json` 坏 ⇒ 那条 run 读成**缺席**(`GET /v1/runs/:id` 404,listRuns 少一行;它若占着 claim,EEXIST 路按「行不在」判残骸接管);`events.jsonl` 一行坏 ⇒ 回放少一个事件(其余照读、按 seq 去重不变);claim 记录坏 ⇒ 走既有的撕裂 claim 路(有非终局行就收编,否则隔离出 activeDir)。第四个盘面 `run-blobs/<会话>.jsonl`(`wiring_manifest` 行容器段的块账本,`plugins/wiring-section-codec.ts`)同一只座、两个检测点:① 重放时一条块记录形不合(`digest` 不是串 / `body` 不是容器)⇒ 跳过;② 读口(`getEvents`)装回时清单行引用的块不在本会话账本里(块缺:账本行被删 / 被跳过、断电丢尾、删会话时 run 目录删不掉留下的僵尸 run)⇒ 那一帧这一次不交出(永不交出缺段的清单),每个 `(taskId, seq)` 每进程计一次,`maxSeq` / `retainedFrom` 照旧按存位;明细 `kind=section-ref taskId= seq= sections=<段:摘要,…>`。块账本整份读不出(权限 / 挂载)不走本 tag,与 `events.jsonl` 读不出同一条路(strict 拒启,否则原样抛)。都是**可用性**损失,不开任何门 ⇒ F。修前三面全零披露(店自己的 `typeof … continue` 与 core 的静默跳过);计数让「这台机器的 run 账在腐」在遥测里显形。⚠️ `RUN_STORE_STRICT_HYDRATE=on` 的射程是「文件读不出」,不含「文件里有坏行」—— strict 档下坏行照走本 tag(未改 strict 语义,见普查表第 96 行)。";
425
441
  };
426
442
  readonly "server.resume-anchor.corrupt-record-skipped": {
427
443
  readonly cls: "F";
@@ -583,11 +599,11 @@ export declare const FAIL_OPEN_TAGS: {
583
599
  };
584
600
  readonly "server.park.held-card-void-failed": {
585
601
  readonly cls: "F";
586
- readonly note: "停驻没落定就以失败收场的腿(`runs.ts` 的 `voidHeldParkCard`;两处:后台腿 catch 的失败臂 `leg=bg`、续跑腿 catch 放认领的取消 / 普通异常两臂 `leg=resume`):手里的结果是宿主层停驻(引擎交回之前已把卡落进 checkpoint 店,卡在本 run 的会话上),而停驻行 / 用量行 / 结果到手之后的账本写抛 ⇒ 腿以 `failed` 收场、放认领;放认领之前先按卡自己的 scope 店级 `expire` 作废那张卡(动词 = `card-void.ts` 的结算作废,与验证器自己的停驻同一只)。作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与本 run 的会话对不上(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店(`no_checkpoint_store`)—— 照样以 `failed` 收场、放认领,每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上,兑现它的 run 已经 `failed`、认领已放,决定口照样受理、续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席成因 ②),由审批兜底收割收走;前提是停驻簿记写失败,并且这张卡没作废成(三个 reason 任一:checkpoint 店这一笔也失败 / 卡与本 run 的会话对不上 / 没接店 —— 后两者不需要第二次写失败)。不是门、不是凭据、不改 run 的判词。计数非零 = 有一次停驻没落定的失败收场留下了一张卡:按 reason 看 —— `store_error` = checkpoint 店在同一拍也出错,`card_mismatch` = 结果里的卡与本 run 的会话对不上,`no_checkpoint_store` = 这台部署没接 checkpoint 店;去看那只会话。detail = `reason=<词> leg=<bg|resume> [err=<原句>]`(不含凭据)。";
602
+ readonly note: "停驻没落定就以失败收场的腿(`runs.ts` 的 `voidHeldParkCard`;两处:后台腿 catch 的失败臂 `leg=bg`、续跑腿 catch 放认领的取消 / 普通异常两臂 `leg=resume`):手里的结果是宿主层停驻(引擎交回之前已把卡落进 checkpoint 店,卡在本 run 的会话上),而停驻行 / 用量行 / 结果到手之后的账本写抛 ⇒ 腿以 `failed` 收场、放认领;放认领之前先按卡自己的 scope 店级 `expire` 作废那张卡(动词 = `card-void.ts` 的结算作废)。作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与本 run 的会话对不上(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店(`no_checkpoint_store`)—— 照样以 `failed` 收场、放认领,每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上,兑现它的 run 已经 `failed`、认领已放,决定口照样受理、续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席成因 ②),由审批兜底收割收走;前提是停驻簿记写失败,并且这张卡没作废成(三个 reason 任一:checkpoint 店这一笔也失败 / 卡与本 run 的会话对不上 / 没接店 —— 后两者不需要第二次写失败)。不是门、不是凭据、不改 run 的判词。计数非零 = 有一次停驻没落定的失败收场留下了一张卡:按 reason 看 —— `store_error` = checkpoint 店在同一拍也出错,`card_mismatch` = 结果里的卡与本 run 的会话对不上,`no_checkpoint_store` = 这台部署没接 checkpoint 店;去看那只会话。detail = `reason=<词> leg=<bg|resume> [err=<原句>]`(不含凭据)。";
587
603
  };
588
604
  readonly "server.verifier-pause.card-void-failed": {
589
605
  readonly cls: "F";
590
- readonly note: "验证门结算(`verifier-pause-card.ts` 的 `voidVerifierPauseCard`;三处结算点:bg 组合腿、同步提交腿、续跑 verify 臂):验证器自己的停驻卡(结果上 `verification.verifierPause`)是本服务兑现不了的卡(它在验证器现铸的会话上,`/decide` 读不到续跑输入),结算时按卡自己的 scope 走店级 `expire` 当场作废;作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与停驻报的验证器会话不一致(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店却收到了带凭据的验证器停驻(`no_checkpoint_store`)。每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上点不动(`/decide` 答 409),由审批兜底收割收走;不是门、不是凭据(凭据由出口封口摘,与本臂无关)、不改 run 的判词。计数非零 = 这台部署的 checkpoint 店在出错,或引擎交回的停驻与店里的卡对不上(去看那只会话)。detail = `reason=<词> [err=<原句>]`(不含凭据)。引擎给出「作废验证器卡并释放其会话」的动词后本臂改调它,本 tag 随臂删或改写。";
606
+ readonly note: "验证门结算(`verifier-pause-card.ts` 的 `voidVerifierPauseCard`;四处结算点:bg 组合腿、同步提交腿、续跑 verify 臂、leader 的 worker 结算):验证器自己的停驻卡(结果上 `verification.verifierPause`)是本服务兑现不了的卡(它在验证器现铸的会话上,`/decide` 读不到续跑输入),结算时交跑过这次验证门的那只 Runner 的动词 `voidVerifierPause` 当场作废(引擎读卡上的组合腿,只收组合腿 `verify` 的卡;卡 `expired` + 停驻环境销毁 + 验证器会话放钉)。动词答 `false`(别的结算者先到)不计;动词抛 ⇒ 结算照常往下走,每次记一次:卡上记的不是验证器的腿(`resume.composition_mismatch`)、卡不在 / 没接店(`checkpoint.not_found`)、作废了但会话钉没放掉(`checkpoint.void_unpin_failed`,引擎同时报 `onError`)、店读 / 作废抛错(引擎原样抛,码读不出记 `thrown`)。放行的最坏后果 = 那张卡回到修前的形:留在待决面上点不动(`/decide` 答 409),由审批兜底收割收走;`void_unpin_failed` 时卡已作废、只是验证器会话留在副本内存里直到进程重启;不是门、不是凭据(凭据由出口封口摘,与本臂无关)、不改 run 的判词。计数非零 = 按码看:`thrown` = checkpoint 店在出错;`composition_mismatch` = 结果上的验证器停驻指向一张不是验证器腿铸的卡(去看那只会话);`void_unpin_failed` = 会话店放钉出错。detail = `code=<码 | thrown> err=<原句>`(不含凭据)。";
591
607
  };
592
608
  readonly "server.run-permission-mode.write-back-unpersisted": {
593
609
  readonly cls: "F";
@@ -189,6 +189,10 @@ export const FAIL_OPEN_TAGS = {
189
189
  cls: "F",
190
190
  note: "7.133.0(`checkpoint-identity.ts` 的 `durableRowIdentity`;两只店的 `listPending` + `/v1/assistant/inbox` handler):三处 durable 读面(运维队列行 `GET /v1/approvals` 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的行)读到一行停驻门**没有可用的 `checkpointId`** ⇒ 这一行**照列、`checkpointId` 键缺席**(消费方按「这一行没有可 join 的身份」处理)。detail = `face=<approvals-queue|assistant-inbox> why=<absent|untrusted>`:`absent` = 行上没有这一格(core 5.42.0 之前停驻的老行 —— core 明写不回填);`untrusted` = 在场却没过信任边界(非串 / 空串 / 超长 / 含 resume token —— 数据污染;没过边界的值不上 wire)。放行(列、键缺席)的最坏后果 = 消费方对这一行做不了跨面 join(三面对账少一行)—— 行本身、决断口(`/v1/approvals/:sessionId/decide`,按会话)、到期收割都照常。不放行有两种形:整只拒的代价是一行老 / 坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧);把这一行藏起来的代价更糟 —— 人看不到要批的东西(审批面上的 fail-open,方向反了)。故 F 类;必须留痕(两种成因从此可分)。计数非零 = 有老 / 坏行在飞(列着、没身份);`/stream` 每 3s 一拍重读,同一行每拍计一次(量值 = 行 × 拍数,在场才是信号)。 7.137.0 起第四面:park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`;唯一构造器 `trace/project.ts` 的 `parkEventData`,写时判一次,detail `face=run-events-park`)—— 暂停因由上的门身份没过信任边界 ⇒ 行照写、`checkpointId` 键缺席(决断方回显不了这道门,退回不带键的决断或从 run 结果取原值;行本身、门、待批调用身份照常)。 第五面:回决门族的 200 体(`/decide` / `/resume` / `/plan_review` 执行之前从选中的那一行捕获一次,`http/resume-legs.ts` 三处;detail `face=decision-receipt`)—— 选中那一行的门身份没过信任边界 ⇒ 结清证据照铸、体上 `checkpointId` 键缺席(决断方只是对不上身份;位、决定、续跑都照常)。每次走到捕获点的决断计一次(计的是选中那一行的事实,与这次决断随后走哪条车道无关)。",
191
191
  },
192
+ "server.peer-inbound.notice-coordinates-unreadable": {
193
+ cls: "F",
194
+ note: "S-1370(`peer-inbound-face.ts` 的 `observe`,部署级通告转发席喂它):core 的 `peer.inbound_disposition{disposition:\"held\"}` / `peer.held_settled{heldCount:0}` 通告的 `detail` 读不出坐标(`sessionId` 不是串或不合 core 的会话地址文法 / `box` 既不是那只会话的箱也不是 principal 收件箱)⇒ 这一条不进(或不出)收信方决断面的坐标表。按构造不可达:core 的两只铸点恒给这两位。放行(忽略这一条)的最坏后果:进表那一形 = 那张被扣的信不出现在 `GET /v1/approvals` 的 `pending[]` 上 —— 它仍在 core 的队列里,按 `dialogExpiry` 带 `expired` 回执结清(core 的安全默认),也仍可经 core 自己的通告面看到;出表那一形 = 坐标表多留一行(读时多查一次空队列)。不放行(抛)会把异常回抛进引擎的通告投递(转发席的隔离契约禁止)。故 F 类;计数非零 = core 改了这两条通告的 detail 形,对 core 的 d.ts 重核坐标读法。",
195
+ },
192
196
  "server.approvals.queue-row-gate-kind-missing": {
193
197
  cls: "F",
194
198
  note: "S-1681(`plugins/checkpoint-store-sql.ts` 的 `queueRowGateKind`,7.132.0;SQL 与 local 两只孪生的 `listPending` 同调这一只判官):运维待批队列(`GET /v1/approvals` / `/stream`)读到一行 pending 的门型(SQL 孪生:`checkpoint.gate_kind` 列;local 孪生:快照上的门)是 NULL 或不是 core 登记的门型 ⇒ 这一行**不列**。按构造不可达:每次停驻都把门型反范式进这一列,一次性迁移 M21 把没有它的存量行从门子树补齐(读不出登记门型的置 expired)。放行(不列、队列其余照常)的最坏后果 = 一行谁也决不了的行不出现在队列上 —— 它本来就不可决:每一条决断口按门型绑定取行(`gate_kind IN (…)`),证不出门型就不当它是这道门;不放行(整只拒)的代价是一行坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧)。也不按缺键投出去:消费方按 `gateKind` 路由、缺键即 fail-closed,队列行的契约是 `gateKind` 恒在场。故 F 类;计数非零 = 写腿或手改出了错,`SELECT session_id FROM checkpoint WHERE status = 'pending' AND gate_kind IS NULL` 查那一行(置 expired 或删会话)。",
@@ -347,7 +351,7 @@ export const FAIL_OPEN_TAGS = {
347
351
  },
348
352
  "server.run-terminal.redact-threw": {
349
353
  cls: "F",
350
- note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形由 S-219 的 atLeast 铸点闭掉;JSON 秘密字段臂的转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉,本臂留作结构性兜底,计数恒零是证据、不归零即新形)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。",
354
+ note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛)+ 活面放行窗(`trace/model-segment.ts` 的 `createLiveModelText`,probe 前缀 `live-hold`:逐块放行口抛 ⇒ 这一拍不放、字节留在窗里;段末整段读法抛 ⇒ 余下的字节整段换同一枚占位):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形由 S-219 的 atLeast 铸点闭掉;JSON 秘密字段臂的转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉,本臂留作结构性兜底,计数恒零是证据、不归零即新形)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。",
351
355
  },
352
356
  "server.run-terminal.provider-echo-shaped": {
353
357
  cls: "F",
@@ -359,11 +363,20 @@ export const FAIL_OPEN_TAGS = {
359
363
  "server.run-terminal.output-truncated": { cls: "F", note: "S-107:持久 error 脱后 UTF-8 字节 > 65,000(TiDB TEXT 65,535 字节)⇒ 脱后切并带标记(切在占位里不是泄漏);计数=多少条终态文案被字节封顶截断" },
360
364
  "server.runs.abort-cause-missing": { cls: "F", note: "S-947 候裁 ⑤(主会话裁:默认值保留,miss 臂必须响):续跑腿 `http/resume-legs.ts` 的 `checkpoint.resume_aborted` 臂 —— core 只在本腿递进去的信号**已中止**时铸该码,而那只信号唯一的写者是取消动词(快路 / 耐久旗轮询,句柄登记 `cancel_verb`),所以**只有 core 契约外的形**(桩 / 非 core Runner 直接抛该码,本腿句柄从未中止)才到得了「句柄上读不到成因」这一臂。取消终局照铸,成因取本腿唯一的中止词 `cancel_verb`(取消终局恒带成因由铸点签名保证,不让它缺);detail `task=<taskId>`;计数 = 多少次取消成因是默认值而不是句柄登记的。生产上恒零是证据,不归零 = 有一条不走 core 契约的 Runner 在跑续跑腿。" },
361
365
  "server.runs.terminal-ledger-write-failed": { cls: "F", note: "S-107(全量门 silent-degradation 棘轮抓出的五处静默 `catch {}`):run 终局链里**账本腿**(flush 缓冲 text/reasoning、append `failed` 行)与终态翻转(setTerminal)已不共命运——账本 INSERT 抛不再让 run 卡 running 到 reaper;但那条抛掉的账本行之前是**静默**吞的。detail `arm=` 四臂(flush-clean-cancel / append-clean-cancel / flush-catch / append-thrown-cancel / append-failed);S-947 起续跑腿(`http/resume-legs.ts`)两座同 tag:resume-append-resume-aborted / resume-append-thrown-cancel(取消抛出两臂的 `failed` 行,修前那两臂根本不落账本终局行):缺的是 run_events 尾巴(缓冲文本或 failed 事件),run 行本身仍是 durable truth,读面/reaper 容忍缺失,故 F 类;计数=多少次终局账本写失败。" },
366
+ "server.live-text.hold-diverged": {
367
+ cls: "P-DEBT",
368
+ note: "live 面模型文本的流式口(`trace/model-segment.ts` 的 `createLiveModelText`,三条 live 腿共用;流式口 = `trace/redact.ts` 的 `createRedactionStream`:喂入 / 取已判定前缀 / 收尾冲刷):一段冲刷时,已取出去的逐块字节与整段读法在那一截上的渲染对不上,或有一条整段读法的跨度横跨放行位 —— 流式口的律被打破(某条臂的判定在脱敏器读出的未决尾上界 `PENDING_TAIL_UNITS` 之外落定,而它没有把那一支标成未决尾)。处置:余下的字节照整段读法出(横跨放行位的跨度整枚换记号,不再出它的原文字节),段末帧照发整段脱敏的正文、消费端按它整段替换;但**已经出门**的那几块收不回 —— 其中可能有整段读法会遮的字节(这就是 P 族的理由)。债:未决尾上界是候选池成员判定距离的最大值,正则臂从正则源读出,手写扫描器的那一项是成员处的陈述(它起头要读全的字面锚),不是从扫描器的代码机械读出;已知一形是多遮方向(方案词锚的头值在文本末尾被读成「收在行尾」,其后隔着长过上界的行内空白又跟了词 ⇒ 整段读法不遮、活流已出记号)。钉 = 基线语料逐条按块流过流式口拼接 = 整段脱敏 + 判定距离最远的几形逐码元。detail `family=<text|reasoning> len=<段长>`;计数 = 多少段的律被打破(期望恒 0)。",
369
+ },
362
370
  "server.redact.ppk-eat-to-end": { cls: "F", note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;出口座有界行的 head 读法(只扫一段头)上「窗内无合格终止」是终止可能在窗外的未决 —— 照样遮到窗尾,但不是兜底,不计;计数=多少次整段吃尾。" },
371
+ "server.redact.forensic-level-cap": { cls: "F", note: "脱敏器取证相(redact.ts armPool 第二相)跑满级数界 FORENSIC_LEVEL_MAX 仍没收敛(证据链里层被认出、外层才认得出,刻意交替嵌套的私钥块 / URL 每层多一级,深过 31 层才够得着;现役语料最深 3 级)⇒ 下一级本会重跑的取证臂各自从它读证据的起点遮到窗尾(fail-closed:多遮可见、不少遮),不再升级;每次调用至多一条;影子扫不计;detail from=/len= 是视图坐标。" },
363
372
  "server.approval-card.closed-word-out-of-set": {
364
373
  cls: "F",
365
374
  note: "S-249(`approval-card.ts` 的边界窄读族;今天四个成员:`readRuleOffersAbsence`、S-934 ② 起的 `readAskClassPair`(core 7.37.0 #1214 的类对 `askClass` / `classes`,活卡帧 / 呈卡帧的卡 / 运维队列行同一只,判法 = core 屏 `screenAskClassMembers`(S-955 ⑥ / core 7.38.0 #1249,本仓零自写对判),两键同在同缺,半对 / 集外 / 首员不符 / 重复员 = 屏拒 = 读不出;detail 前缀 `askClass=` / `classes=`),与 S-721 起的 `readClosedWordSeat` —— 一座一只 core 成员筛,座是参数:`mandate`(core 7.32.0 #1092,活卡帧 / 活挂起行 / 运维队列行同一只,闭集筛 = core `isPersistedRuleMandate`)与 S-755 起的 `origin`(运维队列行读 park 行孪生座,闭集筛 = core `isAskOrigin`,与 inbox 行 core 投影处同一只筛)、S-1170 起的 `classifierNoRuling`(core 7.43.0 #1353 的成因词,活卡帧读 `AskRequest.classifierNoRuling`,闭集筛 = core `isAutoModeFallbackCause`)与 `differs`(core 7.44.0 #1374,载体是 `runs.ts` 的 409 `steering.duplicate_input_id` 体读的引擎拒错 `detail.differs`,闭集筛 = core `STEER_REPLAY_FACETS` 的非空子集;集外成员 / 空 / 非数组 ⇒ 不铸 `detail`,码仍是契约);detail 前缀 `ruleOffersAbsence=` / `mandate=` / `origin=` / `classifierNoRuling=` / `differs=` 判别是哪一座;第四个成员是 `approval-content-kind.ts` 的 `readApprovalCard` —— 卡词 `approvalCard` 自己的闭集窄读,活 ask / LOCAL 店停驻门 / SQL 店读出的门子树三个载体共用一只,闭集 = core `TOOL_APPROVAL_CARDS`,detail 前缀 `approvalCard=`):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。",
366
375
  },
376
+ "server.approval-card.retired-card-key-read": {
377
+ cls: "F",
378
+ note: "一次性迁移 M37 的读侧(`approval-card.ts` 的 `renameRetiredSourceKey`,存卡的唯一读者入口 `readStoredCardEnvelope` 先调它;窗与删法见 `plugins/one-time-migrations.ts` 登记表 M37 —— 窗关随 M37 删本 tag):存下的审批卡信封(`approval_ask.card_json` / local 问账的同一份信封)上,委派出处键还是引擎 7.61.0 之前的旧名 `card.sourceTaskId` ⇒ 读时改名 `sourceSessionId`(值不变),整卡照读。这不是放行:改名前后是同一个值、同一个事实,只是不改名 `.strict()` 的卡 schema 就整卡读不出(断连重放跳过、回决口按要人、幂等重入走 park)。计数非零 = 有本版之前铸的存卡还在被读(滚动升级窗里旧副本新写的、升级前已结仍被幂等重入 / 兑付读的);窗关前应归零。",
379
+ },
367
380
  "server.approval-card.retired-card-word-read": {
368
381
  cls: "F",
369
382
  note: "S-1562(core 7.51.0 #1515 / #1702;一次性迁移 M18 的**读侧半场**,`approval-card.ts` 的 `readStoredCardEnvelope` / `currentParkArm`,窗与删法见 `plugins/one-time-migrations.ts` 登记表 M18 —— 窗关随 M18 删本 tag):存下的审批卡(`approval_ask.card_json`)或停驻行(`pendingAction` 的 `tool_approval` 臂)上带着引擎 7.51.0 退役的词 —— `ruleOffersAbsence: \"shadowed\"` 或类对里的 `governance_unavailable` —— 读时按 M18 那一只改写读(前者删键 = 合法缺席,后者换成 `rule_unreadable`、类集去重按引擎序)。每改写一次记一次;detail = 载体(`card_json` / `parked row`)与座名,不带值。放行的最坏后果 = 无:这一读与存卡的起服改写对同一份字节给同一个答案(一只函数);它不是兜底,它登记在这里是为了**响亮** —— 载体 `card_json` 计数非零 = 有起服改写够不着的卡(滚动升级窗里 7.128.x 副本新写的卡、local 文件 ask 账);载体 `parked row` 计数非零 = 读到了带旧词的停驻行(引擎写的文档,本服务从不改写它 —— 行摘要覆盖类对 `askClass` / `classes`,改写 = 续跑判 `damaged`;所以这一格在窗内随每次读到那样的行而涨,直到那些行被决掉 / 收割),运维据它判断旧副本是否都已换下、窗关删码前是否还有这种行在飞。反方向(不改写)= 修前:整张存卡读不出(断连重放跳过、幂等重入走 park、回决口按要人)、停驻行的 `shadowed` 读成「读不出 ⇒ 按强制」(拿不到「本会话放行」)。",
@@ -382,7 +395,7 @@ export const FAIL_OPEN_TAGS = {
382
395
  },
383
396
  "server.run-store.corrupt-record-skipped": {
384
397
  cls: "F",
385
- note: "S-582(`plugins/file-run-store.ts`,local 车道的 File 形 run 店;三个盘面同一只座):盘上一条记录读不出形 —— 一行 `42` / 缺本店必填键的对象 / 中间的半截 JSON(撕尾不走本 tag,那是崩溃的正常落盘形)⇒ 跳过它继续。三个盘面各自的最坏后果:`run.json` 坏 ⇒ 那条 run 读成**缺席**(`GET /v1/runs/:id` 404,listRuns 少一行;它若占着 claim,EEXIST 路按「行不在」判残骸接管);`events.jsonl` 一行坏 ⇒ 回放少一个事件(其余照读、按 seq 去重不变);claim 记录坏 ⇒ 走既有的撕裂 claim 路(有非终局行就收编,否则隔离出 activeDir)。都是**可用性**损失,不开任何门 ⇒ F。修前三面全零披露(店自己的 `typeof … continue` 与 core 的静默跳过);计数让「这台机器的 run 账在腐」在遥测里显形。⚠️ `RUN_STORE_STRICT_HYDRATE=on` 的射程是「文件读不出」,不含「文件里有坏行」—— strict 档下坏行照走本 tag(未改 strict 语义,见普查表第 96 行)。",
398
+ note: "S-582(`plugins/file-run-store.ts`,local 车道的 File 形 run 店;四个盘面同一只座):盘上一条记录读不出形 —— 一行 `42` / 缺本店必填键的对象 / 中间的半截 JSON(撕尾不走本 tag,那是崩溃的正常落盘形)⇒ 跳过它继续。前三个盘面各自的最坏后果:`run.json` 坏 ⇒ 那条 run 读成**缺席**(`GET /v1/runs/:id` 404,listRuns 少一行;它若占着 claim,EEXIST 路按「行不在」判残骸接管);`events.jsonl` 一行坏 ⇒ 回放少一个事件(其余照读、按 seq 去重不变);claim 记录坏 ⇒ 走既有的撕裂 claim 路(有非终局行就收编,否则隔离出 activeDir)。第四个盘面 `run-blobs/<会话>.jsonl`(`wiring_manifest` 行容器段的块账本,`plugins/wiring-section-codec.ts`)同一只座、两个检测点:① 重放时一条块记录形不合(`digest` 不是串 / `body` 不是容器)⇒ 跳过;② 读口(`getEvents`)装回时清单行引用的块不在本会话账本里(块缺:账本行被删 / 被跳过、断电丢尾、删会话时 run 目录删不掉留下的僵尸 run)⇒ 那一帧这一次不交出(永不交出缺段的清单),每个 `(taskId, seq)` 每进程计一次,`maxSeq` / `retainedFrom` 照旧按存位;明细 `kind=section-ref taskId= seq= sections=<段:摘要,…>`。块账本整份读不出(权限 / 挂载)不走本 tag,与 `events.jsonl` 读不出同一条路(strict 拒启,否则原样抛)。都是**可用性**损失,不开任何门 ⇒ F。修前三面全零披露(店自己的 `typeof … continue` 与 core 的静默跳过);计数让「这台机器的 run 账在腐」在遥测里显形。⚠️ `RUN_STORE_STRICT_HYDRATE=on` 的射程是「文件读不出」,不含「文件里有坏行」—— strict 档下坏行照走本 tag(未改 strict 语义,见普查表第 96 行)。",
386
399
  },
387
400
  "server.resume-anchor.corrupt-record-skipped": {
388
401
  cls: "F",
@@ -544,11 +557,11 @@ export const FAIL_OPEN_TAGS = {
544
557
  },
545
558
  "server.park.held-card-void-failed": {
546
559
  cls: "F",
547
- note: "停驻没落定就以失败收场的腿(`runs.ts` 的 `voidHeldParkCard`;两处:后台腿 catch 的失败臂 `leg=bg`、续跑腿 catch 放认领的取消 / 普通异常两臂 `leg=resume`):手里的结果是宿主层停驻(引擎交回之前已把卡落进 checkpoint 店,卡在本 run 的会话上),而停驻行 / 用量行 / 结果到手之后的账本写抛 ⇒ 腿以 `failed` 收场、放认领;放认领之前先按卡自己的 scope 店级 `expire` 作废那张卡(动词 = `card-void.ts` 的结算作废,与验证器自己的停驻同一只)。作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与本 run 的会话对不上(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店(`no_checkpoint_store`)—— 照样以 `failed` 收场、放认领,每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上,兑现它的 run 已经 `failed`、认领已放,决定口照样受理、续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席成因 ②),由审批兜底收割收走;前提是停驻簿记写失败,并且这张卡没作废成(三个 reason 任一:checkpoint 店这一笔也失败 / 卡与本 run 的会话对不上 / 没接店 —— 后两者不需要第二次写失败)。不是门、不是凭据、不改 run 的判词。计数非零 = 有一次停驻没落定的失败收场留下了一张卡:按 reason 看 —— `store_error` = checkpoint 店在同一拍也出错,`card_mismatch` = 结果里的卡与本 run 的会话对不上,`no_checkpoint_store` = 这台部署没接 checkpoint 店;去看那只会话。detail = `reason=<词> leg=<bg|resume> [err=<原句>]`(不含凭据)。",
560
+ note: "停驻没落定就以失败收场的腿(`runs.ts` 的 `voidHeldParkCard`;两处:后台腿 catch 的失败臂 `leg=bg`、续跑腿 catch 放认领的取消 / 普通异常两臂 `leg=resume`):手里的结果是宿主层停驻(引擎交回之前已把卡落进 checkpoint 店,卡在本 run 的会话上),而停驻行 / 用量行 / 结果到手之后的账本写抛 ⇒ 腿以 `failed` 收场、放认领;放认领之前先按卡自己的 scope 店级 `expire` 作废那张卡(动词 = `card-void.ts` 的结算作废)。作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与本 run 的会话对不上(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店(`no_checkpoint_store`)—— 照样以 `failed` 收场、放认领,每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上,兑现它的 run 已经 `failed`、认领已放,决定口照样受理、续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席成因 ②),由审批兜底收割收走;前提是停驻簿记写失败,并且这张卡没作废成(三个 reason 任一:checkpoint 店这一笔也失败 / 卡与本 run 的会话对不上 / 没接店 —— 后两者不需要第二次写失败)。不是门、不是凭据、不改 run 的判词。计数非零 = 有一次停驻没落定的失败收场留下了一张卡:按 reason 看 —— `store_error` = checkpoint 店在同一拍也出错,`card_mismatch` = 结果里的卡与本 run 的会话对不上,`no_checkpoint_store` = 这台部署没接 checkpoint 店;去看那只会话。detail = `reason=<词> leg=<bg|resume> [err=<原句>]`(不含凭据)。",
548
561
  },
549
562
  "server.verifier-pause.card-void-failed": {
550
563
  cls: "F",
551
- note: "验证门结算(`verifier-pause-card.ts` 的 `voidVerifierPauseCard`;三处结算点:bg 组合腿、同步提交腿、续跑 verify 臂):验证器自己的停驻卡(结果上 `verification.verifierPause`)是本服务兑现不了的卡(它在验证器现铸的会话上,`/decide` 读不到续跑输入),结算时按卡自己的 scope 走店级 `expire` 当场作废;作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与停驻报的验证器会话不一致(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店却收到了带凭据的验证器停驻(`no_checkpoint_store`)。每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上点不动(`/decide` 答 409),由审批兜底收割收走;不是门、不是凭据(凭据由出口封口摘,与本臂无关)、不改 run 的判词。计数非零 = 这台部署的 checkpoint 店在出错,或引擎交回的停驻与店里的卡对不上(去看那只会话)。detail = `reason=<词> [err=<原句>]`(不含凭据)。引擎给出「作废验证器卡并释放其会话」的动词后本臂改调它,本 tag 随臂删或改写。",
564
+ note: "验证门结算(`verifier-pause-card.ts` 的 `voidVerifierPauseCard`;四处结算点:bg 组合腿、同步提交腿、续跑 verify 臂、leader 的 worker 结算):验证器自己的停驻卡(结果上 `verification.verifierPause`)是本服务兑现不了的卡(它在验证器现铸的会话上,`/decide` 读不到续跑输入),结算时交跑过这次验证门的那只 Runner 的动词 `voidVerifierPause` 当场作废(引擎读卡上的组合腿,只收组合腿 `verify` 的卡;卡 `expired` + 停驻环境销毁 + 验证器会话放钉)。动词答 `false`(别的结算者先到)不计;动词抛 ⇒ 结算照常往下走,每次记一次:卡上记的不是验证器的腿(`resume.composition_mismatch`)、卡不在 / 没接店(`checkpoint.not_found`)、作废了但会话钉没放掉(`checkpoint.void_unpin_failed`,引擎同时报 `onError`)、店读 / 作废抛错(引擎原样抛,码读不出记 `thrown`)。放行的最坏后果 = 那张卡回到修前的形:留在待决面上点不动(`/decide` 答 409),由审批兜底收割收走;`void_unpin_failed` 时卡已作废、只是验证器会话留在副本内存里直到进程重启;不是门、不是凭据(凭据由出口封口摘,与本臂无关)、不改 run 的判词。计数非零 = 按码看:`thrown` = checkpoint 店在出错;`composition_mismatch` = 结果上的验证器停驻指向一张不是验证器腿铸的卡(去看那只会话);`void_unpin_failed` = 会话店放钉出错。detail = `code=<码 | thrown> err=<原句>`(不含凭据)。",
552
565
  },
553
566
  "server.run-permission-mode.write-back-unpersisted": {
554
567
  cls: "F",
@@ -133,6 +133,7 @@ export const RESULT_TEXT_LEAVES = {
133
133
  taskId: "verbatim",
134
134
  runId: "verbatim",
135
135
  sessionId: "verbatim",
136
+ placementRoot: "verbatim",
136
137
  model: "verbatim",
137
138
  "terminal.code": "outward-code",
138
139
  "terminal.message": "error-text",
@@ -166,6 +167,7 @@ export const RESULT_TEXT_LEAVES = {
166
167
  "verification.findings.[]": "model-text",
167
168
  "verification.evidence": "model-text",
168
169
  "verification.verifierPause.sessionId": "verbatim",
170
+ "verification.verifierPause.checkpointId": "verbatim",
169
171
  "attempts.[].model": "verbatim",
170
172
  "attempts.[].errorCode": "outward-code",
171
173
  "attempts.[].gateError": "error-text",
@@ -277,6 +279,8 @@ const TERMINAL_FAILURE_DETAIL_TREATMENT = {
277
279
  providerConditionSource: "verbatim",
278
280
  providerResetAtMs: "verbatim",
279
281
  route: "verbatim",
282
+ errorKind: "verbatim",
283
+ tool: "verbatim",
280
284
  },
281
285
  coded: { kind: "verbatim", detail: "free-text-tree" },
282
286
  model_output_invalid: { kind: "verbatim", cause: "verbatim", outputLimit: "verbatim" },
@@ -46,10 +46,9 @@ export interface WorkflowCompletionInbox {
46
46
  * the ownerless fence silently starved the new claimant's push). Omitted `purgedOwner` = conservative
47
47
  * fence-all (an ownerless caller can't distinguish). */
48
48
  purge(sessionId: string, purgedOwner?: string | null): Promise<void>;
49
- /** The originating session OBSERVED this run's terminal state through a model-facing poll
50
- * (core 1.232 `WorkflowCompletionNotifier.ackServed` fires this), so the push would be
51
- * a pure duplicate ("poll-then-also-notify" double delivery: the model burns turns dismissing a completion
52
- * it already summarized). Drops the pending entry (a deliberate ack) AND arms a {@link SERVED_FENCE_MS}
49
+ /** The originating session already OBSERVED this run's terminal state on another delivery (the fleet bus's
50
+ * task-notification fold acks the family it delivered), so the push would be a pure duplicate (double
51
+ * delivery: the model burns turns dismissing a completion it already summarized). Drops the pending entry (a deliberate ack) AND arms a {@link SERVED_FENCE_MS}
53
52
  * enqueue fence for this (session, runId) so a LATE-arriving notify (the notifier races the poll) is
54
53
  * dropped too. Fence is in-memory only — a crash between poll and notify re-delivers (at-least-once; the
55
54
  * shell's own runId dedup + the model tolerate it), mirroring the purge fence's restart posture. */
@@ -147,7 +146,7 @@ export declare class InMemoryWorkflowCompletionInbox implements WorkflowCompleti
147
146
  * refused. Swept on BOTH enqueue and purge (a purge-heavy, enqueue-quiet process must not accumulate
148
147
  * stamps unboundedly — review). */
149
148
  private readonly purgedAt;
150
- /** servedKey(sessionId, runId) → stamp: terminal-poll-served fences; an enqueue for the pair within
149
+ /** servedKey(sessionId, runId) → stamp: served fences; an enqueue for the pair within
151
150
  * {@link SERVED_FENCE_MS} is refused (the poll already told the model). Swept alongside the purge fence. */
152
151
  private readonly servedAt;
153
152
  constructor(onWarn?: ((msg: string, meta: Record<string, unknown>) => void) | undefined);
@@ -323,7 +322,7 @@ export declare function externalNotificationInboxEntry(sessionId: string, owner:
323
322
  * UNTRUSTED free text). `status` collapses onto the inbox's completed|failed column; the ORIGINAL status rides
324
323
  * the payload extras and overrides on drain (killed/cancelled reach the shell verbatim). Workflow-type
325
324
  * notifications are the CALLER's job to filter (they have their own durable chain — double-enqueueing here would
326
- * re-open the poll-served double-push). External notifications enter through {@link externalNotificationInboxEntry}.
325
+ * deliver the same completion twice). External notifications enter through {@link externalNotificationInboxEntry}.
327
326
  */
328
327
  export declare function taskNotificationInboxEntry(sessionId: string, owner: string | null, n: EngineNotificationParkInput, enqueuedAt: number,
329
328
  /** lead-b (dual-ID audit): the PARENT run's durable taskId (the uuid the shell's footer rows
@@ -372,18 +371,5 @@ export declare function resolveCompletionRoute(p: {
372
371
  sessionId: string;
373
372
  owner: string | null;
374
373
  } | undefined>;
375
- /**
376
- * core 1.232 — resolve WHICH session's inbox entry a poll-served ack targets: the payload's
377
- * `originatingSessionId` directly (core threads it on `ackServed`), else the originating run row via
378
- * `sourceTaskId` (mirrors {@link resolveCompletionRoute}'s session half — same fallback, same reason:
379
- * older runs may predate the lookup-free field). Returns undefined when there is no attribution — the
380
- * entry stays pending (at-least-once; the shell's own dedup covers a rare duplicate).
381
- */
382
- export declare function resolveServedSession(p: {
383
- originatingSessionId?: string;
384
- sourceTaskId?: string;
385
- }, getRun?: (taskId: string) => Promise<{
386
- sessionId?: string | null;
387
- } | undefined | null>): Promise<string | undefined>;
388
374
  export {};
389
375
  //# sourceMappingURL=workflow-completion-inbox.d.ts.map
@@ -429,16 +429,4 @@ export async function resolveCompletionRoute(p, getRun, requirePrincipal) {
429
429
  }
430
430
  return { sessionId, owner };
431
431
  }
432
- export async function resolveServedSession(p, getRun) {
433
- if (p.originatingSessionId)
434
- return p.originatingSessionId;
435
- if (p.sourceTaskId && getRun) {
436
- try {
437
- return (await getRun(p.sourceTaskId))?.sessionId ?? undefined;
438
- }
439
- catch {
440
- }
441
- }
442
- return undefined;
443
- }
444
432
  //# sourceMappingURL=workflow-completion-inbox.js.map
@@ -8,8 +8,9 @@ import { type WorkflowAgentSessionIndex } from "./workflow-agent-session-index.j
8
8
  * `completionId` = 引擎的一次完成的相关 id(fill-once,终态转换上铸):逐字、不脱敏、本服务零自铸。
9
9
  * `diagnostics` = 引擎在完成通知上铸的诊断教学文本(未完成 agent 的摘要行、完整记录的读取手柄、journal 坐标、续跑句):补投路径是它在原腿已不在(断线重连 / 重启)时唯一的去处,
10
10
  * 所以搬运;它是自由文本,出门前在入箱处过出口座(见 `buildWorkflowCompleteInboxEntry`),载荷上仍是引擎值。**崩溃恢复臂不带**:`WorkflowRun` 行上没有这一位,本服务不编。
11
- * · `drop`:这条持久链只搬运一句受限摘要 + 诊断文本 + 路由键 —— `task_id` / `task_type` / `toolUseId` 是 live `task_notification` 帧的键(那条路径自带),`result` / `usage` 是完整结果 / 计量
12
- * (`WorkflowStatus` 读得到),不进补投帧。
11
+ * · `drop`:这条持久链只搬运一句受限摘要 + 诊断文本 + 路由键 —— `task_id` / `task_type` / `toolUseId` 是 live `task_notification` 帧的键(那条路径自带),`result` / `usage` 是完整结果 / 计量,
12
+ * 不进补投帧:完整结果在引擎存下的运行记录里,上面搬运的 `diagnostics` 点名它的读取手柄(`ReadToolResult`;引擎没存下时那句如实说)—— 自 core 7.61.0 起
13
+ * 这是模型读到它的唯一一条路(轮询工具已删),所以 `diagnostics` 必须 `carry`。
13
14
  * 键名直呼 core 的入参型(型文本里就有 core 名)⇒ 读表的 literals 段收得到这些键。
14
15
  */
15
16
  export declare const WORKFLOW_NOTIFY_KEY_DISPOSITION: {
@@ -1,4 +1,5 @@
1
1
  import { type CheckpointToken, type ResumeOutcome } from "@sema-agent/core";
2
+ import type { ResumeRestatement } from "../http/resume-restatement.js";
2
3
  import { type JsonColumnReader } from "../plugins/sql-json-column.js";
3
4
  import { type InProcessClaimFence } from "../plugins/store-contracts.js";
4
5
  /** 一条人对审批卡的决定(引擎 `ResumeOutcome` 的 `policy_ask` 臂 —— 子代停驻只会是这一门)。 */
@@ -52,11 +53,15 @@ export interface ParkDecisionLaneSeats {
52
53
  */
53
54
  export declare function parkDecisionStoreRefusal(seats: ParkDecisionLaneSeats): string | undefined;
54
55
  /**
55
- * 决定的同一性判据:判词 + 绑定两位 + 出处词(`decidedBy`)+ 载荷指纹(`updatedInput` / `answer` / `reason` / `feedback`,与 live 腿幂等回放的门⑦
56
- * 同一只 {@link approvalPayloadFingerprint})。通道自报的 `approver` 不进 —— 它是归属标签,不改引擎铸的结算词。
56
+ * 决定的同一性判据:判词 + 绑定两位 + 出处(`decidedBy`,带了机器座位 `decidedVia` 时是 `[decidedBy, decidedVia]` 一对)+ 载荷指纹
57
+ * (`updatedInput` / `answer` / `reason` / `feedback`,与 live 腿幂等回放的门⑦同一只 {@link approvalPayloadFingerprint})。通道自报的 `approver` 不进 ——
58
+ * 它是归属标签,不改引擎铸的结算词;`decidedVia` 进 —— 它定模型读到的那一句(部署策略拒 / PermissionRequest 钩子拒),换了座位是另一个决定。
59
+ * 出处位按在场取形(与下面的重述同一条成员律):不带座位 ⇒ 那一位仍是裸 `decidedBy`,字节不变;带了 ⇒ 一对(数组与串不可能相等,不与任何不带座位的决定撞)。
57
60
  * 载荷序列化不了 ⇒ 抛(两侧都源自 JSON,走到这里是结构异常;那只函数自己已经响亮 warn)。
61
+ * 决定体带的续跑请求重述键(`mcpServers` / `a2aPeers`)也是这次决定的一部分:同一决定带另一张表是另一个决定(投递跑还活着时不答首投回执,
62
+ * 走既有的会话占用拒),同一张表(键序无关,引擎的规范串)才是重投。按在场追加 —— 不带的决定指纹字节不变,落了库的存量指纹不换字节。
58
63
  */
59
- export declare function parkDecisionFingerprint(outcome: WorkflowParkOutcome): string;
64
+ export declare function parkDecisionFingerprint(outcome: WorkflowParkOutcome, restatement?: ResumeRestatement): string;
60
65
  /**
61
66
  * 存下的决定体 → 内域形的**唯一读者**(SQL 列与 local 账本行共用)。判到本仓自己读的那几格为止(门、判词、绑定两位、出处对象);
62
67
  * 其余键的属主是引擎,它在续跑入口 pre-CAS 再判一遍(与 checkpoint 店读 `outcome` 列同一条分工,见 `checkpoint-store-sql.ts` 的形读者注)。