Files
Cyrene_v2/internal/runtime
AskaEth 97c0c1b94a feat(memory): 调优重排改为唤醒通道,不再 Stop/Start(消灭与到期清扫的耦合)
按塔的裁定把「重排心跳」从生命周期操作改成唤醒通道,调优路径上一个 Start/Stop
都没有了。

- tidy_service.go:TidyService 新增 wake(cap 1 + 非阻塞发)与 wakeMu/wakeCancel;
  loop 的等待段抽成 waitForTick,三态判定(睡满 / 被调优唤醒 / 停);被唤醒只
  重算下一拍、**绝不**跑轮。调度读(nextDelay / dueNow)改走 scheduleSnapshot,
  在同一把锁下同取「cfg + lastRun」,于是运行期写 cfg 不必再把后台件停下来。
  Start / Stop / wg / running 与 Enabled 早退语义**一个字节没动**。
- tidy_service_tuning.go:ApplyPanelTuning 变成「持 runMu 写 cfg → 发一次唤醒」。
  唤醒 = 一次性标记(保证不丢:心跳不在等时标记留在缓冲里)**加上**取消当前那次
  等待(保证立刻生效,否则 24h→3h 要等原来那一觉睡完)。

两条冲突就此消失,各配一条可复现断言:
- **面板保存不再触发到期清扫、也不重置清扫 ticker**:新用例在保存前后各造一行已
  到期的记忆,保存之后那行必须**还在**、清扫审计计数必须不变;该用例自带活对照
  (启动清扫会删掉第一行),因此「行还在」不是量具失灵。若保存走 Stop/Start,清扫
  的「启动即扫一轮」会立刻把它删掉。
- **调优路径上不再存在任何 Start 调用**(结构上消灭交叉 Start/Stop):包内生产代码
  已 grep 不到任何 `.Start(` / `.Stop(` 调用,Start 与 Stop 各只剩组合根那一处调用点
  (`cmd/cyrene/main.go` / `cmd/cyrene/wire_services.go`),M3 注释里那条「Start/Stop
  各只调用一次」的不变式因此重新成立;交叉场景由带真心跳的调优用例在 `-race` 下跑过
  (不再 panic)。

热生效没退化:原来那条「23h30m → 30m」的断言按新机制重写 —— 用一个「听得懂 ctx
取消」的假等待看两觉的时长(保存前 23h30m、保存当场的第二觉 30m),并补上「唤醒不
跑轮(lastRun 不前移)」「唤醒后 running 仍为 true」。

变异自证(都跑过再还原,`diff` 逐字一致):
- 去掉 `notifyReschedule` 调用 ⇒「被唤醒后的第二觉」等不到 ⇒ 用例红;
- 只留标记、去掉「取消当前等待」那一半 ⇒ 同一条红(两半都必需)。

M3 的守卫 `expire_loop_test.go` **一行未动**:它的假 wait 只认 ctx,父 ctx 取消时
返回 false,在新机制下语义不变(无人唤醒时照样把心跳停住)。
2026-10-13 22:17:22 +08:00
..