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

10 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-400 离线单次提交安全编排 4
T-305
T-308
TODO 2026-08-04 50 8600c33 task/t-400-offline-submission-orchestrator false true
docs/tasks/T-400.md
client/src/cmbuyer_client/execution/submission_orchestrator.py
client/tests/execution/test_submission_orchestrator.py

问题 / 背景

T-305 的正常 dry-run 会在同一设备会话完成三闸门与最终控件只读观察,随后执行一次安全 Back,并以固定 reason DRY_RUN_COMPLETED_BEFORE_FENCE 终止 attempt。它只证明围栏前代码和判据契约已验收,绝不产生可供真实提交复用的运行时 ReadyForSubmission。T-308 将提供严格 fence/result sink、当前进程唯一 permit 和不可恢复的点击意图能力。T-400 只用测试 fixture 验证纯 SinglePassSubmissionOrchestrator、四条件、一次性调用和崩溃闭包;不读取或复用 T-305 的对象、截图、证据、journal、缓存或旧 proof,不提供生产 ReadyForSubmission 构造入口,也不接入 app/UI/PDD/真机。

关联需求与交互

  • 功能:F-006、F-007、F-011、F-017 的离线提交安全闭包。
  • 用户故事:US-003、US-005、US-008。
  • 交互:IX-011 的后台顺序;本任务无生产界面。
  • 依赖:T-305、T-308。T-305 只提供已验收的代码/判据契约,T-308 提供冻结接口;两者都不向本任务提供可提交的运行时事实。
  • 后续消费者:T-401 在全新真实任务、全新 attempt 的同一趟内重跑全部三闸门并即时构造生产 ReadyForSubmission。
  • 生产 app 的 import/call graph 在 T-401 前继续不可达本任务编排、ReadyForSubmission 生产构造和 SubmitOrderPort。

方案

  1. 在单一窄 execution 模块实现纯 SinglePassSubmissionOrchestrator、只读 ReadyForSubmission 协议和抽象 SubmitOrderPort;只消费 T-308 已冻结的 SubmissionSink 接口,不修改 submissions/remote/localstate/core/PDD/UI/app。
  2. T-305 的 PreFenceDryRunOrchestrator、终止结果、action journal、AssetRef、截图、缓存、页面对象、attempt 或 proof 不能作为 ReadyForSubmission 输入。T-305 已 Back 且终止的 attempt 永远不能申请 fence、取得 permit 或进入提交编排。
  3. 本任务不提供任何生产 ReadyForSubmission 具体类、公共构造器、factory、adapter 或 from_dry_run/from_snapshot/from_evidence/from_localstate 路径。只有 tests 中的 immutable fixture 实现该只读协议;生产包内不存在把真实或旧事实装配成 ReadyForSubmission 的入口。
  4. 测试 fixture 必须显式给出一个虚构但内部一致的同一 task/authorization/attempt/generation/App 版本事实:未消费授权快照、Gate2/Gate3 通过、精确规格/数量/金额、三个受控 AssetRef 和提交文本/唯一计数。它只用于纯逻辑测试,不是页面证据或可发布业务事实。
  5. 编排在申请 fence 前重新校验 fixture 表达的四条件:一次性授权/claim 一致且未过期、Gate2 通过、Gate3 通过、submit 文本精确且唯一计数为一。任一缺失、漂移、金额不规范或版本不一致均零 fence、零 SubmitOrderPort 调用。
  6. 只有测试注入的 T-308 当前进程首次明确 SubmissionPermit 可继续。先调用 prepare_click_intent durable 写入唯一点击意图并取得一次性 ArmedSubmission;未成功 arm、重放许可、LOOKUP_ONLY、并发进入或重启恢复均零 SubmitOrderPort 调用。
  7. SubmitOrderPort 只暴露 submit_once(ArmedSubmission, VerifiedSubmitObservationBoundary)。编排层最多调用一次,不提供 retry、循环、回退、第二调用点或可重复 submit 函数;本任务只注入记录次数/故障点的 fake port,没有具体真机适配器。
  8. 调用前已经进入不可逆 reconcile-only 状态。fake port 返回、抛错、超时、进程停止或结果不明都不能回到可提交状态;异常只形成 UNKNOWN 待调和事实,不再次调用 port。
  9. fake port observation 只允许 SUBMITTED、EXTERNAL_PAYMENT_HANDOFF、SECURITY_CHALLENGE、UNKNOWN。编排通过 T-308 固定接口只上报一次 result;HTTP 不明只恢复原 key/载荷,不重复页面观察或提交动作。本任务不实现或测试任何基于真实页面的 SUBMITTED 推断。
  10. 停止轮询、Esc、查看历史、关闭窗口和重启不是编排的取消/重试输入;它们不能清 permit、点击意图、submission 或 result pending,也不能释放、重新授权、重新领取或推进下一任务。
  11. T-401 前生产 composition/import/call graph 必须同时证明:不可达 SinglePassSubmissionOrchestrator/SubmitOrderPort;不存在生产 ReadyForSubmission factory;T-305 dry-run 结果类型不可流入提交编排。

验收要点

  • 参数化负例逐一移除授权/首次许可、Gate2、Gate3 或唯一控件,证明 fake SubmitOrderPort 调用数均为 0。
  • 首次明确 permit + durable 点击意图的纯测试用例证明 fake port 调用数恰好为 1;并发、二次进入、permit 重放和重复 result 均不能增加计数。
  • fake port 在调用中或返回解析时抛错/超时,结果均为 reconcile-only/UNKNOWN,调用数仍恰好为 1 且无 retry。
  • 崩溃点覆盖 fence 响应前后、permit 落盘前后、点击意图前后、fake port 调用前后和 result 上报前后;恢复全部零 SubmitOrderPort 调用。
  • 固定测试至少包含 test_missing_each_submission_condition_never_calls_port、test_first_permit_calls_port_exactly_once、test_port_exception_never_retries、test_restart_after_click_intent_is_reconcile_only、test_t305_runtime_artifacts_are_not_accepted、test_no_production_ready_for_submission_factory、test_production_app_import_closure_cannot_reach_submission。
  • AST/import/call-graph 证明生产代码不存在 ReadyForSubmission 构造入口,T-305 已终止结果/截图/缓存/proof 到编排无数据流,app/UI/PDD/device/真实 click/payment 不在本任务闭包。
  • 任务 diff 只能修改窄 execution 实现/测试;T-308 submissions/remote/localstate/core 接口保持冻结且零 diff。
  • client 全量 unittest、compileall、wheel metadata、完整 init、上下文、Vikunja export 与 diff-check 通过。
  • needs_human_review:人工审阅 T-305 零运行时复用、生产 factory 不存在、一次性 fake port 调用和全部崩溃点;确认前不得标 DONE。

执行记录

2026-08-06T02:58:55Z · ila

2026-08-06T02:58:54.857Z · Codex 2026-08-06 离线 fixture/编排中的 Gate3 字段改为 gate3_submit_amount,提交控件必须是唯一结构化完整文本“提交订单 ¥{金额}”且最近可点击祖先唯一。Gate3 必须严格等于 Gate2;不再表达独立确认页。

边界

  • 2026-08-06 起离线事实使用 gate3_submit_amount 和结构化完整控件文本,不再使用确认页金额语义。 Gate3 必须严格等于 Gate2,最近可点击祖先必须唯一;fixture 不表达独立确认页或导航动作。

  • T-305 正常 dry-run 已执行一次安全 Back,并以 DRY_RUN_COMPLETED_BEFORE_FENCE 终止 attempt。 本任务只能复用其已验收的代码/判据契约;不得接收或复用该次/任何 dry-run 的结果对象、attempt、 screenshot、AssetRef、action journal、缓存、页面对象、本地快照或旧 proof 来申请围栏或提交。

  • 本任务只在单一窄 execution 文件实现纯 SinglePassSubmissionOrchestrator、只读 ReadyForSubmission 协议和抽象 SubmitOrderPort;仅测试 fixture 可实现/构造该协议。生产代码 不得存在具体 ReadyForSubmission 类、公共构造器、factory、adapter、反序列化或 from_dry_run / from_snapshot / from_evidence / from_localstate 路径。

  • 本任务只消费 T-308 已冻结接口,不得修改 submissions/**、remote/**、localstate/** 或 core/**。不得修改或导入 app.py、ui/**、pdd/**、device/**,不得连接真机、使用 uiautomator2 或实现任何具体页面点击适配器。

  • 测试 fixture 表达的四条件必须属于同一虚构 task/authorization/attempt/generation 和 App 版本: 未消费授权与首次明确围栏许可、Gate2、Gate3、精确唯一提交控件。它只验证纯逻辑,不是可复用的 页面或业务事实;缺失、漂移、金额/规格/数量冲突均零 fake SubmitOrderPort 调用。

  • 编排必须先通过 T-308 durable 写入唯一点击意图,再把当前进程的一次性 ArmedSubmission 交给 fake SubmitOrderPort.submit_once()。该方法在纯测试生命周期最多调用一次;不得存在 retry、循环、 fallback、第二调用点或异常后重新进入路径。

  • 点击意图落盘后即进入 RECONCILE_ONLY。fake port 返回、抛错、超时、停止、关闭或进程崩溃都不得 回到可提交状态;结果不明确只能形成 UNKNOWN 并调和同一 submission,不能再次调用 port、重新 申请许可、释放围栏、重新授权或重新领取。

  • 本任务不实现真实页面结果判据。fake fixture/port 的 SUBMITTED 只用于协议分支测试,不能成为 生产页面事实;不得从 cmroubao/cmpdd、旧版本、页面语义或人工经验推断真实 SUBMITTED。

  • T-401 前 production composition/import/call graph 必须同时不可达本任务编排/SubmitOrderPort, 不存在生产 ReadyForSubmission 构造入口,并证明 T-305 终止结果到提交编排无数据流。应用启动、 Esc、停止轮询、查看历史和关闭窗口均保持零提交调用。

  • T-401 才能领取一条全新已授权真实任务,在新的同一趟 attempt 中重新执行全部实时闸门并即时构造 生产 ReadyForSubmission。本任务不提供人工提交入口,不编写支付、免密支付、先用后付或扣款能力。

  • needs_human_review: true:自动验证后仍保持 DOING,等待人审 T-305 零运行时复用、生产 factory 不存在、一次性 fake port 调用和全部崩溃点;未经明确确认不得标 DONE。