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

72 lines
9.8 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.
## Context
`proposal.md``scripts/context-health.sh` 以集合相等检查主 Specs、`requirement-evidence.json``entry-capability-requirement-matrix.json`;调查确认主 Specs 为 89 条、现有证据为 53 条,缺失 36 条,且没有过期 evidence。矩阵也缺少同一 36 条 Requirement 链接。
## Goals / Non-Goals
**Goals:**
- 仅以当前可达源码、迁移/脚本、配置和已有归档验证记录补齐 36 条的静态可追溯证据。
- 令两个 JSON 文件分别满足脚本的 Requirement 集合双向覆盖;保持 HTTP 路由集和 Asynq 注册入口集不变。
- 每条证据在 `entries``handler_consumer_job``application_service_query``domain_state_amount``store_migration_config``verification` 中只引用实际存在的事实。
**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/admin``internal/routes` 的代理充值入口;`internal/application/agentrecharge/online_creation.go``internal/infrastructure/payment/fuiou_scan.go``pkg/fuiou/scan.go` | `model.AgentRechargeRecord.PaymentMethod/PaymentChannel`、支付配置完整性、`integrationlog.Repository`;验证输出只取当前源码可命中的 `PreCreate``CommonQuery``trans_stat` 映射或既有归档 Change 的可复核命令。 |
| 开放接口本地查询1 条) | 四个 GET 入口:`/api/open/v1/cards/traffic``/cards/status``/cards/realname-status``/devices/traffic``internal/handler/openapi/handler.go``internal/service/agent_open_api/service.go` | 对四个读取方法做静态可达性检查:不得引用观测分发/系列调度;只列实际查询的 Store/模型。 |
| 轮询低写入、轮询审计与外部交互3 条) | 五个已注册 polling Task`internal/task/polling_base.go` 和各 `polling_*_handler.go``internal/infrastructure/cardobservation/series_runner.go` | `internal/application/cardobservation/{apply,network,traffic}.go` 的变化审计/Outbox 事实,及 `internal/task/polling_integration_log.go` 的失败终结事实;正常无变化仅在当前代码存在明确跳过分支时记录,不以 Requirement 文本反推。 |
| 审计/Integration 归档、清理和调查7 条) | 已注册 `constants.TaskTypeAuditDailyArchive``TaskTypeIntegrationDailyArchive``TaskTypeAuditDailyRetention``cmd/worker/main.go` 的注册/总开关;`internal/task/{integration_archive,audit_monthly_retention}.go` | `internal/application/auditarchive/{service,integration,retention}.go``internal/infrastructure/audit/retention.go``pkg/constants/audit_archive.go``cmd/worker/main.go``NewRetentionLogger``internal/query/{audit,retention}` 的在线边界。 |
| 套餐历史层级5 条) | `GET /api/admin/assets/{identifier}/packages``GET /api/c/v1/asset/package-history`;后台 Service、H5 Handler 均调用 `internal/query/asset/package_history.go` | `internal/model/package.go``master_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.py``lib/mapping_loader.py``lib/sql_builder.py` | `package_lifecycle_overrides``tbl_card_life``multiple_active_packages`Step2 INSERT 的 `expiry_base_snapshot``calendar_type_snapshot``duration_months_snapshot``duration_days_snapshot`。 |
| 零金额退款3 条) | `POST /api/admin/refunds/{id}/approve` 与企业微信审批共用的退款决策路径;`internal/service/refund/{approval_decision,service}.go` | `validateApprovedRefundAmount` 拒绝负数;事务更新退款/订单;`refundWalletPayment``amount == 0` 直接返回,不写钱包回款。 |
| 店铺批量导入5 条) | 离线 CLI `scripts/migration/import_shops.py``lib/shop_import.py``lib/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.py``scripts/migration/import_shops.py`;现有健康脚本只比较 `http``async` 的入口集合,`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`