From 9478f2030191708bf8fec7f15b63282f62c8a8a4 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 10 Jul 2026 16:04:14 +0200 Subject: [PATCH] =?UTF-8?q?test:=20=E6=94=B6=E5=8F=A3=E7=BA=AF=20Kafka=20?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E9=9B=86=E6=88=90=E9=97=A8=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../R2.3_Task_Report.md | 17 ++++++++++------- .../MDTODO/pure-kafka-live-single-step-debug.md | 2 +- .../scripts/workbench-server-state.test.ts | 4 ++-- .../workbench-isolated-kafka-debug.test.ts | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/MDTODO/details/pure-kafka-live-single-step-debug/R2.3_Task_Report.md b/docs/MDTODO/details/pure-kafka-live-single-step-debug/R2.3_Task_Report.md index ccd99749..932413b4 100644 --- a/docs/MDTODO/details/pure-kafka-live-single-step-debug/R2.3_Task_Report.md +++ b/docs/MDTODO/details/pure-kafka-live-single-step-debug/R2.3_Task_Report.md @@ -22,11 +22,16 @@ - 所有批次记录固定写入 debug topic 的 partition 0; - 由 Kafka publish metadata 返回 `firstOffset`、`lastOffset` 和 `count`; - 发布后无法形成唯一 offset range 时,以 `debug_publish_barrier_missing` 失败关闭。 +- correlated v2 consumer 直接定位该 barrier: + - 新 group 完成 assignment 后执行 `seek(firstOffset)`,不从增长中的 debug topic 起点盲扫; + - 观测 `lastOffset` 后立即形成完成/停止边界; + - 缺首尾 offset、缺中间 offset、重复 offset、parse/filter/delivery 少于整批 cardinality 均不能得到 `terminal_complete`; + - 未携带 v2 参数时保留明确的 `trace-only-v1` 诊断;出现任一 v2 参数时必须同时具备安全 `replayId` 和完整 offset range,显式 producer/source 失败证据不得降级成功。 - debug SSE 在 JSON parse 和 trace/replay filter 前采集 broker 事实: - 报告 `scanned`、`barrierScanned`、`parsed`、`traceMatched`、`replayMatched`、`matched` 和 `delivered`; - 分别保留 requested、scanned、matched 和 delivered offset range; - completion 保留第一条匹配事件的脱敏 source lineage; - - `clientReceived`、`decoded` 和 `applied` 在客户端尚未回报时保持 `null`,不伪造成功计数。 + - 服务端明确声明 client counts unavailable;`clientReceived`、`decoded` 和 `applied` 保持 `null`,不伪造浏览器成功计数。 - 新增分层结果合同: - producer/source:`producer_not_invoked`、`source_trace_missing`; - Kafka/server:`topic_no_append_for_replay`、`record_parse_rejected`、`records_scanned_but_filter_mismatch`、`sse_write_failed`; @@ -50,7 +55,7 @@ bun test \ ``` - 结果: - - `45 pass`; + - `50 pass`; - `0 fail`。 - 已覆盖四类 MDTODO 验收结果: - empty:`producer_not_invoked`; @@ -64,9 +69,7 @@ bun test \ ## 边界与后续集成 -- 本子任务未修改 `WorkbenchKafkaDebugPanel.vue` 或其他 UI。 - 本子任务未部署或修改 NC01/v03 运行面。 -- UI 集成时应: - - 把 producer 返回的 `replayId` 和 `outputBarrier` 原样传入 debug SSE; - - 把客户端实际 `received/decoded/applied` 计数交给同一分类合同; - - 不把 debug topic、debug group 或结果合同接入产品实时事实源。 +- 集成分支已让 UI 支持透传 `replayId`/`outputBarrier`,并用同一 EventSource 的本地 `received/decoded/applied` 对服务端结果二次复判;四层计数不严格相等时分别返回 transport、decoder 或 reducer typed failure。 +- debug topic、debug group 和结果合同仍只属于隔离调试,不接入产品实时事实源。 +- 真实 Kafka `seek` 行为与浏览器分层计数仍须由 R2.4 在 NC01/v03 原入口受控验收。 diff --git a/docs/MDTODO/pure-kafka-live-single-step-debug.md b/docs/MDTODO/pure-kafka-live-single-step-debug.md index 13727bbc..70821aff 100644 --- a/docs/MDTODO/pure-kafka-live-single-step-debug.md +++ b/docs/MDTODO/pure-kafka-live-single-step-debug.md @@ -25,7 +25,7 @@ ### R2.2 [completed] 在同一产品 EventSource 的 decode/coalesce 前 tee 原始 frame,并在隔离调试卡片增加 Trace 卡片/原始 HWLAB Event 子标签;raw 按到达顺序写入 bounded 内存、暴露 invalid JSON,YAML-first 开关和容量独立配置,不新增 SSE 或 Kafka consumer。上下文:[主 issue #2474](https://github.com/pikasTech/HWLAB/issues/2474)、[前端设计 #2475](https://github.com/pikasTech/HWLAB/issues/2475#issuecomment-4935381417)。依赖:无,可与 Trace 序列权威修复并行。验证:每个产品 frame 恰好 raw+1 且 reducer 一次,切换标签不新增 EventSource,eviction/invalid-json/runtime contract 通过,完成任务后将详细报告写入[任务报告](./details/pure-kafka-live-single-step-debug/R2.2_Task_Report.md)。 -### R2.3 +### R2.3 [in_progress] 修复隔离 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 diff --git a/web/hwlab-cloud-web/scripts/workbench-server-state.test.ts b/web/hwlab-cloud-web/scripts/workbench-server-state.test.ts index 115cd9de..21ee66a2 100644 --- a/web/hwlab-cloud-web/scripts/workbench-server-state.test.ts +++ b/web/hwlab-cloud-web/scripts/workbench-server-state.test.ts @@ -60,7 +60,7 @@ test("Workbench session list accepts server projection while keeping selected me assert.equal(next[0]?.messages?.length, 2); }); -test("Workbench session tab status is read from server projection", () => { +test("Workbench session tab prefers server status authority over the latest message fallback", () => { const session: WorkbenchSessionRecord = { sessionId: "ses_tab_projection", threadId: "thr_tab_projection", @@ -76,7 +76,7 @@ test("Workbench session tab status is read from server projection", () => { const rawTab = sessionToSessionTab(session, "ses_tab_projection"); const tab = sessionToSessionTab(session, "ses_tab_projection", { ses_tab_projection: { sessionId: "ses_tab_projection", status: "running", lastTraceId: "trc_tab_projection" } }); - assert.equal(rawTab.status, "unknown"); + assert.equal(rawTab.status, "completed"); assert.equal(rawTab.running, false); assert.equal(tab.status, "running"); assert.equal(tab.running, true); diff --git a/web/hwlab-cloud-web/src/stores/workbench-isolated-kafka-debug.test.ts b/web/hwlab-cloud-web/src/stores/workbench-isolated-kafka-debug.test.ts index 9236fa79..b78fb222 100644 --- a/web/hwlab-cloud-web/src/stores/workbench-isolated-kafka-debug.test.ts +++ b/web/hwlab-cloud-web/src/stores/workbench-isolated-kafka-debug.test.ts @@ -58,7 +58,8 @@ test("isolated Kafka debug projects assistant and terminal events through produc assert.equal(completed.message?.status, "completed"); assert.equal(completed.message?.text, "isolated partial response"); assert.equal(completed.message?.traceAutoLifecycle, "terminal"); - assert.equal(completed.message?.finalResponse?.text, "isolated partial response"); + const finalResponse = completed.message?.finalResponse as { text?: string } | null | undefined; + assert.equal(finalResponse?.text, "isolated partial response"); assert.equal(completed.message?.runnerTrace?.eventCount, 2); assert.deepEqual(completed.message?.runnerTrace?.events?.map((event) => event.sourceEventId), ["agevt_assistant", "agevt_terminal"]); assert.equal(completed.logs.every((entry) => entry.accepted), true);