Files
junhong_cmp_fiber/openspec/changes/archive/2026-09-08-repair-context-reset-evidence/design.md
2026-09-10 10:53:07 +08:00

9.8 KiB
Raw Blame History

Context

proposal.mdscripts/context-health.sh 以集合相等检查主 Specs、requirement-evidence.jsonentry-capability-requirement-matrix.json;调查确认主 Specs 为 89 条、现有证据为 53 条,缺失 36 条,且没有过期 evidence。矩阵也缺少同一 36 条 Requirement 链接。

Goals / Non-Goals

Goals:

  • 仅以当前可达源码、迁移/脚本、配置和已有归档验证记录补齐 36 条的静态可追溯证据。
  • 令两个 JSON 文件分别满足脚本的 Requirement 集合双向覆盖;保持 HTTP 路由集和 Asynq 注册入口集不变。
  • 每条证据在 entrieshandler_consumer_jobapplication_service_querydomain_state_amountstore_migration_configverification 中只引用实际存在的事实。

Non-Goals:

  • 不修改 openspec/specs/**、业务代码、迁移、测试、运行配置、路由或 Worker 注册。
  • 不以历史 Change 的完成状态、推测的调用链或无法重现的生产结果替代当前可达证据。
  • 不添加、删除或重分类既有 HTTP/async 矩阵入口。

Decisions

1. 固定缺失集合,按 capability 分组补行

以下是唯一可新增的 evidence Requirement 键;实现阶段先由同一解析规则重算,若集合变化则停止并重新调查:

capability 缺失 Requirement
agent-funds-commission 代理在线充值可用支付方式按支付配置判定
agent-open-api 代理开放接口查询不触发可靠卡观测
external-integration 富友主扫统一下单与订单查询;高频轮询外部交互日志保留边界;外部交互日志归档与留存受控执行;外部交互日志逐日物理留存
operations-audit 轮询审计事实保留边界;审计归档与日留存受控执行;审计在线数据逐日物理留存;日留存独立运行日志;审计调查留存边界连续
package-lifecycle 资产套餐层级投影;套餐历史整组筛选与分页;套餐历史稳定排序;物理缺失与不可展示关系区分;套餐历史读取范围与兼容边界
package-queue-activation 当前主套餐过期后自动激活下一个孤儿待生效套餐必须公平恢复Asynq 激活结果必须准确且可重试
polling-load-control 轮询全局并发与背压;正常无变化观测的低写入处理;业务变化与轮询异常仍可追溯
polling-operations 停复机遵循实际生效实名策略
qicheng-migration-package-lifecycle-overrides 迁移配置支持逐卡套餐生命周期覆盖;覆盖必须完整且可验证;未覆盖资产维持严格冲突阻断
qicheng-migration-package-usage 迁移套餐使用记录保存计时条款快照;迁移套餐使用 SQL 满足快照完整性约束
refund-approval 零金额退款审批通过;零金额退款不产生资金回款;非法退款金额仍被拒绝
shop-bulk-import 导入输入必须在生成 SQL 前完成全量预检;生成的 SQL 必须在写入前校验目标库状态;批量导入 SQL 必须创建完整的店铺初始事实;导入执行必须由维护者审核后以单事务手工确认;导入结果必须可审核和重跑定位

2. 各类证据的直接来源

Requirement 分组 入口/任务与调用链事实 状态、存储、迁移或验证事实
代理充值与富友(前两组中相关 2 条) internal/handler/admininternal/routes 的代理充值入口;internal/application/agentrecharge/online_creation.gointernal/infrastructure/payment/fuiou_scan.gopkg/fuiou/scan.go model.AgentRechargeRecord.PaymentMethod/PaymentChannel、支付配置完整性、integrationlog.Repository;验证输出只取当前源码可命中的 PreCreateCommonQuerytrans_stat 映射或既有归档 Change 的可复核命令。
开放接口本地查询1 条) 四个 GET 入口:/api/open/v1/cards/traffic/cards/status/cards/realname-status/devices/trafficinternal/handler/openapi/handler.gointernal/service/agent_open_api/service.go 对四个读取方法做静态可达性检查:不得引用观测分发/系列调度;只列实际查询的 Store/模型。
轮询低写入、轮询审计与外部交互3 条) 五个已注册 polling Taskinternal/task/polling_base.go 和各 polling_*_handler.gointernal/infrastructure/cardobservation/series_runner.go internal/application/cardobservation/{apply,network,traffic}.go 的变化审计/Outbox 事实,及 internal/task/polling_integration_log.go 的失败终结事实;正常无变化仅在当前代码存在明确跳过分支时记录,不以 Requirement 文本反推。
审计/Integration 归档、清理和调查7 条) 已注册 constants.TaskTypeAuditDailyArchiveTaskTypeIntegrationDailyArchiveTaskTypeAuditDailyRetentioncmd/worker/main.go 的注册/总开关;internal/task/{integration_archive,audit_monthly_retention}.go internal/application/auditarchive/{service,integration,retention}.gointernal/infrastructure/audit/retention.gopkg/constants/audit_archive.gocmd/worker/main.goNewRetentionLoggerinternal/query/{audit,retention} 的在线边界。
套餐历史层级5 条) GET /api/admin/assets/{identifier}/packagesGET /api/c/v1/asset/package-history;后台 Service、H5 Handler 均调用 internal/query/asset/package_history.go internal/model/package.gomaster_usage_id,历史 DTO 的 children/关系状态字段Query 的整组筛选、排序、物理存在性核对和分页函数。
套餐队列激活3 条) internal/polling/package_activation_handler.go 的定时检查、孤儿恢复、任务投递和 HandlePackageQueueActivation;已注册 constants.TaskTypePackageQueueActivation tb_package_usage 的状态条件更新、master_usage_id、优先级排序、Asynq MaxRetry(3) 与 Redis 锁冲突返回错误。
轮询总并发与背压1 条) 五个 polling Handler 均调用 PollingBase.acquireConcurrency;调度器 processOneShard 背压跳过 internal/task/polling_base.go 的双计数 Lua、释放/重入队;pkg/config/config.go 的范围校验;pkg/constants/{redis,polling}.go
停复机实名策略1 条) 自动、设备、手动停复机路径经 internal/service/iot_card/stop_resume_service.go;策略管理路由保持现有入口 isRealnameOK 的行业卡、已实名、独立卡和设备策略分支;model.IotCard/Device.RealnamePolicy 与现有套餐/流量判断。
奇成套餐迁移5 条) 离线迁移入口和配置加载:scripts/migration/migrate_runtime.pylib/mapping_loader.pylib/sql_builder.py package_lifecycle_overridestbl_card_lifemultiple_active_packagesStep2 INSERT 的 expiry_base_snapshotcalendar_type_snapshotduration_months_snapshotduration_days_snapshot
零金额退款3 条) POST /api/admin/refunds/{id}/approve 与企业微信审批共用的退款决策路径;internal/service/refund/{approval_decision,service}.go validateApprovedRefundAmount 拒绝负数;事务更新退款/订单;refundWalletPaymentamount == 0 直接返回,不写钱包回款。
店铺批量导入5 条) 离线 CLI scripts/migration/import_shops.pylib/shop_import.pylib/shop_sql.py CSV/配置预检、SQL 事务起始守卫、店铺/账号/角色/双钱包/审计 SQL、bcrypt 哈希、同批次错误/结果/摘要产物;运行说明明确不连接目标 PostgreSQL。

每个 verification 对象保留一条可直接执行的本地只读命令及其确定性源码文字命中;没有稳定文字命中的调用链,使用可重跑的只读脚本/命令输出。不得杜撰隔离库、生产库、外部渠道或已删除临时 smoke 的结果。

3. 两个 JSON 的最小同步策略

  1. requirement-evidence.json:仅追加上述 36 个唯一键各一行;不改动现有 53 行,不新增过期键。
  2. entry-capability-requirement-matrix.json:仅把上述 36 个键链接到现有、已注册的 HTTP 或 async 行。HTTP 条目保持与 Specs 提取路由的集合相同async 条目保持与 Worker 注册常量集合相同。
  3. 对无 HTTP/async 入口的两类离线流程追加 cli 行,入口分别为 scripts/migration/migrate_runtime.pyscripts/migration/import_shops.py;现有健康脚本只比较 httpasync 的入口集合,cli 行仅承担这五条离线 Requirement 的可追溯链接,不改变该比较。不得把离线流程伪挂到 HTTP 或 Asynq 入口。
  4. 写入前用集合差验证恰为 36写入后验证 evidence 键集和 matrix 链接集均等于主 Specs 键集,并确认 HTTP/async 入口集合未漂移。

4. 证据选择优先级

当前可达实现与可复跑只读输出优先;migrations/、脚本和显式配置是存储/离线事实;已归档 Change 只用于发现来源和复核历史验证边界,不能单独作为当前实现证据。无证明项保留为缺口并停止,不以相近功能、相同命名或 Spec 场景推断。

Risks / Trade-offs

  • polling-load-control 和低写入类 Requirement 跨五个 Worker Handler只引用共享基类无法证明每个入口均接入必须在最终写入前逐 Handler 核对获取、释放和令牌不足重入队。
  • 离线迁移与店铺导入没有 HTTP/async 注册入口。将其错误塞入现有入口会使矩阵语义失真;是否允许 cli 类型是唯一可能需要确认的矩阵契约缺口。
  • 历史 Change 中有未完成验证任务。它们不能写成已验证事实;证据只能引用仍在仓库中的源码、脚本、迁移、配置和可重跑只读检查。
  • JSON 人工编辑容易造成重复键或遗漏链接;实现阶段必须在写入前后解析并做三集合精确比较,再运行 ./scripts/context-health.sh