fix(release): 修复发布一致性与并发边界
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s

This commit is contained in:
2026-09-12 16:35:55 +08:00
parent 8d57a63697
commit 30d1cd77e8
20 changed files with 1100 additions and 102 deletions
+15 -3
View File
@@ -77,8 +77,11 @@
current -> versions/<id> # 已汉化后才切换;未汉化状态不发布
versions/<id>/ # 与官方相对路径一致的汉化资源
localized-patch-manifest.json # localized wrapper + generic PatchManifest 审计输入/结果
localized-distribution-manifest.json # 实际 localized bytes/hash 的轻量分发索引
.staging/<id>/ # generic/translation patch 未发布写侧
localized-version-state.json # localized current、官方 source release 和 workflow 状态
.localized-release.lock # 跨进程单写者锁
.localized-transaction.json # publish/rollback 崩溃恢复日志
```
官方资源发布和汉化发布是两个独立状态:
@@ -98,8 +101,16 @@ version state、current symlink、release manifest、文件系统和必要的 CA
`release.distribution` 的默认 channel 是 `official`。只有当前或显式历史、路径归属安全、
source relation 正确且 manifest/artifact integrity 通过的 release 才能被选择;staging、
损坏、缺失、symlink/path escape 或未验证历史项不会回退到另一 channel。返回的
`resource_root` 和 manifest entry 由 Rust 决定,Go 只做 typed forwarding。
损坏、缺失、symlink/path escape 或未验证历史项不会回退到另一 channel。Rust 使用已发布
manifest 做轻量选择,HTTP 热路径不重新执行完整 release auditlocalized 必须额外满足
`localized-distribution-manifest.json` 与 source official manifest 的 destination/URL
集合一致,并返回实际 localized bytes/hash。返回的 `resource_root` 和 manifest entry
由 Rust 决定,Go 只做 typed forwarding;传入 `destination` 时 Rust 会重新校验该文件的
实际 bytes/BLAKE3。
localized publish/rollback 先取得 `.localized-release.lock`,并在 output root 下记录
`.localized-transaction.json`。current、version-state 和 version 目录的切换按日志阶段
推进;下一次写操作会先恢复或完成未决事务,避免跨进程并发写入和中断后留下半发布状态。
`release.cleanup` 先生成 dry-run 计划和 `plan_id`,执行时重新计算并比对计划。current、
rollback previous、active/in-progress、localized source official、state/manifest/CAS
@@ -163,7 +174,8 @@ GET {public-base-url}/prod-clientpatch.bluearchiveyostar.com/<root_token>/...
默认仅服务 **official download manifest 索引内且 Present + size 匹配** 的文件。需要
localized 或历史 release 时,调用 `release.distribution` 选择 Rust 已验证的
`resource_root`,再由 `/v1/distribution` 或带 `channel`/`release_id` 的 CDN path
转发;Go 不在本地判断健康度,也不回退到 official。
转发;localized CDN 使用 Rust 返回的实际 bytes/hash 生成 ETag,并在显式请求时校验
实际文件长度;Go 不在本地判断健康度,也不回退到 official。
### 3.3 launcher 资源引导兼容
+5 -3
View File
@@ -178,13 +178,15 @@ SQLite `ResourceRepository`,索引不存在时返回 `ok=true` 且
|---|---|---|---|
| `release.status` | 已实现 | `null` | official/localized current、source relation、match、历史 release 和 manifest/artifact/distribution integrity 统一视图。 |
| `release.list` | 已实现 | `{ "channel": "official" }``{ "channel": "localized" }`,可省略 | 对应 namespace 的历史 release 摘要,包含 stable ID、created/published、current pointer、`rollback_available`、lifecycle、`stale`/`damaged`/`referenced`/`unknown`、legacy 和诊断。 |
| `release.distribution` | 已实现 | `{ "channel": "official", "release_id": "...", "offset": 0, "limit": 1000 }`,均可省略 | Rust 选择的 verified `resource_root` 和 download manifest entries;默认 channel 为 official,选择失败返回 `available=false`,不跨 channel fallback。 |
| `release.distribution` | 已实现 | `{ "channel": "official", "release_id": "...", "destination": "...", "offset": 0, "limit": 1000 }`,均可省略 | Rust 选择的 verified `resource_root` 和 download manifest entries`destination` 用于对单个实际文件重新校验 bytes/BLAKE3localized 使用发布时生成的实际字节 metadata,不复用 official size/hash默认 channel 为 official,选择失败返回 `available=false`,不跨 channel fallback。 |
| `release.cleanup` | 已实现 | dry-run `{ "execute": false }`;执行 `{ "execute": true, "plan_id": "..." }` | cleanup plan、candidate/retain reasons、blocking references 和 removed paths;执行前会重新生成并比对 `plan_id`。 |
`release.status``release.list``release.distribution` 只读现有 official/localized
state、current、manifest、文件系统和 CAS/reference 元数据,不创建第二套 release 状态。
localized 只有在 source official、current pointer、manifest identity、source/target
hash/size 以及 UnityFS/ZIP 最终语义校验全部通过时才可分发;默认官方分发行为不变。
分发选择使用发布后的轻量 manifest 和文件 size/单文件 BLAKE3 校验,不在 HTTP 热路径
重新执行完整 release auditlocalized 还要求 distribution manifest 的 destination/URL
集合与 source official manifest 一致,并使用发布时记录的实际 localized bytes/hash。
默认官方分发行为不变。
`release.cleanup` 只删除 Rust 能证明是普通目录且未被 current、rollback、staging、
source、state、manifest、CAS 或未知 ownership 引用的历史项,不修改 current,也不承担
rollback 或 repair。
+9
View File
@@ -125,6 +125,15 @@ artifact integrity,损坏产物返回 degraded/corrupt,不自动回滚或删
rollback 与 cleanup 保持独立;缺少 generic manifest 的旧 localized release 仍可读,
明确标记 `legacy`/`unknown`,不会被自动重写。
本轮 P1 一致性修复已完成:CAS repository 的 store/get/reference/GC 使用跨进程操作锁,
release-local CAS 引用通过 durable `(release, ordinal)` ownership ledger 幂等释放;官方
历史复用只对不可变文件使用 hard link,`translation-tasks.sqlite` 及 WAL/SHM 始终独立
复制;localized output 使用单写者锁和事务日志恢复 publish/rollback,并在发布时写入
实际 localized bytes/BLAKE3 的 distribution manifest。分发读取只使用轻量发布 metadata,
保留 path ownership、symlink 和文件完整性检查。P2 尚未由本轮处理:ResourceRepository
更完整的查询/权限/损坏恢复、模糊 TM、bat.sock peer credential/perms、FFI 生命周期、
资源大小/限额与更强的持久化 fsync 语义仍按后续专项推进。
### G-012Translation Memory V1 已实现,扩展能力仍缺失
Rust `bat` 已提供独立项目级 SQLite TM,记录 raw source/hash、完整 context、release/TextUnit/provider/run provenance,区分 candidate/trusted,只有显式 confirm 才能建立 trusted 记录;worker 只自动复用 trusted 的 raw source + 完整 context exact match,并在复用前执行已批准 Glossary 的确定性 QA。Go `bat-api` 已提供鉴权的 summary/query 只读接口和 confirm 转发,但 Go 不持有 TM 状态。仍缺少模糊匹配和更丰富的导入导出历史能力。