From 81752e763edbee72e2cff93765ecd8e320cf0981 Mon Sep 17 00:00:00 2001 From: nav Date: Wed, 8 Apr 2026 23:32:37 +0000 Subject: [PATCH] docs: add yonexus acceptance baseline --- ACCEPTANCE.md | 241 +++++++++++++++++++++++++++++++++++++++++++++++++ TASKLIST.md | 24 +++++ Yonexus.Client | 2 +- Yonexus.Server | 2 +- 4 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 ACCEPTANCE.md diff --git a/ACCEPTANCE.md b/ACCEPTANCE.md new file mode 100644 index 0000000..745b66e --- /dev/null +++ b/ACCEPTANCE.md @@ -0,0 +1,241 @@ +# Yonexus v1 验收与回归清单 + +本清单服务于 `TASKLIST.md` 中的 YNX-1205。 + +目标:给后续开发、联调、回归提供一份统一基线,避免只凭“能跑起来了”判断完成度。 + +--- + +## 1. 范围 + +覆盖对象: + +- `Yonexus.Protocol` +- `Yonexus.Server` +- `Yonexus.Client` +- Server ↔ Client 联调主流程 +- pairing / auth / heartbeat / rule dispatch 关键失败路径 + +不覆盖: + +- 多服务器拓扑 +- 离线消息队列 +- 管理 UI +- 复杂规则匹配 + +--- + +## 2. 协议层验收 + +### 2.1 builtin 编解码 + +必须验证: + +- `builtin::{json}` 能正确编码 +- `builtin::{json}` 能正确解码 +- malformed builtin 消息会返回标准错误 +- 未支持 builtin type 可被明确拒绝 + +### 2.2 rule message 解析 + +必须验证: + +- `${rule}::${content}` 可被正确解析 +- `${rule}::${sender}::${content}` 可被正确解析 +- `content` 中包含 `::` 时不会被错误拆分 +- `builtin` 不能作为普通 rule 注册 + +### 2.3 共享认证约束 + +必须验证: + +- nonce 长度固定为 24 +- timestamp 新鲜度窗口符合协议 +- 签名输入序列化规则固定且可复用 + +--- + +## 3. Server 单体验收 + +### 3.1 启动与配置 + +必须验证: + +- 缺失 `followerIdentifiers` 会 fail fast +- 缺失 `notifyBotToken` / `adminUserId` / `listenPort` 会 fail fast +- 非法 `listenPort` 会 fail fast +- 启动时会加载持久化记录并补齐 allowlist 初始记录 + +### 3.2 pairing + +必须验证: + +- 未配对 allowlisted client 进入 `pair_required` +- server 创建 pending pairing 记录 +- pairing code 不通过 Yonexus WebSocket 下发 +- pairing 通知失败时返回 `admin_notification_failed` +- 正确 pairing code 返回 `pair_success` +- 错误 pairing code 返回 `invalid_code` +- 过期 pairing code 返回 `expired` + +### 3.3 auth + +必须验证: + +- paired client 可通过合法签名拿到 `auth_success` +- 非 allowlisted identifier 被拒绝 +- 未配对 identifier 不可通过 auth +- public key 不匹配会失败 +- stale/future timestamp 会失败 +- nonce collision 会触发 `re_pair_required` +- 超过 `>10 attempts / 10s` 会触发 `re_pair_required` + +### 3.4 liveness + +必须验证: + +- heartbeat 后更新 `lastHeartbeatAt` +- 7 分钟无心跳转为 `unstable` +- 11 分钟无心跳转为 `offline` +- `offline` 时发送 `disconnect_notice` 并断开连接 + +### 3.5 messaging + +必须验证: + +- 未认证 client 发送 rule message 会被拒绝 +- 已认证 client 的消息会被重写为 `${rule}::${sender}::${content}` +- duplicate rule 注册默认失败 +- `sendMessageToClient()` 对离线 client 返回失败 + +--- + +## 4. Client 单体验收 + +### 4.1 启动与本地状态 + +必须验证: + +- 缺失 state 文件时可初始化最小状态 +- 首次启动会自动生成 Ed25519 keypair +- 重启后不会重复生成 keypair +- 已有 secret 时可进入 auth 流程 + +### 4.2 连接与重连 + +必须验证: + +- 可连接到可用的 server +- server 不可用时会按退避策略重连 +- 手动断开不会误触发自动重连 +- 成功重连后退避计数会重置 + +### 4.3 pairing / auth + +必须验证: + +- 收到 `pair_request` 后进入待确认状态 +- 可提交 pairing code +- 收到 `pair_success` 后保存 secret +- 收到 `hello_ack(auth_required)` 后自动发 `auth_request` +- 收到 `auth_success` 后进入 authenticated +- 收到 `re_pair_required` 后清理本地 secret 并回退到 `pair_required` + +### 4.4 heartbeat / dispatch + +必须验证: + +- authenticated 后启动 heartbeat loop +- 断线或未认证时停止 heartbeat loop +- `registerRule()` 拒绝 `builtin` +- `sendMessageToServer()` 拒绝 `builtin::` 和非法格式 + +--- + +## 5. 联调验收 + +### 5.1 首次配对闭环 + +必须通过: + +1. Client 连接 Server +2. Client 发送 `hello` +3. Server 返回 `hello_ack(pair_required)` +4. Server 创建 pairing request 并发出管理员通知 +5. Client 提交正确 pairing code +6. Server 返回 `pair_success` +7. Client 保存 secret +8. Client 发送 `auth_request` +9. Server 返回 `auth_success` +10. Client 进入 authenticated 并开始 heartbeat + +### 5.2 正常重连闭环 + +必须通过: + +1. 已配对 Client 重连 +2. `hello_ack(auth_required)` +3. Client 发送合法 `auth_request` +4. Server 返回 `auth_success` +5. 心跳恢复正常 + +### 5.3 规则消息闭环 + +必须通过: + +1. Client 认证成功 +2. Client 调用 `sendRuleMessage()` +3. Server 收到并完成 sender rewrite +4. Server 规则处理器命中 exact match +5. Server 调用 `sendRuleMessageToClient()` 回发消息 +6. Client 本地规则处理器收到消息 + +--- + +## 6. 失败路径回归矩阵 + +每次关键改动后,至少回归以下场景: + +- pairing code 错误 +- pairing 过期 +- pairing 通知失败 +- unsupported protocol version +- malformed builtin frame +- unknown identifier +- invalid signature +- stale timestamp +- future timestamp +- nonce collision +- handshake rate limit +- duplicate active connection 竞争 +- 未认证连接发送 rule message + +--- + +## 7. 自动化建议 + +建议的最小自动化分层: + +- `Yonexus.Protocol`: 单元测试,锁定 codec / types / auth helpers +- `Yonexus.Server`: 单元测试,覆盖 runtime + pairing/auth/liveness 核心逻辑 +- `Yonexus.Client`: 单元测试,覆盖 state/transport/runtime 主状态机 +- Server + Client: 集成测试,覆盖 happy path 与关键失败路径 + +建议把通过条件固化为: + +- `Yonexus.Protocol` 测试必须全绿 +- Server / Client 类型检查必须全绿 +- 新增联调测试后,happy path 与至少一组安全失败路径必须全绿 + +--- + +## 8. 当前对应关系 + +与 `TASKLIST.md` 对应关系: + +- YNX-1101:协议单元测试 +- YNX-1102:Server 单元测试 +- YNX-1103:Client 单元测试 +- YNX-1104:Server-Client 集成测试 +- YNX-1105:失败路径测试矩阵 +- YNX-1205:协议测试与验收清单(本文件) diff --git a/TASKLIST.md b/TASKLIST.md index e5e793c..7ec16c5 100644 --- a/TASKLIST.md +++ b/TASKLIST.md @@ -1041,13 +1041,29 @@ ## Phase 12 — 文档与交付 ### YNX-1201 补齐 Server README +**状态** +- [x] 已完成(2026-04-08) + **目标** - Server 仓库可独立被安装与使用 +**已完成内容** +- 已补齐 `Yonexus.Server/README.md` +- 已写明当前实现范围、配置字段、启动/连接流程、公开 API、持久化语义与开发方式 +- 已明确当前限制项(真实 Discord DM、测试覆盖、生命周期集成等),避免 README 过度承诺 + ### YNX-1202 补齐 Client README +**状态** +- [x] 已完成(2026-04-08) + **目标** - Client 仓库可独立被安装与使用 +**已完成内容** +- 已补齐 `Yonexus.Client/README.md` +- 已写明配置模型、启动/配对/认证流程、公开 API、本地 state 结构与开发方式 +- 已明确当前限制项(测试、配对输入 UX、生命周期集成等),方便后续交接和联调 + ### YNX-1203 输出部署文档 **目标** - 写清楚单主多从部署方式、配置示例、配对流程 @@ -1057,9 +1073,17 @@ - 写清楚常见报错、状态含义、恢复步骤 ### YNX-1205 输出协议测试与验收清单 +**状态** +- [x] 已完成(2026-04-08) + **目标** - 让后续改动有统一回归基线 +**已完成内容** +- 已新增 `ACCEPTANCE.md` +- 已按协议层、Server、Client、联调、失败路径回归矩阵拆分验收项 +- 已把 `YNX-1101`~`YNX-1105` 与具体验收/测试目标建立对应关系,方便后续补自动化测试时直接对照 + --- ## 推荐执行顺序(最小闭环) diff --git a/Yonexus.Client b/Yonexus.Client index ddeed9a..4322604 160000 --- a/Yonexus.Client +++ b/Yonexus.Client @@ -1 +1 @@ -Subproject commit ddeed9a7b735d41ced855cc3928de60b90f7fa04 +Subproject commit 4322604f78da710b9a1f9fe8b57b1459c2088328 diff --git a/Yonexus.Server b/Yonexus.Server index 988170d..25e1867 160000 --- a/Yonexus.Server +++ b/Yonexus.Server @@ -1 +1 @@ -Subproject commit 988170dcf6df315f56b627d992e61005618ba739 +Subproject commit 25e1867adff0a7a473ad287da3c955ba4f65492d