@sema-agent/server 7.142.0 → 7.144.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 (301) hide show
  1. package/MIGRATION.md +608 -0
  2. package/USAGE.md +63 -36
  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 +155 -39
  9. package/dist/approval-card.js +111 -31
  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/execution-env.js +1 -1
  17. package/dist/boot/governance-seams.d.ts +26 -41
  18. package/dist/boot/governance-seams.js +61 -28
  19. package/dist/boot/late-binds.d.ts +7 -4
  20. package/dist/boot/late-binds.js +3 -1
  21. package/dist/boot/leader.js +2 -4
  22. package/dist/boot/memory-consolidation.d.ts +0 -8
  23. package/dist/boot/memory-consolidation.js +3 -3
  24. package/dist/boot/reapers.js +5 -4
  25. package/dist/boot/resolve-spec.d.ts +1 -1
  26. package/dist/boot/resolve-spec.js +8 -8
  27. package/dist/boot/runner-deps.d.ts +14 -6
  28. package/dist/boot/runner-deps.js +6 -17
  29. package/dist/boot/shutdown.d.ts +9 -0
  30. package/dist/boot/shutdown.js +18 -2
  31. package/dist/boot/stage-01-config.js +2 -0
  32. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  33. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  34. package/dist/boot/stage-03-store-backend.js +1 -2
  35. package/dist/boot/stage-04-stores.d.ts +5 -2
  36. package/dist/boot/stage-04-stores.js +3 -5
  37. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  38. package/dist/boot/stage-06-runners.d.ts +5 -2
  39. package/dist/boot/stage-06-runners.js +1 -3
  40. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  41. package/dist/boot/stage-07-capability-layer.js +6 -2
  42. package/dist/boot/stage-08-reapers.d.ts +5 -2
  43. package/dist/boot/stage-09-leader.d.ts +5 -2
  44. package/dist/boot/stage-10-http-server.d.ts +5 -2
  45. package/dist/boot/stage-10-http-server.js +4 -1
  46. package/dist/boot/stores.d.ts +5 -2
  47. package/dist/boot/stores.js +7 -7
  48. package/dist/boot/web-admin.d.ts +5 -1
  49. package/dist/boot/web-admin.js +31 -19
  50. package/dist/capabilities/hands-lane.d.ts +9 -39
  51. package/dist/capabilities/hands-lane.js +8 -13
  52. package/dist/capabilities/mcp-probe.d.ts +3 -2
  53. package/dist/capabilities/scenarios.d.ts +23 -17
  54. package/dist/capabilities/scenarios.js +2 -2
  55. package/dist/capabilities/skills.d.ts +1 -17
  56. package/dist/capabilities/skills.js +0 -8
  57. package/dist/capability-facts.d.ts +8 -8
  58. package/dist/capability-facts.js +4 -4
  59. package/dist/card-void.d.ts +6 -9
  60. package/dist/config-catalog.js +1 -1
  61. package/dist/config-d-migration.d.ts +2 -0
  62. package/dist/config-dir-trust.js +58 -29
  63. package/dist/config-invariants.d.ts +7 -1
  64. package/dist/config-invariants.js +7 -0
  65. package/dist/config-provider.d.ts +10 -0
  66. package/dist/config-provider.js +15 -4
  67. package/dist/config-root-migration.d.ts +13 -0
  68. package/dist/config-root-migration.js +99 -0
  69. package/dist/config-types.d.ts +5 -5
  70. package/dist/config.d.ts +4 -3
  71. package/dist/config.js +1 -1
  72. package/dist/delegated-session-owner.d.ts +35 -151
  73. package/dist/delegated-session-owner.js +1 -83
  74. package/dist/device-ws-protocol.d.ts +1 -0
  75. package/dist/device-ws-protocol.js +2 -1
  76. package/dist/entry-mode-clamp-notice.d.ts +1 -1
  77. package/dist/env-model-table-gate.d.ts +53 -0
  78. package/dist/env-model-table-gate.js +53 -0
  79. package/dist/execution-placement.d.ts +7 -4
  80. package/dist/fleet/fleet-bus.d.ts +28 -52
  81. package/dist/fleet/fleet-bus.js +35 -100
  82. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  83. package/dist/fleet/fleet-reconciler.js +1 -1
  84. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  85. package/dist/fleet/subagent-tail-bus.js +15 -28
  86. package/dist/hooks/classic-stations.d.ts +3 -1
  87. package/dist/hooks/classic-stations.js +2 -0
  88. package/dist/hooks/hook-runner.js +28 -7
  89. package/dist/host-decision.d.ts +54 -20
  90. package/dist/host-decision.js +42 -14
  91. package/dist/http/admission.d.ts +1 -1
  92. package/dist/http/admission.js +7 -3
  93. package/dist/http/approval-sweeps.js +11 -8
  94. package/dist/http/caller-trust.d.ts +1 -1
  95. package/dist/http/durable-run-leg.js +2 -2
  96. package/dist/http/engine-refusal.d.ts +16 -0
  97. package/dist/http/engine-refusal.js +17 -0
  98. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  99. package/dist/http/memory-compliance-failure-reply.js +4 -6
  100. package/dist/http/owner-access.d.ts +11 -0
  101. package/dist/http/owner-access.js +13 -0
  102. package/dist/http/principal-gate.d.ts +1 -1
  103. package/dist/http/refusal-body.d.ts +7 -3
  104. package/dist/http/refusal-body.js +1 -0
  105. package/dist/http/resume-legs.d.ts +21 -7
  106. package/dist/http/resume-legs.js +82 -53
  107. package/dist/http/resume-restatement.d.ts +39 -0
  108. package/dist/http/resume-restatement.js +25 -0
  109. package/dist/http/route-ctx.d.ts +35 -10
  110. package/dist/http/route-ctx.js +1 -1
  111. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  112. package/dist/http/routes/approvals-assistant.js +143 -31
  113. package/dist/http/routes/capabilities.js +11 -2
  114. package/dist/http/routes/memory-policy.d.ts +2 -2
  115. package/dist/http/routes/memory-policy.js +1 -1
  116. package/dist/http/routes/notify-wake.js +23 -25
  117. package/dist/http/routes/rules.js +48 -13
  118. package/dist/http/routes/runs.js +9 -8
  119. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  120. package/dist/http/routes/session-memory-sync.js +27 -10
  121. package/dist/http/routes/session-sync.js +13 -20
  122. package/dist/http/routes/sessions.d.ts +2 -2
  123. package/dist/http/routes/sessions.js +22 -23
  124. package/dist/http/routes/tasks.js +27 -49
  125. package/dist/http/server.d.ts +13 -7
  126. package/dist/http/server.js +2 -2
  127. package/dist/http/stored-body-replay.js +15 -4
  128. package/dist/http/wire-types.d.ts +17 -7
  129. package/dist/index.d.ts +1 -0
  130. package/dist/index.js +1 -0
  131. package/dist/lane-home-probe.d.ts +15 -0
  132. package/dist/lane-home-probe.js +4 -0
  133. package/dist/leader/leader.js +5 -2
  134. package/dist/leader/wire.d.ts +7 -1
  135. package/dist/leader/wire.js +6 -5
  136. package/dist/leg-facts.d.ts +37 -0
  137. package/dist/leg-facts.js +15 -0
  138. package/dist/memory-bundle-engine.d.ts +1 -4
  139. package/dist/memory-bundle-engine.js +2 -2
  140. package/dist/memory-operator-faces.d.ts +30 -66
  141. package/dist/memory-operator-faces.js +17 -88
  142. package/dist/memory-scope-stores.d.ts +38 -40
  143. package/dist/memory-scope-stores.js +51 -36
  144. package/dist/memory-scope.d.ts +0 -9
  145. package/dist/memory-scope.js +0 -9
  146. package/dist/memory-sync-client.js +14 -3
  147. package/dist/memory-sync.d.ts +20 -4
  148. package/dist/memory-sync.js +23 -4
  149. package/dist/model-plane-face.d.ts +13 -7
  150. package/dist/model-plane-face.js +2 -1
  151. package/dist/observability/corrupt-read-seat.d.ts +5 -9
  152. package/dist/observability/corrupt-read-seat.js +1 -2
  153. package/dist/observability/fail-open.d.ts +30 -10
  154. package/dist/observability/fail-open.js +27 -10
  155. package/dist/observability/run-terminal.d.ts +1 -1
  156. package/dist/observability/run-terminal.js +6 -2
  157. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  158. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  159. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  160. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  161. package/dist/orchestration/workflow-park-decision.js +5 -3
  162. package/dist/org-delivery-gate.d.ts +11 -0
  163. package/dist/org-delivery-gate.js +19 -1
  164. package/dist/parked-decide.d.ts +13 -9
  165. package/dist/parked-decide.js +7 -7
  166. package/dist/parked-revive-surface.d.ts +6 -7
  167. package/dist/parked-revive-surface.js +1 -1
  168. package/dist/peer-inbound-face.d.ts +122 -0
  169. package/dist/peer-inbound-face.js +123 -0
  170. package/dist/permission-rule-list.d.ts +13 -9
  171. package/dist/permission-rule-list.js +7 -3
  172. package/dist/plugins/approval-ask-store-file.js +18 -6
  173. package/dist/plugins/approval-ask-store-memory.js +8 -6
  174. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  175. package/dist/plugins/approval-ask-store-sql.js +30 -11
  176. package/dist/plugins/caching-session-store.d.ts +0 -3
  177. package/dist/plugins/caching-session-store.js +0 -3
  178. package/dist/plugins/checkpoint-store-sql.d.ts +45 -38
  179. package/dist/plugins/checkpoint-store-sql.js +108 -45
  180. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  181. package/dist/plugins/file-history-store-sql.js +24 -19
  182. package/dist/plugins/file-run-store.d.ts +29 -0
  183. package/dist/plugins/file-run-store.js +97 -8
  184. package/dist/plugins/fork-routing-session-store.d.ts +8 -1
  185. package/dist/plugins/fork-routing-session-store.js +6 -0
  186. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  187. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  188. package/dist/plugins/local-checkpoint-store.d.ts +6 -8
  189. package/dist/plugins/local-checkpoint-store.js +5 -4
  190. package/dist/plugins/local-session-store.d.ts +69 -30
  191. package/dist/plugins/local-session-store.js +133 -57
  192. package/dist/plugins/local-session-window.d.ts +4 -2
  193. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  194. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  195. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  196. package/dist/plugins/mailbox-store-sql.js +76 -7
  197. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  198. package/dist/plugins/memory-sync-store-pg.js +21 -0
  199. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  200. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  201. package/dist/plugins/one-time-migrations.d.ts +44 -14
  202. package/dist/plugins/one-time-migrations.js +118 -15
  203. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  204. package/dist/plugins/org-delivery-quota-store.js +2 -12
  205. package/dist/plugins/park-commit-watch.d.ts +1 -1
  206. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  207. package/dist/plugins/permission-rule-store-file.js +15 -2
  208. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  209. package/dist/plugins/permission-rule-store-sql.js +35 -2
  210. package/dist/plugins/pg-session-storage.d.ts +1 -9
  211. package/dist/plugins/pg-session-storage.js +2 -19
  212. package/dist/plugins/remote-env-device.d.ts +8 -11
  213. package/dist/plugins/remote-env-device.js +6 -3
  214. package/dist/plugins/remote-env-host.d.ts +8 -4
  215. package/dist/plugins/remote-env-host.js +7 -9
  216. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  217. package/dist/plugins/remote-env-k8s.js +50 -17
  218. package/dist/plugins/remote-shell.js +12 -2
  219. package/dist/plugins/retention-store-sql.d.ts +2 -2
  220. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  221. package/dist/plugins/sandbox-read-floor.js +2 -7
  222. package/dist/plugins/schema-consistency.d.ts +5 -1
  223. package/dist/plugins/session-fork.d.ts +1 -1
  224. package/dist/plugins/session-ownership.d.ts +9 -0
  225. package/dist/plugins/session-ownership.js +6 -0
  226. package/dist/plugins/session-store.d.ts +13 -16
  227. package/dist/plugins/session-store.js +9 -10
  228. package/dist/plugins/sql-errors.d.ts +10 -1
  229. package/dist/plugins/sql-errors.js +13 -0
  230. package/dist/plugins/sql-json-column.d.ts +24 -2
  231. package/dist/plugins/sql-json-column.js +38 -3
  232. package/dist/plugins/store-backend.d.ts +23 -28
  233. package/dist/plugins/store-backend.js +12 -3
  234. package/dist/plugins/store-contracts.d.ts +29 -5
  235. package/dist/plugins/store-contracts.js +4 -1
  236. package/dist/plugins/task-attachment-store.d.ts +2 -2
  237. package/dist/plugins/task-attachment-store.js +37 -52
  238. package/dist/plugins/tidb-session-store.d.ts +2 -14
  239. package/dist/plugins/tidb-session-store.js +2 -20
  240. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  241. package/dist/plugins/tool-result-store-sql.js +23 -2
  242. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  243. package/dist/plugins/wiring-section-codec.js +55 -0
  244. package/dist/plugins/workspace-fill.d.ts +1 -1
  245. package/dist/rule-entry-list.d.ts +3 -2
  246. package/dist/rule-entry-list.js +5 -1
  247. package/dist/rules-consent.d.ts +28 -5
  248. package/dist/rules-consent.js +40 -28
  249. package/dist/run-local.js +11 -6
  250. package/dist/runs.d.ts +12 -8
  251. package/dist/runs.js +10 -7
  252. package/dist/runtime-governance.d.ts +1 -1
  253. package/dist/security.d.ts +0 -4
  254. package/dist/server-secret-env.d.ts +4 -0
  255. package/dist/session-error-wire.d.ts +34 -23
  256. package/dist/session-error-wire.js +8 -3
  257. package/dist/session-incarnation.d.ts +3 -3
  258. package/dist/session-notify-door.d.ts +106 -0
  259. package/dist/session-notify-door.js +97 -0
  260. package/dist/session-sync.d.ts +9 -14
  261. package/dist/session-sync.js +5 -5
  262. package/dist/session-workspace.d.ts +3 -13
  263. package/dist/session-workspace.js +0 -9
  264. package/dist/standing-answer-grant.d.ts +5 -0
  265. package/dist/standing-answer-grant.js +10 -0
  266. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  267. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  268. package/dist/task-mcp.d.ts +1 -1
  269. package/dist/task-settings.d.ts +11 -8
  270. package/dist/task-settings.js +31 -11
  271. package/dist/tool-approval.d.ts +55 -35
  272. package/dist/tool-approval.js +112 -74
  273. package/dist/trace/core-keyset-guard.d.ts +12 -9
  274. package/dist/trace/engine-notice-wire.d.ts +1 -1
  275. package/dist/trace/engine-notice-wire.js +4 -2
  276. package/dist/trace/exit-seat.d.ts +7 -1
  277. package/dist/trace/exit-seat.js +1 -0
  278. package/dist/trace/ledger-sink.d.ts +2 -2
  279. package/dist/trace/ledger-sink.js +12 -4
  280. package/dist/trace/model-segment.d.ts +74 -25
  281. package/dist/trace/model-segment.js +141 -32
  282. package/dist/trace/project.d.ts +15 -6
  283. package/dist/trace/project.js +12 -4
  284. package/dist/trace/redact.d.ts +72 -9
  285. package/dist/trace/redact.js +1509 -471
  286. package/dist/trace/self-minted-wire.d.ts +598 -47
  287. package/dist/trace/self-minted-wire.js +190 -20
  288. package/dist/trace/wire-projection-faces.d.ts +1 -1
  289. package/dist/trace/wire-projection-faces.js +1 -1
  290. package/dist/verifier-pause-card.d.ts +33 -5
  291. package/dist/verifier-pause-card.js +15 -10
  292. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  293. package/dist/web-admin-client/apply-effective.js +90 -10
  294. package/dist/workspace-space.d.ts +19 -3
  295. package/dist/workspace-space.js +8 -4
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  297. package/package.json +3 -2
  298. package/wire-changes/server@7.143.0.json +1167 -0
  299. package/wire-changes/server@7.144.0.json +291 -0
  300. package/dist/session-records-skipped-notice.d.ts +0 -21
  301. package/dist/session-records-skipped-notice.js +0 -27
@@ -17,15 +17,6 @@ export function isV2ScopeKey(key) {
17
17
  export function memoryScopeContractOf(scopes) {
18
18
  return scopes.some(isV2ScopeKey) ? "v2" : undefined;
19
19
  }
20
- export function memoryLayersNamedBy(config) {
21
- const named = [
22
- ...(config.requirePrincipal === true ? [] : [config.memoryScope]),
23
- ...(config.memorySync !== undefined ? [config.memorySync.scope] : []),
24
- ...Object.values(config.projects ?? {}).flatMap((p) => p.defaultScopes ?? []),
25
- ...config.memoryConsolidationDriver.scopes,
26
- ];
27
- return named.filter((s, i) => typeof s === "string" && s.length > 0 && named.indexOf(s) === i);
28
- }
29
20
  export { MEMORY_SCOPE_COLUMN_CHARS, assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
30
21
  export function memoryScopeFor(config, principal, projectId) {
31
22
  if (config.requirePrincipal === true) {
@@ -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
@@ -9,13 +9,8 @@ import type { Logger } from "./logger.js";
9
9
  * @param event warn 事件名。**留在调用点**:它是那只店的身份,接线锚(test/roster-corrupt-read.test.ts)
10
10
  * 正是靠「构造实参里能读到这个名字」来证明座不是个空回调。
11
11
  * @param note 这张脸的后果句(可选)。缺席 ⇒ 不写这个键。
12
- * @param leg 与披露**同拍**的一条附加腿(可选;[ref] 加):座拿到一次披露时先交给它、再写日志。唯一的用户是
13
- * `LocalSessionStore` 那只 `FileSessionRepo` 的座 —— 会话打开作用域的计数腿(`noteReplayInSessionOpenScope`:
14
- * 作用域在场时把 `replay` 计数记到那次打开上,由打开方铸 `session.records_skipped`)。腿**必须不抛**(那一只是纯内存
15
- * 赋值);先腿后日志,是因为日志座可能坏(core 的 guard 会吞掉它的抛),而一次日志故障不许吃掉这一记。
16
- * 座本身不变:载荷投影、事件名、后果句仍由本属主铸,腿只多拿一份同一个 `info`。
17
12
  */
18
- export declare function createCorruptReadSeat(warn: Logger["warn"], event: string, note?: string, leg?: (info: FileStorageCorruptReadInfo) => void): (info: FileStorageCorruptReadInfo) => void;
13
+ export declare function createCorruptReadSeat(warn: Logger["warn"], event: string, note?: string): (info: FileStorageCorruptReadInfo) => void;
19
14
  /**
20
15
  * 铸一只 **F 类**腐读座([ref]):同一份 core 披露载荷,按 F 类座纪律处置 —— 每命中 `recordFailOpen(tag, 明细)`
21
16
  * (计数逐次 + 探针行逐次 + warn 每进程每 tag 一次;三件套的节流粒度见 `fail-open.ts` 模块头)。
@@ -25,7 +20,8 @@ export declare function createCorruptReadSeat(warn: Logger["warn"], event: strin
25
20
  * · `record` —— 一条被跳过的记录:**一次命中**,明细进探针行(会话 / 行号 / 坏体形 / 原因);
26
21
  * · `file` —— 整个文件 / 目录读不出:**一次命中**(同一 tag,明细里 `kind=file`);
27
22
  * · `replay` —— 同一批 `record` 的**汇总**(跳过 ≥1 条的每次读恰一发):**不计、不 warn** —— 计它就是把一条坏行记两次
28
- * ([ref] 的教训)。汇总的两个实数给了用户面(`leg` 腿 → `session.records_skipped` 帧),运维面逐条明细都在探针里。
23
+ * ([ref] 的教训)。汇总交给 `leg` 腿(唯一的用户 = local 会话店的一次性迁移 M33 整档读作用域);用户面的丢失账在 core 交出的存储上
24
+ * (`getReplayLoss` → 引擎的 `session.lineage_rerooted`,core 7.62.0),不经这只座;运维面逐条明细都在探针里。
29
25
  *
30
26
  * ## 为什么 warn 里不再逐条
31
27
  * 修前每命中一行 warn(本座的 `warn` 参数形):一份有 113 行读不出的存档,一次列表 + 一次查看 = 342 行,存档没修好每读再发 ——
@@ -33,8 +29,8 @@ export declare function createCorruptReadSeat(warn: Logger["warn"], event: strin
33
29
  * `fail_open_total{tag}`,逐条明细进探针。
34
30
  *
35
31
  * @param tag 登记过的 F 类 tag(`fail-open.ts` 闭集词表;`docs/FAIL-OPEN-CENSUS.md` §1 有登记行)。
36
- * @param leg 与披露**同拍**的附加腿,语义同 {@link createCorruptReadSeat} 的 `leg`:**先于**记账交给它(记账故障不许吃掉用户面那一记),
37
- * 必须不抛。**收全部三级**(包括不计数的 `replay`)—— 腿要的恰是那条汇总。
32
+ * @param leg 与披露**同拍**的附加腿(可选):**先于**记账交给它(记账故障不许吃掉腿那一记);腿**必须不抛**(纯内存赋值)。今天唯一的用户 =
33
+ * local 会话店一次性迁移 M33 的整档读作用域(`noteReplayInArchiveRewriteRead`:读时跳过了记录的存档留着不重写)。**收全部三级**(包括不计数的 `replay`)—— 腿要的恰是那条汇总。
38
34
  */
39
35
  export declare function createCorruptReadFailOpenSeat(tag: FailOpenTag, leg?: (info: FileStorageCorruptReadInfo) => void): (info: FileStorageCorruptReadInfo) => void;
40
36
  /**
@@ -1,9 +1,8 @@
1
1
  import { join, sep } from "node:path";
2
2
  import { readJsonlRecords, resolveDataRoot } from "@sema-agent/core";
3
3
  import { recordFailOpen } from "./fail-open.js";
4
- export function createCorruptReadSeat(warn, event, note, leg) {
4
+ export function createCorruptReadSeat(warn, event, note) {
5
5
  return (info) => {
6
- leg?.(info);
7
6
  return warn(event, {
8
7
  kind: info.kind,
9
8
  path: info.path,
@@ -201,12 +201,20 @@ export declare const FAIL_OPEN_TAGS: {
201
201
  };
202
202
  readonly "server.approvals.park-row-blob-unreadable": {
203
203
  readonly cls: "F";
204
- readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)S-2076 起同 tag 另有两座同判据的臂(`SqlCheckpointStore.readParkedSkillCardFacts`:对行上可能带技能卡两位之一的行,整只快照读回的体坏、或体是对象但 core 的摘要读不动它 ⇒ 只丢**这一行**的两位〔`boundGrants` / `classifierNoRuling`〕,授予绑定那一臂〔`boundGrantDigest` / `boundGrantAbsence`〕读自子树、照在(S-1612),队列照常;`not-text` 同样原样抛)—— 计数非零的处置同上:去按 session 查那一行。";
204
+ readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 7.144.0 起同 tag 另有一座:判官判决 `rowVerdict` 与技能卡两位共用的那一次**整行读**(`projectStoredRow`;按快照摘要记忆,未命中才读)—— 整行解不出 ⇒ 这一行不带判决与两位;体是对象但 core 摘要读不动 ⇒ 判决照给、只丢两位;两形逐次计、不进记忆,一行坏体在子树与整行两读上各计一次。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)(S-2076 的 `readParkedSkillCardFacts` 两座已由上面那次 `projectStoredRow` 整行读取代,树上零命中。)";
205
205
  };
206
206
  readonly "server.approvals.durable-row-checkpoint-id-missing": {
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.queue-box-unknown": {
211
+ readonly cls: "F";
212
+ readonly note: "S-1370(`peer-inbound-face.ts` 的 `list`;core 7.62.0 起读 core 的队列枚举 `listPeerHeldQueues`):一只有可审卡的 held 队列,它的箱(`PeerHeldQueueSnapshot.box`)既不是打开它的那只会话自己的箱、也不是 principal 收件箱 ⇒ 拼不出决断口 id 的车道段,那只队列的卡**不列**。按构造不可达:core 的快照注逐字只有这两种箱。放行(不列、队列其余照常)的最坏后果 = 那张被扣的信不出现在 `GET /v1/approvals` 的 `pending[]` 上 —— 它仍在 core 的队列里,按 `dialogExpiry` 带 `expired` 回执结清(core 的安全默认),有序关停照样结清它(关停不按车道筛)。不放行(抛)= 一只读不懂的队列打掉调用方整张审批列表(列表 500、`/stream` 每拍重读每拍抛)。故 F 类;detail `cards=<那只队列的卡数>`;`/stream` 每 3 s 一拍重读,同一只队列每拍计一次(在场才是信号);计数非零 = core 加了第三种箱,对 core 的 d.ts 重核车道读法(`org-delivery-gate.ts` 的 `laneOfBox`)。";
213
+ };
214
+ readonly "server.session-notify.active-run-read-failed": {
215
+ readonly cls: "F";
216
+ readonly note: "`session-notify-door.ts` 的 `deliver`(会话通知门的判活读;两只调用方 `POST /v1/sessions/:id/notify` 与后台子代完成 relay 同经):判活读 —— run 店的单活认领读 `getActiveTaskId` —— 抛(店瞬断 / 超时 / 连接被回收)⇒ 门当这只会话**没有在飞的 run**,落空闲臂:入收件箱,下次开流排空成 `task_notification` 帧,不把读失败抛给调用方。放行的最坏后果 = 这一条通知晚到 —— 本该在活流的下一个边界投递(`priority: next`),改成下一次开流才排空(至少一次,晚一次开流),不丢、不错投;两臂的投递动作之前紧邻的住客复核照做,所以这不是门、不是权限、不是凭证。不放行(抛)= 一次 run 店瞬断把通知路由答成 500(经全局兜底)、把后台子代的完成通知记成 `relay_failed` 交不出去,比晚一次开流更糟。读失败与「会话真的空闲」在外部同形,所以必须留痕:计数 + 探针(detail 带会话 id 与错句,经记录器统一脱敏)+ 每进程一次 warn。";
217
+ };
210
218
  readonly "server.approvals.queue-row-gate-kind-missing": {
211
219
  readonly cls: "F";
212
220
  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 或删会话)。";
@@ -321,7 +329,7 @@ export declare const FAIL_OPEN_TAGS: {
321
329
  };
322
330
  readonly "server.park.toolcallid-read-failed": {
323
331
  readonly cls: "F";
324
- readonly note: "[4913]:durable park 投影腿在 strip 之前拿结果里的 checkpointToken 换 `pendingAction.toolCallId`(cs.get 一次读),那次读**抛了**(store 抖动 / 滚动升级期 checkpoint 格式 version guard / 行已被并发消费)⇒ 本次 park 的 wire 投影**缺 `toolCallId` 键**,其余键(gate/checkpointId)照旧。放行的最坏后果 = 消费方(cli 判别子 v3 写者②)退回该键到货前的行为——同族多兄弟 durable park 分不出主角、诚实全不标(cli 侧 S3 局限,成文的旧现状),纯展示/关联面,不参与任何门/CAS/resume 判定,故 F 类。绝不挡终局路径是承重的:park 投影点全在 done/suspended 事件写链上,让它抛会把一次 store 抖动升级成整条 run 终局写失败。必须留痕:键缺席与「tool-less park 本就无键」([1995]② OMITTED 契约)在 wire 上同形,不计数则「投影为什么总缺」在遥测里永不显形。";
332
+ readonly note: "[4913]:durable park 投影腿在 strip 之前拿结果里的 checkpointToken 换 `pendingAction.toolCallId`(cs.get 一次读),那次读**抛了**(store 抖动 / 坏 checkpoint 行 / 行已被并发消费)⇒ 本次 park 的 wire 投影**缺 `toolCallId` 键**,其余键(gate/checkpointId)照旧。放行的最坏后果 = 消费方(cli 判别子 v3 写者②)退回该键到货前的行为——同族多兄弟 durable park 分不出主角、诚实全不标(cli 侧 S3 局限,成文的旧现状),纯展示/关联面,不参与任何门/CAS/resume 判定,故 F 类。绝不挡终局路径是承重的:park 投影点全在 done/suspended 事件写链上,让它抛会把一次 store 抖动升级成整条 run 终局写失败。必须留痕:键缺席与「tool-less park 本就无键」([1995]② OMITTED 契约)在 wire 上同形,不计数则「投影为什么总缺」在遥测里永不显形。";
325
333
  };
326
334
  readonly "server.exec-lane.ssh-host-key-unverified": {
327
335
  readonly cls: "P-DEBT";
@@ -333,7 +341,7 @@ export declare const FAIL_OPEN_TAGS: {
333
341
  };
334
342
  readonly "server.approvals.stale-echoed-token-unreadable": {
335
343
  readonly cls: "F";
336
- readonly note: "S-02 codex R1-[medium](验真后修):decide 的 409 `approval_stale` 臂对**调用方回显的旧 token** 做归属判定(`cs.get(binding.checkpointToken)`),那次读**抛了**(store 抖动 / 版本守卫 / 行恰被清理)。修前这里是静默 `catch(()=>null)`,「读不出」被折成「确认不在库」——叠上 S-02 的 404 收口后,一次 store 抖动就会把合法旧 token 的 409+terminal 谎报成 404「没有这条审批」。修后:unreadable ⇒ **保持 409**(`terminal` 不披露——证不出归属就不说,与 2026-07-26 纪律同向;404 折叠只在读成功且未命中时走,探测方无法自控 store 故障 ⇒ 谕示封闭不破)。放行的最坏后果 = 该次拒体 `terminal` 诚实缺席(`currentPending` 由它自己的读口与属主复核另判,不受本臂牵连),壳退回重拉列表,judgment 面 fail-safe,故 F 类。留痕理由:这次读是 terminal 披露与 404 折叠两个判定的输入,长期抖动必须在遥测显形。";
344
+ readonly note: "S-02 codex R1-[medium](验真后修):decide 的 409 `approval_stale` 臂对**调用方回显的旧 token** 做归属判定(`cs.get(binding.checkpointToken)`),那次读**抛了**(store 抖动 / 坏 checkpoint 行 / 行恰被清理)。修前这里是静默 `catch(()=>null)`,「读不出」被折成「确认不在库」——叠上 S-02 的 404 收口后,一次 store 抖动就会把合法旧 token 的 409+terminal 谎报成 404「没有这条审批」。修后:unreadable ⇒ **保持 409**(`terminal` 不披露——证不出归属就不说,与 2026-07-26 纪律同向;404 折叠只在读成功且未命中时走,探测方无法自控 store 故障 ⇒ 谕示封闭不破)。放行的最坏后果 = 该次拒体 `terminal` 诚实缺席(`currentPending` 由它自己的读口与属主复核另判,不受本臂牵连),壳退回重拉列表,judgment 面 fail-safe,故 F 类。留痕理由:这次读是 terminal 披露与 404 折叠两个判定的输入,长期抖动必须在遥测显形。";
337
345
  };
338
346
  readonly "server.resume.reopen-confirm-unreadable": {
339
347
  readonly cls: "F";
@@ -341,7 +349,7 @@ export declare const FAIL_OPEN_TAGS: {
341
349
  };
342
350
  readonly "server.approvals.stale-current-pending-unreadable": {
343
351
  readonly cls: "F";
344
- readonly note: "S-02 件2([5522]④→[5525]):409 `approval_stale` 拒体铸 additive 指路键 `currentPending`(本会话当前 pending 的 D-1 坐标,壳一跳重定位)时,当前 pending 行的 `cs.get(token)` 读**抛了**(store 抖动 / 滚动升级期 checkpoint 格式 version guard / 行恰被并发消费)⇒ 本次拒体**缺 `currentPending` 键**,409 本体与 `terminal` 判定照旧。放行的最坏后果 = 壳退回该键到货前的行为(重拉 GET /v1/approvals 自行重定位),纯指路/便利面,不参与任何门/CAS/resume 判定,故 F 类(同族先例:`server.park.toolcallid-read-failed`,同为 cs.get 投影读失败臂)。必须留痕:键缺席与「此刻确实没有 pending」在 wire 上同形,不计数则「指路为什么总缺」在遥测里永不显形。";
352
+ readonly note: "S-02 件2([5522]④→[5525]):409 `approval_stale` 拒体铸 additive 指路键 `currentPending`(本会话当前 pending 的 D-1 坐标,壳一跳重定位)时,当前 pending 行的 `cs.get(token)` 读**抛了**(store 抖动 / 坏 checkpoint 行 / 行恰被并发消费)⇒ 本次拒体**缺 `currentPending` 键**,409 本体与 `terminal` 判定照旧。放行的最坏后果 = 壳退回该键到货前的行为(重拉 GET /v1/approvals 自行重定位),纯指路/便利面,不参与任何门/CAS/resume 判定,故 F 类(同族先例:`server.park.toolcallid-read-failed`,同为 cs.get 投影读失败臂)。必须留痕:键缺席与「此刻确实没有 pending」在 wire 上同形,不计数则「指路为什么总缺」在遥测里永不显形。";
345
353
  };
346
354
  readonly "server.file-history.retention-trim-failed": {
347
355
  readonly cls: "F";
@@ -365,7 +373,7 @@ export declare const FAIL_OPEN_TAGS: {
365
373
  };
366
374
  readonly "server.run-terminal.redact-threw": {
367
375
  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。";
376
+ 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
377
  };
370
378
  readonly "server.run-terminal.provider-echo-shaped": {
371
379
  readonly cls: "F";
@@ -395,14 +403,26 @@ export declare const FAIL_OPEN_TAGS: {
395
403
  readonly cls: "F";
396
404
  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
405
  };
406
+ readonly "server.live-text.hold-diverged": {
407
+ readonly cls: "P-DEBT";
408
+ readonly note: "live 面模型文本的流式口(`trace/model-segment.ts` 的 `createLiveModelText`,三条 live 腿共用;流式口 = `trace/redact.ts` 的 `createRedactionStream`:喂入 / 取已判定前缀 / 收尾冲刷):一段冲刷时,已取出去的逐块字节与整段读法在那一截上的渲染对不上,或有一条整段读法的跨度横跨放行位 —— 流式口的律被打破(某条臂的判定在脱敏器读出的未决尾上界 `PENDING_TAIL_UNITS` 之外落定,而它没有把那一支标成未决尾)。处置:余下的字节照整段读法出(横跨放行位的跨度整枚换记号,不再出它的原文字节),段末帧照发整段脱敏的正文、消费端按它整段替换;但**已经出门**的那几块收不回 —— 其中可能有整段读法会遮的字节(这就是 P 族的理由)。债:未决尾上界是候选池成员判定距离的最大值,正则臂从正则源读出,手写扫描器的那一项是成员处的陈述(它起头要读全的字面锚),不是从扫描器的代码机械读出;已知一形是多遮方向(方案词锚的头值在文本末尾被读成「收在行尾」,其后隔着长过上界的行内空白又跟了词 ⇒ 整段读法不遮、活流已出记号)。钉 = 基线语料逐条按块流过流式口拼接 = 整段脱敏 + 判定距离最远的几形逐码元。detail `family=<text|reasoning> len=<段长>`;计数 = 多少段的律被打破(期望恒 0)。";
409
+ };
398
410
  readonly "server.redact.ppk-eat-to-end": {
399
411
  readonly cls: "F";
400
412
  readonly note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;出口座有界行的 head 读法(只扫一段头)上「窗内无合格终止」是终止可能在窗外的未决 —— 照样遮到窗尾,但不是兜底,不计;计数=多少次整段吃尾。";
401
413
  };
414
+ readonly "server.redact.forensic-level-cap": {
415
+ readonly cls: "F";
416
+ readonly note: "脱敏器取证相(redact.ts armPool 第二相)跑满级数界 FORENSIC_LEVEL_MAX 仍没收敛(证据链里层被认出、外层才认得出,刻意交替嵌套的私钥块 / URL 每层多一级,深过 31 层才够得着;现役语料最深 3 级)⇒ 下一级本会重跑的取证臂各自从它读证据的起点遮到窗尾(fail-closed:多遮可见、不少遮),不再升级;每次调用至多一条;影子扫不计;detail from=/len= 是视图坐标。";
417
+ };
402
418
  readonly "server.approval-card.closed-word-out-of-set": {
403
419
  readonly cls: "F";
404
420
  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
421
  };
422
+ readonly "server.approval-card.retired-card-key-read": {
423
+ readonly cls: "F";
424
+ 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)。计数非零 = 有本版之前铸的存卡还在被读(滚动升级窗里旧副本新写的、升级前已结仍被幂等重入 / 兑付读的);窗关前应归零。";
425
+ };
406
426
  readonly "server.approval-card.retired-card-word-read": {
407
427
  readonly cls: "F";
408
428
  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 +441,7 @@ export declare const FAIL_OPEN_TAGS: {
421
441
  };
422
442
  readonly "server.run-store.corrupt-record-skipped": {
423
443
  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 行)。";
444
+ 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
445
  };
426
446
  readonly "server.resume-anchor.corrupt-record-skipped": {
427
447
  readonly cls: "F";
@@ -465,7 +485,7 @@ export declare const FAIL_OPEN_TAGS: {
465
485
  };
466
486
  readonly "server.session-repo.corrupt-record-skipped": {
467
487
  readonly cls: "F";
468
- readonly note: "S-990(`plugins/local-session-store.ts` `createLocalSessionRepo` 的 `FileSessionRepo` 座,local 车道的会话存档;`LocalSessionStore` 与会话视图口 `audit.ts` 共用,S-879):一份会话存档里有记录读不出(整行毁 / 半截 JSON / 形不对,core 记成 `kind:\"record\"` 逐条披露)或存档文件 / 目录整体读不出(`kind:\"file\"`)⇒ core 的成文处置是跳过 + 披露,会话照常打开、列表少一行。最坏后果 = 模型读到的历史少了一截 / 列表静默少一份会话 —— 都是**可用性**损失,不开任何门;发起这一轮的人另有 wire 面披露(打开自己存档时的 `engine_notice` 码 `session.records_skipped`,与本 tag 无关、不动)⇒ F。**计数单位 = 一条被跳过的记录 = 一次命中**(`replay` 级是同一批记录的汇总,不计,S-616 同教训);**每命中一行探针**(`SEMA_FAILOPEN_PROBE`)带 `path` / `sessionId` / `line` / `form` / `reason` 逐条明细 —— 运维要看「哪份存档的哪一行坏了」读探针,不读本条 warn:本条是每进程每 tag 一次的告警(`detail` 是首条明细,不是全部)。修前:每命中一行 warn,一份 113 行坏记录的存档做一次列表 + 一次查看打出 342 行,存档没修好每读再发。";
488
+ readonly note: "S-990(`plugins/local-session-store.ts` `createLocalSessionRepo` 的 `FileSessionRepo` 座,local 车道的会话存档;`LocalSessionStore` 与会话视图口 `audit.ts` 共用,S-879):一份会话存档里有记录读不出(整行毁 / 半截 JSON / 形不对,core 记成 `kind:\"record\"` 逐条披露)或存档文件 / 目录整体读不出(`kind:\"file\"`)⇒ core 的成文处置是跳过 + 披露,会话照常打开、列表少一行。最坏后果 = 模型读到的历史少了一截 / 列表静默少一份会话 —— 都是**可用性**损失,不开任何门;发起这一轮的人另有 wire 面披露(core 7.62.0 起引擎在 run 里说 `engine_notice` 码 `session.lineage_rerooted`,丢失账读自 core 交出的存储 `getReplayLoss`,与本 tag 无关;7.104.0–7.143.0 是本仓自铸的 `session.records_skipped`)⇒ F。**计数单位 = 一条被跳过的记录 = 一次命中**(`replay` 级是同一批记录的汇总,不计,S-616 同教训);**每命中一行探针**(`SEMA_FAILOPEN_PROBE`)带 `path` / `sessionId` / `line` / `form` / `reason` 逐条明细 —— 运维要看「哪份存档的哪一行坏了」读探针,不读本条 warn:本条是每进程每 tag 一次的告警(`detail` 是首条明细,不是全部)。修前:每命中一行 warn,一份 113 行坏记录的存档做一次列表 + 一次查看打出 342 行,存档没修好每读再发。";
469
489
  };
470
490
  readonly "server.workflow-run.corrupt-record-skipped": {
471
491
  readonly cls: "F";
@@ -579,15 +599,15 @@ export declare const FAIL_OPEN_TAGS: {
579
599
  };
580
600
  readonly "server.remote-write.stage-seal-fallback": {
581
601
  readonly cls: "F";
582
- readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。";
602
+ readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目录上有继承来的 ACL 条目(macOS 原生 ACL / FreeBSD · illumos ZFS 的 NFSv4 ACL)去不掉或读不出,或目标机平台不在读 ACL 的适配器闭集里(`uname -s` 不是 Linux / Darwin / FreeBSD / SunOS);目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 继承来的 ACL 条目去不掉 / 平台不可知 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。";
583
603
  };
584
604
  readonly "server.park.held-card-void-failed": {
585
605
  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=<原句>]`(不含凭据)。";
606
+ 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
607
  };
588
608
  readonly "server.verifier-pause.card-void-failed": {
589
609
  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 随臂删或改写。";
610
+ 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
611
  };
592
612
  readonly "server.run-permission-mode.write-back-unpersisted": {
593
613
  readonly cls: "F";