From b4b4f25cb3c258a7ad3071ef2d9a1f8779ad6f3e Mon Sep 17 00:00:00 2001 From: Yuyi-Oak <1722157266@qq.com> Date: Wed, 15 Jul 2026 21:12:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ffi):=20=E9=99=8D=E7=BA=A7=20FFI=20?= =?UTF-8?q?=E4=B8=BA=E5=8F=AF=E9=80=89=E5=85=BC=E5=AE=B9=E5=B1=82=E5=B9=B6?= =?UTF-8?q?=E6=95=B4=E7=90=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 bat-ffi 明确收敛为无状态 C ABI 兼容层,默认集成路径改为 bat --json 进程边界或未来稳定 SDK。 同步 README、当前状态、项目计划、架构文档、开发指南和缺口清单,移除 FFI 作为主集成边界的表述。 新增 AGENTS.md 和 CONTRIBUTING.md,压缩 CLAUDE.md 为兼容入口,并归档阶段性报告、同步 .gitignore 规则。 --- .gitignore | 7 +- AGENTS.md | 72 +++ CLAUDE.md | 458 +----------------- CONTRIBUTING.md | 68 +++ CURRENT_STATUS.md | 19 +- Cargo.lock | 200 +------- DOCS_INDEX.md | 13 +- PROJECT_PLAN.md | 20 +- README.md | 13 +- crates/bat-ffi/Cargo.toml | 11 - crates/bat-ffi/src/lib.rs | 158 ++++-- docs/architecture/README.md | 8 +- .../0001-engine-and-application-boundaries.md | 9 +- .../architecture/official-resource-backend.md | 6 + docs/archive/ARCHITECTURE_REVIEW_SUMMARY.md | 5 +- docs/guides/baseline.md | 4 +- docs/guides/development.md | 40 +- docs/reports/CURRENT_GAPS.md | 4 +- docs/reports/historical/README.md | 14 + .../current-stage}/current-stage-prepush.md | 0 .../current-stage}/current-status-handoff.md | 0 internal/ffi/ffi.go | 63 +-- 22 files changed, 420 insertions(+), 772 deletions(-) create mode 100644 AGENTS.md create mode 100644 CONTRIBUTING.md create mode 100644 docs/reports/historical/README.md rename docs/reports/{ => historical/current-stage}/current-stage-prepush.md (100%) rename docs/reports/{ => historical/current-stage}/current-status-handoff.md (100%) diff --git a/.gitignore b/.gitignore index 18423e0..8183f47 100644 --- a/.gitignore +++ b/.gitignore @@ -45,8 +45,11 @@ go.work.sum logs/ pg_log/ -# Generated reports -/docs/reports/fuck-u-code-current.md +# Generated/local-only reports +/docs/reports/generated/ +/docs/reports/fuck-u-code-*.md +/docs/reports/*-current.generated.md +/docs/reports/**/SMOKE_REPORT.md # Backups /deployments/backups/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..44bfe6f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,72 @@ +# Agent 开发规则 + +本文件是 BlueArchive Toolkit 中 AI agent、自动化开发助手和长期维护脚本的权威入口。它替代旧 `CLAUDE.md` 中真正长期有效的工程规则。 + +## 语言和表达 + +1. 默认使用简体中文交流、写文档、写提交说明、写开发日志和写 API 说明。 +2. 代码中的包名、类型名、函数名、变量名、数据库字段、协议字段和命令参数保持英文命名规范。 +3. 代码注释只在能降低理解成本时添加;不要写复述代码行为的空泛注释。 +4. 如果任务、上游规范或第三方 API 明确要求英文,可以在对应位置使用英文。 + +## 项目定位 + +BlueArchive Toolkit 是长期维护的开源工具链,不是 demo、一次性脚本或临时试验项目。 + +长期目标包括但不限于: + +1. 官方资源同步、版本管理、增量同步、断点续传、重试、限速、缓存和校验。 +2. Content Addressable Storage(CAS)、引用计数、垃圾回收、多版本共享和完整性校验。 +3. UnityFS / AssetBundle / Addressables / Manifest 解析框架,并保持解析器与业务逻辑解耦。 +4. 文本提取、翻译记忆、术语库、AI Provider 抽象、Patch、CLI、Web、API、SDK 和插件系统。 +5. 支持未来扩展到其他区域、语言或 Unity 游戏;不要把架构写死到单一版本。 + +## 工程边界 + +1. 默认工作于用户本地环境。不要把生产环境当作开发环境。 +2. 真实资源下载、smoke run 和手动验证必须写入隔离目录,例如 `/tmp` 或显式指定的测试目录。 +3. 不要默认读取、修改或污染现有客户端目录、生产资源目录或 `/home/wanye/D/BlueArchive` 这类本地资源目录。 +4. 不要要求安装官方启动器作为生产运行前提。可以分析启动器资源或官方公开数据,但生产链路必须能在 Linux 环境中独立运行。 +5. 涉及官方资源时,优先使用官方 `.hash`、catalog、manifest 和可复现 fixture 做校验依据。 + +## 架构原则 + +1. 仓库采用 monorepo;模块必须边界清晰、高内聚、低耦合。 +2. 公共接口应稳定、可测试、可维护,并为未来扩展保留合理空间。 +3. Rust 侧优先承担二进制解析、AssetBundle、Patch、CAS 和官方资源后端能力;Go 侧优先承担 CLI、运维入口和面向用户的命令编排。边界调整必须先说明理由。 +4. Rust/Go 默认集成路径优先进程边界(当前为 `bat --json`)或未来稳定 SDK;`bat-ffi` 仅作为可选无状态 C ABI 兼容层,不能扩展成 daemon、下载器、CAS handle 或主控制面。 +5. SDK 不得与 CLI 耦合;解析器不得与业务流程耦合;Provider、存储后端、Patch 算法和解析器应保留插件化扩展点。 +6. 不引入 God Object、God Class、超长函数、超长文件、硬编码、魔法数字、重复代码、临时实现或只为当前测试通过的伪实现。 +7. 不使用 `TODO`、`FIXME` 掩盖未完成设计。确实无法完成时,应在当前缺口文档中说明边界、风险和后续工作。 + +## 开发流程 + +1. 动手前先读相关文档和代码,确认当前真实状态。 +2. 对跨模块、架构、数据格式或用户工作流有影响的改动,先给出设计判断或简短计划。 +3. 实现后必须同步验证。验证范围要覆盖改动实际影响面,而不是只跑最窄的命令。 +4. 涉及用户可见行为、运行方式、架构边界或缺口状态时,必须同步更新文档。 +5. 保持改动范围和任务目标一致;不要顺手做无关重构或格式化 churn。 +6. 如果需求、技术路线或设计存在明显风险,应直接指出并给出可执行替代方案。 +7. 不确定的事实必须查证或询问;不要凭空调用不存在的接口、命令、路径或线上资源。 + +## 质量要求 + +1. 所有错误必须显式处理,并给出可诊断信息。 +2. 日志应结构化或至少足够定位阶段、路径、版本、URL、重试、校验和失败原因。 +3. 下载、写文件、状态切换和发布操作必须考虑原子性、断点续传、并发锁、失败恢复和清理策略。 +4. 本地状态文件和索引必须有版本字段或兼容策略。 +5. 新增 fixture、golden 或回归样本时,应说明它覆盖的真实风险。 +6. 默认验证命令见 `docs/guides/development.md`;稳定工程基线见 `docs/guides/baseline.md`。 + +## 文档职责 + +长期规则的权威位置如下: + +1. `AGENTS.md`:agent 行为、工程边界、架构原则和质量要求。 +2. `CONTRIBUTING.md`:贡献者工作流、提交规范、验证和 PR 要求。 +3. `docs/guides/development.md`:环境准备、开发命令、测试、调试和真实资源验证方式。 +4. `PROJECT_PLAN.md`:产品目标、阶段路线图和长期能力规划。 +5. `CURRENT_STATUS.md`:当前实现状态。 +6. `docs/reports/CURRENT_GAPS.md`:当前缺口、优先级和关闭顺序。 + +`CLAUDE.md` 只保留兼容入口,不应继续新增长期规则。 diff --git a/CLAUDE.md b/CLAUDE.md index 64f30b7..2a63de2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,452 +1,14 @@ -# BlueArchive Toolkit 项目开发任务(Claude Opus 4.6) +# Claude 兼容入口 -你现在不是普通 AI,而是本项目唯一的长期架构师(Chief Architect)、首席开发工程师(Lead Developer)、代码审查者(Code Reviewer)、技术负责人(Tech Lead)以及长期维护者(Maintainer)。 +本文件仅作为 Claude Code 等旧工具默认读取 `CLAUDE.md` 时的兼容入口,不再承载项目长期规则。 -请始终牢记: +当前权威文档: -**整个开发过程中,必须始终使用简体中文回复。** +- `AGENTS.md`:AI agent 和自动化开发助手必须遵守的长期规则。 +- `CONTRIBUTING.md`:贡献者协作、提交、验证和文档要求。 +- `docs/guides/development.md`:本地开发环境、工作流、测试和调试指南。 +- `PROJECT_PLAN.md`:项目长期目标和路线图。 +- `CURRENT_STATUS.md`:当前工作区真实状态。 +- `docs/reports/CURRENT_GAPS.md`:当前缺口、优先级和关闭顺序。 -包括但不限于: - -* 所有解释 -* 所有设计 -* 所有分析 -* 所有文档 -* 所有 README -* 所有注释 -* 所有 API 文档 -* 所有提交说明 -* 所有开发日志 - -均默认使用简体中文。 - -代码中的类名、接口名、方法名、变量名、包名等仍然保持英文命名规范。 - ---- - -# 项目背景 - -我要开发一个名为 **BlueArchive Toolkit** 的大型开源项目。 - -该项目目标不是 Demo,也不是脚本,而是一个能够长期维护、持续扩展、达到工业级质量的完整平台。 - -本项目默认工作于用户本地环境。 - -整个系统围绕 Blue Archive(日服)资源展开,但请不要假设项目仅服务于某一个游戏版本,整个架构必须具有良好的可扩展能力,以便未来支持其他区域、其他语言甚至其他 Unity 游戏。 - -整个项目必须按照 Production Ready 标准开发。 - -严禁以 Demo、最小实现(Minimum Viable Product)、临时方案、占位实现等思路完成任何模块。 - ---- - -# 项目目标 - -项目需要逐步实现并形成完整生态。 - -包括但不限于: - -## 资源同步 - -能够同步资源。 - -支持: - -* Manifest -* 版本管理 -* 增量同步 -* Hash 校验 -* 多线程下载 -* 断点续传 -* 自动重试 -* 限速 -* 下载缓存 -* 本地对象存储 - ---- - -## 存储系统 - -采用 Content Addressable Storage(CAS)。 - -必须支持: - -* Hash 去重 -* 引用计数 -* 垃圾回收 -* 多版本共享 -* 完整性校验 - -不得使用简单目录堆放文件。 - ---- - -## Unity AssetBundle - -设计完整解析框架。 - -要求支持插件化。 - -未来能够支持: - -* TextAsset -* Localization -* MonoBehaviour -* ScriptableObject -* Texture -* Sprite -* Audio -* Video -* 其他 Unity 资源 - -解析器必须独立。 - -不得与业务逻辑耦合。 - ---- - -## 文本提取 - -自动提取: - -* 剧情 -* UI -* 系统文本 -* 配置文本 - -统一导出标准格式。 - -不得直接修改原始资源。 - ---- - -## Translation Memory - -建立翻译记忆库。 - -支持: - -* 自动匹配 -* 模糊匹配 -* Provider 来源 -* 审核状态 -* 历史记录 -* 多语言 - ---- - -## Glossary - -建立术语库。 - -术语优先级必须高于 AI。 - -所有 AI 翻译必须优先遵循术语。 - -支持: - -* 多语言 -* 别名 -* 分类 -* 冲突检测 -* 审核 - ---- - -## AI 翻译 - -设计 Provider 抽象层。 - -未来支持: - -* DeepL -* OpenAI -* Anthropic -* Google -* Azure -* 自定义 Provider - -所有 Provider 必须统一接口。 - -不得耦合具体实现。 - ---- - -## Patch - -设计完整 Patch 系统。 - -支持: - -* 增量 Patch -* Binary Patch -* JSON Patch -* Rollback -* Integrity Check - ---- - -## CLI - -设计完整命令体系。 - -例如: - -sync - -extract - -translate - -patch - -verify - -doctor - -serve - -cache - -manifest - -bundle - -所有命令必须统一风格。 - ---- - -## Web - -设计完整后台。 - -包括: - -* 登录 -* 权限 -* 翻译审核 -* 术语管理 -* 全文搜索 -* 历史版本 -* Diff -* Dashboard - ---- - -## SDK - -整个项目必须提供 SDK。 - -方便其他项目调用。 - -不得把 SDK 与 CLI 耦合。 - ---- - -## API - -所有接口: - -RESTful。 - -OpenAPI。 - -版本管理。 - -统一错误码。 - -统一响应结构。 - -支持未来扩展。 - ---- - -## Database - -自行设计完整数据库。 - -要求: - -高性能。 - -规范化。 - -支持 Migration。 - -支持未来扩展。 - ---- - -## Plugin System - -整个项目必须支持插件。 - -以后新增: - -新的解析器 - -新的翻译 Provider - -新的存储后端 - -新的 Patch 算法 - -不得修改核心代码。 - ---- - -# 技术栈 - -请根据不同模块自行选择最适合的技术。 - -我倾向于: - -* Go(CLI、Downloader、API) -* Rust(二进制解析、AssetBundle、Patch) -* Vue3 + TypeScript(Web) -* PostgreSQL -* Redis -* Docker -* GitHub Actions - -但如果你认为有更合理的方案,请给出完整论证后再调整。 - ---- - -# 架构要求 - -采用 Monorepo。 - -严格模块化。 - -高内聚。 - -低耦合。 - -支持长期维护。 - -支持未来十年以上持续开发。 - -所有模块必须具有明确边界。 - -禁止出现: - -* God Object -* God Class -* 超长函数 -* 超长文件 -* Magic Number -* Hard Code -* 重复代码 -* 临时实现 -* Demo 思维 -* TODO -* FIXME - ---- - -# 开发要求 - -不要一次性生成整个项目。 - -必须按照真正的软件工程流程。 - -每开始一个模块: - -先分析。 - -再设计。 - -给出架构。 - -等待确认(如果我没有要求直接实现)。 - -然后编码。 - -然后测试。 - -然后 Benchmark。 - -然后 Documentation。 - -最后 Review。 - -再继续下一模块。 - ---- - -# 代码质量 - -所有代码必须达到 Production Ready。 - -所有公共接口必须稳定。 - -所有配置不得硬编码。 - -所有错误必须处理。 - -所有日志必须结构化。 - -所有模块必须可测试。 - -所有模块必须可维护。 - -所有模块必须具有扩展能力。 - ---- - -# 文档 - -每完成一个模块: - -自动同步更新: - -README - -Architecture - -Sequence Diagram - -Flow Diagram - -API Documentation - -Developer Guide - -User Guide - -Deployment Guide - -Change Log - ---- - -# AI 行为要求 - -你不是代码生成器。 - -你应该主动思考。 - -主动发现问题。 - -主动优化设计。 - -主动指出潜在风险。 - -主动提出更优方案。 - -如果你认为我的设计存在问题,应直接指出并给出充分理由,而不是机械执行。 - ---- - -# 最重要要求 - -不要为了满足当前需求而牺牲整个项目未来架构。 - -整个项目应以工业级开源项目为目标。 - -请像维护一个会持续十年以上的大型开源项目一样进行设计和开发,而不是完成一次性的开发任务。 - -如果你认为我提出的需求、技术路线或设计思路存在不合理之处,请直接指出,不要因为迎合我的要求而保留明显存在缺陷的设计。你的职责是作为首席架构师提供最佳工程方案,而不是机械执行我的所有想法。 - -此外,如果你不知道一些具体的东西,必须询问我,不准虚空调用 +使用 Claude 时,请先读取 `AGENTS.md`,再按任务需要读取 `CONTRIBUTING.md` 和 `docs/guides/development.md`。如果本文件与上述权威文档冲突,以上述权威文档为准。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..06fd662 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,68 @@ +# 贡献指南 + +感谢参与 BlueArchive Toolkit。这个项目按长期维护的开源工具链标准推进,不接受只为临时通过、不可维护或污染本地资源目录的改动。 + +## 开始前 + +1. 阅读 `README.md`、`CURRENT_STATUS.md` 和 `docs/reports/CURRENT_GAPS.md`,确认当前实现状态和优先级。 +2. 涉及官方资源同步、Rust 后端、Go CLI 或架构边界时,额外阅读 `docs/guides/development.md`、`docs/guides/official-resource-test-pull.md` 和相关 ADR。 +3. AI agent 或自动化助手还必须遵守 `AGENTS.md`。 + +## 工作流 + +1. 基于当前开发分支创建功能分支。 +2. 先分析现有文档和代码,再决定实现方式。 +3. 保持改动聚焦,避免无关重构、批量格式化或元数据 churn。 +4. 实现后运行覆盖改动范围的测试、格式化和 lint。 +5. 如果用户可见行为、运行命令、架构边界、缺口状态或数据格式发生变化,同步更新文档。 + +## 提交规范 + +提交信息使用 Conventional Commits: + +- `feat:` 新功能 +- `fix:` 修复 bug +- `docs:` 文档更新 +- `test:` 测试或 fixture 更新 +- `refactor:` 不改变行为的重构 +- `chore:` 构建、依赖或辅助工具调整 + +提交说明默认使用简体中文。代码标识符和协议字段仍使用英文。 + +## 验证要求 + +基础验证命令见 `docs/guides/development.md`。常用最低门禁: + +```bash +cargo fmt --check +cargo test --workspace +cargo clippy --workspace -- -D warnings +``` + +如果改动只影响部分 crate,可以先跑更窄的测试,但合并前必须确保影响面被覆盖。官方资源同步、下载、daemon、status、verify 或 repair 相关改动还应运行: + +```bash +cargo test -p bat-infrastructure --bin bat -- --nocapture +cargo test -p bat-infrastructure -- --nocapture +``` + +真实官方网络 smoke run 必须写入隔离目录,禁止写入现有游戏客户端或生产资源目录。 + +## 资源和数据安全 + +1. 不要把生产环境当作开发环境。 +2. 不要默认读取或修改 `/home/wanye/D/BlueArchive` 等已有资源目录。 +3. 不要提交真实账号、token、cookie、私有路径、下载产物、数据库或本地 CAS 数据。 +4. 新增 fixture 应尽量最小化,只保留验证解析、校验或错误处理所需的数据。 + +## PR 要求 + +PR 描述应包含: + +1. 改动摘要。 +2. 影响范围。 +3. 已运行的验证命令。 +4. 未覆盖风险或后续缺口。 +5. 相关 issue、ADR 或文档链接。 + +如果改动关闭缺口或 issue,请在 PR 或提交中明确引用。 diff --git a/CURRENT_STATUS.md b/CURRENT_STATUS.md index 70c7c8c..03fa0d2 100644 --- a/CURRENT_STATUS.md +++ b/CURRENT_STATUS.md @@ -173,28 +173,35 @@ Rust 侧官方日服资源链路已经从实验验证推进到正式入口: ### `bat-ffi` -状态:**粗粒度 JSON API 可用,稳定边界仍需继续收敛** +状态:**可选无状态兼容层,非主集成边界** 已包含: - `bat_version`。 - `bat_manifest_inspect_json`:解析 Addressables manifest 并返回 JSON summary。 - `bat_sync_plan_json`:根据 current/previous snapshot 生成官方同步计划 JSON。 -- `internal/ffi/ffi.go` 提供 Go 包装骨架。 +- `internal/ffi/ffi.go` 提供可选 CGO 兼容包装骨架。 + +定位约束: + +- `bat-ffi` 只暴露粗粒度、无状态、一次调用一次 JSON 输入输出的 C ABI helper。 +- 它不持有 downloader、daemon、CAS handle、资源目录锁或长生命周期状态。 +- Go CLI 和生产运维默认应调用 `bat --json` 进程边界;未来稳定 SDK 也优先于 FFI。 +- FFI 仅用于需要嵌入 C ABI 的兼容场景,不能作为官方同步控制面或主集成边界。 待完成: -- Go CLI 调用链路。 - 错误码与结构化响应约定。 -- 发布用头文件、构建脚本和跨平台产物。 +- 如确有兼容需求,再补发布用头文件、构建脚本和跨平台产物。 ### Go / API / Web -状态:**Go FFI 包骨架存在,CLI/API/Web 仍未实现** +状态:**CLI/API/Web 仍未实现,仅有可选 CGO 兼容包装** 当前情况: - `internal/ffi/ffi.go` 已存在。 +- Go CLI 默认集成方向是调用 Rust `bat --json` 并转发结构化 report,而不是依赖 FFI。 - `cmd/`、`pkg/`、`api/`、`web/` 仍无可用产品入口。 - `go test ./...` 在没有 Go package 时可能无测试可运行;Makefile 会清晰跳过空 Go 阶段。 @@ -256,7 +263,7 @@ cargo run -p bat-infrastructure --bin bat -- \ 下一阶段必须优先完成: -1. Go CLI 最小可用入口:`bat doctor`、`bat sync --help`、Rust 官方同步命令包装。 +1. Go CLI 最小可用入口:`bat doctor`、`bat sync --help`、通过 `bat --json` 包装 Rust 官方同步命令。 2. 官方同步结果接入 CAS + ResourceRepository 的用户级工作流。 3. AssetBundle UnityFS 基础解析。 4. Addressables parser 对更多官方 catalog 结构的覆盖。 diff --git a/Cargo.lock b/Cargo.lock index cb250bc..5759441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,56 +8,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - [[package]] name = "anyhow" version = "1.0.103" @@ -180,15 +130,8 @@ dependencies = [ name = "bat-ffi" version = "0.1.0" dependencies = [ - "anyhow", "bat-adapters", - "bat-assetbundle", - "bat-cas-engine", - "bat-core", "bat-infrastructure", - "bat-patch", - "cbindgen", - "libc", "serde", "serde_json", "tokio", @@ -279,25 +222,6 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" -[[package]] -name = "cbindgen" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" -dependencies = [ - "clap", - "heck 0.4.1", - "indexmap", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn", - "tempfile", - "toml", -] - [[package]] name = "cc" version = "1.2.67" @@ -314,39 +238,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "clap" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -680,12 +571,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -838,12 +723,6 @@ dependencies = [ "hashbrown 0.17.1", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - [[package]] name = "itoa" version = "1.0.18" @@ -1050,12 +929,6 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - [[package]] name = "parking" version = "2.2.1" @@ -1317,15 +1190,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1498,7 +1362,7 @@ checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" dependencies = [ "dotenvy", "either", - "heck 0.5.0", + "heck", "hex", "once_cell", "proc-macro2", @@ -1635,12 +1499,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.6.1" @@ -1766,47 +1624,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - [[package]] name = "tracing" version = "0.1.44" @@ -1890,12 +1707,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "vcpkg" version = "0.2.15" @@ -2011,15 +1822,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - [[package]] name = "writeable" version = "0.6.3" diff --git a/DOCS_INDEX.md b/DOCS_INDEX.md index 35159d3..922cb1d 100644 --- a/DOCS_INDEX.md +++ b/DOCS_INDEX.md @@ -1,6 +1,6 @@ # BlueArchiveToolkit 文档索引 -- **更新时间**:2026-07-14 +- **更新时间**:2026-07-15 - **说明**:本索引用于快速定位当前权威文档和历史资料。 --- @@ -15,7 +15,9 @@ - `docs/guides/official-full-pull-smoke.md`:真实官方全量拉取 smoke runbook 和可重复命令。 - `docs/architecture/official-resource-backend.md`:官方资源后端职责、工作原理和审核说明。 - `CHANGELOG.md`:版本变更记录。 -- `CLAUDE.md`:长期开发约束和项目要求。 +- `AGENTS.md`:AI agent 和自动化开发助手长期规则。 +- `CONTRIBUTING.md`:贡献者协作、提交和验证要求。 +- `CLAUDE.md`:Claude Code 等旧工具的兼容入口。 --- @@ -28,8 +30,6 @@ - `deployments/systemd/`:官方资源同步生产 systemd unit 和环境文件示例。 - `docs/guides/official-resource-test-pull.md`:官方资源拉取与自动更新用户指南。 - `docs/guides/official-full-pull-smoke.md`:真实官方全量拉取 smoke runbook 和可重复命令。 -- `docs/reports/current-stage-prepush.md`:当前阶段说明与推送前核查记录。 -- `docs/reports/current-status-handoff.md`:给下一次对话使用的当前进度交接说明。 - `docs/guides/baseline.md`:稳定工程基线指南。 - `docs/architecture/adr/0001-engine-and-application-boundaries.md`:Rust/Go 边界决策。 - `docs/architecture/adr/0002-cas-v1-design-boundary.md`:CAS V1 边界决策。 @@ -60,6 +60,7 @@ 历史报告已按来源和主题归档,供追溯使用,不再代表当前状态。 - `docs/reports/historical/root/`:原根目录阶段报告。 +- `docs/reports/historical/current-stage/`:已被 `CURRENT_STATUS.md` 和当前指南取代的阶段交接、推送前核查报告。 - `docs/reports/historical/week2/`:Week 2 相关报告。 - `docs/reports/historical/week3/`:Week 3 相关报告。注意:这些报告中存在“完成”和“回滚”的冲突描述。 - `docs/reports/historical/build-logs/`:历史构建、测试、Clippy 输出。 @@ -81,6 +82,8 @@ 7. `docs/guides/baseline.md` 8. `docs/architecture/README.md` 9. `docs/guides/development.md` +10. `CONTRIBUTING.md` +11. `AGENTS.md` --- @@ -98,7 +101,7 @@ - 真实官方网络全量拉取 smoke 已固化为 `scripts/official-full-pull-smoke.sh` 和 `make official-smoke`,默认写入 `/tmp` 隔离目录并输出本地运行报告。 - `bat` 运行时 progress log 已覆盖总体下载进度、单文件下载进度和校验结果摘要。 - Addressables 当前真实形态 fixture/golden 覆盖。 -- SQLite Resource Repository 和粗粒度 FFI JSON 接口。 +- SQLite Resource Repository 和可选无状态 `bat-ffi` JSON 兼容接口。 优先待办: diff --git a/PROJECT_PLAN.md b/PROJECT_PLAN.md index 5ca9e93..19f257b 100644 --- a/PROJECT_PLAN.md +++ b/PROJECT_PLAN.md @@ -15,7 +15,7 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 2. **Rust 核心引擎**:负责 CAS、AssetBundle 解析、Patch、二进制安全处理和性能敏感逻辑。 3. **Go 服务层**:负责 CLI 编排、资源同步、下载器、API Server、任务调度和外部集成。 4. **Web 管理后台**:负责翻译审核、术语管理、全文搜索、历史版本、Diff 和 Dashboard。 -5. **SDK/API**:提供稳定的 Go SDK、进程边界或必要时的 FFI 边界和 REST/OpenAPI 接口,方便其他工具复用。 +5. **SDK/API**:提供稳定的 Go SDK、进程边界和 REST/OpenAPI 接口,方便其他工具复用;FFI 仅保留为可选兼容层。 6. **插件系统**:允许新增解析器、翻译 Provider、存储后端、Patch 算法,而不修改核心代码。 --- @@ -33,14 +33,14 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 5. `bat-infrastructure` 已改为 CAS 仓储适配层,不再重复实现对象存储。 6. `bat-infrastructure` 已提供官方资源 pull/update 服务,正式入口是 Rust binary `bat`。 7. `bat` 支持 `--auto-discover`、`--watch`、`--daemon`、默认 1 小时间隔、本地 manifest audit/repair、官方 seed `.hash` 校验、snapshot/cache,以及基于 Unix socket JSON-RPC 的 `status/stop/restart/reload/refresh/logs/verify/repair/doctor/clean-stable` 运维命令。 -8. `bat-ffi` 已提供 Manifest inspect 和官方 sync plan 的粗粒度 JSON API。 +8. `bat-ffi` 已提供 Manifest inspect 和官方 sync plan 的可选无状态粗粒度 JSON C ABI helper。 9. 文档已整理:根目录保留入口文档,历史报告进入 `docs/reports/historical/`,误嵌套的 `docs/docs` 已合并。 ### 仍是骨架或占位 1. AssetBundle 解析器仍是占位 trait,未解析 UnityFS、压缩块、TypeTree 或对象表。 2. Patch 的 Binary/JSON 模块仍返回空结果,不具备真实补丁能力。 -3. Go CLI/API/SDK 仍没有产品级入口;只有 `internal/ffi` 的早期包装。 +3. Go CLI/API/SDK 仍没有产品级入口;只有 `internal/ffi` 的可选兼容包装骨架。 4. Addressables parser 已覆盖当前真实形态 fixture/golden,但还不是完整 Unity Addressables/SBP catalog 兼容层。 5. 官方同步结果尚未作为用户级流程自动导入 CAS + ResourceRepository。 6. 真实官方网络全量下载 smoke test 尚未记录。 @@ -66,11 +66,11 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 2. **Engine**:Rust 实现性能敏感和安全敏感能力,包括 CAS、AssetBundle、Patch、二进制格式校验。 3. **Infrastructure**:实现数据库、文件系统、缓存、对象存储、HTTP 客户端、任务队列。 4. **Application**:编排用例,例如同步资源、提取文本、生成补丁、审核翻译。 -5. **Interface**:CLI、REST API、Web UI、SDK、FFI。 +5. **Interface**:CLI、REST API、Web UI、SDK,以及可选 FFI 兼容层。 ### 3.2 技术决策 -1. **Rust**:保留为核心引擎语言,用于 CAS、AssetBundle、Patch、完整资源拉取和更新检查核心逻辑,FFI 仅作为可选边界。 +1. **Rust**:保留为核心引擎语言,用于 CAS、AssetBundle、Patch、完整资源拉取和更新检查核心逻辑;`bat --json` 进程边界是当前主集成路径,FFI 仅作为可选兼容层。 2. **Go**:用于最小稳定 CLI、服务编排、API Server、任务编排、Provider 集成;不强制要求 Rust 核心能力必须写成库供 Go 调用。 3. **PostgreSQL**:作为服务端主数据库,承载翻译记忆库、术语库、任务、审核和用户权限。 4. **SQLite**:仅作为本地 CLI 可选元数据后端,必须通过仓储抽象隔离,不能绑定业务逻辑。 @@ -141,7 +141,7 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 **目标**:完成可长期使用的 Content Addressable Storage。 -**当前状态**:已完成 CAS V1。Go CLI 以最小稳定入口优先,Rust 继续承载完整资源拉取与更新检查核心逻辑;FFI 边界保持可选。 +**当前状态**:已完成 CAS V1。Go CLI 以最小稳定入口优先,Rust 继续承载完整资源拉取与更新检查核心逻辑;`bat-ffi` 仅保留为可选兼容层。 交付物: @@ -150,7 +150,7 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 3. 实现引用计数、对象元数据、完整性校验、GC、统计信息。 4. 实现本地元数据后端:优先 SQLite,但必须隔离在 repository adapter 中。 5. 编写迁移、恢复、损坏检测和 `doctor cas`。 -6. 提供稳定的 Go 调用边界,优先进程或 SDK,必要时再补 FFI。 +6. 提供稳定的 Go 调用边界,优先进程或 SDK;FFI 只保留为可选兼容层,不作为默认集成方案。 验收标准: @@ -175,7 +175,7 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 3. Rust 官方下载器:**已完成当前生产入口需要的核心能力**。包含官方 URL 校验、`.part` 续传、重试、本地 manifest size+BLAKE3 校验、官方 seed `.hash` 校验和 repair。 4. Rust 自动更新入口:**已完成当前生产入口**。`bat` 支持 snapshot、marker diff、bootstrap cache、one-shot、`--watch`、`--daemon`、默认 1 小时间隔、北京时间固定强制刷新,以及 Unix socket JSON-RPC 后台运维命令返回。 5. Go CLI:**未完成**。需要实现 `bat doctor`、`bat sync --help`、Rust 官方同步命令包装和 JSON/human 输出。 -6. 用户级 `sync`、`manifest inspect`、`cache status`:**未完成**。Rust FFI 已提供 Manifest inspect 和 sync plan JSON 边界,但 Go CLI 尚未串联。 +6. 用户级 `sync`、`manifest inspect`、`cache status`:**未完成**。Rust `bat --json` 是 Go CLI 默认进程边界;`bat-ffi` 只提供可选兼容用的 Manifest inspect 和 sync plan JSON helper。 7. 下载结果写入 CAS + ResourceRepository:**部分完成**。CAS 和 SQLite ResourceRepository 已存在,官方同步入口尚未把完整下载结果作为用户级流程自动导入。 8. Linux 生产同步不依赖已安装官方启动器:**已完成当前 Rust 入口**。`--auto-discover` 只使用官方 HTTP metadata 和临时目录解析 `GameMainConfig`。 9. 真实官方网络全量下载 smoke test:**未完成记录**。需要在隔离目录执行并记录 dry-run、首次下载、二次 up-to-date 和本地损坏 repair。 @@ -381,7 +381,7 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 ## 6. 近期 10 个具体任务 1. 落地 Go CLI 的最小生产入口:`bat doctor`、`bat sync --help`、`bat official sync --help`。 -2. 让 Go CLI 能调用 Rust `bat --json` 官方同步入口或 FFI/进程边界,并稳定转发结构化 report。 +2. 让 Go CLI 默认调用 Rust `bat --json` 官方同步入口,并稳定转发结构化 report;除非有明确兼容需求,不走 FFI。 3. 记录一次真实官方网络 smoke:dry-run、首次下载、二次 up-to-date、本地损坏 repair。 4. 将官方同步下载结果接入 CAS + `SqliteResourceRepository` 的用户级流程。 5. 继续扩展 Addressables parser 的真实 catalog 变体覆盖和错误诊断。 @@ -416,7 +416,7 @@ BlueArchiveToolkit 不是一次性脚本,也不是演示项目。最终交付 1. Rust 提供稳定引擎能力,不承担 CLI 编排,但负责完整资源拉取和更新检查的核心逻辑。 2. Go 负责用户命令、最小稳定 CLI、服务编排、网络和 Provider。 -3. 跨边界优先进程或 SDK,FFI 只作为可选的粗粒度、安全、可测试 API。 +3. 跨边界优先进程或 SDK,FFI 只作为可选的粗粒度、无状态、安全、可测试兼容 API。 4. Rust 不需要被强制写成 Go 调用库;当前 `bat --watch` / `bat --daemon` 是允许长期运行的 Rust 生产任务。 ### 官方资源真实下载风险 diff --git a/README.md b/README.md index 831c90e..8fcb0c3 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ - `bat`:官方资源自动发现、全量拉取、原子发布到 `current -> versions/`、本地 manifest audit/repair、`.part` 断点续传、403/404/5xx 分类重试、下载 quarantine 诊断、ZIP 结构校验、官方 seed `.hash` 校验、snapshot/cache、`--watch` 常驻更新、`--daemon` 后台运行,以及 Unix socket JSON-RPC 后台控制命令 `status/stop/restart/reload/refresh/logs/verify/repair/doctor/clean-stable`。 - 官方同步会维护 `/official-version-state.json`,明确记录当前已完成版本、正在拉取版本、上一个可用版本和失败版本。 - 资源导入链路可将 manifest 条目写入 CAS + `ResourceRepository`,AssetBundle 会记录 UnityFS 摘要,TextAsset/Table/Media 会按类型分类索引。 -- `bat-ffi` 粗粒度 JSON 接口:Manifest inspect 和官方 sync plan。 +- `bat-ffi` 可选无状态 C ABI 兼容层:仅保留 Manifest inspect 和官方 sync plan 的粗粒度 JSON helper,不作为 Go CLI 或生产同步的主集成边界。 - 文档路线图、当前状态、缺口清单、官方资源运行指南。 仍未完成: @@ -134,8 +134,9 @@ make official-smoke ## 技术栈 -- Rust:CAS、官方资源同步核心、AssetBundle/Patch 引擎、FFI。 -- Go:计划中的最小 CLI、服务编排、API Server、SDK;当前仅有 FFI 包骨架。 +- Rust:CAS、官方资源同步核心、AssetBundle/Patch 引擎;当前生产同步入口是 `bat` binary。 +- Go:计划中的最小 CLI、服务编排、API Server、SDK;默认通过 `bat --json` 进程边界或未来 SDK 集成 Rust 能力。 +- `bat-ffi`:可选兼容层,只暴露无状态粗粒度 JSON C ABI,不承载 daemon、下载器、CAS handle 或主控制面。 - PostgreSQL:计划中的服务端主数据库。 - Redis:计划中的缓存、队列状态、限流和短期锁。 - Vue 3 + TypeScript:计划中的 Web 管理后台。 @@ -154,8 +155,8 @@ BlueArchiveToolkit/ │ ├── bat-cas-engine/ │ ├── bat-assetbundle/ │ ├── bat-patch/ -│ └── bat-ffi/ -├── internal/ffi/ # Go 调用 Rust FFI 的早期包装 +│ └── bat-ffi/ # 可选无状态 C ABI 兼容层 +├── internal/ffi/ # 可选 CGO 兼容包装,不是 Go CLI 主路径 ├── cmd/ # Go CLI 入口,尚未实现 ├── pkg/ # Go SDK 包,尚未实现 ├── api/ # API 定义,尚未实现 @@ -173,7 +174,7 @@ BlueArchiveToolkit/ 近期优先级: -1. 落地 Go CLI 最小可用入口:`bat doctor`、`bat sync --help`、Rust 同步命令包装。 +1. 落地 Go CLI 最小可用入口:`bat doctor`、`bat sync --help`、通过 `bat --json` 包装 Rust 同步命令。 2. 补齐 AssetBundle UnityFS header/block/directory 解析。 3. 扩展 Addressables catalog 解析覆盖,继续用真实形态 fixture/golden 锁定行为。 4. 将官方同步结果接入 CAS + ResourceRepository 的用户级工作流。 diff --git a/crates/bat-ffi/Cargo.toml b/crates/bat-ffi/Cargo.toml index ecfaf3f..b28a471 100644 --- a/crates/bat-ffi/Cargo.toml +++ b/crates/bat-ffi/Cargo.toml @@ -9,20 +9,9 @@ license.workspace = true crate-type = ["cdylib", "staticlib"] [dependencies] -bat-core = { path = "../../core" } bat-adapters = { path = "../../adapters" } bat-infrastructure = { path = "../../infrastructure" } -bat-cas-engine = { path = "../bat-cas-engine" } -bat-assetbundle = { path = "../bat-assetbundle" } -bat-patch = { path = "../bat-patch" } -anyhow.workspace = true serde.workspace = true serde_json.workspace = true tokio.workspace = true - -# FFI 绑定 -libc = "0.2" - -[build-dependencies] -cbindgen = "0.27" diff --git a/crates/bat-ffi/src/lib.rs b/crates/bat-ffi/src/lib.rs index 0c1b72a..9276004 100644 --- a/crates/bat-ffi/src/lib.rs +++ b/crates/bat-ffi/src/lib.rs @@ -1,8 +1,9 @@ -//! # BAT FFI +//! # BAT FFI compatibility layer //! -//! Go 和 Rust 之间的 FFI 绑定层 -//! -//! 导出 C ABI 接口供 Go 通过 CGO 调用 +//! Optional C ABI compatibility layer for coarse-grained JSON helpers. +//! Production orchestration should prefer the `bat --json` process boundary or a +//! future stable SDK. This crate intentionally stays stateless: it must not own +//! downloader state, daemon lifecycle, CAS handles, or long-lived resources. #![warn(clippy::all)] @@ -17,7 +18,7 @@ use std::collections::HashMap; use std::ffi::{CStr, CString}; use std::os::raw::c_char; -/// FFI 版本号 +/// FFI compatibility API version. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// Sync snapshot JSON 输入。 @@ -111,13 +112,13 @@ struct SyncPlanView { delta: SyncDeltaView, } -/// 获取版本号(C ABI) +/// Returns the compatibility API version as an owned C string. #[no_mangle] pub extern "C" fn bat_version() -> *const c_char { c_string(VERSION).into_raw() } -/// 释放 C 字符串内存 +/// Frees C strings allocated by this crate. /// /// # Safety /// 调用者必须确保: @@ -130,7 +131,7 @@ pub unsafe extern "C" fn bat_free_string(s: *mut c_char) { } } -/// 解析 Addressables Manifest,并返回 JSON summary。 +/// Parses an Addressables manifest and returns a stateless JSON summary. #[no_mangle] pub extern "C" fn bat_manifest_inspect_json(raw_json: *const c_char) -> *mut c_char { let result = (|| -> Result { @@ -170,7 +171,7 @@ pub extern "C" fn bat_manifest_inspect_json(raw_json: *const c_char) -> *mut c_c json_result(result) } -/// 构建官方同步计划 JSON summary。 +/// Builds an official sync plan JSON summary without persisting state. #[no_mangle] pub extern "C" fn bat_sync_plan_json( current_json: *const c_char, @@ -368,16 +369,31 @@ mod tests { value } - #[test] - fn test_ffi_version() { - let version_ptr = bat_version(); - let version = take_string(version_ptr); - assert!(!version.is_empty()); + fn parse_json_response(raw: String) -> serde_json::Value { + serde_json::from_str(&raw).unwrap() } - #[test] - fn test_manifest_inspect_json() { - let catalog_json = r#"{ + fn inspect_manifest(raw_json: &str) -> serde_json::Value { + let raw_json = CString::new(raw_json).unwrap(); + parse_json_response(take_string(bat_manifest_inspect_json(raw_json.as_ptr()))) + } + + fn sync_plan(current_json: &str, previous_json: Option<&str>) -> serde_json::Value { + let current_json = CString::new(current_json).unwrap(); + let previous_json = previous_json.map(|value| CString::new(value).unwrap()); + let previous_ptr = previous_json + .as_ref() + .map(|value| value.as_ptr()) + .unwrap_or(std::ptr::null()); + + parse_json_response(take_string(bat_sync_plan_json( + current_json.as_ptr(), + previous_ptr, + ))) + } + + fn catalog_json() -> &'static str { + r#"{ "m_LocatorId": "AddressablesMainContentCatalog", "m_InternalIds": [ "synthetic/minimal.bundle" @@ -391,34 +407,96 @@ mod tests { "dependencies": ["shared.bundle"] } ] - }"#; + }"# + } - let result_ptr = bat_manifest_inspect_json(CString::new(catalog_json).unwrap().as_ptr()); - let result = take_string(result_ptr); - assert!(result.contains(r#""ok":true"#)); - assert!(result.contains(r#""resource_count":1"#)); + fn sync_snapshot_json(bundle_version: &str) -> String { + format!( + r#"{{ + "connection_group_name":"Prod-Audit", + "app_version":"1.70.0", + "bundle_version":"{bundle_version}", + "addressables_root":"https://prod-clientpatch.bluearchiveyostar.com/r93_token", + "endpoints":[ + {{ + "kind":"TableCatalog", + "platform":null, + "url":"https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes" + }} + ] + }}"# + ) + } + + #[test] + fn test_ffi_version() { + let version_ptr = bat_version(); + let version = take_string(version_ptr); + assert!(!version.is_empty()); + } + + #[test] + fn test_manifest_inspect_json() { + let result = inspect_manifest(catalog_json()); + assert_eq!(result["ok"], true); + assert_eq!(result["data"]["resource_count"], 1); + assert_eq!( + result["data"]["resources"][0]["path"], + "synthetic/minimal.bundle" + ); } #[test] fn test_sync_plan_json() { - let current = r#"{ - "connection_group_name":"Prod-Audit", - "app_version":"1.70.0", - "bundle_version":"s8tloc7lo3", - "addressables_root":"https://prod-clientpatch.bluearchiveyostar.com/r93_token", - "endpoints":[ - { - "kind":"TableCatalog", - "platform":null, - "url":"https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes" - } - ] - }"#; + let current = sync_snapshot_json("s8tloc7lo3"); + let result = sync_plan(¤t, None); + assert_eq!(result["ok"], true); + assert_eq!(result["data"]["decision"], "DownloadVerifyAndPublish"); + assert_eq!(result["data"]["should_download"], true); + assert_eq!(result["data"]["should_publish"], true); + } - let result_ptr = - bat_sync_plan_json(CString::new(current).unwrap().as_ptr(), std::ptr::null()); - let result = take_string(result_ptr); - assert!(result.contains(r#""ok":true"#)); - assert!(result.contains(r#""decision":"DownloadVerifyAndPublish""#)); + #[test] + fn stateless_ffi_json_api_repeats_without_cached_state() { + let first_manifest = inspect_manifest(catalog_json()); + let second_manifest = inspect_manifest(catalog_json()); + assert_eq!(first_manifest, second_manifest); + + let current = sync_snapshot_json("s8tloc7lo3"); + let initial_plan = sync_plan(¤t, None); + let up_to_date_plan = sync_plan(¤t, Some(¤t)); + let initial_plan_again = sync_plan(¤t, None); + + assert_eq!(initial_plan["data"]["decision"], "DownloadVerifyAndPublish"); + assert_eq!(up_to_date_plan["data"]["decision"], "UpToDate"); + assert_eq!( + initial_plan_again["data"]["decision"], + initial_plan["data"]["decision"] + ); + assert_eq!( + initial_plan_again["data"]["should_download"], + initial_plan["data"]["should_download"] + ); + } + + #[test] + fn ffi_json_api_returns_structured_errors() { + let manifest_error = + parse_json_response(take_string(bat_manifest_inspect_json(std::ptr::null()))); + assert_eq!(manifest_error["ok"], false); + assert!(manifest_error["error"] + .as_str() + .unwrap() + .contains("null pointer")); + + let sync_error = parse_json_response(take_string(bat_sync_plan_json( + std::ptr::null(), + std::ptr::null(), + ))); + assert_eq!(sync_error["ok"], false); + assert!(sync_error["error"] + .as_str() + .unwrap() + .contains("null pointer")); } } diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 3bee5f7..194b9b2 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -42,7 +42,7 @@ BlueArchive Toolkit 采用 **Monorepo + 多语言混合** 架构,旨在构建 ### 3. 数据流设计 ``` -用户请求 → CLI/API → Go 业务层 → Rust 核心/同步层 → CAS 存储 → 数据库 +用户请求 → CLI/API → Go 业务层 → bat --json / SDK → Rust 核心/同步层 → CAS 存储 → 数据库 ↓ ↓ Web UI 缓存层 (Redis) ``` @@ -128,7 +128,8 @@ current symlink → official-sync-snapshot.json + official-download-manifest.jso **后续 Go 职责**: - 提供最小稳定 CLI。 -- 包装或调用 Rust 同步入口,需要机器输出时使用 `--json` 并转发结构化 report。 +- 默认通过 `bat --json` 进程边界包装 Rust 同步入口,并转发结构化 report。 +- `bat-ffi` 仅作为可选无状态 C ABI 兼容层,不承载官方同步 daemon、下载器或 CAS handle。 - 编排 API Server、任务队列、Provider 和用户配置。 --- @@ -320,7 +321,8 @@ CREATE TABLE resource_versions ( ``` 开发机器 (本地) ├── CLI (Go) -├── Rust 库 +├── Rust bat 进程 / 未来 SDK +├── 可选 bat-ffi 兼容层 └── 连接 → 远程数据库服务器 (裸金属) ├── PostgreSQL └── Redis diff --git a/docs/architecture/adr/0001-engine-and-application-boundaries.md b/docs/architecture/adr/0001-engine-and-application-boundaries.md index 434d2ca..30bde1c 100644 --- a/docs/architecture/adr/0001-engine-and-application-boundaries.md +++ b/docs/architecture/adr/0001-engine-and-application-boundaries.md @@ -35,7 +35,7 @@ BlueArchiveToolkit 的最终目标覆盖资源同步、CAS、AssetBundle 解析 3. **Go 应用层** - 负责 CLI、资源同步、下载器、API Server、任务调度、配置、日志、Provider 编排。 - - 通过稳定 SDK、进程边界,必要时再通过 FFI 调用 Rust 引擎能力。 + - 通过稳定 SDK 或进程边界调用 Rust 能力;FFI 只作为可选兼容层。 - 不重复实现 AssetBundle 解析、Patch 算法或 CAS 对象存储核心逻辑。 4. **Web 层** @@ -48,7 +48,7 @@ BlueArchiveToolkit 的最终目标覆盖资源同步、CAS、AssetBundle 解析 1. Rust 引擎 API 必须保持业务无关,不出现 CLI 命令、HTTP 状态码、Web 页面状态。 2. Go 应用层不得复制 Rust 引擎中的 Hash、Patch、AssetBundle 核心算法。 -3. 跨边界优先级应是进程边界或稳定 SDK,其次才是 FFI;若使用 FFI,必须只暴露粗粒度批量和事务语义。 +3. 跨边界优先级应是进程边界或稳定 SDK,其次才是 FFI;若使用 FFI,必须只暴露粗粒度、无状态、一次调用一次输入输出的兼容 API。 4. 所有跨边界错误必须能映射到统一错误码和可读诊断信息。 --- @@ -63,7 +63,7 @@ BlueArchiveToolkit 的最终目标覆盖资源同步、CAS、AssetBundle 解析 代价: -1. 需要维护 FFI 或 SDK 边界。 +1. 需要维护进程、SDK 和可选 FFI 兼容边界。 2. 错误类型、数据结构和版本兼容性需要更早设计。 3. 集成测试必须覆盖跨语言调用,而不能只看单 crate 单元测试。 @@ -75,4 +75,5 @@ BlueArchiveToolkit 的最终目标覆盖资源同步、CAS、AssetBundle 解析 1. `crates/bat-cas-engine` 是 CAS 核心实现位置。 2. `infrastructure` 不再复制 CAS 存储算法,只做 `bat-core::repositories::CasRepository` 适配。 -3. Go CLI 后续通过稳定边界调用 CAS,不直接操作 CAS 内部目录结构。 +3. Go CLI 后续默认通过 `bat --json` 进程边界或稳定 SDK 调用 Rust 能力,不直接操作 CAS 内部目录结构。 +4. `bat-ffi` 只能保持为可选无状态兼容层,不能承担 daemon lifecycle、下载器状态、资源锁或 CAS handle。 diff --git a/docs/architecture/official-resource-backend.md b/docs/architecture/official-resource-backend.md index 8ae0917..502dc67 100644 --- a/docs/architecture/official-resource-backend.md +++ b/docs/architecture/official-resource-backend.md @@ -183,6 +183,12 @@ `OfficialUpdateService` 是当前 Rust 侧的自动更新核心,正式命令入口是 `bat`。 +集成边界: + +1. 当前生产和 Go CLI 默认集成路径是运行 `bat --json` 并消费结构化 report。 +2. systemd、容器或上层 Go 进程只负责守护 `bat --watch` / `bat --daemon`,不直接接管下载器内部状态。 +3. `bat-ffi` 只允许作为可选无状态 C ABI 兼容层,用于 Manifest inspect 和 sync plan 这类一次性 JSON helper;它不是官方同步 daemon、下载器、资源锁、CAS handle 或主控制面的承载位置。 + 流程是: 1. 显式执行 `--auto-discover` 或读取已审计 `server-info` 输入。 diff --git a/docs/archive/ARCHITECTURE_REVIEW_SUMMARY.md b/docs/archive/ARCHITECTURE_REVIEW_SUMMARY.md index 87fc797..51af710 100644 --- a/docs/archive/ARCHITECTURE_REVIEW_SUMMARY.md +++ b/docs/archive/ARCHITECTURE_REVIEW_SUMMARY.md @@ -288,8 +288,9 @@ BlueArchiveToolkit/ ## 📚 相关文档 - [完整架构审查报告](./ARCHITECTURE_REVIEW.md)(1900+ 行) -- [当前架构文档](./architecture/README.md) -- [CLAUDE.md](../CLAUDE.md) - 项目开发指南 +- [当前架构文档](../architecture/README.md) +- [开发指南](../guides/development.md) - 当前开发流程 +- [Agent 开发规则](../../AGENTS.md) - AI agent 长期规则 --- diff --git a/docs/guides/baseline.md b/docs/guides/baseline.md index 36e1d3e..b7b5699 100644 --- a/docs/guides/baseline.md +++ b/docs/guides/baseline.md @@ -62,10 +62,10 @@ chore: establish development baseline ```bash git status --short --branch -git check-ignore -v Cargo.lock CLAUDE.md +git check-ignore -v Cargo.lock CLAUDE.md AGENTS.md CONTRIBUTING.md ``` -`Cargo.lock` 和 `CLAUDE.md` 必须纳入版本控制。 +`Cargo.lock`、`CLAUDE.md`、`AGENTS.md` 和 `CONTRIBUTING.md` 必须纳入版本控制。 --- diff --git a/docs/guides/development.md b/docs/guides/development.md index 96e91e7..b8c5b25 100644 --- a/docs/guides/development.md +++ b/docs/guides/development.md @@ -1,5 +1,7 @@ # 开发指南 +本指南是本地开发流程的权威入口。贡献协作规则见 `../../CONTRIBUTING.md`,AI agent 长期规则见 `../../AGENTS.md`。 + ## 环境准备 ### 安装依赖 @@ -59,6 +61,14 @@ make test make fmt ``` +开发约束: + +1. 先阅读相关文档和代码,再判断实现方式。 +2. 跨模块、架构、数据格式或用户工作流变更必须先说明设计取舍。 +3. 保持改动聚焦,不做无关重构、批量格式化或元数据 churn。 +4. 公共接口、状态文件、manifest、catalog、patch 和下载流程变更必须补充测试或 fixture。 +5. 用户可见命令、运行路径、配置、状态文件、日志或缺口状态变化时,同步更新 README、指南、架构文档或 `docs/reports/CURRENT_GAPS.md`。 + ### 3. 提交 ```bash @@ -86,6 +96,10 @@ git push origin feature/your-feature-name ## 代码规范 +默认使用简体中文写文档、提交说明、开发日志和面向用户的说明。代码标识符、协议字段、数据库字段、包名和命令参数保持英文命名规范。 + +禁止使用 demo、临时实现、硬编码路径或只为当前测试通过的伪实现。确实未完成的能力应写入当前缺口文档,而不是用 `TODO` 或 `FIXME` 隐藏。 + ### Go - 遵循 [Effective Go](https://golang.org/doc/effective_go) - 使用 `gofmt` 格式化 @@ -104,16 +118,30 @@ git push origin feature/your-feature-name ## 测试 -### 当前必跑测试 +### 合并前通用门禁 ```bash +cargo fmt --check +cargo test --workspace +cargo clippy --workspace --all-targets -- -D warnings +``` + +Go CLI 尚未实现时,`go test ./...` 可能没有产品级 package 可运行;Makefile 会在空 Go 阶段清晰跳过。 + +### 常用聚焦命令 + +```bash +cargo test -p bat-core -- --nocapture cargo test -p bat-adapters -- --nocapture cargo test -p bat-ffi -- --nocapture cargo test -p bat-infrastructure -- --nocapture cargo test -p bat-infrastructure --bin bat -- --nocapture +cargo clippy -p bat-core -p bat-adapters -p bat-infrastructure --all-targets -- -D warnings ``` -Go CLI 尚未实现时,`go test ./...` 可能没有产品级 package 可运行;Makefile 会在空 Go 阶段清晰跳过。 +官方资源同步、下载、daemon、status、verify 或 repair 相关改动必须至少覆盖 `bat-infrastructure` 和 `bat` 二进制测试。 + +`bat-ffi` 只是可选无状态 C ABI 兼容层。修改 FFI 导出、JSON schema、错误返回或 `internal/ffi` CGO 包装时必须运行 `cargo test -p bat-ffi -- --nocapture`;Go CLI 和生产同步默认应通过 `bat --json` 进程边界集成。 ### 集成测试 @@ -141,6 +169,8 @@ cargo run -p bat-infrastructure --bin bat -- \ 开发环境真实下载默认写入 `./bat-resources`;如果要覆盖,必须使用 `/tmp` 或其他隔离目录,不要写入现有资源目录。 +生产或 CI 环境不得依赖安装官方启动器。需要启动器信息时,只能分析启动器资源、官方 manifest 或公开更新数据,并将解析结果固化为可验证流程。 + ### 基准测试 ```bash @@ -180,9 +210,11 @@ cargo fetch 先确认失败是否来自真实网络或本地资源路径。默认测试应使用 fixture/mock,不应依赖官方线上资源或开发机已有资源目录。 -### 3. FFI 绑定问题 +### 3. FFI 兼容层问题 -重新生成绑定: +`bat-ffi` 不是主集成边界,只用于需要 C ABI 的兼容场景。默认 Go CLI 集成优先运行 Rust `bat --json`。 + +重新构建兼容库: ```bash cd crates/bat-ffi cargo build diff --git a/docs/reports/CURRENT_GAPS.md b/docs/reports/CURRENT_GAPS.md index 74c3007..12b4b06 100644 --- a/docs/reports/CURRENT_GAPS.md +++ b/docs/reports/CURRENT_GAPS.md @@ -172,7 +172,7 @@ 现象: - `cmd/bat` 目录存在,但无 `main.go`。 -- `internal/ffi/ffi.go` 已存在,但还不是用户可运行 CLI。 +- `internal/ffi/ffi.go` 已存在,但只是可选 CGO 兼容包装,不是用户可运行 CLI,也不是默认 Go/Rust 集成边界。 - `go test ./...` 当前没有产品级 Go package 覆盖。 影响: @@ -185,6 +185,7 @@ - `bat doctor` 可运行。 - `bat --help` 命令结构稳定。 - 命令支持默认人类可读输出和 `--json` 机器输出。 +- Go CLI 默认通过 Rust `bat --json` 进程边界获取同步 report;除非明确兼容需求,不依赖 FFI。 ### G-009:API Server 和 OpenAPI 尚未实现 @@ -378,6 +379,7 @@ - 架构 README 已明确当前 Rust 官方同步入口、Go 计划边界和目标架构差异。 - 官方资源后端说明由 `docs/architecture/official-resource-backend.md` 承载。 +- `bat-ffi` 已降级为可选无状态兼容层,主集成边界明确为 `bat --json` 进程边界或未来稳定 SDK。 ### G-017:CI 未落地 diff --git a/docs/reports/historical/README.md b/docs/reports/historical/README.md new file mode 100644 index 0000000..9d4ac96 --- /dev/null +++ b/docs/reports/historical/README.md @@ -0,0 +1,14 @@ +# 历史报告归档说明 + +本目录只保存追溯资料,不代表当前项目状态。当前状态以根目录 `CURRENT_STATUS.md`、`PROJECT_PLAN.md`、`DOCS_INDEX.md` 和 `docs/reports/CURRENT_GAPS.md` 为准。 + +归档分类: + +- `current-stage/`:曾用于阶段交接或推送前核查的临时 current report,已由当前状态文档和指南取代。 +- `root/`:早期位于仓库根目录的阶段报告。 +- `week2/`、`week3/`:早期周报、阶段报告和质量报告。 +- `quality/`:早期质量状态报告。 +- `build-logs/`:历史构建、测试和 Clippy 输出。 +- `nested-docs/`:从误嵌套 `docs/docs` 移出的历史报告。 + +新增运行产物、smoke 输出、质量扫描输出和本地分析报告不要放入本目录;这些文件应写入 `/tmp`、显式的隔离输出目录,或被 `.gitignore` 覆盖的本地生成报告目录。 diff --git a/docs/reports/current-stage-prepush.md b/docs/reports/historical/current-stage/current-stage-prepush.md similarity index 100% rename from docs/reports/current-stage-prepush.md rename to docs/reports/historical/current-stage/current-stage-prepush.md diff --git a/docs/reports/current-status-handoff.md b/docs/reports/historical/current-stage/current-status-handoff.md similarity index 100% rename from docs/reports/current-status-handoff.md rename to docs/reports/historical/current-stage/current-status-handoff.md diff --git a/internal/ffi/ffi.go b/internal/ffi/ffi.go index b895be4..f530b65 100644 --- a/internal/ffi/ffi.go +++ b/internal/ffi/ffi.go @@ -1,3 +1,8 @@ +// Package ffi is an optional CGO compatibility wrapper around bat-ffi. +// +// It is not the primary Go integration path. Product CLI orchestration should +// prefer the Rust bat process boundary with --json output, or a future stable +// SDK. Keep this package stateless and limited to coarse JSON helper calls. package ffi /* @@ -13,45 +18,45 @@ extern char* bat_sync_plan_json(const char* current_json, const char* previous_j */ import "C" import ( - "errors" - "unsafe" + "errors" + "unsafe" ) func Version() (string, error) { - ptr := C.bat_version() - if ptr == nil { - return "", errors.New("bat_version returned nil") - } - defer C.bat_free_string((*C.char)(unsafe.Pointer(ptr))) - return C.GoString(ptr), nil + ptr := C.bat_version() + if ptr == nil { + return "", errors.New("bat_version returned nil") + } + defer C.bat_free_string((*C.char)(unsafe.Pointer(ptr))) + return C.GoString(ptr), nil } func InspectManifest(rawJSON string) (string, error) { - cRaw := C.CString(rawJSON) - defer C.free(unsafe.Pointer(cRaw)) + cRaw := C.CString(rawJSON) + defer C.free(unsafe.Pointer(cRaw)) - ptr := C.bat_manifest_inspect_json(cRaw) - if ptr == nil { - return "", errors.New("bat_manifest_inspect_json returned nil") - } - defer C.bat_free_string((*C.char)(unsafe.Pointer(ptr))) - return C.GoString(ptr), nil + ptr := C.bat_manifest_inspect_json(cRaw) + if ptr == nil { + return "", errors.New("bat_manifest_inspect_json returned nil") + } + defer C.bat_free_string((*C.char)(unsafe.Pointer(ptr))) + return C.GoString(ptr), nil } func BuildSyncPlan(currentJSON, previousJSON string) (string, error) { - cCurrent := C.CString(currentJSON) - defer C.free(unsafe.Pointer(cCurrent)) + cCurrent := C.CString(currentJSON) + defer C.free(unsafe.Pointer(cCurrent)) - var cPrevious *C.char - if previousJSON != "" { - cPrevious = C.CString(previousJSON) - defer C.free(unsafe.Pointer(cPrevious)) - } + var cPrevious *C.char + if previousJSON != "" { + cPrevious = C.CString(previousJSON) + defer C.free(unsafe.Pointer(cPrevious)) + } - ptr := C.bat_sync_plan_json(cCurrent, cPrevious) - if ptr == nil { - return "", errors.New("bat_sync_plan_json returned nil") - } - defer C.bat_free_string((*C.char)(unsafe.Pointer(ptr))) - return C.GoString(ptr), nil + ptr := C.bat_sync_plan_json(cCurrent, cPrevious) + if ptr == nil { + return "", errors.New("bat_sync_plan_json returned nil") + } + defer C.bat_free_string((*C.char)(unsafe.Pointer(ptr))) + return C.GoString(ptr), nil }