M8-R10: document meter lifecycle recovery runbook
This commit is contained in:
@@ -1211,3 +1211,33 @@ M8 收尾的前置条件。agent 不得执行、记录为已执行,或以 mock
|
||||
- [x] 最终报告完整交付 §12 九步及用户证据模板;真实 serial/HA walkthrough 由用户在交付后自行验收,不能在报告中伪称 agent 已执行。
|
||||
- [x] 文档、Roadmap 和 milestone 状态反映真实完成度;没有删除用户数据、没有真实 secret/设备身份,
|
||||
没有未经授权的 push/tag。
|
||||
|
||||
## 14. Post-M8 lifecycle repair(M8-R08~R10)
|
||||
|
||||
M8 交付后的 Meter lifecycle 修复链记录在本地 `review-notes/M8-meter-lifecycle-repair-plan.md`。
|
||||
它不新增 ORM / **数据库** schema 或 Alembic revision,不做启动自动修复、一次性数据脚本或历史删除;R08 虽然
|
||||
更新了 API/Pydantic schema 及 OpenAPI/codegen,但没有变更 ORM 或数据库 schema。已有的 stranded binding 只能
|
||||
由用户通过 UI 的原子 Transfer 恢复。
|
||||
|
||||
| 修复卡 | 当前状态 | 独立 review | 实现 / review 简报 |
|
||||
| --- | --- | --- | --- |
|
||||
| M8-R08:Close、Unbind、原子 Transfer 与 API 契约 | done | PASS(第 6 轮) | `M8-R08-impl-*`、`M8-R08-review-6.md` |
|
||||
| M8-R09:Meter 管理 UI | done | PASS(第 12 轮) | `M8-R09-impl-*`、`M8-R09-review-12.md` |
|
||||
| M8-R10:文档、最终报告与全量收尾 | done | PASS(第 2 轮) | `M8-R10-impl-1.md`、`M8-R10-review-2.md` |
|
||||
|
||||
R08 的 base commit 为 `c40ee65`(其自动化链包含 5 个 `fixup!`,最终 review 范围止于 `5a50b07`);R09
|
||||
的 base commit 为 `5c08825`(包含 10 个 `fixup!`,最终 review 范围止于 `c4f4ff7`)。R10 已由第 2 轮
|
||||
冷启动独立 Reviewer 判定 PASS 后标记 `done`;实现者的本地闸门结果没有被当作人工或 review 验收。
|
||||
|
||||
R08/R09 规定的用户操作为:Close active Meter、Unbind open binding、以单请求 same-Meter Transfer 切 source,
|
||||
以及把 closed previous Meter 上的 stranded DSMR binding 恢复到 active Meter。`meter_swap` 在唯一兼容 open
|
||||
binding 时可自动 handoff;歧义一律 fail closed。恢复可选择晚于新 Meter 起点的时间并留下可见 gap,所有
|
||||
lifecycle 写入与相应成本重算同事务,失败整笔回滚,HA discovery 只在 commit 后 best-effort republish。
|
||||
|
||||
stranded DSMR walkthrough 只能使用开发库中已经存在、由用户报告的 stranded row;不得通过 SQL、API、脚本、
|
||||
migration 或直接改数据库制造前置状态。若隔离开发库没有这类历史行,应将该项记录为 `N/A/blocked`。Close、
|
||||
Unbind、Transfer、Recovery 与 handoff 都会改写 Meter/binding 状态,walkthrough 必须将它们视为独立场景,使用
|
||||
彼此独立且满足前置条件的 Meter/binding(或先恢复各自前置状态),不得按一个会破坏后续前置条件的连续故事操作。
|
||||
|
||||
按用户指定顺序,R10 必须先获得独立 Reviewer PASS;之后才由 Orchestrator 对未 push 的 fixup 执行 autosquash,
|
||||
并回填最终交付 SHA 与 commit 数。在此之前,报告只可陈述 pre-autosquash 审计状态,不得伪称历史已干净收口。
|
||||
|
||||
@@ -114,6 +114,49 @@ M8 将“协议连接”和物理 Meter 分开:`MeterSource` 产生稳定 chan
|
||||
|
||||
部署与回滚串口 source 参见 [`warmtelink-energy.md`](./warmtelink-energy.md);保留旧 source/binding/history 可使审计和重算可重复,不能通过删除历史来“修复”边界周期。
|
||||
|
||||
## 生命周期操作与 stranded binding 恢复
|
||||
|
||||
M8-R08/R09 为 Meter 与 binding 增加了显式的生命周期操作。所有时间都由前端按本地日期时间输入,再按既有
|
||||
Principle-A 约定交给后端;未来时间会被拒绝。
|
||||
|
||||
- **Close Meter**:只能关闭 active Meter。该 Meter 与它的所有 open binding 在同一个 `ended_at`、同一
|
||||
事务中关闭;关闭后该 commodity 没有 active Meter。
|
||||
- **Unbind**:可对任意 open binding 执行,只写 binding 的 `ended_at`,绝不删除历史。已关闭 Meter 上残留的
|
||||
open binding 也可在 UI 中解绑,默认关闭时间为该 Meter 的 `ended_at`。
|
||||
- **Transfer**:以单个原子请求切换 source channel,不采用浏览器端“先关闭再新建”的两步操作。同一 Meter
|
||||
的 source switch 在同一 `effective_at` 关闭旧 binding、开启新 binding。失败时 binding、Meter 与受影响的
|
||||
成本重算全部回滚,不会显示或留下部分成功。
|
||||
|
||||
新建或更新 binding 必须完整落在所属 Meter epoch 内:
|
||||
`meter.started_at <= binding.started_at < binding.ended_at <= meter.ended_at`(Meter 已关闭时);
|
||||
open-ended binding 只允许属于 active Meter。Close、Unbind、Transfer 与声明 Meter 都从最早受影响边界重算到
|
||||
当前时间;electricity 与 heating/hot-water 分别使用对应的成本引擎,重算失败时整笔生命周期变更回滚。数据库
|
||||
提交成功后才会 best-effort 重新发布 HA discovery;发布失败不会伪装成持久化失败。
|
||||
|
||||
### 换表自动交接与人工恢复
|
||||
|
||||
声明 `reason=meter_swap` 的新 Meter 时,若未选择 channel 且旧 active Meter 恰好有一条唯一、单位兼容的
|
||||
open binding,系统会在新 Meter 起点自动把该 channel 原子交接过去。存在多个候选或时间线歧义时,声明会
|
||||
fail closed 并整体回滚。不是这种唯一自动交接的声明,也会在新边界关闭旧 Meter 的 open binding,避免产生新的
|
||||
“closed Meter + open binding”。
|
||||
|
||||
旧版本或历史异常可能已经留下 stranded binding:前一块 closed Meter 仍有 open DSMR/WarmteLink binding,
|
||||
而当前 active Meter 没有 binding。不要修改数据库、不要跑 migration,也不会在启动时自动修复。请在
|
||||
**Energy → Meters** 使用该 stranded binding 的 **Recover binding** 操作:选择当前 active Meter 与兼容
|
||||
channel,提交一次 Transfer。来源必须是同 commodity、唯一且紧邻的前一块 closed Meter;旧 binding 固定在旧
|
||||
Meter 的 `ended_at` 结束,新 binding 从选择的 `effective_at` 开始。默认是新 Meter 的 `started_at`;选择更晚
|
||||
时间是允许的,但 UI 会提示这段明确的 unbound gap。非前序 Meter、单位不匹配、channel 在无关区间被占用或歧义
|
||||
都会被拒绝,不会误关历史。
|
||||
|
||||
人工 walkthrough 只能使用开发库里**已经存在且由用户报告的** stranded row。不得为了演示通过 SQL、API、
|
||||
脚本、migration 或直接改数据库制造这种历史异常;隔离开发库中没有该 row 时,记录此项为 `N/A/blocked` 即可。
|
||||
Close、Unbind、same-Meter Transfer、Recover binding 与 meter-swap handoff 都会改变 Meter 或 binding 状态,
|
||||
因此应作为彼此独立的验收场景:每个场景使用各自满足前置条件的 Meter/binding,或在执行前恢复独立前置状态,
|
||||
不能把它们串成会互相破坏前提的单一故事。
|
||||
|
||||
上述恢复不新增 Alembic migration、不执行启动修复,也不删除 Meter、binding、reading 或成本历史;它只把用户
|
||||
确认的时间线修正为可审计的闭区间。
|
||||
|
||||
## 非目标(本里程碑不做)
|
||||
|
||||
- Gas 计费 strategy。
|
||||
|
||||
Reference in New Issue
Block a user