# Kafka 源码直连调试

- `hwlab-cli kafka render trace` 用于把持久 HWLAB Kafka 事件直接渲染为与 Web 同构的 Markdown Trace：
  - 命令为 `hwlab-cli kafka render trace --from kafka --trace-id <traceId> --format markdown`；
  - 默认输入 topic 优先读取 owning YAML 注入的 `HWLAB_KAFKA_EVENT_TOPIC`；
  - 未注入时使用 canonical `hwlab.event.v1`；
  - Kafka 读取必须使用 `--group-prefix` 或 owning YAML 注入的 `HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX`；
  - group prefix 必须标识独立 debug group，不复用产品 consumer group；
  - 命令只读取 Kafka，不发布事件，不启动 Cloud API，不访问数据库，也不依赖 projector。

- `hwlab-cli kafka inspect order` 用于定位同一 session 在纯 Kafka 刷新链路中的首个顺序分歧：
  - 已知 session 时使用 `--session-id <sessionId>`；
  - 只知道用户输入时使用 `--contains-text <bounded-user-text>` 从 HWLAB retention 解析唯一 session；
  - 文本发现只披露长度和 SHA-256，不回显用户正文；
  - 命令分别扫描 `agentrun.event.v1` 与 `hwlab.event.v1` 到启动时捕获的 topic 上界；
  - `hwlabSessionId` 及其 snake case 形式属于两个 topic 共用的正式 session identity 候选；
  - HWLAB retained event 复用生产 refresh handoff、SSE frame 解码、queue/reducer 和 timeline row model；
  - 输出逐层 transport、stable identity、`sourceSeq`、message index 和 DOM row index；
  - `firstDivergence` 只报告首个改变顺序的层，不过滤、重排或补写任何事件；
  - `--row-limit` 只限制返回的有界尾部明细，不改变全量计数和判定；
  - 任一 topic 未到捕获上界时返回 `partial/source_scan_incomplete`，禁止根据部分扫描下结论；
  - 命令只读取既有 Kafka retention，不发布事件，也不启动新的 AgentRun。

- Trace 渲染与 Web 共享最后一个展示分叉之前的生产管线：
  - JSON frame 使用 `decodeWorkbenchRealtimeEventFrame` 解码；
  - 事件使用 `reduceWorkbenchRealtimeEvent` 与 `planWorkbenchRealtimeApply` 分类；
  - 卡片使用 `projectWorkbenchLiveKafkaMessage` 增量归约；
  - Trace 行使用 `traceDisplayRows` 生成同一 row model；
  - assistant、tool 与 terminal 使用同一稳定 item lifecycle 收敛可见行；
  - 同一 `itemId` 已出现 `completed-agent-message` 后到达的 `agent-message-delta-progress` 不再生成第二条 assistant row；
  - lifecycle 收敛只影响可见行，不删除 source/applied 事件，也不使用正文、时间戳或 DOM 去重；
  - Web 最后渲染 HTML，CLI 最后使用 `renderTraceRowsMarkdown` 渲染 Markdown；
  - Final Response 只放在 Trace 外层，Trace 内保留非最终的助手进展消息。

- Kafka Trace 查询必须披露有界扫描事实：
  - `--run-id` 与 `--command-id` 用于在复用或异常混合的 traceId 内精确隔离一轮；
  - `--command-id` 先按 trace、session 与 run 读取基础流，再在 CLI 内形成 command 生命周期；
  - 只有基础流中至少存在一条精确 commandId 事件，才允许吸附同一 run 的无 commandId final/terminal；
  - 精确 commandId 不存在时必须返回 `source_command_missing`，不得由 commandless 生命周期冒充成功；
  - 精确 command 事件保留；
  - 同一 run 中缺少 commandId 的 final/terminal 生命周期事件保留；
  - 其他 command 和无法归属的 commandless 事件排除；
  - 禁止把 commandId 直接下推后丢失 run-scoped terminal/final；
  - identity 输出必须同时披露观测到的 session、run、command 和 source event 去重计数；
  - command scope 输出必须披露每个 commandId 的计数、缺失 commandId 数、可归属生命周期数、歧义数与排除数；
  - command 生命周期只有在 `scanComplete=true` 时才可判定完整；
  - `limit` 或 `timeout` 只能作为部分流证据，`commandLifecyclePreserved` 必须保持 false；
  - 输出 `scannedCount`、`parsedCount`、`matchedCount`、`invalidJsonCount` 与 `filterRejectedCount`；
  - 输出 topic end offsets、最后扫描 offsets 与 `completionReason`；
  - `completionReason=end-offset` 表示已经扫描到命令启动时捕获的 Kafka 上界；
  - `completionReason=limit` 表示达到显式事件上限，不能冒充完整 trace；
  - `completionReason=timeout` 表示未在 YAML 或 CLI 时限内证明到达上界；
  - 未到捕获上界时，Trace 命令必须返回 `status=partial`、`ok=false`、`source_scan_incomplete` 和非零退出码，同时保留有界 Markdown artifact 供下钻；
  - 默认文本首词必须是 `partial`，并披露 `completionReason` 与 `reachedEndOffsets`，禁止用 `ok` 或 `succeeded` 表示部分扫描；
  - 没有匹配事件时以 `source_trace_missing` 失败，并保留上述扫描计数。

- Trace Markdown artifact 默认写入 `.state/hwlab-cli/kafka-render/`：
  - `--output-markdown` 可显式选择路径；
  - `--from jsonl --jsonl-file <path>` 可完全离线复用同一管线；
  - 默认文本输出保持 compact；
  - 默认 JSON 只返回 `--row-limit` 指定的尾部行，并披露 returned/omitted/window；
  - `--full` 仅用于显式下钻完整 row payload；
  - `--format markdown` 输出 Markdown 正文；
  - `--json` 输出身份、投影、row model、终态、计数和 artifact SHA 的结构化证据；
  - `replayLineage` 分别披露 source、applied、render 与 outer final 层的计数；
  - 助手重复只披露正文 SHA-256、首次 identity、`sourceSeq` 和 Kafka transport，不回显正文；
  - source/applied 重复保持可见而 render 重复归零时，表示稳定 item lifecycle 已在共享 row model 收敛。

- `hwlab-cli kafka regenerate hwlab` 用于把指定 AgentRun session 的事件直接映射为 HWLAB debug 事件：
  - 调用生产路径同源的 AgentRun 解码与 HWLAB 事件映射函数；
  - 不启动 Cloud API；
  - 不访问数据库；
  - 不依赖 transactional projector；
  - 默认输出到 `hwlab.event.debug.v1`，并禁止写入产品 topic `hwlab.event.v1`。

- Kafka 输入适合验证真实 topic 中的 session 事件：
  - 命令为 `hwlab-cli kafka regenerate hwlab --from kafka --session-id <sessionId>`；
  - 默认输入 topic 优先读取 owning YAML 注入的 `HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC`；
  - 未注入时默认读取 canonical `agentrun.event.v1`；
  - stdio partial reconstruction 必须显式指定 `--input-topic agentrun.event.debug.v1`；
  - output topic 优先读取 owning YAML 注入的 `HWLAB_KAFKA_HWLAB_DEBUG_EVENT_TOPIC`；
  - consumer group prefix 优先读取 owning YAML 注入的 `HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX`；
  - output topic 未注入时使用隔离合同值 `hwlab.event.debug.v1`；
  - Kafka 读取或发布缺少 group prefix 时失败关闭，不生成代码内运维默认值；
  - Workbench 消费 debug topic 由 `HWLAB_WORKBENCH_KAFKA_DEBUG_REPLAY_ENABLED` 独立控制；
  - CLI 的离线映射不依赖该运行面开关；
  - Kafka 输入默认只做映射预检和本地证据固化；
  - Kafka 输入未扫描到命令启动时捕获的 topic 上界时返回 `partial/source_scan_incomplete`，保留 artifact，但即使指定 `--publish` 也不得调用 producer；
  - 只有显式 `--publish` 才发布到独立 HWLAB debug topic。

- JSONL 输入适合完全离线的单步测试：
  - 命令为 `hwlab-cli kafka regenerate hwlab --from jsonl --session-id <sessionId> --jsonl-file <path> --no-publish`；
  - 每行可直接放 envelope，也可使用 `{topic,partition,offset,key,value,headers}` transport wrapper；
  - JSONL 输入默认不连接 Kafka；
  - 完整映射结果写入 `.state/hwlab-cli/kafka-debug/` 下的 JSONL artifact；
  - `--output-jsonl` 可显式选择 artifact 路径。

- 输入合同按来源隔离：
  - `agentrun.event.v1` 必须通过严格 canonical decoder；
  - `agentrun.event.reconstruction.v1` 只允许进入 debug decoder；
  - stdio reconstruction 的 `eventId`、`outboxSeq`、`originalEventId` 和 `originalOutboxSeq` 必须保持 `null`；
  - reconstruction 必须声明 `stdio-derived partial reconstruction`；
  - reconstruction 必须保留 `identityDerivation`，禁止静默猜测 HWLAB session identity。

- 输出验证采用一对一和原序映射：
  - `--expect-count <n>` 在映射和发布前锁定输入数量；
  - 每次预检生成 `replayId`，也可用 `--replay-id rpl_...` 显式复用同一批次标识；
  - `next.command` 保留同一个 `replayId`，用于先预检、再显式发布；
  - 发布固定使用隔离 debug topic 的 partition 0；
  - 成功发布必须从 Kafka metadata 形成 `firstOffset`、`lastOffset` 和 `count` barrier；
  - 无法形成唯一 barrier 时以 `debug_publish_barrier_missing` 失败关闭；
  - 命令验证 source sequence、trace、raw AgentRun session、派生 HWLAB session 和源消息哈希；
  - 默认输出为 compact text；
  - 只有显式 `--json` 才输出结构化 JSON；
  - 成功和失败都返回可执行的 `next` hint。
