Files

6.3 KiB
Raw Permalink 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-207 授权有效期与围栏前人工恢复 2
T-206
TODO 2026-08-04 46 cd47d0c task/t-207-prefence-recovery false false
docs/tasks/T-207.md
admin/internal/taskrecovery/**
admin/internal/taskdetail/**
admin/internal/server/task_recovery.go
admin/internal/server/task_recovery_test.go
admin/internal/server/router.go
admin/internal/transport/webui/webui.go
admin/internal/transport/webui/templates/task-detail.html
admin/cmd/server/**
admin/README.md
docs/api.md
docs/routes.md
docs/08-interaction-checklist.md
docs/current-state.md

问题 / 背景

T-205 的围栏前失败只把 attempt 置 FAILED、task 置 NEEDS_MANUAL,并刻意保留 authorization 与 claim;T-302 也明确租约丢失不等于手机停止。T-207 提供管理员人工检查后的围栏前恢复,使旧授权永久关闭后任务回到 DRAFT 或 CANCELED,同时保证任何围栏事实都永远不可释放。

关联需求与交互

  • 功能:F-010、F-011、F-017 的围栏前恢复部分。
  • 用户故事:US-005、US-008。
  • 交互:IX-009、IX-010;T-206 负责状态与安全主动作,本任务接通被允许的写操作。
  • 依赖:T-206;由其传递 T-205/T-302 的 claim、attempt 和详情事实。
  • API:POST /tasks/{id}/reset-to-draft、POST /tasks/{id}/cancel。
  • 后续消费者:T-208 submission fence 与恢复/围栏竞态测试。

方案

  1. 管理写请求只接受有效管理员 session + CSRF 和有界严格表单;设备 Bearer 无权调用。请求绑定 expected_task_version、authorization_id、可选 expected_attempt_id、可选 expected_claim_generation、必要时的 confirmed_device_stopped 和安全 return_to。
  2. 定义围栏前恢复资格:PENDING 且无 attempt/claim/submission 可关闭;NEEDS_MANUAL 且 T-205 attempt 已明确 FAILED、无 submission 可重置/取消;租约过期或设备撤销但 attempt 曾进入 CLAIMED/ORDERING 时,只有管理员明确确认设备已安全停止才可继续。活跃租约、仍在 ORDERING 且无明确停止事实时拒绝。
  3. authorization/attempt 已 FENCED 或任何关联 order_submission 存在时永久拒绝,不受 task.status、租约、授权过期、人工确认或调和终态影响。数据归属缺失或互相冲突时 fail closed。
  4. 在一个有界 SQLite 写事务中先取得写入线性化位置,再重新读取 task、authorization、attempt、claim 和 submission。首先检查 submission;随后精确比较版本、authorization、attempt 和 generation,最后校验恢复资格。
  5. 成功只做状态关闭,不删除事实:过期 authorization 转 EXPIRED,其余围栏前关闭转 ABANDONED;CLAIMED/ORDERING attempt 可安全关闭为 ABANDONED,已 FAILED 保持 FAILED;claim 留存并只写 closed_at,恢复原因由 authorization/attempt/task 的固定终态组合表达,不新增自由文本字段;task 转 DRAFT 或 CANCELED、version 恰好加一。
  6. 本任务不新增 migration。利用旧 authorization/attempt/claim 终止足迹和 task 版本形成自然幂等:相同旧版本与旧授权的相同终态请求稳定返回原成功;一旦出现新版本/新授权,旧请求必须冲突,不能关闭新授权。
  7. 重置成功不创建新授权。重新开始必须回到列表,由管理员重新点击“开始采购(只创建待付款订单)”并通过 T-203 产生新版本、新授权。
  8. T-206 详情仅在服务端计算确定可恢复时渲染 POST 表单。重置是高影响动作,明确说明旧授权永久关闭和必须重新开始;取消为独立后果,不把两者混为同一动作。

验收要点

  • 覆盖无领取 PENDING、明确 FAILED、租约过期但未证明停止、人工停止确认、活跃 ORDERING 及归属冲突。
  • 覆盖 expires_at 边界相等、旧 generation、task/version 漂移、不同 authorization/attempt 和设备撤销。
  • 覆盖响应丢失、服务重启、同请求重放、同键异终态、reset/cancel 并发和新授权后旧请求重放。
  • 覆盖恢复与模拟围栏插入并发:只要任一 order_submission 已存在或先提交,恢复必须零释放;已 MANUAL_RESOLVED 也仍拒绝。
  • 成功后 authorization、claim、attempt、events、evidence 全部保留;只改变获准状态,不删除审计事实。
  • 管理/设备身份隔离、CSRF、请求大小/字段、错误不泄露均有测试。
  • go test ./...、go test -race ./...、go vet ./...、go build ./...、完整 init、上下文、Vikunja export 与 diff-check 通过。

执行记录

(暂无)

边界

  • 只有有效管理员 session + CSRF 的显式人工动作可以执行围栏前重置或取消;设备 Bearer、后台过期 扫描、租约超时、设备撤销、停止轮询和进程重启都不得自动关闭授权或把任务送回 DRAFT。
  • 租约过期或设备撤销不证明手机已停止。曾进入 CLAIMED / ORDERING 且没有 T-205 明确失败事实 的 attempt,必须由管理员明确确认设备已安全停止;活跃执行或事实不完整时一律拒绝恢复。
  • 事务必须先取得写入线性化位置,并把任何关联 order_submission 的存在作为最高优先级硬拒绝。 authorization/attempt 已 FENCED,或 submission 已 FENCED、SUBMITTED、 RECONCILIATION_REQUIRED、MANUAL_RESOLVED,均不得释放、取消、重置或重新授权。
  • 恢复只能关闭既有围栏前状态并保留全部审计事实;不得删除 authorization、claim、attempt、event、 evidence 或 submission。重置不得创建新授权,重新开始必须由管理员重新执行 T-203。
  • 自然幂等必须绑定旧 task version、旧 authorization、预期 attempt/generation 和精确终态;不能仅因 当前已是 DRAFT / CANCELED 就返回成功。出现新版本或新授权后,旧请求必须冲突,不能影响新事实。
  • 本任务不建立、查询为许可或模拟 submission fence,不实现设备 result、人工 submission 调和、 真机动作、点击“提交订单”或任何支付、免密支付、先用后付和扣款能力。