Files
cmbuyer/docs/tasks/T-307.md
T

79 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-307
title: 客户端 attempt 事件与失败 sink
phase: 3
deps: [T-205, T-303]
status: TODO
created: 2026-08-04
vikunja_task_id: 39
context_ref: 35d7ce1
work_branch: task/t-307-attempt-sink
needs_device: false
needs_human_review: false
write_paths:
- docs/tasks/T-307.md
- client/src/cmbuyer_client/attempts/**
- client/src/cmbuyer_client/core/**
- client/src/cmbuyer_client/remote/**
- client/src/cmbuyer_client/localstate/**
- client/tests/attempts/**
- client/tests/core/**
- client/tests/remote/**
- client/tests/localstate/**
---
<!-- BEGIN VIKUNJA EXPORT id=39 synced=2026-08-04T13:50:06Z sha256=3f13f517446bbcab0a4415eaf2232a990053d35fad510ef296fe38b075282ee0 -->
## 问题 / 背景
T-205 将冻结 attempt events/fail 的严格服务端 wire,T-303 提供安全 HTTP 传输原则、DPAPI/SQLite 与恢复快照,但刻意不伪造完整 ResultSink。T-307 补齐客户端围栏前 HttpAttemptSink 和事件/失败幂等状态,使 T-305 只组合已完成能力;不连接真机、UI、证据、围栏或结果接口。
## 关联需求与交互
- 功能:F-006、F-007。
- 用户故事:US-003、US-004、US-005。
- 依赖:T-205、T-303。
- 后续消费者:T-305。
- API:只消费 T-205 的 events 与 fail;不实现 fence/result。
## 方案
1. 定义窄 AttemptSink 协议:append_events 与 fail_attempt。模型只含 T-205 固定 step/outcome/reason、规范十进制金额、正整数和受控 AssetRef;不接收自由页面文本、XML、路径、token、selector、坐标或动作脚本。
2. HttpAttemptSink 复用 T-303 直连 transport 规则:精确 loopback、禁代理/重定向、有界请求/响应、严格 Content-Type/UTF-8/重复 key/未知字段;每次方法最多一个 HTTP 请求,不隐藏重试。
3. 扩展 T-303 localstate,而不创建第二个数据库:event batch 与 fail 在首次 HTTP 前原子保存幂等 key、active claim 绑定、完整载荷与预期序号/状态;结果不明和重启只能重放原键原载荷。
4. event 成功必须严格核对服务端批次结果并持久化已确认序号/摘要;同批重复只返回原结果。异载荷、序号漂移、服务端摘要漂移或 active claim 不一致转协议冲突,不覆盖本地事实。
5. fail 仅消费 T-205 固定 reason 与 last_safe_step;成功后原子保存终态响应。结果不明保留同 key,不能创建第二个 fail 或把任务本地假设为已释放。
6. 401、403、409、协议错误、本地 DPAPI/SQLite 错误按 T-303 分类停止;网络/超时/5xx 只标记同键可恢复。retryable 永不表示页面动作、证据替换、围栏或提交可重试。
7. 停止轮询/关闭窗口不取消在途请求、不清 pending、不 release/abandon claim。重启可调和 HTTP,但绝不自动恢复真机点击。
8. 构造与 import 闭包中没有 fence client、ResultSink、SubmissionPermit、click_permitted、submitter、PDD 或 payment 能力。
## 验收要点
- 覆盖 event/fail 成功、严格 schema、同键同载荷重放、异载荷冲突、响应截断/漂移、401/403/409/503。
- 证明首次网络前已持久化;崩溃与重启后键/载荷/claim 绑定完全一致,active claim 漂移时零 HTTP。
- 并发 append 序列化且不跳号;fail 与未决 event 的顺序失败闭合;成功结果不可覆盖。
- SQLite/WAL、日志、异常不含 claim/device token、页面全文、XML、本机路径或 selector。
- 静态检查没有 evidence 文件读取、uiautomator2/PDD、release/abandon、fence/result、提交订单或付款能力。
- client 全量 unittest、compileall、wheel metadata、完整 init、Vikunja 导出、上下文校验与 diff-check 通过。
## 执行记录
### 2026-08-04T13:49:39Z · ila
2026-08-04 从 T-305 预研拆出:只实现 events/fail 的客户端窄 sink 与本地幂等恢复,避免在 dry-run 集成任务中临时扩展 T-303;不实现完整 ResultSink、fence/result 或真机/UI。
<!-- END VIKUNJA EXPORT -->
## 边界
- 只实现 T-205 已冻结的 `append_events` 与 `fail_attempt` 窄客户端契约;不得提供完整 ResultSink,
不得访问 submission fence、result、`click_permitted`、SubmissionPermit 或提交许可。
- 事件只含版本化固定 step/outcome/reason、规范十进制金额、正整数和受控 AssetRef。不得接收或发送
自由页面文本、完整 XML、地址、手机号、本机路径、token、selector、坐标或动作脚本。
- HTTP 必须复用 T-303 精确 loopback、禁代理/重定向、严格解析与每方法一次传输边界。本任务不得
建第二个 transport 或隐藏重试循环;`retryable` 只允许同幂等键 HTTP 恢复,不能推导页面动作可重试。
- Event batch 与 fail 的幂等键、active-claim 绑定和完整载荷必须先进入 T-303 同一 localstate 事务再
发送。结果不明、重启和停止只能重放原键原载荷;不得另建 SQLite、换 key、跳序号或覆盖已确认摘要。
- 停止轮询、Esc、关闭窗口和进程退出不取消在途请求、不清 pending、不 release/abandon claim,
也不触发 ADB/PDD 动作。重启只可调和同一 attempt 的 HTTP 事实,不自动恢复任何真机点击。
- 不读取或上传 evidence 文件,不导入 uiautomator2/PDD,不实现规格、数量、确认页、围栏或结果动作;
不编写或引用点击“提交订单”的代码,不编写支付、免密支付、先用后付或任何扣款能力。