From 8d9333d8b0c6f91d2fc1f5ef6ccd4d95632a5eb7 Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Tue, 8 Sep 2026 12:51:23 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E8=AE=B0=E5=BD=95=E7=94=9F=E4=BA=A7?= =?UTF-8?q?=E9=AA=8C=E6=94=B6=E5=B9=B6=E9=AA=8C=E8=AF=81=20Sync=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 + .../OpenNexus验收报告-2026-09-08.json | 374 ++++++++++++++++++ .../OpenNexus验收报告-2026-09-08.md | 78 ++++ server sync/Dockerfile | 2 +- server sync/README.md | 6 +- 5 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 docs/development/OpenNexus验收报告-2026-09-08.json create mode 100644 docs/development/OpenNexus验收报告-2026-09-08.md diff --git a/docs/README.md b/docs/README.md index 2a26352..8f4a1e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,7 @@ # OpenNexus 文档索引 +最新:[2026-09-08 验收报告与 Sync 测试部署](development/OpenNexus验收报告-2026-09-08.md)。全量验收未通过,含已复现阻塞项与逐 ID 结果。 + > 第二阶段收尾:标准 Agent/RAG Benchmark 与报告页、函数图预览、三格式快照导出及真实 Provider/MCP 结果见[实现与验收记录](development/第二阶段收尾实现与验收-2026-09-07.md)。当前分支尚未合并,不更改下文历史 main 基线。 本目录集中保存团队开发期间需要长期维护的架构、接口、实现、协作和问题复盘文档。文档按用途分类,避免设计约束、开发记录与故障复盘混放。 diff --git a/docs/development/OpenNexus验收报告-2026-09-08.json b/docs/development/OpenNexus验收报告-2026-09-08.json new file mode 100644 index 0000000..81b289f --- /dev/null +++ b/docs/development/OpenNexus验收报告-2026-09-08.json @@ -0,0 +1,374 @@ +{ + "baseline": "e064f46", + "deployment_patch": "server sync/Dockerfile: use opennexus instead of reserved sync system user", + "overall": "NOT_PASSED", + "full_cases_passed": 0, + "full_cases_total": 30, + "checks": [ + { + "name": "backend", + "command": [ + "uv", + "run", + "--directory", + "backend", + "pytest", + "-q", + "--junitxml=G:\\OSProject\\NotesAgent-phase3\\.build\\acceptance\\e064f46\\backend.xml" + ], + "exit_code": 0, + "seconds": 295.9, + "finished_utc": "2026-09-08T04:38:12.067022+00:00" + }, + { + "name": "packaged-core", + "command": [ + "uv", + "run", + "--directory", + "backend", + "pytest", + "-q", + "tests/test_sidecar_auth.py", + "--junitxml=G:\\OSProject\\NotesAgent-phase3\\.build\\acceptance\\e064f46\\packaged-core.xml" + ], + "exit_code": 0, + "seconds": 7.264, + "finished_utc": "2026-09-08T04:38:19.340103+00:00" + }, + { + "name": "frontend", + "command": [ + "pnpm", + "--dir", + "frontend", + "exec", + "vitest", + "run", + "--reporter=default", + "--reporter=junit", + "--outputFile.junit=G:\\OSProject\\NotesAgent-phase3\\.build\\acceptance\\e064f46\\frontend.xml" + ], + "exit_code": 1, + "seconds": 69.319, + "finished_utc": "2026-09-08T04:34:25.531377+00:00" + }, + { + "name": "types", + "command": [ + "pnpm", + "--dir", + "frontend", + "type-check" + ], + "exit_code": 0, + "seconds": 33.504, + "finished_utc": "2026-09-08T04:34:59.212101+00:00" + }, + { + "name": "build", + "command": [ + "pnpm", + "--dir", + "frontend", + "build" + ], + "exit_code": 0, + "seconds": 148.893, + "finished_utc": "2026-09-08T04:37:28.169120+00:00" + }, + { + "name": "rust-fmt", + "command": [ + "cargo", + "fmt", + "--manifest-path", + "frontend/src-tauri/Cargo.toml", + "--check" + ], + "exit_code": 0, + "seconds": 0.604, + "finished_utc": "2026-09-08T04:33:16.807564+00:00" + }, + { + "name": "rust-tests", + "command": [ + "cargo", + "test", + "--manifest-path", + "frontend/src-tauri/Cargo.toml", + "--features", + "desktop", + "--all-targets", + "--locked" + ], + "exit_code": 0, + "seconds": 346.627, + "finished_utc": "2026-09-08T04:39:03.456525+00:00" + }, + { + "name": "rust-clippy", + "command": [ + "cargo", + "clippy", + "--manifest-path", + "frontend/src-tauri/Cargo.toml", + "--features", + "desktop", + "--all-targets", + "--locked", + "--", + "-D", + "warnings" + ], + "exit_code": 0, + "seconds": 2.395, + "finished_utc": "2026-09-08T04:39:05.874012+00:00" + }, + { + "name": "sync", + "command": [ + "uv", + "run", + "--directory", + "server sync", + "pytest", + "-q", + "--junitxml=G:\\OSProject\\NotesAgent-phase3\\.build\\acceptance\\e064f46\\sync.xml" + ], + "exit_code": 0, + "seconds": 35.96, + "finished_utc": "2026-09-08T04:33:52.124616+00:00" + }, + { + "name": "community", + "command": [ + "uv", + "run", + "--directory", + "community-server", + "pytest", + "-q", + "--junitxml=G:\\OSProject\\NotesAgent-phase3\\.build\\acceptance\\e064f46\\community.xml" + ], + "exit_code": 0, + "seconds": 16.786, + "finished_utc": "2026-09-08T04:34:08.920635+00:00" + }, + { + "name": "smoke", + "command": [ + "G:\\OSProject\\NotesAgent-phase3\\backend\\.venv\\Scripts\\python.exe", + "scripts/phase3-isolated-smoke.py" + ], + "exit_code": 0, + "seconds": 10.452, + "finished_utc": "2026-09-08T04:34:19.377480+00:00" + }, + { + "name": "docs", + "command": [ + "python", + "scripts/check-doc-links.py" + ], + "exit_code": 0, + "seconds": 32.323, + "finished_utc": "2026-09-08T04:34:51.736262+00:00" + }, + { + "name": "diff", + "command": [ + "git", + "diff", + "--check", + "--", + ".", + ":(exclude)backend/data/vault" + ], + "exit_code": 0, + "seconds": 0.721, + "finished_utc": "2026-09-08T04:34:52.492509+00:00" + } + ], + "frontend_retry": { + "tests": 498, + "failed": 0, + "max_workers": 2, + "first_run_failure_retained": true + }, + "cases": [ + { + "id": "A-01", + "status": "partial", + "reason": "打包Core运行与完整性库测试通过;缺签名安装包、干净VM、20次冷启动和更新证据" + }, + { + "id": "A-02", + "status": "partial", + "reason": "鉴权和真实Core测试通过;未完成全部路由各100次负向与秘密扫描矩阵" + }, + { + "id": "A-03", + "status": "failed", + "reason": "专项复现普通桌面请求忽略已取消信号及自定义超时;缺提交确认" + }, + { + "id": "A-04", + "status": "partial", + "reason": "进程关闭与代际隔离有证据;缺6次崩溃/子孙进程与更新中断矩阵" + }, + { + "id": "B-01", + "status": "partial", + "reason": "域检查与真实管道凭据写入通过;缺完整四域/三秘密及泄漏扫描矩阵" + }, + { + "id": "B-02", + "status": "partial", + "reason": "100条Fernet迁移和冲突测试通过;缺完整环境钥匙/坏token/空库/断点矩阵" + }, + { + "id": "B-03", + "status": "not_implemented", + "reason": "手动锁定和改密有测试;OS锁屏联动与备份恢复未完成" + }, + { + "id": "B-04", + "status": "failed", + "reason": "复现两个已解锁Broker先后写入导致先提交凭据丢失;缺完整迁移故障注入和清除链路" + }, + { + "id": "C-01", + "status": "not_implemented", + "reason": "生产OS沙箱未实现,禁止启动不是正向隔离验收" + }, + { + "id": "C-02", + "status": "not_implemented", + "reason": "生产OS沙箱未实现,禁止启动不是正向隔离验收" + }, + { + "id": "C-03", + "status": "not_implemented", + "reason": "生产OS沙箱未实现,禁止启动不是正向隔离验收" + }, + { + "id": "C-04", + "status": "not_implemented", + "reason": "生产OS沙箱未实现,禁止启动不是正向隔离验收" + }, + { + "id": "D-01", + "status": "not_implemented", + "reason": "Rust扩展管理全生命周期未实现" + }, + { + "id": "D-02", + "status": "not_implemented", + "reason": "Rust扩展管理全生命周期未实现" + }, + { + "id": "D-03", + "status": "not_implemented", + "reason": "Rust扩展管理全生命周期未实现" + }, + { + "id": "D-04", + "status": "not_implemented", + "reason": "Rust扩展管理全生命周期未实现" + }, + { + "id": "E-01", + "status": "blocked", + "reason": "缺完整桌面链路、签名RC和双实机端到端证据" + }, + { + "id": "E-02", + "status": "blocked", + "reason": "缺完整桌面链路、签名RC和双实机端到端证据" + }, + { + "id": "E-03", + "status": "blocked", + "reason": "缺完整桌面链路、签名RC和双实机端到端证据" + }, + { + "id": "E-04", + "status": "blocked", + "reason": "缺完整桌面链路、签名RC和双实机端到端证据" + }, + { + "id": "E-05", + "status": "blocked", + "reason": "缺完整桌面链路、签名RC和双实机端到端证据" + }, + { + "id": "S-01", + "status": "not_implemented", + "reason": "完整Rust同步客户端、冲突和分类链路未实现" + }, + { + "id": "S-02", + "status": "not_implemented", + "reason": "完整Rust同步客户端、冲突和分类链路未实现" + }, + { + "id": "S-03", + "status": "not_implemented", + "reason": "完整Rust同步客户端、冲突和分类链路未实现" + }, + { + "id": "S-04", + "status": "partial", + "reason": "真实PostgreSQL/MinIO双worker:100竞争1成功99冲突、重复100次不增加revision;分页和全部故障矩阵未完成" + }, + { + "id": "S-05", + "status": "partial", + "reason": "100MiB对象与重复complete100次通过;缺两worker offset/清理竞争各100轮矩阵" + }, + { + "id": "S-06", + "status": "partial", + "reason": "真实服务未认证100次拒绝及撤销后下一请求拒绝通过;完整跨账号/过期/故障矩阵未完成" + }, + { + "id": "S-07", + "status": "not_implemented", + "reason": "测试实例已部署;一致备份/全新实例恢复/RPO/RTO未实现或未验证" + }, + { + "id": "S-08", + "status": "not_implemented", + "reason": "完整Rust同步客户端、冲突和分类链路未实现" + }, + { + "id": "S-09", + "status": "not_run", + "reason": "未运行30分钟10客户端负载和4并发100MiB峰值测量;单次100MiB结果不可替代" + } + ], + "test_service": { + "url": "http://yui.kronecker.cc:18080", + "purpose": "user-authorized HTTP test deployment", + "image": "opennexus-sync:e064f46-audit1", + "production_approved": false + }, + "evidence_directory": ".build/acceptance/e064f46", + "contains_credentials": false, + "remote_checks": { + "100MiB_sha256_roundtrip": true, + "complete_retries": 100, + "CAS_attempts": 100, + "CAS_success": 1, + "CAS_conflicts": 99, + "revision_idempotent_retries": 100, + "unauthenticated_denials": 100, + "revoked_next_status": 401, + "observed_ready_failure_seconds": 3.005, + "observed_ready_recovery_seconds": 0.767, + "restart_ready_seconds": 6.339, + "100MiB_hash_after_restart": true, + "all_fault_matrix_passed": false + } +} diff --git a/docs/development/OpenNexus验收报告-2026-09-08.md b/docs/development/OpenNexus验收报告-2026-09-08.md new file mode 100644 index 0000000..058088d --- /dev/null +++ b/docs/development/OpenNexus验收报告-2026-09-08.md @@ -0,0 +1,78 @@ +# OpenNexus 验收报告(2026-09-08) + +结论:**全量生产验收未通过;Sync 独立 HTTP 测试服务已部署并通过本轮服务集成检查。** + +代码基线为 `e064f46`,工作树为 `G:\OSProject\NotesAgent-phase3`。部署中修复了 Dockerfile 系统用户名冲突;部署镜像标识为 `opennexus-sync:e064f46-audit1`。用户明确要求临时测试环境,无需 HTTPS。个人 Vault 的三处已有改动未纳入验收或提交。 + +验收标准采用[第三阶段生产化规划](../architecture/第三阶段生产化工程规划与验收目标.md),逐 ID 结果见[机器可读报告](OpenNexus验收报告-2026-09-08.json)。30 个完整验收 ID 中,**0 个具备该行全部断言的通过证据**;这是发布门禁结果,不表示所有已实现功能失效。统一生产验收 runner 尚未交付,本次没有声称执行该 runner。 + +## 已执行检查 + +| 检查 | 结果 | +| --- | --- | +| AI Core 后端 | 894 项通过,0 skip | +| Sync 隔离协议测试 | 23 项通过,SQLite,不替代下方真实服务检查 | +| Community | 13 项通过 | +| 前端首轮 | 497 通过、1 失败:EditorPane 源码编辑器等待渲染超时 | +| 前端复跑 | `--maxWorkers=2`,498 项全部通过;首轮失败记录保留 | +| 前端类型 / 生产构建 | 通过;仍有既有大分块构建警告 | +| Rust desktop 全目标 | 21 项通过:库 15、桌面命令 3、包校验 1、真实 Core 集成 2 | +| Rust fmt / Clippy | 通过,Clippy 使用 `--features desktop --all-targets --locked -- -D warnings` | +| 打包 Core | 独立可执行文件的 4 项测试通过 | +| 三服务隔离冒烟、文档链接、代码 diff 检查 | 通过,diff 排除个人 Vault | +| 新增桌面取消 / 超时探针 | 2 项失败,见 F-02 | +| 新增凭据并发写入探针 | 1 项失败,见 F-01 | + +本地 Windows GNU 工具链为 1.98.1;未将其作为 MSVC 签名发布证据。首轮同时运行 Rust 编译和前端测试,前端限并发复跑成功与负载有关,但单次复跑不能证明该等待问题已消除。 + +## 阻塞问题 + +### F-01 / P1:多个已解锁凭据实例会丢失已提交记录 + +两个 `CredentialBroker` 打开同一快照后,A 写入 first 并成功返回,B 写入 second 并成功返回。重新解锁快照,first 已不存在。新验收探针复现失败,输入均为临时目录中的虚构秘密。 + +代码位置:[credentials.rs](../../frontend/src-tauri/src/credentials.rs) 的 `unlock`、`put` 和 `persist` 调用。当前仅有实例内内存快照与 Mutex,没有保险库跨实例写锁或磁盘版本冲突检测。桌面 Host 未限制整个应用为单实例,因此这不是仅通过测试构造才能出现的使用方式。 + +修复退出条件:同一保险库最多一个写入拥有者,或持久化前执行不可绕过的版本 CAS;并发、迁移和改密冲突必须拒绝或合并,已成功提交记录不得丢失。关联 B-04 和凭据发布安全门。 + +### F-02 / P1:普通桌面请求忽略取消和自定义超时 + +在调用前已 abort 的 POST 仍进入 `hostInvoke('core_request')` 并返回成功;设置 1 ms 超时、模拟 Host 30 ms 后响应,仍得到成功结果。两项独立探针均失败。 + +代码位置:[apiClient.ts](../../frontend/src/services/apiClient.ts) 的 desktop 分支。AbortController 仅连接 Web fetch,原生命令没有取消标识或截止时间;超时标记只在异常分支检查。SSE 的独立取消测试通过,不能覆盖常规 JSON/二进制请求。 + +修复退出条件:调用前取消不得发出请求;在途取消由 Host 跟踪;超时必须返回稳定错误;已经提交的变更提供可查询操作结果,不能依靠自动重试掩盖结果未知。关联 A-03。 + +### F-03 / 已修复:Dockerfile 无法构建 + +真实基础镜像已包含系统用户 `sync`,`useradd ... sync` 返回 exit 9。将服务用户改为 `opennexus`,保留 UID 10001。修复后镜像构建成功并以非 root 用户运行,以下部署检查均使用修复后的镜像。 + +## Sync 测试部署与真实服务结果 + +- 地址:`http://yui.kronecker.cc:18080`。这是 API 服务,不包含桌面同步客户端或网页管理界面。 +- 服务器:Debian 13 x64,Docker Compose;独立项目 `opennexus-sync-test`,配置目录 `/opt/opennexus-sync`。未修改原 Nginx 站点,未占用其他应用的 8080。 +- Sync 运行两个 Uvicorn worker,共享持久 staging;仅 18080 对外发布。PostgreSQL 和 MinIO 无宿主机公开端口。 +- 私有 Bucket,Sync 使用独立、非 root 的对象存储账号;历史对象没有 DeleteObject 授权,删除权限仅用于 health-probe 前缀。 +- 服务器 `.env` 与 `/opt/opennexus-sync/test-account.json` 为 root 所有、权限 600。后者含本轮独立随机测试账号,可由服务器管理员读取。SSH 密码未写入仓库、配置或报告。 + +| 真实服务操作 | 实测结果 | +| --- | --- | +| 从当前工作机访问公开 health / ready | 200,ready schema=1 | +| 100 MiB 对象上传、每 10 MiB 查询 offset、另一设备下载 | 长度 104857600,SHA-256 一致 | +| 上述对象重复 complete 100 次 | 全部成功;本次往返加重试用时 5.611 秒,不作为性能基准 | +| 20 个并发客户端线程,共 100 次同 file/base 不同 operation_id 提交 | 1 成功,99 次 409 | +| 成功提交再重复 100 次 / 复用同键改变 payload | 返回相同修订,changes 仍只有 1 条 / 改 payload 返回 409 | +| 未认证访问对象 100 次 | 全部 401 | +| 撤销第二设备后的下一次访问 | 401 | +| 暂停该测试栈 MinIO,随后恢复 | 首次观察到 ready=503 为 3.005 秒;恢复后首次 200 为 0.767 秒;未覆盖所有 worker、DB/staging 故障矩阵 | +| 重启 Sync 容器 | 6.339 秒后 ready=200;已提交 100 MiB 对象长度与摘要仍一致 | + +镜像锁定记录:PostgreSQL 为 `postgres@sha256:00bc86618629af00d2937fdc5a5d63db3ff8450acf52f0636ec813c7f4902929`;MinIO 为 `minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e`;Sync 镜像 ID 为 `sha256:073e4da92f1d15a8548b25ba85a095a8db34386036b136e1fa5d47e4f3c2d0f9`。尚未完成漏洞扫描、生产 TLS、备份恢复及 30 分钟负载门禁。 + +运维检查:`sudo docker compose -f /opt/opennexus-sync/compose.json ps`。只停止该测试服务可执行 `sudo docker compose -f /opt/opennexus-sync/compose.json stop sync`;保留 volumes,不使用 `down -v`。故障测试结束时 MinIO 已恢复,三个服务均运行。 + +## 证据与剩余范围 + +原始命令、耗时、日志、JUnit、探针源码位于工作树 `.build/acceptance/e064f46/`。`checks.json` 保留首次前端失败;`frontend-retry.xml` 是独立复跑结果;`transport-probe.log` 和 `credential-probe.log` 保留三项失败断言。服务器对应记录为 `/opt/opennexus-sync/acceptance.log`、`acceptance-results.json`、`fault-results.log`。本地 `.build` 为忽略目录,不属于 Git 发布产物,也没有远端 CI 180 天留存保证。 + +完整 AI Vault 接入、OS 沙箱、Rust 扩展安装与回滚、Rust 同步 push/pull 和冲突 UI 仍未实现。生产备份恢复、所有故障边界、签名安装更新包和两台独立桌面设备 E-01–05 尚未验收。不得把当前服务协议测试称为完整客户端同步通过。 diff --git a/server sync/Dockerfile b/server sync/Dockerfile index 4481c81..7503f0b 100644 --- a/server sync/Dockerfile +++ b/server sync/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /service COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-dev COPY sync_server ./sync_server -RUN useradd --uid 10001 --create-home sync && mkdir /staging && chown sync /staging +RUN useradd --uid 10001 --create-home opennexus && mkdir /staging && chown opennexus /staging USER 10001 ENV SYNC_STAGING_DIR=/staging CMD ["/service/.venv/bin/python", "-m", "sync_server", "serve"] diff --git a/server sync/README.md b/server sync/README.md index fca5926..69022c4 100644 --- a/server sync/README.md +++ b/server sync/README.md @@ -1,4 +1,4 @@ -# NotesAgent Sync 服务原型 +# OpenNexus Sync 服务原型 协议及限制见 [Sync v1](../docs/contracts/Sync-v1契约.md)。服务独立于 AI Core,生产入口仅支持 PostgreSQL 和 S3。当前尚未达到 M3 运维退出条件。 @@ -18,8 +18,10 @@ uv run pytest 2. 启动 `docker compose up -d database objects`。由管理员在 MinIO 建立 `notesagent` 私有 Bucket,并创建仅能访问该 Bucket 的同步账号;服务不使用 MinIO root 身份。 3. 执行 `docker compose run --rm sync /service/.venv/bin/python -m sync_server create-user`,密码交互输入,不放命令参数。 4. 启动 `docker compose up -d sync`;使用 Caddy 示例配置 TLS。8080 仅绑定本机,不直接公开明文 HTTP。 -5. 检查 `/health`、`/ready` 及经过授权的上传/读取;`/ready` 当前不探测对象存储。 +5. 检查 `/health`、`/ready` 及经过授权的上传/读取;`/ready` 探测数据库 schema、staging 读写和对象存储测试前缀。 Dockerfile/Compose 是待实测部署配置,不能视为已验证安装程序。未提供自动 MinIO 初始化和备份恢复命令,发布前必须完成。 +2026-09-08 已在独立 Docker 项目完成真实 PostgreSQL/MinIO 双 worker 测试部署,修正基础镜像中的 `sync` 系统用户名冲突。测试专用 HTTP 地址、故障检查、完整验收缺口与运维入口见[验收报告](../docs/development/OpenNexus验收报告-2026-09-08.md)。仓库通用 Compose 的生产 TLS 与备份恢复仍未通过发布验收。 + 升级前同时备份 PostgreSQL 和 Bucket,停止提交以取得一致切点。Schema v1 拒绝未知数据库版本,不自动降级。当前历史永久保留,容量管理不能手动删除被历史引用的对象。