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

5.4 KiB
Raw Blame History

id, title, phase, deps, status, created, vikunja_task_id, context_ref, work_branch, needs_device, needs_human_review, write_paths
id title phase deps status created vikunja_task_id context_ref work_branch needs_device needs_human_review write_paths
T-307 客户端 attempt 事件与失败 sink 3
T-205
T-303
TODO 2026-08-04 39 35d7ce1 task/t-307-attempt-sink false false
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/**

问题 / 背景

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。

边界

  • 只实现 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,不实现规格、数量、确认页、围栏或结果动作; 不编写或引用点击“提交订单”的代码,不编写支付、免密支付、先用后付或任何扣款能力。