# R3.2 纯 Kafka retention-to-live 实现报告

- 上下文：
  - [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486)
  - [HWLAB #2487](https://github.com/pikasTech/HWLAB/issues/2487)
  - [AgentRun #283](https://github.com/pikasTech/agentrun/issues/283)
  - [UniDesk PR #1738](https://github.com/pikasTech/unidesk/pull/1738)

## 完成结果

- 用户输入沿唯一权威链成为正式事件：
  - AgentRun `user_message`；
  - HWLAB direct mapper 一对一映射到正式 `hwlab.event.v1` user event；
  - Web reducer 按稳定 `userMessageId` upsert，HTTP-first 与 Kafka-first 均只保留一个用户气泡。
- 页面刷新使用 retention bootstrap 与 shared live fanout 无缝交接：
  - 先订阅 shared live 并有界缓冲；
  - 再捕获 topic end-offset barrier；
  - 从 Kafka retention 起点完整扫描到 barrier；
  - 原样发送历史 `hwlab.event.v1`；
  - 按 transport identity、`eventId` 和 `sourceEventId` 去重后切到同一 live writer。
- fail-closed 覆盖：
  - retention 起点移动或不可验证；
  - timeout、scan limit、matched limit；
  - scope 跨 partition；
  - transport 或稳定事件身份冲突；
  - live buffer overflow；
  - pre-barrier gap；
  - SSE write 或客户端断开。
- `workbench.error` 只披露有界安全诊断：
  - completion reason；
  - timeout、scan 和 matched limit；
  - scanned、matched count；
  - partition；
  - 不透传未列入白名单的 payload。
- capability、group prefix、timeout、scan limit、matched-event limit 和 live-buffer limit 均独立且 YAML-first。
- UniDesk PR #1738 已先合入并 apply 到 NC01/v03 控制面；旧 runtime 未被触发，满足先配置后代码的无窗口顺序。

## 架构边界

- 数据链保持：
  - `codex-stdio.raw.v1`；
  - `agentrun.event.v1`；
  - HWLAB direct mapper；
  - `hwlab.event.v1`；
  - Kafka retention bootstrap；
  - shared live fanout；
  - SSE；
  - Web reducer。
- 未引入 projector、read model、snapshot、sync、gap-fill、finalizer、polling 或 HTTP 历史 fallback。
- `kafkaRefreshReplay=false` 保留既有 live-only 行为；各 capability 可组合、可独立开关。

## 测试

- cloud、handoff、CLI 与 AgentRun adapter：`98/98`。
- Web reducer、runtime policy 与 capability config：`51/51`。
- 本次变更相关 cloud-web runtime config：`4/4`。
- 合计：`153/153`。
