@sema-agent/server 7.125.1 → 7.126.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.
- package/MIGRATION.md +195 -2
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +263 -140
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +47 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +5 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -2
- package/deploy/sema-up/chart/templates/server.yaml +1 -1
- package/deploy/sema-up/chart/values.yaml +12 -0
- package/deploy/sema-up/cluster-up.sh +10 -10
- package/deploy/sema-up/kube-up.sh +11 -10
- package/deploy/sema-up/lib/dataroot.sh +1 -1
- package/deploy/sema-up/lib/envf.sh +5 -5
- package/deploy/sema-up/lib/flagshape.sh +5 -5
- package/deploy/sema-up/lib/lanes.py +1 -1
- package/deploy/sema-up/lib/parse-machines.py +2 -2
- package/deploy/sema-up/lib/shell-access.sh +1 -1
- package/deploy/sema-up/preflight.sh +4 -4
- package/deploy/sema-up/sema-up.sh +33 -33
- package/deploy/sema-up/smoke.sh +3 -3
- package/deploy/sema-up/stack/compose.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +6 -2
- package/dist/approval-card.d.ts +20 -4
- package/dist/approval-content-kind.d.ts +38 -22
- package/dist/approval-content-kind.js +20 -4
- package/dist/audit.js +2 -0
- package/dist/bench/s1/arms.d.ts +9 -37
- package/dist/bench/s1/arms.js +4 -61
- package/dist/bench/s1/live-deps.d.ts +4 -10
- package/dist/bench/s1/live-deps.js +1 -222
- package/dist/bench/s1/oracle.d.ts +1 -1
- package/dist/bench/s1/reviewer.d.ts +1 -1
- package/dist/bench/s1/row.d.ts +5 -7
- package/dist/bench/s1/row.js +0 -2
- package/dist/bench/s1/run-firm.d.ts +2 -2
- package/dist/bench/s1/run-firm.js +1 -4
- package/dist/bench/s1/runner-ctx.d.ts +17 -29
- package/dist/bench/s1/runner-ctx.js +3 -7
- package/dist/bench/s1/tasks.d.ts +9 -46
- package/dist/bench/s1/tasks.js +4 -63
- package/dist/boot/execution-env.d.ts +38 -26
- package/dist/boot/execution-env.js +71 -38
- package/dist/boot/memory-boundary.d.ts +1 -1
- package/dist/boot/parked-revive-gate.d.ts +0 -3
- package/dist/boot/parked-revive-gate.js +2 -2
- package/dist/boot/resolve-spec.d.ts +0 -2
- package/dist/boot/resolve-spec.js +12 -17
- package/dist/boot/retention-lane.d.ts +12 -1
- package/dist/boot/retention-lane.js +2 -0
- package/dist/boot/runner-deps.d.ts +5 -5
- package/dist/boot/runner-deps.js +5 -16
- package/dist/boot/session-faces.d.ts +5 -1
- package/dist/boot/session-faces.js +37 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +7 -4
- package/dist/boot/stage-06-runners.d.ts +2 -3
- package/dist/boot/stage-06-runners.js +8 -7
- package/dist/boot/stage-07-capability-layer.d.ts +2 -3
- package/dist/boot/stage-07-capability-layer.js +3 -4
- package/dist/boot/stage-08-reapers.d.ts +2 -3
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -3
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +2 -3
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +4 -3
- package/dist/boot/web-admin.d.ts +5 -0
- package/dist/boot/web-admin.js +28 -14
- package/dist/brain.d.ts +2 -2
- package/dist/brain.js +1 -1
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +2 -2
- package/dist/capabilities/memory-notice.d.ts +27 -22
- package/dist/capabilities/memory-notice.js +15 -17
- package/dist/capabilities/scenarios.d.ts +9 -14
- package/dist/capabilities/scenarios.js +5 -50
- package/dist/capability-facts.d.ts +22 -11
- package/dist/capability-facts.js +13 -6
- package/dist/config-catalog.js +11 -4
- package/dist/config-provider.d.ts +5 -1
- package/dist/config-provider.js +23 -4
- package/dist/config-types.d.ts +14 -1
- package/dist/config.js +39 -6
- package/dist/deployment-governance.d.ts +19 -21
- package/dist/deployment-governance.js +7 -8
- package/dist/env-name-allowlist-knobs.d.ts +5 -5
- package/dist/env-name-allowlist-knobs.js +1 -0
- package/dist/execution-lane-caps.js +2 -2
- package/dist/hooks/hook-runner.d.ts +84 -30
- package/dist/hooks/hook-runner.js +74 -41
- package/dist/http/admission.js +12 -9
- package/dist/http/durable-run-leg.d.ts +5 -4
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/idempotency.d.ts +28 -6
- package/dist/http/idempotency.js +10 -2
- package/dist/http/principal-gate.d.ts +14 -0
- package/dist/http/principal-gate.js +7 -0
- package/dist/http/request-guards.d.ts +2 -1
- package/dist/http/request-guards.js +14 -14
- package/dist/http/resume-legs.d.ts +2 -2
- package/dist/http/resume-legs.js +5 -4
- package/dist/http/route-ctx.d.ts +8 -6
- package/dist/http/routes/a2a-serve.js +11 -7
- package/dist/http/routes/approvals-assistant.js +18 -3
- package/dist/http/routes/background.d.ts +1 -1
- package/dist/http/routes/background.js +1 -1
- package/dist/http/routes/capabilities.js +10 -2
- package/dist/http/routes/mcp-reconnect.js +1 -1
- package/dist/http/routes/runs.d.ts +4 -3
- package/dist/http/routes/runs.js +12 -13
- package/dist/http/routes/session-sync.js +14 -11
- package/dist/http/routes/sessions.js +9 -9
- package/dist/http/routes/side-query.js +6 -19
- package/dist/http/routes/tasks.js +12 -11
- package/dist/http/server.d.ts +4 -0
- package/dist/http/server.js +2 -2
- package/dist/http/submit-replay.d.ts +15 -8
- package/dist/http/submit-replay.js +6 -0
- package/dist/http/wire-types.d.ts +7 -9
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/internal-run-env.d.ts +30 -0
- package/dist/internal-run-env.js +11 -0
- package/dist/leader/wire.js +3 -2
- package/dist/memory-scope.d.ts +19 -7
- package/dist/memory-scope.js +25 -8
- package/dist/observability/fail-open.d.ts +15 -3
- package/dist/observability/fail-open.js +15 -3
- package/dist/observability/metrics.d.ts +0 -1
- package/dist/observability/metrics.js +0 -3
- package/dist/observability/run-terminal.js +69 -4
- package/dist/permission-rule-vocab.d.ts +3 -2
- package/dist/permission-rule-vocab.js +1 -1
- package/dist/plan-cache-probe.d.ts +3 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/caching-session-store.d.ts +3 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +42 -28
- package/dist/plugins/checkpoint-store-sql.js +57 -11
- package/dist/plugins/file-run-store.d.ts +25 -21
- package/dist/plugins/file-run-store.js +70 -34
- package/dist/plugins/local-checkpoint-store.d.ts +8 -5
- package/dist/plugins/local-checkpoint-store.js +5 -15
- package/dist/plugins/local-session-store.d.ts +66 -7
- package/dist/plugins/local-session-store.js +168 -101
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +1 -1
- package/dist/plugins/memory-run-store.d.ts +19 -3
- package/dist/plugins/memory-run-store.js +38 -3
- package/dist/plugins/one-time-migrations.d.ts +141 -39
- package/dist/plugins/one-time-migrations.js +32 -143
- package/dist/plugins/pg-pool.js +11 -1
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +9 -5
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +1 -0
- package/dist/plugins/remote-env-e2b.d.ts +4 -0
- package/dist/plugins/remote-env-e2b.js +2 -1
- package/dist/plugins/remote-env-host.d.ts +12 -0
- package/dist/plugins/remote-env-host.js +11 -1
- package/dist/plugins/remote-env-k8s.d.ts +227 -39
- package/dist/plugins/remote-env-k8s.js +450 -75
- package/dist/plugins/remote-env-local-docker.d.ts +4 -0
- package/dist/plugins/remote-env-local-docker.js +2 -1
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/retention-store-sql.d.ts +4 -1
- package/dist/plugins/retention-store-sql.js +11 -9
- package/dist/plugins/run-store-sql.d.ts +44 -6
- package/dist/plugins/run-store-sql.js +92 -27
- package/dist/plugins/session-capture-record-store-sql.d.ts +2 -3
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/store-backend.js +1 -1
- package/dist/plugins/store-contracts.d.ts +17 -3
- package/dist/plugins/tidb-pool.js +15 -3
- package/dist/plugins/tidb-session-storage.js +3 -2
- package/dist/plugins/tidb-session-store.d.ts +2 -0
- package/dist/plugins/tidb-session-store.js +6 -2
- package/dist/request-key-closure.d.ts +12 -0
- package/dist/request-key-closure.js +3 -3
- package/dist/router/route-orchestration.d.ts +3 -11
- package/dist/router/route-orchestration.js +0 -4
- package/dist/rule-entry-list.js +0 -4
- package/dist/rules-consent.js +3 -0
- package/dist/run-local.d.ts +5 -4
- package/dist/run-local.js +23 -17
- package/dist/runs.js +1 -0
- package/dist/security.d.ts +7 -0
- package/dist/security.js +5 -0
- package/dist/server-secret-env.d.ts +10 -4
- package/dist/server-secret-env.js +4 -1
- package/dist/session-incarnation.d.ts +46 -2
- package/dist/session-incarnation.js +14 -3
- package/dist/session-sync-kernel.d.ts +10 -3
- package/dist/spec-fields.d.ts +24 -8
- package/dist/spec-fields.js +21 -2
- package/dist/task-workflow.d.ts +6 -4
- package/dist/task-workflow.js +3 -2
- package/dist/tool-approval.d.ts +78 -6
- package/dist/tool-approval.js +7 -3
- package/dist/trace/core-keyset-guard.d.ts +4 -4
- package/dist/trace/engine-notice-wire.d.ts +26 -1
- package/dist/trace/engine-notice-wire.js +7 -2
- package/dist/trace/project.d.ts +8 -3
- package/dist/trace/project.js +15 -1
- package/dist/trace/self-minted-wire.d.ts +104 -2
- package/dist/trace/self-minted-wire.js +35 -0
- package/dist/web-admin-client/apply-effective.d.ts +13 -7
- package/dist/web-admin-client/apply-effective.js +11 -26
- package/dist/web-admin-client/apply-ledger.d.ts +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +39 -4
- package/dist/web-admin-client/candidate-gate.js +9 -2
- package/dist/web-admin-client/facade.d.ts +2 -2
- package/dist/web-admin-client/http-client.d.ts +3 -1
- package/dist/web-admin-client/http-client.js +4 -3
- package/dist/web-admin-client/read-warnings.d.ts +33 -5
- package/dist/web-admin-client/read-warnings.js +41 -6
- package/dist/web-admin-client/types.d.ts +11 -5
- package/dist/workspace-cache.d.ts +45 -0
- package/dist/workspace-cache.js +30 -0
- package/dist/workspace-space-commit.d.ts +116 -0
- package/dist/workspace-space-commit.js +117 -0
- package/dist/workspace-space.d.ts +75 -9
- package/dist/workspace-space.js +52 -1
- package/package.json +4 -4
- package/dist/boot/session-incarnation-seat.d.ts +0 -41
- package/dist/boot/session-incarnation-seat.js +0 -69
- package/dist/capabilities/code-review-council.d.ts +0 -49
- package/dist/capabilities/code-review-council.js +0 -111
- package/dist/capabilities/collab-wire.d.ts +0 -36
- package/dist/capabilities/collab-wire.js +0 -2
- package/dist/capabilities/collab-workflows.d.ts +0 -47
- package/dist/capabilities/collab-workflows.js +0 -90
- package/dist/capabilities/prompts/code-review.d.ts +0 -25
- package/dist/capabilities/prompts/code-review.js +0 -39
- package/dist/capabilities/prompts/team.d.ts +0 -10
- package/dist/capabilities/prompts/team.js +0 -6
- package/dist/capabilities/team.d.ts +0 -113
- package/dist/capabilities/team.js +0 -70
|
@@ -1,21 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔴 **一次性迁移段 —— 每一条都有窗;窗、删法与删码后存量旧形由谁接住,逐条只写在下面的 {@link ONE_TIME_MIGRATIONS}
|
|
3
|
+
* 登记表里**(唯一属主,S-590)。硬 breaking 纪律:持久数据面 = **响亮拒 + 一次性迁移,窗过即删,永不长期双读**。
|
|
4
|
+
*
|
|
5
|
+
* ## 窗(规则写在这里一次,日期只在登记表)
|
|
6
|
+
* 窗 = 引入该条的版本的发布日 + 30 天(`removableAfter`);窗关之后的第一个 minor 删该条的码(删什么 = 那一行的 `removal`)。
|
|
7
|
+
* 删码之后,存量库若还带着迁移前的形,由那一行 `afterRemoval` 点名的**既有**响亮臂接住;没有响亮臂的那一条如实写「没有」,
|
|
8
|
+
* 不为它新造兼容读。到期不靠人记:`test/one-time-migration-window-gate.test.ts` 读系统日期,今天晚于某条的 `removableAfter`
|
|
9
|
+
* 即红,红句点名那一条与它的删法。删码本身是持久数据面的行为变化,由单独的车带三问与发布帖去做;门只负责「到期了」。
|
|
10
|
+
* **未发布形**(裁定 2026-09-28):引入它的那一版还没发布时,`releasedOn` / `removableAfter` 两格写 `null`(显式,不是缺席,也不是一个
|
|
11
|
+
* 占位日期)—— 不计到期;抬 `package.json` 到那一版的发布准备提交把两格回填成发布日 / +30 天。到期门同时是回填门:`package.json` 版本
|
|
12
|
+
* **≥** 某条未发布形的 `introducedIn` 而两格仍是 `null` ⇒ 红(发布准备提交就是回填时刻,忘了就当场红;版本更低 = 还在开发,不红)。
|
|
13
|
+
* 最后一条删掉时,整个文件连同 `StoreBackend.runOneTimeMigrations()`(npm 公面上的接口成员)、`NO_SQL_MIGRATIONS`、
|
|
14
|
+
* `logOneTimeMigrations`、三只后端与两只测试夹具上的实现、`boot/stores.ts` 的调用点一起删 —— 唯一例外是退役 schema 的拒启臂
|
|
15
|
+
* ({@link assertRetiredSchemaAbsent} 与它的错误类型、两件退役 schema 的坐标):它没有窗,跳版升级的库永远可能出现,删最后一条的车要把它搬进启动期对账
|
|
16
|
+
* (`schema-consistency.ts` 一族)并保住它先于一切改写的位置,不要随文件删掉。
|
|
17
|
+
* 其余文件一律只写「窗口见本文件登记表」,不各写一个日期或版本 —— S-590 之前本文件头写死了一个删除版本号(7.75.0),
|
|
18
|
+
* 连同 11 个文件里的复述共 23 行;那一版过去了 28 个 minor 也没删,7.93.5 反倒往本文件里又加了 M4。
|
|
19
|
+
*
|
|
20
|
+
* ## 为什么这一段存在,以及为什么它必须短命
|
|
21
|
+
* 本仓的退役落在**已经写进库里的字节**上(例:审计明细里「没有明细」的第二种写法 —— JSON 文本 `null`,7.93.5 引入)。这类旧读法
|
|
22
|
+
* 此前各自靠一条**长期兼容臂**活着(探针 / 折叠读 / 改名表 / 读侧容忍),而兼容臂是最坏的一种持久层欠债:它没有到期日,
|
|
23
|
+
* 读腿与写腿从此对同一份字节有两种理解,而下一个人只看得到「有两条路」这个事实、看不到「哪一条是真的」。
|
|
24
|
+
*
|
|
25
|
+
* ⇒ 换成本段:**升一次级把存量字节改完,兼容臂当场删干净**。代价是每一条在它的窗内每次启动都要跑一遍(第二次起全是
|
|
26
|
+
* 零命中的空转);收益是窗关之后可以把那一条连同它的 SQL 一起删,而读腿从引入那一版起就只有**一种**读法。
|
|
27
|
+
*
|
|
28
|
+
* ## 已退役的 M1–M3 与跳版库的拒启臂
|
|
29
|
+
* 退役的整树快照索引表(M1)、退役的单座 steering 列(M2)、被上游改了名的闭集词 `unresolvable` → `ancestor_marked`(M3)三条的改写、计数与读数
|
|
30
|
+
* 窗关后已全部删码,登记行一并删。删码之后唯一还认得它们的地方是 {@link assertRetiredSchemaAbsent} 这只**拒启臂**(持久数据面:响亮拒 + 点名出路,
|
|
31
|
+
* 不新造兼容读):判据复用三条当年判「幂等」的同一批探针,不另造 —— 表在场探针({@link tableExists})与列在场探针({@link columnExists})。
|
|
32
|
+
* M3 当年的「本版是否已在这只库上跑过」标记本来就是这两件退役 schema 的在场(7.74.0 之前的每一版 DDL 都建表与列,7.74.0 起都不再建;能写下旧拼法的
|
|
33
|
+
* 7.64.0–7.71.0 恒带两者),所以**旧拼法不另扫**:一条对账本 `task_event` 的无索引整表扫正是当年被判不许成为每次启动固定开销的东西
|
|
34
|
+
* (账本是全站最大的表,PG 池级 `statement_timeout` 会把它切成拒启、每次启动重演)。两件任一在场 ⇒ 这只库没有经过带迁移的版本 ⇒ 旧拼法的改写同样没跑过 ⇒ 拒句一并点名。
|
|
35
|
+
* 出路:先在 7.125.x(最后一个带这三条迁移的版)上起服一次让它们跑完,再升。
|
|
36
|
+
* 残余(如实):运维在第一次起带迁移的版本之前就把两件退役 schema 都手工删掉的库,残留的旧拼法由读腿既有的 `server.trace.gate-outcome-defective`
|
|
37
|
+
* (整条 withhold + 计数 + 一次 warn)接住;local 文件车道没有表与列,没有这一问的探针 —— 引擎 File 店里已决 checkpoint 的记录按契约物理不可变,本仓没有合法缝改写它们
|
|
38
|
+
* (下游不改上游盘上格式),同由那条读腿接住。
|
|
39
|
+
*
|
|
40
|
+
* ## 窗内的迁移(逐条的失败方向刻意不同)
|
|
41
|
+
* · **M4 审计明细里的 JSON 文本 `null`** —— S-550 把 `device_audit.detail` 的读回收进唯一属主并判形之后,
|
|
42
|
+
* 「这条审计没有明细」在盘上**只剩一种**表示(列 NULL);而 7.93.4 及更早的写腿对**显式** `detail: null`
|
|
43
|
+
* 会写下 JSON 文本 `null`(写口 `input.detail === undefined ? null : json(input.detail)`)。那种旧行在新读腿
|
|
44
|
+
* 下是坏体 ⇒ `listAudit` 整页 5xx。持久数据面是**唯一不能「翻红」的面**(CLAUDE.md 硬 breaking 三句):
|
|
45
|
+
* ⇒ **一次性迁移**把那些行改成列 NULL,**不留**「把 JSON null 读成缺席」的兼容臂(那正是被禁的长期双读)。
|
|
46
|
+
* 判等用的正是读腿的那只投影表达式(`sql-json-column.ts#jsonColumnTextExpr`),两种承载(真 JSON 列 /
|
|
47
|
+
* MariaDB 的 LONGTEXT 别名)同一句 SQL、同一份字节 —— 迁移与读腿不可能对同一行有两种看法。
|
|
48
|
+
*
|
|
49
|
+
* · **M5 会话化身列**(S-790 / S-506,[ref] §2)—— `session_meta.incarnation` 是本版新加的列(新库由中央 DDL 建;
|
|
50
|
+
* 列定义唯一属主 = `src/session-incarnation.ts` 的 `SESSION_META_INCARNATION_COLUMN`,迁移内插同一串)。存量库:
|
|
51
|
+
* 列不在 ⇒ `ADD COLUMN`(加法 DDL,不丢任何字节),然后给每一行空化身(`''` = 没铸过)铸一只**互不相同**的令牌
|
|
52
|
+
* (应用侧 `randomUUID()`,不用库函数:MySQL / TiDB 的 `UUID()` 是 v1,带时间与机器信息)。回填之后读到空化身 ⇒ 读口响亮拒
|
|
53
|
+
* (`SessionIncarnationMissingError`),**不**当作通配 —— 硬 breaking 三句里持久数据那一句的形:一次性迁移 + 响亮拒,无双读。
|
|
54
|
+
* local 文件车道没有这张表:那一车道的存量会话在第一次被读时补铸并落盘(`LocalSessionStore.sessionFaceOf`),本段答
|
|
55
|
+
* `"lazy-on-read"` / 0。
|
|
56
|
+
*
|
|
57
|
+
* · **M6 run 死亡判决码** —— 7.116.0 起 run 店的两只死亡判决(失联收割 / 重启孤儿回收)在行上同写机读码(`error_code` =
|
|
58
|
+
* `run.stalled` / `run.orphaned`,词表属主 `store-contracts.ts` 的 `RUN_HOLDER_DEATH_CODES`),读者(`isHolderDeathVerdict`)只认码。
|
|
59
|
+
* 存量行只有句子、码列空 ⇒ **写回半场**:起服把「`failed` ∧ 码列空 ∧ 句子是那两句(接管臂的括注形按前缀认)」的行补上码
|
|
60
|
+
* (SQL 两条 `UPDATE`;local 腿 = `FileRunStore.migrateLegacyHolderDeathCodes`)。**读侧半场**:滚动升级窗里 ≤7.115.x 的副本还会写只有句子的行
|
|
61
|
+
* (起服写回覆盖不到窗内新写的行)⇒ SQL 两孪生的行映射经 `readLegacyHolderDeathCode` 把这种行读成码 —— 它是本条的一半,窗关随本条一起删
|
|
62
|
+
* (见登记表 M6 的 `removal`)。local 车道没有滚动窗(数据根独占:窗内不会有旧写者与本进程并存),读侧半场不在 File 店上。
|
|
63
|
+
*
|
|
64
|
+
* · **M7 停驻行 allow 读法记号的拒臂**(7.117.0,core 7.42.0)—— 不是改写,是**持久数据「响亮拒」那一半**:续跑重放体里带非空
|
|
65
|
+
* `settings.permissions.allow`、而 ctx 上的读法记号不是当前的 `"allow_layer"`(名单时代无记号 / 表态时代 `"statement"`)⇒ 认领之前 409、行字节不动。
|
|
66
|
+
* 规则、两句拒句与写点在 `http/checkpoint-ctx.ts`;本条只登记它的窗(7.115.0 引入的同一只拒臂当时没有登记,7.117.0 起它覆盖两代旧词,登记在这里)。
|
|
67
|
+
* 没有读数键(它不写库)。
|
|
68
|
+
* · **M8 停驻行披露位换名**(7.117.0,core 7.42.0 #1327)—— `checkpoint.risk_descriptor` 里 7.41.x 引擎写下的 `shadowedRule`(字符串:命中却清不掉的
|
|
69
|
+
* 持久 allow 规则原文)在 7.42.0 引擎里换成 `allowRuleShadowed: { member, entries, omitted? }[]`,旧键不再有人读。旧键只由规则店成员铸
|
|
70
|
+
* (core 7.41.2 `gate-lanes.ts` 的披露臂是它唯一的铸点;本仓的策略从不自铸它)⇒ **一次性改写**:`{ ..., shadowedRule: v }` →
|
|
71
|
+
* `{ ..., allowRuleShadowed: [{ member: "persisted", entries: [v] }] }`(旧值是引擎拼好的「最多五条 + and N more」一串,原样作为一条条目,不拆)。
|
|
72
|
+
* 列是 TEXT(两方言),改写在应用侧解析 + 按 `token` CAS 回写(`WHERE risk_descriptor = <读到的原文>`;第二跑零命中)。**没有读侧半场**:滚动窗里
|
|
73
|
+
* 旧副本新写的旧键行只缺结构化披露(卡上 `message` 那句话引擎已经写好),旧键的文本由脱敏叶表的「认不得的键按内容脱敏」一条兜住
|
|
74
|
+
* (`observability/run-terminal.ts` `redactRiskDescriptor`)—— 不为它开第二种读法。local 车道没有这一列(停驻行在引擎自己的 File 店里,本仓
|
|
75
|
+
* 没有合法缝改写那些字节,盘上格式归引擎):旧键留在原地、不被读,文本同样经脱敏叶表兜住。
|
|
76
|
+
* · **M9 park 指针索引改主键**(7.118.0,core 7.44.0 #1319)—— `workflow_agent_session` 的主键由 `(session_id)` 改成 `(session_id, run_id)`:
|
|
77
|
+
* 停驻持有者有了引擎写的位(`parkedBy` / `generation`)之后,判决要看到点名同一个子会话的每一条记录(续跑链 / 分叉),单列主键下后写覆盖先写。
|
|
78
|
+
* 表是派生投影 ⇒ **复制 + 原子换名**:照中央 DDL 建新形暂存表 → 一条 `INSERT … SELECT` 整表复制 → 正表换成待删名、暂存表换成正表名、删待删旧表
|
|
79
|
+
* (TiDB 一条 `RENAME TABLE`;PG 一个事务)→ 补建索引(旧行就是指针,新读法从记录判,不存在第二种读法)。**暂存表在场 = 上一次没走完**(唯一判据,
|
|
80
|
+
* 与正表在不在无关 —— 起服的中央 DDL 先于本段,正表可能是它刚建的空表):收尾 = 暂存表并进正表、删暂存表、再删待删旧表;旧行始终在库里
|
|
81
|
+
* (`reshapeWorkflowParkIndex` 头注)。「一次」的标记 = 主键宽度(两列且暂存表不在 ⇒ 跳过,只补收尾)。local 车道是账本(一行 = 一条 run 的
|
|
82
|
+
* 指针集),没有主键可改 ⇒ `file-ledger` / 0。
|
|
83
|
+
* **读侧半场**(同一行、同一窗,不另起登记行):升级那一刻还停驻着、由引擎 7.43 及以前写下的 workflow 子代卡 —— 路由里的 `older_engine` 读臂
|
|
84
|
+
* (点名它的条目没有持有者位 ⇒ 响亮拒、指路续跑)与随包排空判据 `workflow-park-holder`(升级前把这些卡数清);窗关随本条一起删(登记表 M9 的 `removal`)。
|
|
85
|
+
* · **M10 local 车道会话属主落盘**(7.119.0)—— local 店的属主从进程内存搬进会话边档(与化身同一格,`LocalSessionStore` 头注);SQL 两孪生的属主
|
|
86
|
+
* 一直是 `session_meta.owner` 列,没有可迁的东西。存量:7.118.x 及更早写下的边档那一格只有化身、**没有属主事实** —— 迁移**不伪造**它
|
|
87
|
+
* (起服时没有 principal,把它判给任何人都是编造)。本条是一只**窗内才生效的行为臂**:窗开着时,带 principal 的第一次提交(attach)认领
|
|
88
|
+
* 这种会话(写进那一格,一行 info `session_owner_claimed`);窗关之后(运行期读本表 M10 行的 `removableAfter`,见 {@link migrationWindowOpen})
|
|
89
|
+
* 它照今天答无主,删码因此是零行为变化。认领的安全前提是单用户:local 后端起服门拒 `REQUIRE_PRINCIPAL=true`,授权口也只在
|
|
90
|
+
* `REQUIRE_PRINCIPAL` 关时把 principal 递给店认领。没有起服读数(它不在起服时写库,与 M7 同形)。
|
|
91
|
+
* · **M11 持久规则里 scope 不被引擎收的行**(7.120.0,引擎 7.46.0)—— 7.119.0 及更早的本仓入口(`/v1/rules` 写口、两条导入面)按
|
|
92
|
+
* 「root 非空」收 project scope,引擎 7.45 也收;引擎 7.46.0 把 project root 收窄成**完整绝对路径**(`isValidDurableScope`),本仓入口
|
|
93
|
+
* 同版改调同一只谓词。存量的 `project:myproj` 一类行在新引擎的读侧被丢(每次 run 一条 `permission.rule_row_dropped`),而撤销口删不动它
|
|
94
|
+
* (引擎的写臂对这种 scope 的墓碑同样拒)⇒ **一次性删除**:SQL 两孪生逐桶 OCC 回写,规则行与墓碑里 scope 不被谓词收的都删
|
|
95
|
+
* (`permission-rule-store-sql.ts` `purgeUndurableScopeRows`),一行一条 `permission_rule_row_removed` warn(桶键 / 工具名 / 态 / root,
|
|
96
|
+
* 规则正文不出店;root 在店里已过 `redactSecrets`)。逐行的句子按态分:仍有活 add 的规则行劝按完整路径重写,**早已撤销**的规则行
|
|
97
|
+
* (判活 = core 的 `applyTombstones`)与墓碑不劝 —— 劝了就是把人撤销过的规则加回去。每只桶先过**读腿的唯一判官**(`decodeBucketRow`),
|
|
98
|
+
* 回写走**写臂的唯一写法**(`writeBucket`):判官整桶拒的桶(形 / 指纹 / behavior 闭集 / 墓碑规范文本 / 单一验证器)一个字节不动,
|
|
99
|
+
* 一桶一条 `permission_rule_bucket_unreadable` warn(桶键 + 不带行内容的拒因),读腿对它照旧整桶拒,出路是人 —— 从一只被整桶拒的桶里删行
|
|
100
|
+
* 再重算指纹,等于把整桶拒「修」成可读、让原本被拒的 allow 行上线。**不**给撤销口另开一条「坏 scope 也认」的读法(那是长期双读)。删除是破坏性步骤,排在最后: * 它自己半途失败时拒因自带已删的行。残余(如实):回写提交之后、日志行写出之前**进程死掉**,
|
|
101
|
+
* 那几行的记录随之丢失(重扫看不见已删的行),不为它另建迁移账表。
|
|
102
|
+
* ⚠️ **够不到的一格(如实登记)**:local 车道的持久规则在**引擎自己的** File 分区里 —— 引擎的写面对这种 scope 的加 / 墓碑一律拒,
|
|
103
|
+
* 本仓没有合法缝删它们,改那份盘上格式是下游改上游。那里的存量坏行照旧由引擎读侧丢并告知;出路候引擎给
|
|
104
|
+
* 一条删除缝。
|
|
105
|
+
* · **M12 停驻行模式词读法记号的拒臂**(7.122.0)—— 与 M7 同形,持久数据「响亮拒」那一半:7.122.0 起一条 run 的模式词只有存储体顶层 `permissionMode`
|
|
106
|
+
* 一个源(提交入口盖运维 `AUTONOMY=plan` 的地板一次),更早的行体里是请求原词、运维的 plan 由治理层每段重钳。续跑重放之前:本部署此刻有入口地板 ∧
|
|
107
|
+
* ctx 上没有当前记号 `"entry_word"` ∧ 体的生效词被地板钳会变 ⇒ 认领之前 409、行字节不动。规则、拒句与写点在 `http/checkpoint-ctx.ts` 顶注第三问。
|
|
108
|
+
* 没有读数键(它不写库)。
|
|
109
|
+
* · **M13 盘上 LKG 信封 formatVersion 2 → 3**(7.123.0)—— 不在店里(配置候选的盘上缓存,与 M12 同为非店迁移):被丢目录条目收成一份载体
|
|
110
|
+
* (`models.droppedCatalogEntries`,每条被丢的目录条目一行),7.122.0 及以前写的 2 只记了有名且启用的行、记在旧键 `droppedIdentities`。
|
|
111
|
+
* `config-lkg.ts` `loadLkg` 读回 2 时在内存里迁一次(旧载体搬成新载体、去掉 etag ⇒ 中心可达后第一拍整拉、重铸完整载体),启动日志
|
|
112
|
+
* `config_lkg_migrated` 一行;写盘一律写 3。规则与判形在 `config-lkg.ts` 顶注与 `migrateV2Lkg`。没有读数键(它不写库)。
|
|
113
|
+
* · **M15 盘上 LKG 的载荷换代(settings-schema 11)**(7.124.0)—— 不在店里(与 M13 同为非店迁移):3 及更早的 LKG 载荷是 settings-schema 10.x
|
|
114
|
+
* 的形(`models.atModelAllowlist` / `models.default` / `runtime` 域 …)。`config-lkg.ts` `loadLkg` 每次读回都过契约的一次性迁移
|
|
115
|
+
* `migrateEffectiveSnapshot`(本仓不写任何一条改写规则;只认形、幂等),迁不动(收据 `refused`)⇒ 不读回(拒因点名迁移 id);迁过的由读回方
|
|
116
|
+
* 按信封 formatVersion 4 写回原位,启动日志 `config_lkg_migrated` 一行(带收据)。没有读数键(它不写库)。
|
|
117
|
+
* · **M16 本地 config.d 的一次性迁移(settings-schema 11)**(7.124.0)—— 不在店里:起服时本地配置源第一次读之前,`config-d-migration.ts`
|
|
118
|
+
* 调契约的目录迁移 `migrateConfigDirectory`(覆盖面 = 本地读腿会读的那几份:便携域 + 退役域 `runtime`),整域终判通过才原子写回、搬空的
|
|
119
|
+
* `runtime.json` 删;读腿不读的域文件(`hosts` / `workers` …)不碰;
|
|
120
|
+
* 迁不动 ⇒ 一字不写、`config_dir_migration_refused` 一行。没有读数键(它不写库)。
|
|
121
|
+
*
|
|
122
|
+
* ## 幂等
|
|
123
|
+
* 改写的几条都可重复跑(M4:`detail IS NOT NULL AND … = 'null'`;M5:列在场探针 + `WHERE incarnation = ''`;M6:`WHERE error_code IS NULL`;
|
|
124
|
+
* M8:旧键扫描 + CAS 回写 —— 写过的行不再命中,第二跑都是零命中)。并发起动由 {@link runSqlOneTimeMigrations} 的 advisory 锁 + 每步的后置条件判据兜住(见那里)。
|
|
125
|
+
*/
|
|
1
126
|
import type { Pool as MysqlPool } from "mysql2/promise";
|
|
2
127
|
import type { Pool as PgPool } from "pg";
|
|
3
128
|
import { type UndurableScopeRuleRow, type UnreadableRuleBucket } from "./permission-rule-store-sql.js";
|
|
4
129
|
/** 迁移读数(启动日志一行写全;测试按它断言,不靠日志文本)。 */
|
|
5
130
|
export interface OneTimeMigrationReport {
|
|
6
|
-
/** M1:退役快照索引表 —— `dropped` = 本次真 drop 了,`absent` = 早已不在(第二跑/全新库)。 */
|
|
7
|
-
readonly retiredSnapshotTable: "dropped" | "absent";
|
|
8
|
-
/** M1:drop 之前表上的行数(`absent` 时恒 0)。非零 = 这次迁移真删掉了存量行,必须让运维看见。 */
|
|
9
|
-
readonly retiredSnapshotRows: number;
|
|
10
|
-
/** M2:退役单座列 —— `dropped` = 本次真 DROP COLUMN,`absent` = 早已不在。 */
|
|
11
|
-
readonly retiredSteerSeatColumn: "dropped" | "absent";
|
|
12
|
-
/** M2:DROP COLUMN 随手丢掉的**陈旧**格数(行已不是 `pending` ⇒ 那条指令再也送不出去;`absent` 时恒 0)。
|
|
13
|
-
* 非零 = 这次迁移真丢了存量字节,必须让运维看见 —— 与 M1 的 `retiredSnapshotRows` 逐字同一条纪律。 */
|
|
14
|
-
readonly retiredSteerSeatStaleRows: number;
|
|
15
|
-
/** M3:改名的行数(账本 `tool_end` 行 + 已决 checkpoint 的执行判词列,两者相加)。 */
|
|
16
|
-
readonly askOriginRowsMigrated: number;
|
|
17
131
|
/** M4:`device_audit.detail` 里 JSON 文本 `null` 被改成列 NULL 的行数(表不在 ⇒ 恒 0)。
|
|
18
|
-
* 非零 =
|
|
132
|
+
* 非零 = 这次迁移真改写了存量字节,必须让运维看见。 */
|
|
19
133
|
readonly auditDetailJsonNullRows: number;
|
|
20
134
|
/** M5:`session_meta.incarnation` 列 —— `added` = 本次 `ADD COLUMN`;`present` = 早已在(新库 / 第二跑);
|
|
21
135
|
* `no-table` = 这只库上没有 `session_meta`(中央 DDL 之后不可达,只为不编一个读数);`lazy-on-read` = local 文件车道
|
|
@@ -47,7 +161,7 @@ export interface OneTimeMigrationReport {
|
|
|
47
161
|
* 规则(窗长 30 天、窗关后的第一个 minor 删码、门怎么判)在本文件头注「窗」一节,只写那一次。
|
|
48
162
|
*/
|
|
49
163
|
export interface OneTimeMigrationWindow {
|
|
50
|
-
/** 本文件里的条目名(
|
|
164
|
+
/** 本文件里的条目名(头注「窗内的迁移」的 M4…)。 */
|
|
51
165
|
readonly id: string;
|
|
52
166
|
/** 引入这条迁移的版本 = 第一只含其代码的发布 tag(取证:`git log -S '<该条的常量名>' -- src/plugins/one-time-migrations.ts`
|
|
53
167
|
* 找引入提交 → `git tag --contains <提交> --sort=creatordate` 取最早)。 */
|
|
@@ -68,9 +182,6 @@ export interface OneTimeMigrationWindow {
|
|
|
68
182
|
/**
|
|
69
183
|
* 🔴 S-590 —— 一次性迁移登记表,本文件各条的窗的**唯一属主**。别处(接口 JSDoc、调用点、DDL 注、部署文档)只写「窗口见
|
|
70
184
|
* `plugins/one-time-migrations.ts` 登记表」;起动日志行 `one_time_migrations_applied` 的 `removableAfter` 读数由本表派生。
|
|
71
|
-
*
|
|
72
|
-
* M1–M3 同一次删:M3 的「本版在这只库上跑过了」标记就是 M1 / M2 的退役 schema 在不在(头注「幂等」一节),先删 M1 / M2
|
|
73
|
-
* 而留着 M3 会让 M3 永远判「跑过了」。三条同版引入、同一天到期,本来就同一次删。
|
|
74
185
|
*/
|
|
75
186
|
export declare const ONE_TIME_MIGRATIONS: readonly OneTimeMigrationWindow[];
|
|
76
187
|
/**
|
|
@@ -79,23 +190,22 @@ export declare const ONE_TIME_MIGRATIONS: readonly OneTimeMigrationWindow[];
|
|
|
79
190
|
* 只给**窗内才生效的行为臂**用(M10 的认领):那种臂过了窗就停,不等删码车 —— 删码因此是零行为变化,而窗只写在本表这一处。
|
|
80
191
|
*/
|
|
81
192
|
export declare function migrationWindowOpen(window: Pick<OneTimeMigrationWindow, "removableAfter">, today: string): boolean;
|
|
82
|
-
/** local 车道上 SQL
|
|
83
|
-
export declare const NO_SQL_MIGRATIONS: Omit<OneTimeMigrationReport, "
|
|
193
|
+
/** local 车道上 SQL 独有的几条(M4 / M5 / M8 / M9 / M11)的读数(M6 两车道都有,local 腿的读数由 `LocalBackend.runOneTimeMigrations` 现算):那几张表 / 列在文件底座上从来不存在 ⇒ 如实答 absent / 0。 */
|
|
194
|
+
export declare const NO_SQL_MIGRATIONS: Omit<OneTimeMigrationReport, "holderDeathCodeRowsBackfilled">;
|
|
84
195
|
/** 启动期的日志口(logger 的子集)。两级各有其用,见 {@link logOneTimeMigrations}。 */
|
|
85
196
|
export interface MigrationLogSink {
|
|
86
197
|
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
87
198
|
info(msg: string, meta?: Record<string, unknown>): void;
|
|
88
199
|
}
|
|
89
200
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* (`RunStoreStrictHydrateError` 的同族先例,锚类型不锚文案)。
|
|
201
|
+
* 跳版升级的库的拒启:退役 schema(快照索引表 / 单座列)任一仍在。**具名类型**:锚类型不锚文案(日志消费端与测试按 `code` / 类型分派);
|
|
202
|
+
* 码取列册探针同一个 `config.schema_mismatch`,运维面一个拒启码一个读法。拒句点名在场的坐标、说明第三条(旧拼法改写)同样没跑过、给出路。
|
|
93
203
|
*/
|
|
94
|
-
export declare class
|
|
95
|
-
readonly
|
|
96
|
-
/**
|
|
97
|
-
readonly code = "
|
|
98
|
-
constructor(
|
|
204
|
+
export declare class RetiredSchemaPresentError extends Error {
|
|
205
|
+
readonly found: readonly string[];
|
|
206
|
+
/** 机读码(与列册探针的拒启同一个)。 */
|
|
207
|
+
readonly code = "config.schema_mismatch";
|
|
208
|
+
constructor(found: readonly string[]);
|
|
99
209
|
}
|
|
100
210
|
/**
|
|
101
211
|
* SQL 两后端的一次性迁移。**调用点只有一个**(boot/stores.ts,在 `ensureSchema` 之后、任何路由装配
|
|
@@ -106,16 +216,14 @@ export declare class RetiredSteerSeatError extends Error {
|
|
|
106
216
|
* 一次滚动升级会让 N 个副本同时跑到这里。整段因此跑在**一条连接**上、握着**与 `ensureSchema` 同一把**
|
|
107
217
|
* advisory 锁(MySQL 具名锁 / PG `pg_try_advisory_lock`,两方言都是非阻塞轮询形):本段是「启动期改 schema 的活」,与 ensure 属同一类,
|
|
108
218
|
* 复用同一把锁 ⇒ 本仓这一类工作只有**一个**锁身份,而且本段也不会与另一个副本的 ensure 交错。
|
|
109
|
-
* 锁拿不到时按既有先例**降级**(不阻塞启动)
|
|
110
|
-
* **后置条件**({@link execUntil})
|
|
219
|
+
* 锁拿不到时按既有先例**降级**(不阻塞启动),但降级不等于放任:每条改表 DDL 的成功判据是它的
|
|
220
|
+
* **后置条件**({@link execUntil}),所以「另一个副本抢先把同一张表换了形」不会把滚动升级变成启动失败循环。
|
|
111
221
|
*
|
|
112
222
|
* ## 次序(codex r1 [medium] 同轮)
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* 破坏性步骤及其读数**写进拒因 —— 否则「删完表再失败」会让重试看到一张不存在的表、把行数报成 0,
|
|
116
|
-
* 证据永久消失。
|
|
223
|
+
* 非破坏改写(M4–M9:改值不丢信息 / 加法 DDL / 复制 + 换名)在前,破坏性步骤(M11 的删行)排在最后;
|
|
224
|
+
* M11 自己半途失败时,拒因自带已删的行(`purgeUndurableScopeRows`)—— 否则重试看不见已删的行,证据永久消失。
|
|
117
225
|
*
|
|
118
|
-
* @throws {
|
|
226
|
+
* @throws {RetiredSchemaPresentError} 退役 schema 仍在(跳版升级的库;唯一的拒启臂,先于一切改写)。
|
|
119
227
|
*/
|
|
120
228
|
export declare function runSqlOneTimeMigrations(dialect: "tidb" | "pg", pool: MysqlPool | PgPool): Promise<OneTimeMigrationReport>;
|
|
121
229
|
/** M8 一行改写的结论:改写后的文本 / 这一行不用动 / 读不懂(解析错误原样交出,调用方计数进读数)。 */
|
|
@@ -134,12 +242,6 @@ export type ShadowedRuleRewrite = {
|
|
|
134
242
|
* JSON 读不懂 ⇒ `unreadable`(这一行不动,交给读口自己的响亮臂;迁移不替它猜,但把它数进读数,不静默)。
|
|
135
243
|
*/
|
|
136
244
|
export declare function migrateShadowedRuleDescriptor(text: string): ShadowedRuleRewrite;
|
|
137
|
-
/**
|
|
138
|
-
* M3 的**纯**改写(local 车道的账本行逐条走它;SQL 两腿在服务端用等价的 JSON 路径表达同一件事)。
|
|
139
|
-
* 不改原对象(调用方与账本共享引用):有变更时换一只信封,原记录一个字节不动;无变更 ⇒ 返回
|
|
140
|
-
* `undefined`,调用方据此**不写盘**(第二跑因此零 I/O)。
|
|
141
|
-
*/
|
|
142
|
-
export declare function migrateRetiredAskOrigin(type: string, data: unknown): Record<string, unknown> | undefined;
|
|
143
245
|
/**
|
|
144
246
|
* 启动日志的读数行。零命中(第二跑起的常态)也照发 —— 「跑过了、什么都没改」与「压根没跑」必须可分辨。
|
|
145
247
|
*
|
|
@@ -6,43 +6,8 @@ import { decodeJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTe
|
|
|
6
6
|
import { SESSION_META_INCARNATION_COLUMN, mintSessionIncarnation } from "../session-incarnation.js";
|
|
7
7
|
import { RUN_HOLDER_DEATH_CODES, RUN_ORPHANED_ERROR, RUN_STALLED_ERROR } from "./store-contracts.js";
|
|
8
8
|
import { PERMISSION_RULE_TABLE, purgeUndurableScopeRows } from "./permission-rule-store-sql.js";
|
|
9
|
+
import { SCHEMA_MISMATCH_ERROR_CODE } from "./schema-consistency.js";
|
|
9
10
|
export const ONE_TIME_MIGRATIONS = [
|
|
10
|
-
{
|
|
11
|
-
id: "M1",
|
|
12
|
-
introducedIn: "7.74.0",
|
|
13
|
-
releasedOn: "2026-09-13",
|
|
14
|
-
removableAfter: "2026-10-13",
|
|
15
|
-
removal: "删 migrateOn 的 M1 步(`snapshot_manifest` 在场探针、删前计数、`DROP TABLE`)与 `RETIRED_SNAPSHOT_TABLE`;报告、日志行与 " +
|
|
16
|
-
"`NO_SQL_MIGRATIONS` 的 `retiredSnapshotTable` / `retiredSnapshotRows`;与 M2 / M3 同一次删(见本表头注)",
|
|
17
|
-
afterRemoval: "没有响亮臂:存量库里的 `snapshot_manifest` 成一张无人读写的孤儿表 —— 它的读腿(边界探针)已随 core 7.17.0 的 seam 退役删掉," +
|
|
18
|
-
"起服列册探针 `schema-consistency.ts` 只对账代码认识的表。只有从 7.73.x 及更早直接跳到删码版的库会遇到;不影响任何读腿," +
|
|
19
|
-
"运维可手工 `DROP TABLE snapshot_manifest`",
|
|
20
|
-
reportKeys: ["retiredSnapshotTable", "retiredSnapshotRows"],
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
id: "M2",
|
|
24
|
-
introducedIn: "7.74.0",
|
|
25
|
-
releasedOn: "2026-09-13",
|
|
26
|
-
removableAfter: "2026-10-13",
|
|
27
|
-
removal: "删 migrateOn 的 M2 拒启臂与 `DROP COLUMN` 步、`RETIRED_STEER_SEAT_COLUMN`、`DELIVERABLE_STEER_SEAT_WHERE`、`RetiredSteerSeatError`;" +
|
|
28
|
-
"报告、日志行与 `NO_SQL_MIGRATIONS` 的 `retiredSteerSeatColumn` / `retiredSteerSeatStaleRows`;与 M1 / M3 同一次删",
|
|
29
|
-
afterRemoval: "起服列册探针 `schema-consistency.ts`(S-547)的多列臂:存量库的 `checkpoint.pending_steer` 是代码不认识的列 ⇒ 拒启 " +
|
|
30
|
-
"`config.schema_mismatch`(响亮;⚠️ 那一臂的句子说「库比代码新」,对这一形方向说反了)。运维照拒句手工删列时,列上还送得出去的 " +
|
|
31
|
-
"steering 指令随之丢失 —— 删码之后没有任何东西再替它数一遍送得出去的格",
|
|
32
|
-
reportKeys: ["retiredSteerSeatColumn", "retiredSteerSeatStaleRows"],
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "M3",
|
|
36
|
-
introducedIn: "7.74.0",
|
|
37
|
-
releasedOn: "2026-09-13",
|
|
38
|
-
removableAfter: "2026-10-13",
|
|
39
|
-
removal: "删 migrateOn 的 M3 两条整表 `UPDATE` 与它的「退役 schema 还在」跳过标记、`RETIRED_ASK_ORIGIN`、`GATE_BEARING_LEDGER_EVENT_TYPE`、" +
|
|
40
|
-
"`migrateRetiredAskOrigin`,以及 local 腿(`FileRunStore.migrateRetiredAskOriginRows` 与 `LocalBackend.runOneTimeMigrations` 里那一格);" +
|
|
41
|
-
"报告与日志行的 `askOriginRowsMigrated`;与 M1 / M2 同一次删",
|
|
42
|
-
afterRemoval: "读腿既有的 `server.trace.gate-outcome-defective`(`trace/project.ts` 的 `screenedGate`):残留旧拼法的 gate 与任何集外词同判 —— " +
|
|
43
|
-
"整条 withhold + 计数 + 一次 warn(与头注「M3 够不到的一格」同一条臂)",
|
|
44
|
-
reportKeys: ["askOriginRowsMigrated"],
|
|
45
|
-
},
|
|
46
11
|
{
|
|
47
12
|
id: "M4",
|
|
48
13
|
introducedIn: "7.93.5",
|
|
@@ -218,10 +183,6 @@ export function migrationWindowOpen(window, today) {
|
|
|
218
183
|
return window.removableAfter === null || today <= window.removableAfter;
|
|
219
184
|
}
|
|
220
185
|
export const NO_SQL_MIGRATIONS = {
|
|
221
|
-
retiredSnapshotTable: "absent",
|
|
222
|
-
retiredSnapshotRows: 0,
|
|
223
|
-
retiredSteerSeatStaleRows: 0,
|
|
224
|
-
retiredSteerSeatColumn: "absent",
|
|
225
186
|
auditDetailJsonNullRows: 0,
|
|
226
187
|
sessionIncarnationColumn: "lazy-on-read",
|
|
227
188
|
sessionIncarnationsBackfilled: 0,
|
|
@@ -233,31 +194,24 @@ export const NO_SQL_MIGRATIONS = {
|
|
|
233
194
|
undurableScopeRuleBucketsDeferred: 0,
|
|
234
195
|
undurableScopeRuleBucketsUnreadable: [],
|
|
235
196
|
};
|
|
236
|
-
const RETIRED_SNAPSHOT_TABLE = "snapshot_manifest";
|
|
237
|
-
const RETIRED_STEER_SEAT_COLUMN = "pending_steer";
|
|
238
|
-
const DELIVERABLE_STEER_SEAT_WHERE = "(status = 'pending' OR (status = 'resolved' AND execution_outcome IS NULL))";
|
|
239
|
-
const RETIRED_ASK_ORIGIN = { from: "unresolvable", to: "ancestor_marked" };
|
|
240
|
-
const GATE_BEARING_LEDGER_EVENT_TYPE = "tool_end";
|
|
241
197
|
const AUDIT_TABLE = "device_audit";
|
|
242
198
|
const AUDIT_DETAIL_COLUMN = "detail";
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
`
|
|
251
|
-
`
|
|
252
|
-
`
|
|
253
|
-
`
|
|
254
|
-
|
|
255
|
-
`
|
|
256
|
-
`
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
this.rows = rows;
|
|
260
|
-
this.name = "RetiredSteerSeatError";
|
|
199
|
+
const RETIRED_SNAPSHOT_TABLE = "snapshot_manifest";
|
|
200
|
+
const RETIRED_STEER_SEAT_COLUMN = "pending_steer";
|
|
201
|
+
export class RetiredSchemaPresentError extends Error {
|
|
202
|
+
found;
|
|
203
|
+
code = SCHEMA_MISMATCH_ERROR_CODE;
|
|
204
|
+
constructor(found) {
|
|
205
|
+
super(`${SCHEMA_MISMATCH_ERROR_CODE}: this database still carries schema that the one-time migrations of server 7.74.0–7.125.x retired — refusing to start. ` +
|
|
206
|
+
`Found: ${found.join("; ")}. ` +
|
|
207
|
+
`Both come from builds that predate those migrations, so this database never booted a build that still carried them — which also means the third one never ran: ` +
|
|
208
|
+
`ask-origin gates stored under the retired spelling \`unresolvable\` (in task_event.data and in checkpoint.execution_outcome) were not rewritten to \`ancestor_marked\`. ` +
|
|
209
|
+
`This build carries none of the three and will not guess. ` +
|
|
210
|
+
`Way out: start a server 7.125.x build once against this database and let its one-time migrations finish (it drops the table and the column and rewrites the spelling; ` +
|
|
211
|
+
`while the column still holds a steering instruction that can be delivered it refuses and names the rows and its own ways out), then upgrade to this build. ` +
|
|
212
|
+
`The check behind this refusal is read-only and runs before any rewrite: it changed nothing in the database.`);
|
|
213
|
+
this.found = found;
|
|
214
|
+
this.name = "RetiredSchemaPresentError";
|
|
261
215
|
}
|
|
262
216
|
}
|
|
263
217
|
function mysqlDb(conn) {
|
|
@@ -304,6 +258,15 @@ async function columnExists(db, dialect, table, column) {
|
|
|
304
258
|
const sql = `SELECT 1 AS present FROM information_schema.columns WHERE table_schema = ${scope} AND table_name = '${table}' AND column_name = '${column}'`;
|
|
305
259
|
return (await db.rows(sql)).length > 0;
|
|
306
260
|
}
|
|
261
|
+
async function assertRetiredSchemaAbsent(db, dialect) {
|
|
262
|
+
const found = [];
|
|
263
|
+
if (await tableExists(db, dialect, RETIRED_SNAPSHOT_TABLE))
|
|
264
|
+
found.push(`table ${RETIRED_SNAPSHOT_TABLE} (the retired whole-tree snapshot index)`);
|
|
265
|
+
if (await columnExists(db, dialect, "checkpoint", RETIRED_STEER_SEAT_COLUMN))
|
|
266
|
+
found.push(`column checkpoint.${RETIRED_STEER_SEAT_COLUMN} (the retired single-seat steering column)`);
|
|
267
|
+
if (found.length > 0)
|
|
268
|
+
throw new RetiredSchemaPresentError(found);
|
|
269
|
+
}
|
|
307
270
|
export async function runSqlOneTimeMigrations(dialect, pool) {
|
|
308
271
|
return dialect === "tidb" ? withMysqlLock(pool, (db) => migrateOn(db, "tidb")) : withPgLock(pool, (db) => migrateOn(db, "pg"));
|
|
309
272
|
}
|
|
@@ -336,29 +299,7 @@ async function withPgLock(pool, run) {
|
|
|
336
299
|
}
|
|
337
300
|
}
|
|
338
301
|
async function migrateOn(db, dialect) {
|
|
339
|
-
|
|
340
|
-
let retiredSteerSeatStaleRows = 0;
|
|
341
|
-
if (seatColumnPresent) {
|
|
342
|
-
const seatRows = await db.rows(`SELECT COUNT(*) AS n FROM checkpoint WHERE ${RETIRED_STEER_SEAT_COLUMN} IS NOT NULL AND ${DELIVERABLE_STEER_SEAT_WHERE}`);
|
|
343
|
-
const seats = Number(seatRows[0]?.n ?? 0);
|
|
344
|
-
if (seats > 0)
|
|
345
|
-
throw new RetiredSteerSeatError(seats);
|
|
346
|
-
const staleRows = await db.rows(`SELECT COUNT(*) AS n FROM checkpoint WHERE ${RETIRED_STEER_SEAT_COLUMN} IS NOT NULL`);
|
|
347
|
-
retiredSteerSeatStaleRows = Number(staleRows[0]?.n ?? 0);
|
|
348
|
-
}
|
|
349
|
-
const snapshotTablePresent = await tableExists(db, dialect, RETIRED_SNAPSHOT_TABLE);
|
|
350
|
-
const retiredSchemaPresent = seatColumnPresent || snapshotTablePresent;
|
|
351
|
-
const askOriginRowsMigrated = !retiredSchemaPresent
|
|
352
|
-
? 0
|
|
353
|
-
: dialect === "tidb"
|
|
354
|
-
? (await db.exec(`UPDATE task_event SET data = JSON_SET(data, '$.gate.origin', '${RETIRED_ASK_ORIGIN.to}') ` +
|
|
355
|
-
`WHERE type = '${GATE_BEARING_LEDGER_EVENT_TYPE}' AND JSON_UNQUOTE(JSON_EXTRACT(data, '$.gate.origin')) = '${RETIRED_ASK_ORIGIN.from}'`)) +
|
|
356
|
-
(await db.exec(`UPDATE checkpoint SET execution_outcome = JSON_SET(execution_outcome, '$.origin', '${RETIRED_ASK_ORIGIN.to}') ` +
|
|
357
|
-
`WHERE JSON_UNQUOTE(JSON_EXTRACT(execution_outcome, '$.origin')) = '${RETIRED_ASK_ORIGIN.from}'`))
|
|
358
|
-
: (await db.exec(`UPDATE task_event SET data = jsonb_set(data, '{gate,origin}', '"${RETIRED_ASK_ORIGIN.to}"') ` +
|
|
359
|
-
`WHERE type = '${GATE_BEARING_LEDGER_EVENT_TYPE}' AND data->'gate'->>'origin' = '${RETIRED_ASK_ORIGIN.from}'`)) +
|
|
360
|
-
(await db.exec(`UPDATE checkpoint SET execution_outcome = jsonb_set(execution_outcome, '{origin}', '"${RETIRED_ASK_ORIGIN.to}"') ` +
|
|
361
|
-
`WHERE execution_outcome->>'origin' = '${RETIRED_ASK_ORIGIN.from}'`));
|
|
302
|
+
await assertRetiredSchemaAbsent(db, dialect);
|
|
362
303
|
const auditTablePresent = await tableExists(db, dialect, AUDIT_TABLE);
|
|
363
304
|
const auditDetailJsonNullRows = !auditTablePresent
|
|
364
305
|
? 0
|
|
@@ -373,46 +314,14 @@ async function migrateOn(db, dialect) {
|
|
|
373
314
|
: "UPDATE task_run SET error_code = $1 WHERE status = 'failed' AND error_code IS NULL AND (error = $2 OR error LIKE $3)", [RUN_HOLDER_DEATH_CODES.orphaned, RUN_ORPHANED_ERROR, `${RUN_ORPHANED_ERROR} (%`]));
|
|
374
315
|
const { migrated: allowRuleShadowRowsMigrated, unreadable: allowRuleShadowRowsUnreadable } = await migrateShadowedRuleKey(db, dialect);
|
|
375
316
|
const { key: workflowParkIndexKey, carried: workflowParkIndexRowsCarried } = await reshapeWorkflowParkIndex(db, dialect);
|
|
376
|
-
let retiredSnapshotTable = "absent";
|
|
377
|
-
let retiredSnapshotRows = 0;
|
|
378
|
-
let retiredSteerSeatColumn = "absent";
|
|
379
317
|
let undurableScopeRuleRows = [];
|
|
380
318
|
let undurableScopeRuleBucketsDeferred = 0;
|
|
381
319
|
let undurableScopeRuleBucketsUnreadable = [];
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (await tableExists(db, dialect, PERMISSION_RULE_TABLE)) {
|
|
385
|
-
({ removed: undurableScopeRuleRows, bucketsDeferred: undurableScopeRuleBucketsDeferred, bucketsUnreadable: undurableScopeRuleBucketsUnreadable } = await purgeUndurableScopeRows(db, dialect));
|
|
386
|
-
for (const r of undurableScopeRuleRows)
|
|
387
|
-
destroyed.push(`removed permission_rule ${r.kind} (bucket ${r.ownerKey}, ${r.behavior} ${r.tool}, scope root ${JSON.stringify(r.scopeRoot)})`);
|
|
388
|
-
}
|
|
389
|
-
const counted = !snapshotTablePresent
|
|
390
|
-
? undefined
|
|
391
|
-
: await db.rows(`SELECT COUNT(*) AS n FROM ${RETIRED_SNAPSHOT_TABLE}`).then((rows) => Number(rows[0]?.n ?? 0), async (e) => {
|
|
392
|
-
if (await tableExists(db, dialect, RETIRED_SNAPSHOT_TABLE))
|
|
393
|
-
throw e;
|
|
394
|
-
return undefined;
|
|
395
|
-
});
|
|
396
|
-
if (counted !== undefined) {
|
|
397
|
-
retiredSnapshotRows = counted;
|
|
398
|
-
await execUntil(db, `DROP TABLE IF EXISTS ${RETIRED_SNAPSHOT_TABLE}`, async () => !(await tableExists(db, dialect, RETIRED_SNAPSHOT_TABLE)));
|
|
399
|
-
retiredSnapshotTable = "dropped";
|
|
400
|
-
destroyed.push(`dropped table ${RETIRED_SNAPSHOT_TABLE} (${retiredSnapshotRows} row(s))`);
|
|
401
|
-
}
|
|
402
|
-
if (seatColumnPresent) {
|
|
403
|
-
await execUntil(db, `ALTER TABLE checkpoint DROP COLUMN ${RETIRED_STEER_SEAT_COLUMN}`, async () => !(await columnExists(db, dialect, "checkpoint", RETIRED_STEER_SEAT_COLUMN)));
|
|
404
|
-
retiredSteerSeatColumn = "dropped";
|
|
405
|
-
destroyed.push(`dropped column checkpoint.${RETIRED_STEER_SEAT_COLUMN}`);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
catch (e) {
|
|
409
|
-
if (destroyed.length === 0)
|
|
410
|
-
throw e;
|
|
411
|
-
throw new Error(`one-time migration failed AFTER completing destructive steps — record these before retrying, a retry cannot ` +
|
|
412
|
-
`report them again: ${destroyed.join("; ")}. Underlying failure: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
320
|
+
if (await tableExists(db, dialect, PERMISSION_RULE_TABLE)) {
|
|
321
|
+
({ removed: undurableScopeRuleRows, bucketsDeferred: undurableScopeRuleBucketsDeferred, bucketsUnreadable: undurableScopeRuleBucketsUnreadable } = await purgeUndurableScopeRows(db, dialect));
|
|
413
322
|
}
|
|
414
323
|
return {
|
|
415
|
-
|
|
324
|
+
auditDetailJsonNullRows,
|
|
416
325
|
sessionIncarnationColumn, sessionIncarnationsBackfilled, holderDeathCodeRowsBackfilled, allowRuleShadowRowsMigrated, allowRuleShadowRowsUnreadable,
|
|
417
326
|
workflowParkIndexKey, workflowParkIndexRowsCarried, undurableScopeRuleRows, undurableScopeRuleBucketsDeferred, undurableScopeRuleBucketsUnreadable,
|
|
418
327
|
};
|
|
@@ -599,32 +508,13 @@ async function backfillSessionIncarnations(db, dialect) {
|
|
|
599
508
|
}
|
|
600
509
|
return written;
|
|
601
510
|
}
|
|
602
|
-
export function migrateRetiredAskOrigin(type, data) {
|
|
603
|
-
if (type !== GATE_BEARING_LEDGER_EVENT_TYPE)
|
|
604
|
-
return undefined;
|
|
605
|
-
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
606
|
-
return undefined;
|
|
607
|
-
const d = data;
|
|
608
|
-
const gate = d.gate;
|
|
609
|
-
if (gate === null || typeof gate !== "object" || Array.isArray(gate))
|
|
610
|
-
return undefined;
|
|
611
|
-
const g = gate;
|
|
612
|
-
if (g.origin !== RETIRED_ASK_ORIGIN.from)
|
|
613
|
-
return undefined;
|
|
614
|
-
return { ...d, gate: { ...g, origin: RETIRED_ASK_ORIGIN.to } };
|
|
615
|
-
}
|
|
616
511
|
export function logOneTimeMigrations(log, backend, report) {
|
|
617
|
-
const changed = report.
|
|
512
|
+
const changed = report.auditDetailJsonNullRows > 0 ||
|
|
618
513
|
report.sessionIncarnationColumn === "added" || report.sessionIncarnationsBackfilled > 0 || report.holderDeathCodeRowsBackfilled > 0 || report.allowRuleShadowRowsMigrated > 0 || report.allowRuleShadowRowsUnreadable > 0 ||
|
|
619
514
|
report.workflowParkIndexKey === "reshaped" || report.undurableScopeRuleRows.length > 0 || report.undurableScopeRuleBucketsDeferred > 0 ||
|
|
620
515
|
report.undurableScopeRuleBucketsUnreadable.length > 0;
|
|
621
516
|
(changed ? log.warn : log.info).call(log, "one_time_migrations_applied", {
|
|
622
517
|
backend,
|
|
623
|
-
retiredSnapshotTable: report.retiredSnapshotTable,
|
|
624
|
-
retiredSnapshotRows: report.retiredSnapshotRows,
|
|
625
|
-
retiredSteerSeatColumn: report.retiredSteerSeatColumn,
|
|
626
|
-
retiredSteerSeatStaleRows: report.retiredSteerSeatStaleRows,
|
|
627
|
-
askOriginRowsMigrated: report.askOriginRowsMigrated,
|
|
628
518
|
auditDetailJsonNullRows: report.auditDetailJsonNullRows,
|
|
629
519
|
sessionIncarnationColumn: report.sessionIncarnationColumn,
|
|
630
520
|
sessionIncarnationsBackfilled: report.sessionIncarnationsBackfilled,
|
|
@@ -637,8 +527,7 @@ export function logOneTimeMigrations(log, backend, report) {
|
|
|
637
527
|
undurableScopeRuleBucketsDeferred: report.undurableScopeRuleBucketsDeferred,
|
|
638
528
|
undurableScopeRuleBucketsUnreadable: report.undurableScopeRuleBucketsUnreadable.length,
|
|
639
529
|
removableAfter: Object.fromEntries(ONE_TIME_MIGRATIONS.map((w) => [w.id, w.removableAfter])),
|
|
640
|
-
note: "one-time persisted-data migrations (
|
|
641
|
-
"steering column dropped; M3: retired ask-origin spelling rewritten in place; M4: audit details written as the JSON " +
|
|
530
|
+
note: "one-time persisted-data migrations (M4: audit details written as the JSON " +
|
|
642
531
|
"text `null` by an earlier version folded to a SQL NULL, so \"no detail\" has exactly one on-disk spelling; M5: the " +
|
|
643
532
|
"session incarnation column added and every session row without one minted its own token; M6: run rows failed by a " +
|
|
644
533
|
"holder-death verdict written by an earlier version with only the sentence got the machine-readable code run.stalled / " +
|
package/dist/plugins/pg-pool.js
CHANGED
|
@@ -45,6 +45,9 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
45
45
|
-- idem_key: the submission's scoped Idempotency-Key (idemKeyOf, 64 hex); its UNIQUE index uq_task_run_idem is
|
|
46
46
|
-- declared in schema-indexes.ts (PG has no inline KEY). NULL = no key (UNIQUE does not constrain NULL).
|
|
47
47
|
idem_key VARCHAR(64) COLLATE "C",
|
|
48
|
+
-- idem_body_fingerprint: the fingerprint of the request that bound idem_key (submissionFingerprintOf / A2A message
|
|
49
|
+
-- fingerprint, 64 hex); a hit with a different one is a 409. NULL = no key, or a key bound before the column existed.
|
|
50
|
+
idem_body_fingerprint VARCHAR(64) COLLATE "C",
|
|
48
51
|
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
49
52
|
updated_at TIMESTAMPTZ(3) NOT NULL,
|
|
50
53
|
PRIMARY KEY (task_id)
|
|
@@ -62,6 +65,13 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
62
65
|
task_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
63
66
|
since TIMESTAMPTZ(3) NOT NULL,
|
|
64
67
|
PRIMARY KEY (session_id)
|
|
68
|
+
)`,
|
|
69
|
+
`CREATE TABLE IF NOT EXISTS workspace_space_commit (
|
|
70
|
+
space_key VARCHAR(64) COLLATE "C" NOT NULL,
|
|
71
|
+
task_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
72
|
+
state VARCHAR(16) COLLATE "C" NOT NULL,
|
|
73
|
+
expires_at_ms BIGINT NOT NULL,
|
|
74
|
+
PRIMARY KEY (space_key)
|
|
65
75
|
)`,
|
|
66
76
|
`CREATE TABLE IF NOT EXISTS checkpoint (
|
|
67
77
|
token VARCHAR(120) COLLATE "C" NOT NULL,
|
|
@@ -83,7 +93,7 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
83
93
|
gate_kind VARCHAR(32) COLLATE "C",
|
|
84
94
|
bound_input_hash VARCHAR(190) COLLATE "C",
|
|
85
95
|
-- pending_steer_queue / pending_steer_rev:队列化后的 durable steering(core 5.14.0 #147)。两列的语义、
|
|
86
|
-
-- 退役的单座列 pending_steer 为何删列(
|
|
96
|
+
-- 退役的单座列 pending_steer 为何删列(以及存量库里仍带着它时的拒启臂)、48000 字节预算、
|
|
87
97
|
-- 以及「为什么另起一个整数 rev 列而不拿 JSON 旧值当 CAS 守卫」的完整由来见 MySQL 孪生
|
|
88
98
|
-- (tidb-pool.ts 同两列的行内注)。PG text 无长度上限,预算由 core 侧统一执法。
|
|
89
99
|
pending_steer_queue TEXT COLLATE "C",
|
|
@@ -84,6 +84,8 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
84
84
|
ownerOf(sessionId: string): Promise<string | null | undefined>;
|
|
85
85
|
/** S-790:属主 + 化身一次读(TiDB 孪生 {@link TiDBSessionStore.sessionFaceOf} 同义;空化身 ⇒ 响亮拒)。 */
|
|
86
86
|
sessionFaceOf(sessionId: string): Promise<SessionFace | undefined>;
|
|
87
|
+
/** core `SessionStore.incarnationOf`:{@link sessionFaceOf} 的化身那一半(TiDB 孪生同义)。 */
|
|
88
|
+
incarnationOf(sessionId: string): Promise<string | undefined>;
|
|
87
89
|
touch(sessionId: string): Promise<void>;
|
|
88
90
|
/** E18 — the session's current leaf SessionTreeEntry.id (session_meta.leaf_id). @see TiDBSessionStore.getLeafId
|
|
89
91
|
* (single SELECT, no acquire/lock, cache-bypassing — safe to read mid-run for the resume-at turn-capture). */
|
|
@@ -12,7 +12,7 @@ import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING
|
|
|
12
12
|
import { isPgUniqueViolation } from "./sql-errors.js";
|
|
13
13
|
import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSource } from "../instruction-source-notice.js";
|
|
14
14
|
import { MANAGED_RETENTION } from "./retention-store-sql.js";
|
|
15
|
-
import { SESSION_META_INCARNATION_COLUMN, carriedIncarnation, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
15
|
+
import { SESSION_META_INCARNATION_COLUMN, carriedIncarnation, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
16
16
|
import { ensureIndexes } from "./ensure-index.js";
|
|
17
17
|
const isDupKey = isPgUniqueViolation;
|
|
18
18
|
function baselineQuery(text, values) {
|
|
@@ -99,7 +99,7 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
static async wake(pool, sessionId) {
|
|
102
|
-
const metaRes = await pool.query(`SELECT created_at, leaf_id, leaf_seq, ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = $1`, [sessionId]);
|
|
102
|
+
const metaRes = await pool.query(`SELECT created_at, leaf_id, leaf_seq, incarnation, ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = $1`, [sessionId]);
|
|
103
103
|
const meta = metaRes.rows[0];
|
|
104
104
|
if (!meta)
|
|
105
105
|
return null;
|
|
@@ -107,7 +107,7 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
107
107
|
const eventRes = await pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq >= $2 ORDER BY seq ASC`, [sessionId, floorSeq]);
|
|
108
108
|
const entries = eventRes.rows.map((r) => decodeSessionEventPayload(r.payload, sessionId, r.seq));
|
|
109
109
|
const placement = placementFromRow(meta);
|
|
110
|
-
const metadata = { id: sessionId, createdAt: toIso(meta.created_at), ...(placement !== undefined ? { placement } : {}) };
|
|
110
|
+
const metadata = { id: sessionId, createdAt: toIso(meta.created_at), incarnation: incarnationFromCell(sessionId, meta.incarnation), ...(placement !== undefined ? { placement } : {}) };
|
|
111
111
|
return new PgSessionStorage(pool, sessionId, metadata, entries, meta.leaf_id ?? null, floorEntryId, floorSeq);
|
|
112
112
|
}
|
|
113
113
|
static async computeFloor(pool, sessionId) {
|
|
@@ -259,6 +259,9 @@ export class PgSessionStore {
|
|
|
259
259
|
return undefined;
|
|
260
260
|
return sessionFaceFromRow(sessionId, res.rows[0].owner ?? null, res.rows[0].incarnation);
|
|
261
261
|
}
|
|
262
|
+
async incarnationOf(sessionId) {
|
|
263
|
+
return (await this.sessionFaceOf(sessionId))?.incarnation;
|
|
264
|
+
}
|
|
262
265
|
async touch(sessionId) {
|
|
263
266
|
await this.pool.query("UPDATE session_meta SET updated_at = $1 WHERE session_id = $2", [
|
|
264
267
|
new Date(),
|
|
@@ -661,13 +664,14 @@ class PgStagingHandle {
|
|
|
661
664
|
const now = new Date();
|
|
662
665
|
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,$5,$6,$7) " +
|
|
663
666
|
"ON CONFLICT (session_id) DO UPDATE SET owner = EXCLUDED.owner, updated_at = EXCLUDED.updated_at, leaf_id = EXCLUDED.leaf_id, leaf_seq = EXCLUDED.leaf_seq", [this.realSessionId, owner, now, now, leafId, srcIds.length, mintSessionIncarnation()]);
|
|
664
|
-
const afterMeta = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = $1", [this.realSessionId]);
|
|
667
|
+
const afterMeta = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = $1", [this.realSessionId]);
|
|
665
668
|
if (afterMeta.rows[0]?.placement_kind != null) {
|
|
666
669
|
await conn.query("ROLLBACK");
|
|
667
670
|
throw new SessionError("placement_refused", `session ${this.realSessionId} belongs to the ${afterMeta.rows[0].placement_kind} placement partition — a session-sync staged commit cannot overwrite a delegation transcript`);
|
|
668
671
|
}
|
|
672
|
+
const face = sessionFaceFromRow(this.realSessionId, afterMeta.rows[0]?.owner ?? null, afterMeta.rows[0]?.incarnation);
|
|
669
673
|
await conn.query("COMMIT");
|
|
670
|
-
return { relation: rel.relation };
|
|
674
|
+
return { relation: rel.relation, face };
|
|
671
675
|
}
|
|
672
676
|
catch (err) {
|
|
673
677
|
try {
|
|
@@ -88,6 +88,9 @@ export declare class RemoteAdbExecutionEnv implements RemoteExecutionEnv {
|
|
|
88
88
|
private connectPromise?;
|
|
89
89
|
/** core `ExecutionEnv.homeDir`([ref]):本沙箱/目标机上执行用户的 home。见配置同名字段。 */
|
|
90
90
|
readonly homeDir?: string | undefined;
|
|
91
|
+
/** core 7.49.0 #1554:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在 Android 设备的 shell 里(设备自己的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
|
|
92
|
+
* 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
|
|
93
|
+
readonly cdPath: "unknown";
|
|
91
94
|
private readonly cfg;
|
|
92
95
|
private readonly spawnFn;
|
|
93
96
|
private readonly injectedConnectOnce?;
|