Commit Graph
72 Commits
Author SHA1 Message Date
nyaKazuhaandClaude Fable 5 ec40ed4660 docs: 关闭 G-017(决策不引入托管 CI)并同步 CURRENT_GAPS
- G-017 按决策关闭:不加入 GitHub Workflows,质量门禁由本地
  fmt/clippy/test 与 Makefile/scripts 可重复脚本承担
- G-008 补记对接边界现状:issue #1 RPC Backend API 已就绪,
  实现 Go CLI 时应重写 cmd/bat 现有 cgo 骨架
- 关闭顺序移除已关闭的 G-018,更新时间与结语同步
- PROJECT_PLAN 里程碑 11 的 CI 交付物改为本地可重复验证,
  与 G-017 决策一致

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:44:01 -07:00
nyaKazuhaandClaude Fable 5 3e24e51f5d chore: 忽略本地会话记录 BAT.txt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:37:53 -07:00
nyaKazuhaandClaude Fable 5 d22f9258ad docs: 补 RPC Backend API 与 Go/FFI 边界章节,同步 issue #1 进展
- 架构文档新增 §7「RPC Backend API 与 Go 调用边界」:协议契约
  (envelope、ApiError、任务模型、方法命名空间与实现状态,细节指向
  USERGUIDE §6)、Go 层职责边界(HTTP/鉴权/分发 + RPC client,不嵌
  FFI、不直接读 daemon 内部状态)、FFI 降级说明(bat-ffi 保留为可选
  历史兼容边界,新能力一律先落 RPC)。对应 issue #1 文档要求第 8 项。
- CURRENT_STATUS:issue #1 条目由"待实现"改为"主体已实现 + 剩余清单",
  下一步清单同步。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 10:27:44 -07:00
nyaKazuhaandClaude Fable 5 eac1edd455 feat(official-sync): 配置校验与 server-info/marker 拉取接入错误码
catalog.refresh 真机 e2e 暴露的缺口:任务配置校验失败落 900001,而
输入域码表(100xxx)早已定义却无使用点。本次接入:

- 配置校验四处类型化(经 anyhow::Error::new 保留 DownloadError,
  task worker 现有 downcast 直接归类):
  缺少应用版本 → MISSING_APP_VERSION(100001)、
  缺少连接组 → MISSING_CONNECTION_GROUP(100002)、
  缺少服务器信息来源 → MISSING_SERVER_INFO_SOURCE(100003)、
  GameMainConfig 缺 ServerInfoDataUrl → LAUNCHER_RESPONSE_INVALID
  (600004,launcher 链内容缺陷)。
- fetch_bytes 改返回 DownloadError:非官方 URL → NON_OFFICIAL_URL、
  curl 失败保留准确网络域码。server-info / endpoint marker / 种子
  catalog 拉取(5 处调用)由 anyhow::Error::msg 改 Error::new 保留
  类型——发现阶段的网络失败此前全部落 internal,现携带 3xx 网络码。

测试:3 个配置校验错误码 downcast 断言 + fetch_bytes 双拒绝映射
(非官方 URL / 404)。真机 e2e:无 auto-discover 的 daemon 上
catalog.refresh → task.status 报 BAT-ERR-100001 missing_app_version
(domain=input),修复前为 900001。全量 fmt / clippy --workspace
--all-targets -D warnings / test --workspace 全绿。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 10:20:08 -07:00
nyaKazuhaandClaude Fable 5 11b19683cf feat(daemon): 实现 catalog.* 命名空间与 resource.manifest(issue #1)
补齐 issue #1 方法分层中可立即实现的剩余命名空间:

- catalog.status:当前已发布版本的 catalog 概览(读取其 snapshot:
  app/bundle 版本、connection group、addressables 根、端点与 marker
  计数、launcher 元数据、GameMainConfig 摘要)。
- catalog.versions:版本历史(current / in_progress / previous /
  failed,来自 official-version-state.json)。
- catalog.diff:当前 snapshot 相对上一个可用版本的差异(复用
  YostarJpSyncSnapshot::diff 与 diff_extended_snapshot;含
  changed_endpoint_urls 与 previous_snapshot_missing 标志;无上一版本
  时按首次观察处理 is_initial=true)。
- catalog.refresh:经任务执行器的 catalog 更新检查任务(dry-run +
  plan,不下载不审计;支持 force),返回 task_id 可轮询。
- resource.manifest:当前版本下载 manifest 的分页只读查询(offset
  默认 0、limit 默认 100/上限 1000,BTreeMap 按 URL 有序保证分页稳定;
  非法 limit → 700002)。infrastructure 公开
  OfficialDownloadManifest(Entry) 与 read_download_manifest_at。

约定:只读查询在尚无已发布版本或文件缺失时返回 ok=true 且
data.available=false(正常状态而非错误,便于 Go 层直接分支)。
is_pending_rpc_method 缩减为 task.create / resource.repair /
patch.* / unityfs.*(后两者待引擎;repair 待引擎独立修复模式)。
新增 read_daemon_resource_state 供 resource/catalog 只读查询共用。

测试:新增 10 个 dispatch 单测(含 fixture:状态文件 + 版本状态 +
snapshot + manifest)与 read_download_manifest_at 单测;调整 pending
断言。真实 daemon 端到端验证(fixture 资源根 + 死代理快速失败):
raw socket 依次断言 catalog.status/versions/diff、resource.manifest
分页与参数错误、catalog.refresh 入队与 task.status 轮询、
resource.repair→700003、未知方法→700001,全部通过,daemon.stop 干净
退出。全量 fmt / clippy --workspace --all-targets -D warnings /
test --workspace 全绿。

已知后续(另行推进):任务配置校验失败(如缺少 app-version)尚未接入
输入域错误码(应为 100001,现落 900001)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 10:10:03 -07:00
nyaKazuhaandClaude Fable 5 e9b7ce219f fix(official-sync): 修正对抗性审查发现的 launcher 链错误码误归类
d2466d6 做三视角对抗性核查(错误码语义/消费者破坏面/测试与文档),
确认 8 项(去重后 5 类)误归类,全部集中在 official_game_main_config
的 From<String> 兜底面。逐项修正:

- launcher_package_url 改返回 DownloadError:非官方 CDN 根(来自远端
  API 响应,安全边界拒绝)→ NON_OFFICIAL_URL;非法包路径(来自远端
  manifest 内容)→ LAUNCHER_RESPONSE_INVALID。此前提交说明称其为
  "硬编码常量上的内部不变量"不成立——三个生产调用点传入的都是远端
  API 下发的 cdn_root。
- select_game_main_config_source / find_resources_assets:目录 source
  缺 resources.assets 条目、无可用游戏包路径、包内容缺必需文件均为
  远端可触发的内容缺陷 → LAUNCHER_RESPONSE_INVALID(此前落 INTERNAL,
  且官方 manifest 布局已演进过一次,是现实的主要失败面)。
- extract_archive 解压失败 → ZIP_STRUCTURE_INVALID:结构校验不覆盖
  压缩数据流,数据区损坏(bad CRC)在 unzip 阶段首次暴露,主导成因
  是损坏/截断下载,归完整性域;本地原因保留在 stderr 消息中。
- verify_manifest_file_size 三种失败分码:size 字段无效 →
  LAUNCHER_RESPONSE_INVALID、本地读文件失败 → INTERNAL、真正不符 →
  SIZE_MISMATCH(此前三者统归 SIZE_MISMATCH)。
- 空 --launcher-version(用户 CLI 输入)→ INVALID_ARGUMENT,在发起
  任何请求前拒绝(此前落 INTERNAL)。
- 统一 launcher 链内容口径:API 响应、远端 manifest、包内容的解析/
  缺失问题全部归 LAUNCHER_RESPONSE_INVALID(600004),
  MANIFEST_PARSE_FAILED(600001) 保留给资源侧 manifest/catalog;
  fetch_remote_manifest 两处 600001 改 600004,error_code.rs 注释与
  USERGUIDE 描述同步。
- download_file_with_fallback:备用 URL 构造失败不再经 `?` 丢弃主地址
  失败上下文,错误码与"两次都失败以最终一次为准"策略一致。

新增 7 个错误码断言测试(launcher_package_url 两类拒绝、空版本、
manifest 无条目、fallback 终码保留、fallback 构造失败保留主上下文、
select/verify/extract 各失败面)。全量 fmt / clippy --workspace
--all-targets -D warnings / test --workspace 全绿。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:42:17 -07:00
nyaKazuhaandClaude Fable 5 d2466d60db feat(official-sync): 错误码模型铺开到 launcher/metadata 拉取路径
此前统一错误码只覆盖资源下载路径,launcher/metadata 链
(fetch_game_config → manifest URL → remote manifest → resources.assets
→ GameMainConfig)整条返回 Result<_, String>,经 anyhow::Error::msg
抹成无类型消息,RPC/任务层只能标为 internal。

本次将该链整体改为 Result<_, DownloadError>:

- core 新增两个错误码:LAUNCHER_API_REJECTED(300031,官方启动器 API
  返回非 200 业务码)与 LAUNCHER_RESPONSE_INVALID(600004,响应无法
  解析或缺少必需字段),并纳入唯一性测试;
- official_launcher:curl 失败保留准确网络域码(403/404/DNS/超时/TLS/
  代理等,final_error_code()),API 非 200 → LAUNCHER_API_REJECTED,
  响应解析/缺字段 → LAUNCHER_RESPONSE_INVALID,非官方 manifest URL →
  NON_OFFICIAL_URL,manifest 无文件条目 → MANIFEST_PARSE_FAILED,
  空版本/路径入参 → INVALID_ARGUMENT;
- official_game_main_config:下载失败带网络域码(主/备 CDN 均失败时
  以最终一次为准),ZIP 结构 → ZIP_STRUCTURE_INVALID,manifest 大小
  不符 → SIZE_MISMATCH,GameMainConfig 解密/解析 →
  GAME_MAIN_CONFIG_FAILED,manifest 缺 source →
  LAUNCHER_RESPONSE_INVALID;
- official_update 两处调用改 anyhow::Error::msg → anyhow::Error::new,
  保留类型,task worker 现有 downcast 直接接住 launcher 路径错误码;
- USERGUIDE 错误码表补录 300031 与 600004。

内部工具函数(launcher_package_url / launcher_api_url /
launcher_authorization_header)仍返回 String,经 From<String> 归
INTERNAL——这些是硬编码常量上的内部不变量校验,非运行时主要失败面。

验证:新增 4 个单测断言映射(API 403 业务码 → 300031、非法 JSON →
600004、HTTP 404 → 300002、非官方 URL → 300030);全量
fmt / clippy --workspace --all-targets -D warnings / test --workspace
全绿(含 examples 编译)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 08:29:16 -07:00
nyaKazuhaandClaude Opus 4.8 d4d6d0a686 fix(curl): 将 ETXTBSY 归为可重试的瞬时启动失败
启动 curl 子进程时命中的 ETXTBSY(可执行文件忙)是多线程程序里
fork/exec 与"刚写出的可执行文件"之间的瞬时竞态:另一线程 fork 出的
子进程在 fork 与 execve 之间短暂持有目标文件的可写句柄,导致本次
exec 被内核判为忙。此前它被并入 ProcessFailed 且 retryable=false,
per-URL 层直接放弃。

新增 CurlFailureKind::ProcessBusy(retryable=true),在 process_failed
中按 io::ErrorKind::ExecutableFileBusy 精确识别,并在 busy 重试前做
极短退避让兄弟进程完成 execve。生产中 curl 是固定系统二进制、从不
被并发改写,故此变更对真实下载无影响,仅消除集成测试在高并发下偶发的
"Text file busy" flake(fetch_bootstrap unwrap 失败)。

验证:以 8-10 并发 worker × 8 线程 / 4 核(≈64-80 线程超载)复现,
修复前首轮即挂;修复后 200+ 次超压运行零 ETXTBSY、零失败。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 07:30:20 -07:00
nyaKazuhaandClaude Fable 5 ab11cc899d feat(official-sync): 下载失败携带类型化错误码
用类型化错误(不做 message 匹配)把资源下载失败归到准确的 BAT-ERR 码:

- curl_transfer:CurlFailureKind::error_code() 映射到网络域码(403→300001、
  404→300002、5xx→300005、dns/connect/timeout/tls/interrupted→30001x、
  proxy→310001、其余→300015);CurlRetryError::final_error_code() 取末次失败的码。
- official_download:新增 pub DownloadError { code, message },From<String>→internal,
  Display/Error;pull_* 的返回类型由 String 改为 DownloadError,内部 String 错误经
  From 归 internal,下载失败边界带 pull_one 的 error_code(),非官方 URL 带
  non_official_url(300030)。
- official_update:pull 错误改用 anyhow::Error::new 保留 DownloadError 类型。
- 任务 worker:downcast DownloadError 取 code 写入任务记录的 error(失败不再一律
  900001),非下载失败仍归 internal。

验证:新增 kind→code 映射单测;pull 404 测试断言 DownloadError.code()==HTTP_NOT_FOUND;
lib/core 全量测试通过,clippy --all-targets 干净。

范围说明:本次覆盖资源下载路径;launcher/metadata 拉取(official_launcher)的网络
失败仍归 internal,可按同一模式后续接入。

对应 issue #1(错误码细分映射,第一批)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:01:31 -07:00
nyaKazuhaandClaude Fable 5 9a5fef23af feat(daemon): 补全 task.cancel 与 task.logs
完成 task.* 命名空间:

- task.cancel:每个任务带共享的取消标志(Arc<AtomicBool>),worker 的 should_cancel
  同时检查它和 daemon 停止标志;请求取消后置标志,worker 在下一个同步检查点中止,
  任务转为 cancelled(协作式,不硬杀正在执行的 curl)。已结束任务返回 already-finished,
  不存在返回 700004。
- task.logs:worker 把每条 progress 追加到任务的有界日志(上限 200 行),task.logs
  返回该历史;不存在返回 700004。TaskRecord 的 cancel/log 字段 serde(skip),不进
  task.status 输出。
- 新增 cancelled 任务状态;prune 与已结束判定纳入 cancelled。
- USERGUIDE RPC 方法表更新(cancel/logs 标为已实现)。

验证:新增取消/日志注册表单测(63 bin 测试全过);真机端到端确认触发 resource.sync
后 task.cancel 使任务转为 cancelled、task.logs 返回进度历史。

对应 issue #1(task.* 命名空间补全)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 06:00:26 -07:00
nyaKazuhaandClaude Fable 5 d7257db447 docs(userguide): 增加 Daemon RPC 接口章节
记录 bat.sock JSON-RPC 2.0 控制面:统一 envelope(ok/status/data/error/request_id)、
方法命名空间与实现状态(daemon.*/resource.*/task.* 已实现,catalog/patch/unityfs
及 task.cancel/logs 返回 700003)、异步任务模型(resource.sync/verify 返回 task_id、
task.status/list 轮询)和调用示例。

对应 issue #1(协议文档)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 05:31:57 -07:00
nyaKazuhaandClaude Fable 5 01ea5d8fa5 feat(daemon): 任务执行器 + 异步 resource.sync/verify(阶段 1b)
在 daemon 里加入任务模型,让 resource.sync/verify 经 RPC 触发即返回 task_id、
可轮询进度与最终结果:

- 专用任务 worker 线程 + FIFO 队列(mpsc);TaskRegistry 句柄包住内存态注册表
  (HashMap,有界保留 64 条已结束任务,运行中/排队中不裁剪),方法访问便于将来
  换 Redis 等持久化后端。
- 进程内 sync 锁:watch 循环与 worker 在跑同步前都获取它,互相等待而非撞
  .official-sync.lock 文件锁失败;只在实际跑同步的那段持锁,空闲睡眠不持锁。
- resource.sync/resource.verify 入队返回 { task_id, kind }(envelope status=accepted);
  worker 复用 run_with_progress_and_cancellation(sync 用 daemon config + force;
  verify 用 dry-run+plan+audit),进度写入任务记录,should_cancel 接 daemon 停止标志
  (停机中止在途任务)。task.status/task.list 从注册表读真实数据。

验证:新增任务分发/入队/注册表单测(62 bin 测试全过);真机端到端确认经裸 socket
触发 resource.sync 返回 task_id、轮询 task.status 走到最终态(fake curl 失败→
failed 且记录 error)、task.list 可见、resource.verify 亦入队。满足 issue #1 验收
「至少一个同步任务可经 RPC 触发并经 task.status 查询进度和最终结果」。

任务仅存内存(随 daemon 生死);task.cancel/logs、错误码细分映射留待后续。

对应 issue #1(任务模型 + 最小方法集打通)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 05:23:24 -07:00
nyaKazuhaandClaude Fable 5 b95a3ac417 feat(daemon): RPC 统一 envelope、命名空间分发与只读方法
阶段 1a 的协议地基(承接错误码模型):

- 统一 envelope:所有响应装入 JSON-RPC 2.0 result 的
  { ok, status, data, error, request_id };error 用 bat_core::ApiError
  (含 BAT-ERR 码/kind/domain/location/retryable)。传输层解析失败仍走
  JSON-RPC 顶层 error。request_id 为 req-<pid>-<seq>。
- 命名空间分发:方法改用国际惯例 <namespace>.<action>;daemon.* 为规范名,
  bat.* 保留为兼容别名(canonical_rpc_method 解析)。未知方法 → 700001,
  已规划未实现(resource.sync/verify、catalog.*/patch.*/unityfs.* 等)→ 700003。
- 只读方法:daemon.status/logs/stop/reload/refresh(迁移)、resource.state(新增,
  返回资源发布根 + 版本状态 + 上次同步结果)、task.status/task.list(任务库暂空,
  status → 700004,list → 空列表;执行器留待阶段 1b)。
- 客户端 daemon_rpc_call 解包 envelope(检查 ok、返回 data、把 error 转 Err),
  CLI 调用方零改动。

验证:新增分发/别名/envelope 单测(60 bin 测试全过);真机起停 daemon 端到端确认
daemon.status/logs/resource.state/task.list 经 CLI 与裸 socket 均正确返回 envelope,
bat.* 别名生效,未知/未实现返回对应 BAT-ERR 码。

对应 issue #1(协议基础设施 + 最小只读方法集)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 04:19:22 -07:00
nyaKazuhaandClaude Fable 5 1a25ea58dd feat(core): 引入统一错误码模型并新增 USERGUIDE
为 issue #1 的公共错误契约建立错误码模型:

- core/src/error_code.rs:ErrorCode(BAT-ERR-<域3位><序号3位>,如 BAT-ERR-300012)
  + 44 个初始码表(覆盖输入/路径安全/网络下载/校验/发布存储/解析/任务RPC/内部
  八域,网络域含代理故障 310001)、ErrorDomain、以及进入 RPC envelope / --json /
  bat-events.jsonl 的统一 ApiError { code, kind, domain, location, message, retryable }。
  location 用稳定的组件·操作标签(不随行号漂移)。
- 新增 USERGUIDE.md:bat 命令、选项(发现/同步/守护/输出)、退出码(含 75=locked)、
  运行时默认值,以及从码表同步的错误码参考(含承载结构与域一览)。

命名遵循国际惯例(英文 kind/domain slug)。码表以 error_code.rs 为准,USERGUIDE
错误码表与之逐条一致。后续各链路报错接入该码表在 issue #1 下推进。

对应 issue #1(错误码模型 + 用户指南)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:57:17 -07:00
nyaKazuhaandClaude Fable 5 17b86ab9fa docs: 修正文档漂移,与当前实现和 G-018 状态对齐
- PROJECT_PLAN:"真实官方网络 smoke 尚未记录/未完成记录" 与 G-018 已关闭矛盾,
  改为"命令已固化(G-018 已关闭),真实运行处于长期运行测试阶段、报告待后续提供"。
- PROJECT_PLAN §6 标题"近期 10 个具体任务"实列 8 条,去掉误导性数字。
- official-resource-test-pull.md 重复的 "## 8" 编号(代码入口/验证)修正为 8/9。
- CURRENT_GAPS G-008 "cmd/bat 无 main.go" 已过时(main.go 存在但为 cgo 最小
  骨架、非产品入口),更正现象描述。

对应 issue #18 维护清单 4-1、4-2、4-3、4-4。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:04:09 -07:00
nyaKazuhaandClaude Fable 5 232d74b617 fix(daemon): 串行化 bat-status.json 的读-改-写
watch 循环线程(update_daemon_status/update_daemon_progress)与 RPC 处理线程
(update_daemon_state_only)都会对状态文件做“读→改字段→写回”。单次写虽原子,
但整段读-改-写非原子,无互斥时并发线程会 last-writer-wins 丢失对方的字段更新。

新增进程级 DAEMON_STATUS_FILE_LOCK,三处读-改-写函数入口统一获取该锁串行化
(锁中毒时取 into_inner 继续,不 panic)。

对应 issue #18 维护清单 3-4。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 09:38:46 -07:00
nyaKazuhaandClaude Fable 5 0656ad05c7 refactor(official-sync): CLI 工程卫生清理
- doctor daemon_status 检查(3-3):ok 与 message 原分别取自解析结果和文件存在性,
  文件存在但解析失败时会“ok=false 却提示可解析”。改为从同一次解析结果派生两者。
- localize_error_message(3-1a):空壳直通占位函数,删除并在 4 处调用点直接用消息。
- daemon 路径 helper 形参(3-1b):output_root 实际语义是 state_dir,统一改名消除误导。
- stop_daemon 非 RPC 路径(3-1c):删除无意义的 let-else(_pid 读后丢弃、两分支
  逐字重复 stop_daemon_inner+print_report),stop_daemon_inner 内已做 PID 校验。
- parse_http_status(3-1d):改为锚定 curl stderr 中 "error" 之后再取三位状态码,
  避免误取端口/字节数等无关三位数字;新增锚定测试。
- 非 Unix process_exists(3-1e):补注释说明保守返回 true 的理由(无法探测存活,
  避免误回收可能仍在运行的 daemon)。

对应 issue #18 维护清单 3-1、3-3。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 09:35:31 -07:00
nyaKazuhaandClaude Fable 5 28e9ce5a6f refactor(official-sync): 删除误导性的 skipped_this_run 字段并修正措辞
quarantine 条目的 skipped_this_run 字段恒写 true、仅测试读取、不参与任何逻辑,
且名称误导——下载失败是 fail-fast 中止整轮,并非“跳过该 URL 继续”。删除该字段,
并把失败消息中的“跳过本轮”改为“中止本轮同步、不发布不完整资源”以消除歧义。
(quarantine 为瞬时状态文件且未启用 deny_unknown_fields,旧文件多出的键会被忽略。)

对应 issue #18 维护清单 3-2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 09:21:16 -07:00
nyaKazuhaandClaude Fable 5 11f4c6c36e test(adapters): 真实 resources.assets 测试改用环境变量注入
reads_text_asset_from_real_resource_file 原硬编码 /home/wanye 绝对路径,文件
不存在时静默 return——在其它机器/CI 上是永远跳过、从不断言的死测试。改为
#[ignore] + BAT_REAL_RESOURCES_ASSETS 环境变量注入(与仓库既有 real_* 测试一致),
并把机器特定的精确字节 golden 断言放宽为结构性断言(name 匹配、bytes 非空),
便于任意真实 resources.assets 显式运行。

对应 issue #18 维护清单 3-5。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 09:16:07 -07:00
nyaKazuhaandClaude Fable 5 fd86eafebd chore(deps): 移除占位 crate 中零引用的依赖
bat-patch 的 bsdiff/json-patch、bat-assetbundle 的 byteorder/lz4/lzma-rs 在
源码中完全未使用,占位阶段白增编译负担。移除并留注释说明待对应引擎/解析器真正
实现时再按需引入。

对应 issue #18 维护清单 3-6。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 09:12:40 -07:00
nyaKazuhaandClaude Fable 5 48ffc08cdf fix(cas): exists 返回 Result 以区分不存在与存储故障
CasRepository::exists(core trait)与底层 Storage::exists 原返回裸 bool,
FileSystemStorage 用 try_exists(...).unwrap_or(false)、infra 适配层吞掉解析/
初始化错误返回 false,调用方无法区分“对象不存在”和权限/IO/初始化故障。

将整条链改为 Result<bool>:
- storage trait 与 FileSystemStorage:try_exists 的 IO 错误经 ? 传播;
- repository FileSystemCasRepository::exists 及 store/add_reference 内部调用;
- core CasRepository::exists trait;
- infra 适配层:解析失败→InvalidArgument、初始化/引擎错误经 map_error 传播。
Ok(false) 仅表示确实不存在。

新增单测:合法但不存在的对象返回 Ok(false),非法 ObjectId 返回 Err(而非静默
false);同步更新各层 exists 断言。

对应 issue #18 维护清单 2-1。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:53:54 -07:00
nyaKazuhaandClaude Fable 5 c362fd80f4 fix(import): 导入失败回滚,find_bundle 拒绝同名歧义
- 非事务导入:import_manifest_bundles 中途某条目失败(如 AssetBundle 解析失败)
  时,之前已写入的 CAS 对象和 ResourceRepository 行不回滚,留下部分导入状态。
  改为记录本次写入的 object_id 与资源 id,任一条目失败时 best-effort 回滚
  (删除资源行、对 store 增加的引用逐一 remove_reference)。
- find_bundle 跨目录同名:文件名回退无目录约束,jp/data.bundle 与 en/data.bundle
  同名时可能静默取错数据。改为返回 Result:精确路径优先,文件名回退仅在唯一匹配
  时采用,多个同名 bundle 时显式报错而非猜测。

新增单测:后续条目失败时回滚先前成功条目(资源行清空、CAS 引用回滚为 0)、
同名 bundle 歧义显式报错。

对应 issue #18 维护清单 2-2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:29:27 -07:00
nyaKazuhaandClaude Fable 5 b570f37359 fix(official-sync): 区分代理故障并前置校验代理配置
- 代理故障分类:curl 因代理 407 认证失败(CONNECT 隧道失败)、无法解析/连接
  代理等,原被归为普通可重试网络错误,永不成功也会耗尽 per-URL 重试且无诊断。
  新增 CurlFailureKind::Proxy,从 curl stderr 识别代理故障,per-URL 层 fail-fast
  不重试(daemon error-retry 循环仍整体重试以覆盖代理短暂抖动),并在失败摘要里
  以 kind=proxy 明确标注。
- 前置校验:parse_proxy_config 新增 validate_proxy_url,拒绝拼错的代理 scheme
  (如 htp://),只接受 http/https/socks4/socks4a/socks5/socks5h 或无 scheme
  的 host:port。
- doctor:proxy 检查不再恒 ok,对解析出的代理 URL(含 auto 从环境变量取得的)
  做 scheme 校验,前置暴露配置错误。

新增单测:代理 407/解析失败归为 Proxy 且不重试、源站连接失败仍为 Connect 可重试、
拒绝不支持的 scheme、无 scheme host:port 接受。

对应 issue #18 维护清单 2-5。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:18:37 -07:00
nyaKazuhaandClaude Fable 5 f0fbe65429 fix(official-sync): 复用 staging 异常时退化为全量重下而非中止
recoverable_staging_path 对「staging 存在但不可用」(被外部替换成文件/symlink、
路径组件含 symlink、id 不匹配、路径逃逸等)一律返回 Err,经调用方 ? 中止整轮
同步。失败记录残留时每轮都会撞到同一异常而永久失败、无法自愈。

改为返回 Option<PathBuf>:任何无法安全复用的情况返回 None,走全量重下(始终安全
的 fail-closed 回退,复用文件仍逐一校验,不会发布损坏内容)。新增单测:staging
被替换成普通文件时不复用、返回 None 而非 Err。

对应 issue #18 维护清单 2-6。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:08:12 -07:00
nyaKazuhaandClaude Fable 5 42d69dd4bd fix(official-sync): 拒绝携带 query/fragment 的资源 URL
destination_for_url 原来对每个路径片段静默剥除 query/fragment,导致仅 query
不同的两个官方 URL 映射到同一目标文件而相互覆盖,并使每轮 hash 复用校验失配、
反复重下。官方资源 URL 本不携带 query(发现 URL 由固定拼接生成),改为直接拒绝,
消除该碰撞路径。新增单测。

对应 issue #18 维护清单 2-3。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:03:40 -07:00
nyaKazuhaandClaude Fable 5 02d5bc537e fix(patch): 占位函数返回未实现错误而非空结果
apply_patch / apply_json_patch 原返回 Ok(空 Vec/空 String),调用方会把未实现
的占位误当成一次成功的补丁应用。改为返回 PatchError::ApplyFailed,让占位状态
显式可见。新增单测确认返回错误。

对应 issue #18 维护清单 2-4。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:00:31 -07:00
nyaKazuhaandClaude Fable 5 962261ba12 fix(ffi): 统一 cgo 链接与 release 构建 profile
internal/ffi/ffi.go 的 cgo LDFLAGS 硬编码 -L target/debug,而 build-rust 用
--release(产物落 target/release),干净环境下 make build/test-go/ci 必然
报 cannot find -lbat_ffi。

- LDFLAGS 指向 target/release,并加 -Wl,-rpath 让产物运行时无需 LD_LIBRARY_PATH
  即可定位 libbat_ffi.so;删除指向不存在目录的无效 CFLAGS -I(C 声明本就内联)。
- 新增 build-ffi target(cargo build --release -p bat-ffi),build-go 与 test-go
  依赖它,确保链接前 release FFI 库已存在。

验证:make build-go 成功产出并可运行 bin/bat;make test-go 链接并通过;
go vet(check-go)通过。

对应 issue #18 维护清单 1-6。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:46:37 -07:00
nyaKazuhaandClaude Fable 5 1c4debbaa7 fix(cas): 消除 store/gc 之间的引用计数竞态
store() 原来先 ensure_object(建行 ref_count=0)再 add_reference,两条语句
非事务;即使连接池 max_connections=1,两语句间的 await 也会释放连接,让并发
gc() 在 ref_count=0 窗口删掉刚存的对象文件与元数据(静默丢数据),或使 store
返回 ObjectNotFound。

- 新增 store_reference:单条 UPSERT 原子建行为 ref_count=1 或 +1,对象行不再
  出现 ref_count=0 的可见窗口;store() 与 repository add_reference() 均改用它。
- 新增 delete_zero_ref_metadata:gc 先原子执行 DELETE ... WHERE ref_count=0,
  仅当 rows_affected>0 才删对象文件;被并发递增抢先时跳过,绝不删除仍被引用对象。
  元数据先删、文件后删,最坏只留无元数据的孤儿文件(可覆盖,无数据丢失)。

新增单测:store 后不暴露 ref_count=0(gc_candidates 为空)、候选被重新引用后
gc 跳过、store_reference 原子建行/递增、delete_zero_ref_metadata 守卫。

对应 issue #18 维护清单 1-5。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:39:07 -07:00
nyaKazuhaandClaude Fable 5 3cb3637749 fix(official-sync): 清理未被引用的孤儿 staging 目录
同版本失败 staging 复用已由 060b7d7 处理,但跨版本失败、或被 trim/去重挤出
failed_versions 记录后残留的 .staging/<id> 目录永不被复用也无人清理,长期 daemon
场景下多 GB 孤儿目录会累积耗尽磁盘。

新增 gc_orphan_staging:枚举 .staging 下子目录,只保留 in_progress_version 与
failed_versions 引用的 <id>,删除其余(跳过普通文件与 symlink,staging 根不存在
时无操作)。在同步成功发布后(读取最新版本状态)和 clean-stable(后台已停止)两处
调用;GC 失败仅告警,不影响发布结果。

对应 issue #18 维护清单 1-4。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:29:37 -07:00
nyaKazuhaandClaude Fable 5 4d818f512c fix(official-sync): 发布成功后立即提交版本状态事务
publish() 切换 current symlink 后,写最终 snapshot 或 complete_version_state
若失败会经 ? 提前返回,此时 VersionStateGuard 尚未 commit,其 Drop 会把这个
磁盘上已发布(current 已指向)的版本记入 failed_versions,造成状态与磁盘不一致。

改为在 publish() 成功后立即取出完成记录并 commit 版本状态事务,随后 snapshot
与 version-state 写入失败降级为可下轮重试的警告(不再 ? 中断、不再误记 failed);
snapshot_written 也改为仅在实际写入成功时置 Some。

对应 issue #18 维护清单 1-3。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:17:51 -07:00
nyaKazuhaandClaude Fable 5 4c123b9560 docs(official-sync): 推荐环境变量配置代理并同步凭据处理说明
- official-resource-test-pull.md §0.1:新增"带凭据代理优先用 HTTPS_PROXY 环境
  变量"推荐(含 systemd EnvironmentFile 0600 用法),补充凭据处理说明——日志/
  status 脱敏、经 ALL_PROXY 环境变量传 curl、daemon 经环境变量下传、bat-proxy.secret
  复用与 clean-stable 清除;修正旧表述"代理参数写入后台子进程启动参数"。
- official-resource-backend.md 第 13 条:把"仅日志脱敏"扩写为全链路凭据不落
  世界可读位置。
- CURRENT_STATUS.md 第 7 条:同步上述凭据处理描述。
- deployments/systemd/official-sync.env.example:新增注释版 HTTPS_PROXY/NO_PROXY
  示例,并提示含凭据时按 0600 安装。
- CHANGELOG:新增"修复"(Unity 版本数值比较)与"安全"(代理凭据不落世界可读
  位置)条目。

对应 issue #18 维护清单 1-1 / 1-2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 06:58:11 -07:00
nyaKazuhaandClaude Fable 5 565fd317de fix(official-sync): curl 代理改由环境变量传入,堵住 argv 泄漏
apply_curl_proxy 原来对 URL 型代理执行 command.arg("--proxy").arg(url),
把可能含凭据的代理 URL 放进 curl 子进程 argv,会出现在 curl 进程的
/proc/<pid>/cmdline(0444,世界可读,每次下载存活期间可被本地任意用户读到)。

改为先 remove_proxy_env 清除所有继承的代理变量,再通过 ALL_PROXY/all_proxy
环境变量把代理传给 curl(大小写两种写法都设,避免 curl 只读其一)。凭据从
世界可读的 cmdline 转移到仅属主可读的 /proc/<pid>/environ(0400)。no_proxy
列表不含凭据,仍以 --noproxy 参数传入;disabled 模式保持 --noproxy * 直连。

至此代理凭据泄漏面全部堵住:daemon 子进程 argv、0600 状态文件(55b9bab),
以及本次的 curl 孙进程 argv。

验证:
- 新增单测用 Command::get_args/get_envs 断言 URL 只进 ALL_PROXY 环境变量、
  不进 argv,且 scheme 专用变量被清除;disabled 模式强制直连。
- 真机经本地混合代理 127.0.0.1:7897 端到端验证:curl shim 记录 11 次调用,
  凭据 0 次进 argv、0 个 --proxy 参数,代理仅在 ALL_PROXY 环境变量中;curl
  确实经该代理到达官方 launcher/CDN/resources.assets 端点,功能未回归。
- 对抗性审查确认 auto/disabled/no_proxy 语义与旧 --proxy 等价,env 先删后设
  最终为 set,无其它遗漏的凭据落 argv/日志/状态文件路径。

对应 issue #18 维护清单 1-2 残留项(curl 子进程)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 06:44:03 -07:00
nyaKazuhaandClaude Fable 5 55b9babb66 fix(official-sync): 代理凭据改由环境变量下传子进程
彻底收敛 1-2 的两处残留凭据暴露:

- 子进程 argv:daemon_child_args 不再把 --proxy <url> 写入子进程参数,改为
  只放不含凭据的内部 flag --proxy-from-env,真实 URL 经 BAT_OFFICIAL_SYNC_PROXY_URL
  环境变量下传。凭据从世界可读的 /proc/<pid>/cmdline(0444)转移到仅属主可读的
  /proc/<pid>/environ(0400);子进程解析后立即 env::remove_var,避免被 curl 等
  孙进程继承。
- 状态文件 at-rest:0600 状态文件的 command 字段不再含凭据。restart/reload 复用
  所需的 URL 改存专用 0600 文件 bat-proxy.secret,该文件永不进入状态输出或 --json;
  clean-stable 会在后台停止后清除它。

复用型 restart/reload 从 bat-proxy.secret 还原代理;凭据文件缺失时(如 clean-stable
后)明确报错要求重新传入 --proxy,而非静默丢弃代理。

验证:新增 curl_proxy_url、secret 文件 0600 往返、--proxy-from-env 读取并清除
环境变量的单测;真机起停 daemon 确认凭据不在 cmdline/状态文件/status --json,
仅在 0600 secret 文件与 owner-only environ;复用 restart 成功还原、clean-stable
后复用 restart 明确报错。

对应 issue #18 维护清单 1-2 残留项。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 05:20:22 -07:00
nyaKazuhaandClaude Fable 5 810b39c2cd fix(official-sync): 修复阻塞 clippy 门禁的两处告警
cargo clippy --workspace -- -D warnings 在 HEAD 上失败,阻塞项目验证门禁:
- resolve_bootstrap 因 c123f6c 新增 curl_proxy 参数达到 8 个,触发
  too_many_arguments。将 curl_command/curl_proxy/unzip_command 收拢为
  BootstrapTools 结构体,降到 6 个参数。
- visible_historical_failed_versions(169822a 引入)的显式生命周期可省略,
  触发 needless_lifetimes。按 clippy 建议省略。

对应 issue #18 维护清单 1-7。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 04:40:35 -07:00
nyaKazuhaandClaude Fable 5 eef8c52497 fix(official-sync): 脱敏代理凭据避免泄漏到日志与状态输出
两处代理凭据泄漏:
- redact_proxy_url 对无 scheme 的代理串(user:secret@host:port,curl 默认按
  http 处理)完全不脱敏,明文凭据会进入 progress 事件、daemon 日志和
  bat-events.jsonl。现对无 scheme 但含 userinfo 的情况一并脱敏。
- daemon 保存的 command 含原始代理 URL,经 RPC status 和 bat status --json
  回显到 stdout(会被监控采集)。新增 redact_command_proxy_credentials,在
  build_daemon_status_report 的对外拷贝里脱敏 --proxy 值;磁盘状态文件
  (0600)仍保留原始 command 供 restart/reload 复用。

残留:子进程 argv 仍可经 /proc/<pid>/cmdline 读到代理凭据,以及 0600 状态
文件内的 at-rest 凭据,二者受进程属主与文件权限约束,后续可改由环境变量下
传子进程进一步收敛。

对应 issue #18 维护清单 1-2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 04:31:10 -07:00
nyaKazuhaandClaude Fable 5 7c2f6824a6 fix(adapters): 按数值比较 Unity 版本范围
VersionRange::contains 原为字符串字典序比较,2021.3.9f1 等带发布后缀的
正式版本会被 can_handle/parse 误拒,多位补丁号排序也不正确。改为解析
(major, minor, patch) 数值元组比较,补丁号剥离发布后缀,无法解析的版本
一律视为不在范围内,并补充多位补丁号、后缀版本和畸形版本的测试。

对应 issue #18 维护清单 1-1。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:27:28 -07:00
nyaKazuha 060b7d73e5 fix(official-sync): 复用失败 staging 避免重复全量下载 2026-07-16 00:35:24 +08:00
nyaKazuha c123f6c0dc feat(official-sync): 支持本地代理下载官方资源 2026-07-16 00:12:27 +08:00
nyaKazuha 169822abaf fix(daemon): 区分当前下载与历史失败状态 2026-07-15 23:22:30 +08:00
nyaKazuha 5b31112e91 fix(daemon): 使用人类格式输出 RPC 下载进度 2026-07-15 23:05:28 +08:00
nyaKazuha 7890b667d7 fix(daemon): 去重失败版本并优化状态输出 2026-07-15 22:32:27 +08:00
nyaKazuha 60b8339ecb fix(official): 修正官方 hash 校验算法 2026-07-15 22:30:33 +08:00
nyaKazuhaandClaude Fable 5 9e83bbea11 docs(docs): 按 issue 状态同步当前状态与缺口文档
G-018 已关闭并进入长期运行测试阶段,运行报告后续提供;阻塞项和下一步按
open issue #1/#2/#3 重排,明确 daemon JSON-RPC 扩展为 Rust Resource
Backend API 的优先级和最小方法集。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 06:51:46 -07:00
nyaKazuha 0d07b9f4ce docs(docs): 记录提交信息 scope 规则
要求提交标题统一使用 type(scope): 中文说明,并在开发指南和贡献指南中补充示例。
2026-07-15 21:28:22 +08:00
nyaKazuha b4b4f25cb3 refactor(ffi): 降级 FFI 为可选兼容层并整理文档
将 bat-ffi 明确收敛为无状态 C ABI 兼容层,默认集成路径改为 bat --json 进程边界或未来稳定 SDK。

同步 README、当前状态、项目计划、架构文档、开发指南和缺口清单,移除 FFI 作为主集成边界的表述。

新增 AGENTS.md 和 CONTRIBUTING.md,压缩 CLAUDE.md 为兼容入口,并归档阶段性报告、同步 .gitignore 规则。
2026-07-15 21:12:17 +08:00
nyaKazuha 90307a3243 feat: track official versions and import resources
Persist official version-state, extend CAS/ResourceRepository import classification, and add offline catalog and failure regression fixtures.

Fixes #13

Fixes #12

Fixes #8
2026-07-15 19:53:10 +08:00
nyaKazuha 4192d7ee4c fix: classify official download failures
Distinguish terminal HTTP errors from retryable CDN/network failures, record quarantined resources, and report failed download progress.

Fixes #7
2026-07-14 01:30:12 +08:00
nyaKazuha 25c2c4d40f feat: add official full pull smoke
Emit runtime download progress and validation summaries for official sync.

Add repeatable real-network full pull smoke runbook and script.

Fixes #4

Fixes #6
2026-07-14 00:48:59 +08:00
nyaKazuha 2ff99ae9e3 feat: publish official resources atomically
Fixes #9

Fixes #11
2026-07-14 00:10:39 +08:00
nyaKazuha 45aba2fb2b fix: harden official sync filesystem boundaries
Fixes #14
2026-07-13 23:17:52 +08:00