> **Delta Spec**：本文件仅描述对 `openspec/specs/polling-task-handlers/spec.md` 的变更部分。未提及的需求保持不变。

---

### 变更：polling_realname_handler——实名逆转防抖

**原行为**：检测到实名状态 `1→0` 逆转时，立即更新 DB 并调用 `EvaluateAndAct` 触发停机。

**新行为**：检测到逆转时，通过 Redis 计数器累计，仅在**连续 3 次**检测到逆转后才触发 `EvaluateAndAct`；检测到 `true` 时重置计数器。DB 写入行为（`real_name_status` 同步）保持不变——只延迟停机决策，不延迟状态记录。

**Redis Key**：`polling:card:realname:reversal:{card_id}`（`RedisPollingRealnameReversalCountKey`）  
**TTL**：10 分钟（超时自动归零）  
**阈值常量**：`PollingRealnameReversalThreshold = 3`

#### Scenario: 单次逆转不触发停机
- **GIVEN** card_id=4，`real_name_status=1`（DB），Gateway 返回 `false`
- **WHEN** `polling_realname_handler` 处理该卡
- **THEN** DB 更新 `real_name_status=0`；Redis 计数器 `reversal:4` 变为 1（TTL=10min）；**不调用 EvaluateAndAct**；卡保持在线

#### Scenario: 连续 3 次逆转才触发停机
- **GIVEN** card_id=4，前 2 次轮询 Gateway 均返回 false（计数器为 2）
- **WHEN** 第 3 次轮询 Gateway 仍返回 false
- **THEN** Redis 计数器变为 3（达到阈值）；删除计数器；调用 `EvaluateAndAct(freshCard)`；触发停机，`stop_reason='not_realname'`

#### Scenario: 中间出现 true 则重置计数
- **GIVEN** card_id=4，已记录 2 次逆转（计数器=2）
- **WHEN** 下次轮询 Gateway 返回 true（`real_name_status` 重置为 1）
- **THEN** 删除 Redis 计数器（归零）；若状态从 0→1 则触发复机评估（逻辑不变）

#### Scenario: 10 分钟未触发则计数自动归零
- **GIVEN** card_id=4 记录了 1 次逆转（计数器=1），之后 10 分钟内 Gateway 均返回 true
- **WHEN** 10 分钟 TTL 到期
- **THEN** Redis key 自动过期，计数归零；后续逆转需重新从 1 开始累计

---

### 变更：polling_cardstatus_handler——网关侧停机补写 stop_reason

**原行为**：检测到 Gateway 报告卡为"停机"（在线→停机状态变化）时，仅更新 `network_status=0`，不设置 `stop_reason`。

**新行为**：在写入 `network_status=0` 的同时，若当前 `stop_reason` 为空，补写 `stop_reason='carrier_stopped'`。

> **说明**：只在状态变化时补写，若 `stop_reason` 已有值（如已被本系统停机）则不覆盖。

#### Scenario: 网关侧停机事件补写 carrier_stopped
- **GIVEN** 卡在线（`network_status=1`，`stop_reason=''`），Gateway 本次返回"停机"
- **WHEN** `polling_cardstatus_handler` 处理，检测到 `statusChanged=true`，`newNetworkStatus=0`
- **THEN** DB 更新 `network_status=0, stop_reason='carrier_stopped'`；`isPollingStopReason('carrier_stopped')=false`；`EvaluateAndAct` 不触发自动复机

#### Scenario: 不覆盖已有 stop_reason
- **GIVEN** 卡已停机（`network_status=0`，`stop_reason='not_realname'`），Gateway 本次仍返回"停机"
- **WHEN** `polling_cardstatus_handler` 处理，`statusChanged=false`（状态未变）
- **THEN** 不写入任何字段；`stop_reason` 保持 `not_realname` 不变

#### Scenario: carrier_stopped 的卡通过 EvaluateAndAct 调用时正确跳过复机
- **GIVEN** 卡停机，`stop_reason='carrier_stopped'`，套餐有效，已实名
- **WHEN** 其他轮询任务调用 `EvaluateAndAct(card)`
- **THEN** `isPollingStopReason('carrier_stopped')=false`；直接返回 nil，不执行复机
