docs: import wiki at afc651f75a3a

ila
2026-08-07 16:36:17 +08:00
parent ec0ef70e5e
commit 1d6efc282a
+224
@@ -0,0 +1,224 @@
<!-- docs-wiki-sync:docs/05-coding-rules.md@afc651f75a3abc2676bb13aa8a80f6aa6a25a72e -->
> 同步来源:[`docs/05-coding-rules.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/05-coding-rules.md) · commit `afc651f75a3a`
# 编码规则
> 每次写代码前完整读取。需求决定“该不该做”,架构和 API 决定“如何接入”。
## 1. 黄金法则
1. 不臆造源码结构、拼多多页面、字段、接口或依赖。
2. 一次只完成当前任务,不夹带后续功能。
3. 高风险自动化先做可停止、可观察的最小探针。
4. 代码完成不等于任务完成;必须有可运行验证证据。
5. 安全规则不能只写在提示词或 UI 中,服务端和工作流都要强制执行。
## 2. 动手前
- 读取当前任务关联的需求、US、IX、架构、API 和页面路由。
- 使用 codebase-memory 图谱优先发现代码;非代码和字符串搜索可用 `rg`。
- 先核实上游 Roubao 的真实模块和测试,再决定修改位置。
- 检查任务 `write_paths` 与用户已有改动,不覆盖无关内容。
- 不确定的业务决策标为 blocker,不让实现自行发明。
## 3. 领域边界
- 原始任务输入是不可变事实;AI 派生字段单独保存。
- 任务状态只能由领域状态机转换,HTTP 路由和 UI 不直接写状态。
- Android 工作流只通过 task client、automation adapter 和 AI client 访问外部边界。
- 管理页面调用 usecase,不直接执行 SQL。
- 供应商 VLM SDK 类型停留在 infrastructure adapter。
- 拼多多 UI 文本、资源标识和版本差异集中维护,不散落在工作流。
## 4. Android 自动化规则
- 动作前确认目标 App 包名和预期页面,动作后确认状态确实变化。
- 节点必须可见、启用且唯一匹配;资源 ID 重复时不得选择第一个结果。
- 首页、搜索输入和结果页的已验证路径只用可访问性语义和稳定文本,禁止坐标降级。
其他页面如需坐标必须另立任务、固定版本并提供语义方案确实不可用的证据。
- 文本写入动作返回成功不等于完成;必须从新的根节点精确回读后再推进。
- 每个步骤定义 timeout、有限 retry、成功条件和停止条件。
- 禁止无界循环、无限滑动、无限候选遍历和随机点击。
- 默认最多检查 5 个候选;扩大范围必须先改需求。
- 候选卡必须满足视口可见比例、单列尺寸、价格和语义文本约束;图片加载状态只作观测,
不作否决。根不可点击时只允许唯一的大面积覆盖点击目标,禁止点击卡内小按钮。
- 候选标题 signature 不能作为节点唯一标识。读取和点击之间必须携带仅在当前视口存活
的 locator,重读树后以完整内容/节点指纹和位置唯一复核,再对节点执行
`ACTION_CLICK`;禁止按重复 signature 取第一个、坐标点击或跨快照保留节点引用。
- 候选尝试去重使用不含位置的完整内容指纹,locator、bounds、path 和指纹不得写入证据、
outbox 或 Admin。打开失败必须区分页面变化、locator 缺失/失配/多命中和动作拒绝。
- 候选和滚动预算在发出动作前消耗,并跨 workflow retry 保持;返回 `false` 或取消
也不能恢复预算。
- 详情截图只保存在 App 内部目录,文件名匿名;manifest 不保存标题/页面原文,并
校验文件 SHA-256、尺寸和字节数。
- 前台服务通知要展示任务编号和当前步骤,并提供安全停止入口。
- 验证码、风控、登录、支付、未知页面统一进入安全失败。
- MVP 不得实现最终提交订单或支付动作,包括“先写好但不调用”的隐藏代码。
## 5. AI 规则
- 输入明确区分原始事实、用户硬约束、页面观察和待推断字段。
- 输出只接受结构化 schema;解析失败不能回退到自由文本猜测。
- SKU、数量、预算、允许平台和停止边界由确定性代码覆盖模型输出;数量和预算不进入
需求提取 prompt。
- 提示词、schema、模型名和阈值版本化并记录到 execution。
- 不向模型发送密码、token、支付信息或与候选判断无关的个人信息。
- 模型低置信度或前后结果冲突时转人工,不自动增加动作权限。
- 需求提取一次用户操作最多发出一次付费调用;重试必须由上层人员显式触发并另计预算。
- GUI-Owl、MAI-UI 等动作模型不能复用为需求提取 provider。
- 需求提取 provider 的远程端点必须使用 HTTPS;HTTP 只允许不携带 API Key 的本机
回环地址,且禁止 userinfo、query、fragment 和重定向。
- 结构化 VLM 调用必须禁用底层自动重试,协程取消必须取消 HTTP call;非成功响应
不读取正文,成功响应最多读取 64 KiB。
- 需求提取标题和 SKU 分别限制为 2048、512 个 UTF-8 字节,超过上限不得截断后发送。
- 图片进入 VLM 前必须校验媒体类型、字节上限、JPEG 魔数、SHA-256 和可解码尺寸,并
按声明长度一次分配精确读取、有界缩放;prompt、Base64 和原始响应不得写普通日志。
- 候选评估按 ordinal 串行执行,每个候选最多调用一次、整批最多 5 次;失败、取消或
无效输出立即停止后续调用,不保留部分结果作为自动建议。
- 候选证据只能通过当前 session 的 metadata allowlist 读取;复核固定文件名、规范
路径、PNG/IHDR、字节数、尺寸和 SHA-256,VLM adapter 不得遍历 cache。
- 候选 session 必须绑定完整需求快照和精确搜索词;旧需求、其他关键词或失败
workflow 的截图不得进入评估。
- 模型不能设置建议 ordinal、人工确认状态或 `order_submitted`;预算缺失时价格或
预算匹配声明无效。
- 候选规格先走确定性规则。节点扁平化必须有 `200/12/160` 的数量、深度和文字上限,
只保留临时 ID、path、状态和 bounds;禁止上传 `AccessibilityNodeInfo.toString()`。
- 规格模型定位只接受严格 JSON 中当前快照的唯一 node ID;返回后必须重读树并以 path
加局部指纹复核。禁止坐标点击、多步计划、购买/确认/下单/支付动作和旧快照复用。
- 颜色与尺码每次只允许一个动作。最终视觉证明必须以同一截图同时覆盖两个冻结目标,
置信度至少 `0.95` 并绑定截图 SHA-256;证明不得改写无障碍节点 selected 字段。
- 模型解析拆分价格时只允许返回当前快照中最多 4 个 price node ID。代码必须按屏幕位置
拼接节点原文并使用人民币解析器计算分值;模型文字不一致、独立金额或未知节点必须拒绝。
- 候选不得用空字符串隐含成功:SKU 状态固定为 `MATCH/MISMATCH/UNVERIFIED`,价格状态
固定为 `VERIFIED/UNVERIFIED`,并记录 `DETERMINISTIC/LOCAL_VLM` 来源。
- 每候选最多两次规格定位和一次最终复核,同 group/snapshot/use case 不重试。disabled、
登录/验证码/风控/订单确认/支付边界和第二轮保底禁止调用规格模型;完整分组仅因目标
别名未命中时可调用一次模型,模型不得覆盖明确 disabled 的本地证据。
- 规则点击后必须复读选中态,未选中不得当作成功;模型调用各自限制为 20 秒,超时、
取消、网关异常、合同不符、低置信度和节点/证据失效必须落稳定原因码。
- 无价格上限的候选仍尽力读价,但缺价或歧义不得单独触发模型或阻止 SKU 匹配。价格
解析只允许固定白名单标签加唯一人民币金额,区间、“起”、未知标签和多金额不得放宽。
- 自动采购图片搜索固定为两轮:第一轮最多检查 6 个并保存最多 5 个严格候选,第二轮最多
检查和保存 2 个保底候选。新 execution、事件和候选 payload 禁止出现第三轮。
- 规格 VLM 只使用 Roubao 本机 Keystore-backed 配置;Admin/任务不能代理、下发或覆盖。
outbox 只写调用/接受/拒绝/双规格复核计数和稳定拒绝原因聚合,不写节点 JSON、图片、
响应或 endpoint。
- 采购诊断使用 `noBackupFilesDir` 私有 SQLite,最多保留 50 次 execution、每次 200 个
事件。不得入库截图/Base64、API Key、token、Cookie、地址、HTTP header 或完整任务
描述;无效模型原文只保留 hash/长度,合法响应只保留严格 schema 白名单字段。
- 模型配置测试必须使用内置无业务信息图片,单击只调用一次并禁用重试/重定向;测试
结果不持久化。失败只暴露稳定分类,不读取或显示供应商错误正文、Key、完整 endpoint
或测试图 Base64;协程取消必须取消 HTTP call。
## 6. 后端与 API 规则
- 请求在边界完成类型、大小、媒体类型和业务规则校验。
- 领取任务必须使用事务;不得先 GET 再由客户端自行标记占用。
- 状态变化校验当前状态、设备归属、claim token、版本和租约。
- App 必须在 claim 请求前生成并安全保存独立的 256 bit claim token 与幂等 key;
后端只存 token SHA-256,响应、事件和日志不得返回原值或 hash。
- claim/start/release/cancel-ack 和后续完成/证据上传的重试路径必须幂等;`NO_TASK`
也必须是稳定幂等结果。
- Android 写 execution event 前必须按 UTF-8 字节复核后台 1000 字节上限;大段诊断拆成
独立事件,禁止截断。历史 payload 修复只能匹配已确认的旧格式,并使用稳定派生 ID。
- outbox 不可越过队首。I/O 和可重试 5xx 保留并重试;后台返回 `retryable=false` 时只
持久化稳定错误码、HTTP 状态和白名单字段名,停止后续自动化,不记录响应正文或字段值。
- 收到 HTTP 4xx 证明后台可达,UI 不得显示“后台离线”;仅连接类异常和暂时不可用的
5xx 使用离线/重试文案。
- 设备/任务 heartbeat 使用服务端时钟且不写高频事件;过期 RUNNING 不得自动回到
队列。管理取消执行中任务必须等 App 安全确认,不能提前伪装为终态。
- 不向客户端返回 `password_hash`、`token_hash`、存储绝对路径或供应商密钥。
- 管理 Cookie 与 BUYER 设备 Bearer token 使用独立 middleware,不得互相替代;
每次鉴权都重新检查用户/设备启用、绑定、撤销和过期状态。
- 密码使用 bcrypt;session、设备 secret 和 access token 使用至少 256 bit 随机值,
持久化层只保存 SHA-256。账号和设备只由 `authctl` 显式预置。
- 管理 Web POST 校验双提交 CSRF;Cookie 管理 API 的非安全方法额外校验
`X-CSRF-Token`。登录成功同时轮换 session 和 CSRF。
- 管理/App 登录必须在 bcrypt 前按服务端 `RemoteAddr` 独立限流;不得信任未配置的
转发头。限流器必须并发安全、内存有界,超限返回通用 `429` 和 `Retry-After`。
- 创建和取消任务必须从认证上下文传入真实 ADMIN actor 并写入只追加事件;共享
`local-admin` 只用于 MVP 资源可见范围,不能冒充操作者。
- 通用错误使用稳定 code 和可读 message;内部堆栈只进受控日志。
- 文件访问通过鉴权接口,防止路径遍历和猜测 URL;App 参考图读取必须重新校验当前
task/user/device/generation/token/租约,不能只凭 asset UUID 授权。
- T-208 候选数据必须分离 observation、model prediction、deterministic
recommendation 和 human label;模型理由不得预填或复制为人工标签。
- 人工结论使用版本化结构化 reason code;接受/拒绝至少一个理由,`OTHER` 才要求
受限备注。人工修正只追加并引用被替代版本,不能覆盖历史。
- 第三方商品/图片 URL 只作为长度受限的不可执行观测字段;后端不得根据客户端 URL
发起任意网络请求。候选图片使用鉴权、解码、大小/像素/哈希和脱敏受控上传。
- Go 命令固定 `GOTOOLCHAIN=local`;`go.mod` 不得出现更高 Go 版本或未固定的
`@latest` 依赖。
- 后端默认不得加载 `.env`。唯一例外是 `cmd/api` 在读取配置前,以有界、allowlist 解析器
将 `backend-api/.env` 中三个 ERP 值作为进程环境变量的回退;不得修改全局环境、加载
`authctl`/数据库/HTTP/TLS 配置或接受任意路径。后端不得默认开启 CORS、使用包级数据库
单例,或在库、handler、健康检查中调用 `log.Fatal`/`os.Exit`。
- `http.Server` 必须配置 read-header/read/write/idle/header 上限和有界关闭;
recovery 不得把 Authorization、Cookie、panic 或请求正文写普通日志。
- SQLite 数据文件必须位于被忽略目录,启用 foreign keys、有限 busy timeout 和
WAL;连接由进程入口显式关闭,migration 使用固定版本和受控 SQL 文件。
- ERP adapter 只能把版本化 allowlist 归一化对象交给 `FreightSource`;其 HTTP 会话只在
Go API 进程内。收件人、电话、
地址、完整响应、Cookie、JWT、账号、密码和验证码不得进入领域对象、错误、日志、
fixture、SQLite、浏览器或 VLM。T-230 仅允许 API 进程把 ERP 验证码图片一次性发送给
受控 `CMROUBAO_OCR_API_URL`;不持久化结果、不重试、不轮询、不暴露人工验证码接口。
## 7. 安全与隐私
- 后端密钥只来自环境变量或被忽略的本地配置;管理后端不保存、下发或代理 VLM
provider Key。
- App provider Key 使用 Android Keystore 包装的加密存储;每台采购设备使用独立、
可撤销、有限额度的 Key。Key、Authorization 和完整敏感 endpoint 不得进入任务、
结果、日志、崩溃报告或普通 SharedPreferences。
- App provider HTTP client 必须禁用自动重试和重定向;Release 远程目标只允许
HTTPS,HTTP 仅允许 Debug 下无 Key 的 loopback mock。后台任务不能覆盖端点。
- 管理后端不得根据 App 提交的 provider 或商品 URL 发起第三方网络请求。
- 真实蝦皮订单文本、参考图及其规范化生成物只能位于仓库外或 `.local/`、
`private-fixtures/` 等被忽略目录;测试使用脱敏 fixture。
- 普通日志不得输出完整蝦皮订单号、店铺名或本机原图绝对路径。
- 日志默认脱敏,不记录 Authorization、Cookie、密码、设备令牌或完整地址。
- ERP 外部 URL 必须在配置层限制为完整 HTTPS origin;仅离线 `httptest` 可使用 HTTP。
Cookie jar 和 token 只能停留在受锁保护的进程内会话,进程重启后要求人工重新登录。
- 上传文件限制媒体类型、大小和解码结果,随机化服务端文件名。
- 不绕过第三方平台限制、验证码、风控或系统权限。
- 不可逆动作必须由明确需求、服务端授权、App 确认和幂等保护共同允许。
- 发现需求与平台条款冲突时停止实现并记录 blocker。
## 8. 测试
每个任务按风险选择测试:
- 领域状态机、硬约束、错误码:单元测试。
- API 权限、事务、幂等、文件校验:集成测试。
- VLM adapter:固定 fixture/契约测试,不让普通测试依赖真实付费 API。
- VLM 隐私测试必须用 sentinel 断言订单号、店铺名、路径和数量不进入 provider
prompt,并断言一次 extractor 调用只调用 gateway 一次。
- 蝦皮文件导入:覆盖同名配对、缺图、重复图片、未知格式、非法数量和敏感日志检查。
- 私有 ProbeTask 只允许通过 `-PprobeFixturesDir` 注入 Debug APK;验证后必须再做
一次不带属性的普通构建,并确认 APK 不含 `assets/probe-fixtures/`。
- Android workflow:用 fake automation/AI client 测状态和安全停止。
- 拼多多真实流程:指定版本测试机手工 smoke,保存脱敏截图和步骤证据。
- UI:覆盖默认、加载、空、错误、权限和中断状态。
不得用删除断言、放宽预算、安全停止或候选上限来让测试通过。
- 自动下单金额边界必须由后端授权快照冻结并纳入命令哈希;Android 不得自行采用可变默认值。
- 候选展示价是观察数据。实际所选规格单价、数量、确认页总额和授权总额上限必须由代码
复核;价格缺失、歧义、溢出或超额均失败关闭。
## 9. 完成定义
- [ ] 修改在任务 `write_paths` 内。
- [ ] 构建、相关测试和格式检查通过。
- [ ] 关联需求、US 和 IX 的验收通过。
- [ ] 安全停止路径至少验证一次。
- [ ] 没有真实密钥或敏感证据进入仓库。
- [ ] 接口、数据模型或命令变化已同步文档。
- [ ] 当前任务文件记录命令、结果、环境和未验证项。
- [ ] `current-state.md` 与真实目录、命令和 blocker 一致。
项目标准验证命令是根目录 `.\init.ps1`;它执行 Android Gradle `test` 和
`assembleDebug`,再以 `GOTOOLCHAIN=local` 执行后端 `go test ./...`、
`go vet ./...`、`gofmt` 检查和 API/migration/authctl 三个入口构建。设置
`RUN_START_COMMAND=1` 时使用
SDK 内新版 ADB 安装并启动 Android App。