feat: 实现纯 Kafka 页面刷新重放交接
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# R2.4 纯 Kafka Trace 可读性闭环报告
|
||||
|
||||
- 上下文:
|
||||
- [HWLAB #2474](https://github.com/pikasTech/HWLAB/issues/2474)
|
||||
- [HWLAB PR #2484](https://github.com/pikasTech/HWLAB/pull/2484)
|
||||
- 合入提交:`55f85a164d0e219b648c3639f6a1d3fa1463bf49`
|
||||
- 固定 trace:`trc_fb6031c28d79495e`
|
||||
|
||||
## 完成结果
|
||||
|
||||
- CLI 直接读取持久 `hwlab.event.v1`,并复用 Web 的 decoder、reducer、row model 和 Markdown renderer。
|
||||
- Kafka 扫描只有抵达 captured end-offset barrier 才声明完整;timeout、limit 或缺失 barrier 均返回 typed partial。
|
||||
- Final Response 只在 Trace 外层显示一次,不再作为内部助手行重复渲染。
|
||||
- tool-call summary 显示命令、输出摘要、耗时和 exit code,不再只有 `commandExecution` 名称。
|
||||
- 每个正式 HWLAB SSE ingress 都刷新 `lastEventAt`,terminal 后到达的新事件仍更新时间。
|
||||
- 产品链继续保持 projection 关闭,不引入 snapshot、sync、gap-fill、finalizer 或 HTTP 历史拼接。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- PR #2484 已合入 `v0.3`,提交 `55f85a16` 属于 `origin/v0.3`。
|
||||
- 固定 trace 的后续 retention 重放再次确认:
|
||||
- `finalResponsePresent=true`;
|
||||
- `finalResponseInTrace=false`;
|
||||
- tool rows 为 `111`;
|
||||
- assistant rows 为 `80`;
|
||||
- terminal 状态为 `completed`。
|
||||
- 页面刷新 retention bootstrap 属于 R3,不把 R2.4 的既有可读性修复改造成第二事实源。
|
||||
@@ -0,0 +1,77 @@
|
||||
# R3.1 纯 Kafka 页面刷新重放状态机调查报告
|
||||
|
||||
- 上下文:
|
||||
- [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486)
|
||||
- [AgentRun #283](https://github.com/pikasTech/agentrun/issues/283)
|
||||
- 固定离线 trace:`trc_fb6031c28d79495e`
|
||||
- 目标运行面:NC01/v03 + AgentRun NC01/v0.2
|
||||
|
||||
## 结论
|
||||
|
||||
- 当前产品实时链没有偏离纯 Kafka 目标:
|
||||
- `agentrun.event.v1` 由 HWLAB direct mapper 一对一发布到 `hwlab.event.v1`;
|
||||
- 产品 SSE 订阅 shared Kafka fanout;
|
||||
- Web 已让 `hwlab.event.v1` 进入统一 decode、reducer 和卡片投影。
|
||||
- 页面刷新缺口位于 transport bootstrap:
|
||||
- 当前 SSE 只发送连接建立后的 future event;
|
||||
- 浏览器内存清空后没有从 Kafka retention 恢复历史事件。
|
||||
- 用户消息还存在一个上游正式事件缺口:
|
||||
- AgentRun `EventType` 当前没有 `user_message`;
|
||||
- command 创建只发布 `backend_status(command-created)`;
|
||||
- HWLAB mapper 和 Web live projector 也没有 user event 语义;
|
||||
- 因此历史 trace 不得伪造用户消息,根因由 AgentRun #283 沿同一 Kafka 链修复。
|
||||
|
||||
## 已确认的可复用基础
|
||||
|
||||
- `startLiveHwlabKafkaEventBridge()` 已维护单个 shared `hwlab.event.v1` consumer,并向浏览器 listener fanout;浏览器连接不会创建长期 consumer group。
|
||||
- `kafkaMessageKey()` 优先使用 `sessionId`,因此同一 HWLAB session 的正式事件使用同一 Kafka key;bootstrap 仍须从实际 transport metadata 证明命中事件只在一个 partition,不能只信任代码假设。
|
||||
- `queryKafkaEventStream()` 已具备:
|
||||
- Kafka admin end-offset snapshot;
|
||||
- 从 retention 起点读取;
|
||||
- 按 trace/session 过滤;
|
||||
- transport `topic/partition/offset` 证据;
|
||||
- 到达 barrier 后有界退出。
|
||||
- 当前查询缺少独立 scanned-record 上限,且默认 limit/timeout 不能作为运行时隐藏策略;refresh 调用必须从 owning YAML 显式传入 group prefix、timeout、scan、event 和 live buffer 预算。
|
||||
- SSE scope authorization 已先通过 server-side session ownership 校验;数据库仅用于授权,不参与历史状态重建。
|
||||
|
||||
## 冻结状态机
|
||||
|
||||
1. `authorize`
|
||||
- 解析并校验请求的 session/trace;
|
||||
- trace-only 请求解析到授权 session;
|
||||
- 浏览器不能请求任意外部 trace。
|
||||
2. `buffer-live`
|
||||
- 在抓 barrier 前注册 shared live fanout listener;
|
||||
- 只缓冲当前授权 scope 的 envelope;
|
||||
- buffer 超过 YAML 预算立即记录 typed failure。
|
||||
3. `capture-and-replay`
|
||||
- 抓取 `hwlab.event.v1` 每个 partition 的 end-offset barrier;
|
||||
- 从 Kafka retention 起点读取到该 barrier;
|
||||
- scan、matched event 和 timeout 均使用 YAML 预算;
|
||||
- 要求扫描完整、至少命中一条、命中事件只属于一个 partition,且每条都有稳定 transport identity 和 event identity。
|
||||
4. `handoff`
|
||||
- replay 按目标 partition offset 递增发送到原有 `hwlab.event.v1` SSE event name;
|
||||
- replay payload 保持原始 HWLAB envelope,不生成 snapshot 或第二种业务事件;
|
||||
- flush live buffer 时,barrier 内记录必须已由 replay identity 证明并去重;barrier 后记录按 offset 继续发送;
|
||||
- flush 完成后切换到 shared live direct delivery,仍保留 barrier/identity 去重以处理 consumer lag。
|
||||
5. `fail-closed`
|
||||
- barrier 缺失、扫描未完成、预算耗尽、多 partition、identity 缺失、buffer 溢出或 barrier overlap 无对应 replay 记录时,通过 `workbench.error` 返回 phase/code/evidence 后关闭 SSE;
|
||||
- 禁止回退 projector、read model、snapshot、sync、gap-fill、finalizer、polling 或 HTTP 历史。
|
||||
|
||||
## 可组合能力与配置合同
|
||||
|
||||
- `directPublish`、`liveKafkaSse`、`kafkaRefreshReplay`、`transactionalProjector`、`projectionOutboxRelay`、`projectionRealtime` 均为独立 boolean capability,不建立二选一 mode。
|
||||
- `kafkaRefreshReplay=false` 时保留现有纯 live 行为。
|
||||
- `kafkaRefreshReplay=true` 时显式要求 owning YAML 提供:
|
||||
- bootstrap consumer group prefix;
|
||||
- timeout;
|
||||
- scanned-record limit;
|
||||
- matched-event limit;
|
||||
- live buffer limit。
|
||||
- 代码只校验这些字段为所需类型并执行,不增加运行时数值默认。
|
||||
|
||||
## 验收边界
|
||||
|
||||
- 固定历史 trace 用于验证 retained replay、terminal、Final Response 唯一性、tool summary、timing 和 projection=0。
|
||||
- 固定历史 trace 生成于 `user_message` 合同之前,不能伪造用户输入;用户消息先用 AgentRun #283 的正式 event fixture 验证一对一 lineage,最终部署后再由 semantic internal web-probe 的真实刷新验收确认。
|
||||
- CLI 必须直接调用生产 bootstrap、decoder、reducer 和 Markdown renderer;若无法表达 barrier/handoff/user event 证据,先改进 CLI 再继续。
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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`。
|
||||
@@ -0,0 +1,86 @@
|
||||
# R3.3 固定 Trace 生产 handoff 单步重放报告
|
||||
|
||||
- 上下文:
|
||||
- [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)
|
||||
- 固定 trace:`trc_fb6031c28d79495e`
|
||||
- 未启动新的真实 AgentRun 运行。
|
||||
|
||||
## 执行命令
|
||||
|
||||
```bash
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts kafka render trace \
|
||||
--from kafka \
|
||||
--trace-id trc_fb6031c28d79495e \
|
||||
--input-topic hwlab.event.v1 \
|
||||
--group-prefix hwlab-v03-workbench-isolated-debug-refresh-2487-final \
|
||||
--limit 500 \
|
||||
--timeout-ms 30000 \
|
||||
--format markdown \
|
||||
--output-markdown /tmp/hwlab-refresh-2487-trc_fb6031c28d79495e-final.md \
|
||||
--row-limit 1 \
|
||||
--json
|
||||
```
|
||||
|
||||
## 生产路径证据
|
||||
|
||||
- 临时只读源码副本:`/tmp/hwlab-refresh-2487.ACKmyy`。
|
||||
- CLI 明确调用生产模块:
|
||||
- `internal/cloud/workbench-kafka-refresh-handoff:createWorkbenchKafkaRefreshHandoff`。
|
||||
- 后续路径与 Web 同构:
|
||||
- `decodeWorkbenchRealtimeEventFrame`;
|
||||
- `reduceWorkbenchRealtimeEvent`;
|
||||
- `planWorkbenchRealtimeApply`;
|
||||
- `workbenchLiveKafkaProjectionTarget`;
|
||||
- `projectWorkbenchLiveKafkaUserMessage|projectWorkbenchLiveKafkaMessage`;
|
||||
- `traceDisplayRows`;
|
||||
- Markdown 只在最后一步与 HTML 分叉。
|
||||
- `liveSubscriptionInstalled=true`,证明 CLI 使用完整生产 handoff 合同,不是直接把查询结果塞给 renderer。
|
||||
|
||||
## Kafka 与 handoff 结果
|
||||
|
||||
- 扫描:
|
||||
- `scanned=2727`;
|
||||
- `parsed=2727`;
|
||||
- `invalidJson=0`;
|
||||
- `completionReason=end-offset`。
|
||||
- barrier:
|
||||
- topic 为 `hwlab.event.v1`;
|
||||
- partition 为 `0`;
|
||||
- start offset 为 `0`;
|
||||
- exclusive end offset 为 `2727`;
|
||||
- last scanned offset 为 `2726`。
|
||||
- trace 命中与应用:
|
||||
- matched 为 `381`;
|
||||
- replayed 为 `381`;
|
||||
- decoded、planned、applied 均为 `381`;
|
||||
- rejected 为 `0`;
|
||||
- stable identity missing 与 duplicate 均为 `0`。
|
||||
- 终态和可读性:
|
||||
- terminal 为 `completed`;
|
||||
- rendered rows 为 `194`;
|
||||
- tool rows 为 `111`;
|
||||
- assistant rows 为 `80`;
|
||||
- `finalResponsePresent=true`;
|
||||
- `finalResponseInTrace=false`。
|
||||
|
||||
## Markdown artifact
|
||||
|
||||
- 运行面路径:`/tmp/hwlab-refresh-2487-trc_fb6031c28d79495e-final.md`。
|
||||
- 行数:`4181`。
|
||||
- 字节数:`225131`。
|
||||
- SHA-256:`39f9fe38c05c8ece6a056dcac405fb29fd7b2d81954a5b3b90a47c6ed52c26e7`。
|
||||
|
||||
## 用户正式事件补充验收
|
||||
|
||||
- 该固定 trace 生成于 `user_message` 合同上线之前,因此历史记录中的 `userMessageCount=0`;未伪造用户输入。
|
||||
- 正式 user event fixture 验证:
|
||||
- AgentRun `user_message` 一对一映射为稳定 HWLAB user event;
|
||||
- retention SSE 在 lifecycle 事件前重放用户事件;
|
||||
- Web reducer 同时覆盖 Kafka-first 和 HTTP-first,稳定为单个用户气泡;
|
||||
- session replay 保留 steer 用户输入与 target trace 的独立身份;
|
||||
- 同构 Markdown 的“用户输入”区域只渲染一次。
|
||||
- CLI 另有定向合同:
|
||||
- incomplete scan 即使零匹配也返回 `partial/source_scan_incomplete`;
|
||||
- 只有完整 barrier 后零匹配才返回 `source_trace_missing`。
|
||||
@@ -28,21 +28,21 @@
|
||||
### R2.3 [completed]
|
||||
|
||||
修复隔离 debug replay 的 producer/barrier 与分层结果合同:保留 source lineage、replayId、offset range,报告 scanned/matched/delivered/decoded/applied/terminal,区分 producer-not-invoked、trace missing、filter mismatch、decoder/reducer rejection 与 terminal missing;仅写 debug topic。上下文:[隔离根因 #2476](https://github.com/pikasTech/HWLAB/issues/2476#issuecomment-4935398125)、[主方案 #2474](https://github.com/pikasTech/HWLAB/issues/2474#issuecomment-4935408473)。依赖:无。验证:empty、matched-no-terminal、reducer-rejected、terminal-complete 四类 typed 结果及 semantic internal web-probe command,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R2.3_Task_Report.md)。
|
||||
### R2.4 [in_progress]
|
||||
### R2.4 [completed]
|
||||
|
||||
以 [HWLAB #2474](https://github.com/pikasTech/HWLAB/issues/2474) 和固定 trace trc_fb6031c28d79495e 完成纯 Kafka 集成闭环:先增强 CLI 直接读取持久 hwlab.event 并复用前端 decode、merge、row model、summary 输出同构 Markdown,再修复 Trace 内 final response 重复、tool-call summary 可见性和每个 HWLAB SSE ingress 刷新 lastEventAt;最后受控部署 NC01/v03,以 Kafka 重放、semantic internal web-probe、DOM 与 OTel 验收 projection=0 且无补洞路径,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R2.4_Task_Report.md)。
|
||||
## R3 [in_progress]
|
||||
|
||||
在纯 Kafka SSE 权威链内实现页面刷新后的可重放引导,明确区别于 projector/read model/snapshot/sync/gap-fill/finalizer 补洞;上下文:[HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486),完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3_Task_Report.md)。
|
||||
### R3.1 [in_progress]
|
||||
### R3.1 [completed]
|
||||
|
||||
依据 [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486) 深入分析 Kafka retention、SSE 刷新连接、offset barrier、shared live handoff 与去重身份,形成不引入第二事实源的状态机和失败可见性,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3.1_Task_Report.md)。
|
||||
### R3.2
|
||||
### R3.2 [completed]
|
||||
|
||||
依据 [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486) 实现 trace/session-scoped Kafka refresh bootstrap 与 shared live fanout 无缝交接,全部开关、group、容量和时限保持 YAML-first,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3.2_Task_Report.md)。
|
||||
### R3.3
|
||||
依据 [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486) 与 [AgentRun #283](https://github.com/pikasTech/agentrun/issues/283),先让用户输入成为 agentrun.event.v1 与 hwlab.event.v1 正式事件,再实现 trace/session-scoped Kafka refresh bootstrap 和 shared live fanout 无缝交接;全部开关、group、容量和时限保持 YAML-first,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3.2_Task_Report.md)。
|
||||
### R3.3 [completed]
|
||||
|
||||
使用 [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486) 的固定 trace trc_fb6031c28d79495e 通过 CLI 单步重放验证顺序、barrier、handoff、terminal、final 唯一性、tool summary 和 timing,不启动新真实运行,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3.3_Task_Report.md)。
|
||||
依据 [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486) 与 [AgentRun #283](https://github.com/pikasTech/agentrun/issues/283),使用固定 trace trc_fb6031c28d79495e 做 CLI 单步重放,并用正式 user_message fixture 补充历史 trace 不具备的用户输入验收;验证顺序、barrier、handoff、terminal、final 唯一性、tool summary 和 timing,不启动新真实运行,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3.3_Task_Report.md)。
|
||||
### R3.4
|
||||
|
||||
完成 [HWLAB #2486](https://github.com/pikasTech/HWLAB/issues/2486) 的定向测试、受控 CI/CD 与 semantic internal web-probe 页面刷新验收,确认 projection=0 且无 snapshot/sync/gap-fill/finalizer 路径,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R3.4_Task_Report.md)。
|
||||
|
||||
Reference in New Issue
Block a user