// Responsibility: regenerate isolated HWLAB debug Kafka events from AgentRun Kafka or JSONL input without Cloud API, DB, or projector dependencies.

import { createHash, randomUUID } from "node:crypto";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";

import { Kafka, logLevel } from "kafkajs";

import {
  buildHwlabKafkaProducerMessage,
  DEFAULT_AGENTRUN_DEBUG_EVENT_TOPIC,
  DEFAULT_AGENTRUN_EVENT_TOPIC,
  DEFAULT_HWLAB_DEBUG_EVENT_TOPIC,
  DEFAULT_HWLAB_EVENT_TOPIC,
  kafkaSocketFactory,
  projectAgentRunKafkaEventToHwlabEvent,
  projectAgentRunKafkaMessageToHwlabDebugEvent,
  queryKafkaEventStream
} from "../../../internal/cloud/kafka-event-bridge.ts";
import { createWorkbenchKafkaRefreshHandoff } from "../../../internal/cloud/workbench-kafka-refresh-handoff.ts";
import { decodeWorkbenchRealtimeEventFrame } from "../../../web/hwlab-cloud-web/src/api/workbench-realtime-codec.ts";
import { reduceWorkbenchRealtimeEvent } from "../../../web/hwlab-cloud-web/src/stores/workbench-event-reducer.ts";
import { projectWorkbenchLiveKafkaMessage, projectWorkbenchLiveKafkaUserMessage, workbenchLiveKafkaAssistantText, workbenchLiveKafkaProjectionTarget } from "../../../web/hwlab-cloud-web/src/stores/workbench-live-kafka-event.ts";
import { planWorkbenchRealtimeApply } from "../../../web/hwlab-cloud-web/src/stores/workbench-realtime-plan.ts";
import { createWorkbenchServerState, reduceWorkbenchServerState, selectActiveMessages } from "../../../web/hwlab-cloud-web/src/stores/workbench-server-state.ts";
import { buildWorkbenchTimelineRows, workbenchMessageIdentity } from "../../../web/hwlab-cloud-web/src/stores/workbench-timeline-model.ts";
import { renderTraceRowsMarkdown, traceDisplayRows } from "./trace-renderer.ts";

const CLI_NAME = "hwlab-cli";
const VERSION = "0.3.6-kafka-session-order";
const DEFAULT_LIMIT = 500;
const DEFAULT_TIMEOUT_MS = 5000;
const DEBUG_OUTPUT_PARTITION = 0;

type JsonRecord = Record<string, any>;
type EnvLike = Record<string, string | undefined>;
type ParsedArgs = Record<string, unknown> & { _: string[] };

type KafkaCliOptions = {
  cwd?: string;
  env?: EnvLike;
  now?: () => string;
  readKafka?: (input: Record<string, any>) => Promise<Record<string, any>>;
  createProducer?: (input: { env: EnvLike; clientId: string }) => Promise<any> | any;
};

export async function mainKafkaCli(argv = process.argv.slice(3), options: KafkaCliOptions = {}) {
  const result = await runKafkaCli(argv, options);
  const json = argv.includes("--json");
  const markdown = requestedFormat(argv) === "markdown" && !json ? result.markdownOutput : null;
  process.stdout.write(json ? `${JSON.stringify(result.payload, null, 2)}\n` : markdown ? `${markdown.trimEnd()}\n` : `${renderKafkaCliText(result.payload)}\n`);
  process.exitCode = result.exitCode;
}

export async function runKafkaCli(argv: string[], options: KafkaCliOptions = {}) {
  const now = options.now ?? (() => new Date().toISOString());
  let action = "kafka.help";
  try {
    const parsed = parseOptions(argv);
    const command = parsed._[0] || "help";
    const resource = parsed._[1] || "";
    if (["help", "--help", "-h"].includes(command) || parsed.help === true) {
      return result(0, kafkaHelp(), now);
    }
    if (command === "render" && resource === "trace") {
      action = "kafka.render.trace";
      const rendered = await renderHwlabKafkaTrace(parsed, {
        cwd: options.cwd ?? process.cwd(),
        env: options.env ?? process.env,
        now,
        readKafka: options.readKafka ?? defaultKafkaReader,
        createProducer: options.createProducer ?? defaultProducerFactory
      });
      return { ...result(rendered.payload.status === "partial" ? 2 : 0, rendered.payload, now), markdownOutput: rendered.markdown };
    }
    if (command === "inspect" && resource === "order") {
      action = "kafka.inspect.order";
      const payload = await inspectKafkaSessionOrder(parsed, {
        env: options.env ?? process.env,
        now,
        readKafka: options.readKafka ?? defaultKafkaReader
      });
      return result(payload.status === "partial" ? 2 : 0, payload, now);
    }
    if (command !== "regenerate" || resource !== "hwlab") throw cliError("unsupported_kafka_command", "supported commands: kafka regenerate hwlab | kafka render trace", { command, resource });
    action = "kafka.regenerate.hwlab";
    const payload = await regenerateHwlabDebugEvents(parsed, {
      cwd: options.cwd ?? process.cwd(),
      env: options.env ?? process.env,
      now,
      readKafka: options.readKafka ?? defaultKafkaReader,
      createProducer: options.createProducer ?? defaultProducerFactory
    });
    return result(payload.status === "partial" ? 2 : 0, payload, now);
  } catch (error) {
    return result(1, failure(error, action), now);
  }
}

export async function regenerateHwlabDebugEvents(parsed: ParsedArgs, dependencies: Required<KafkaCliOptions>) {
  const sessionId = requiredSessionId(parsed.sessionId);
  const traceId = optionalTraceId(parsed.traceId);
  const replayId = optionalReplayId(parsed.replayId) || `rpl_${randomUUID().replaceAll("-", "")}`;
  const sourceMode = enumValue(parsed.from ?? "kafka", "from", ["kafka", "jsonl"]);
  const inputTopicResolution = sourceMode === "kafka"
    ? resolveConfig(parsed.inputTopic, dependencies.env.HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC, DEFAULT_AGENTRUN_EVENT_TOPIC, "--input-topic", "env:HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC")
    : resolveConfig(parsed.inputTopic, undefined, DEFAULT_AGENTRUN_DEBUG_EVENT_TOPIC, "--input-topic", "contract-default");
  const outputTopicResolution = resolveConfig(parsed.outputTopic, dependencies.env.HWLAB_KAFKA_HWLAB_DEBUG_EVENT_TOPIC, DEFAULT_HWLAB_DEBUG_EVENT_TOPIC, "--output-topic", "env:HWLAB_KAFKA_HWLAB_DEBUG_EVENT_TOPIC");
  const inputTopic = inputTopicResolution.value;
  const outputTopic = outputTopicResolution.value;
  assertDebugTopic(outputTopic);
  const limit = boundedInteger(parsed.limit, DEFAULT_LIMIT, 1, 5000, "limit");
  const timeoutMs = boundedInteger(parsed.timeoutMs, DEFAULT_TIMEOUT_MS, 250, 60000, "timeoutMs");
  const expectedCount = optionalPositiveInteger(parsed.expectCount, "expectCount");
  const readLimit = expectedCount === undefined ? limit : Math.min(5000, expectedCount + 1);
  const shouldPublish = parsed.publish === true && parsed.noPublish !== true;
  const groupPrefixResolution = resolveKafkaGroupConfig(
    parsed.groupPrefix,
    dependencies.env.HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX,
    sourceMode === "kafka" || shouldPublish
  );
  const groupPrefix = text(groupPrefixResolution.value);
  if (groupPrefix) assertDebugGroup(groupPrefix);

  const readResult = sourceMode === "jsonl"
    ? await readJsonlInput(requiredText(parsed.jsonlFile, "jsonlFile"), dependencies.cwd, inputTopic)
    : await dependencies.readKafka({
      env: dependencies.env,
      stream: "agentrun",
      topic: inputTopic,
      sessionId,
      traceId: traceId || null,
      limit: readLimit,
      timeoutMs,
      fromBeginning: parsed.fromEnd !== true,
      groupIdPrefix: groupPrefix
    });
  const records = Array.isArray(readResult.events) ? readResult.events : [];
  const sourceScanComplete = sourceMode === "jsonl" || readResult.completionReason === "end-offset";
  const mapped = mapAgentRunRecordsToHwlabDebugEvents(records, {
    sessionId,
    traceId,
    expectedCount,
    producedAt: dependencies.now(),
    replayId
  });

  const stateId = `hwlab-debug-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
  const artifactPath = path.resolve(
    dependencies.cwd,
    text(parsed.outputJsonl) || path.join(".state", "hwlab-cli", "kafka-debug", stateId, "hwlab-events.jsonl")
  );
  const artifactRows = mapped.events.map((envelope) => {
    const message = buildHwlabKafkaProducerMessage(envelope);
    return {
      topic: outputTopic,
      partition: DEBUG_OUTPUT_PARTITION,
      offset: null,
      key: message.key,
      value: envelope,
      headers: message.headers
    };
  });
  const artifactText = artifactRows.map((row) => JSON.stringify(row)).join("\n") + "\n";
  await mkdir(path.dirname(artifactPath), { recursive: true });
  await writeFile(artifactPath, artifactText, "utf8");

  let publishedCount = 0;
  let publishMetadata: unknown[] = [];
  const publishAllowed = shouldPublish && sourceScanComplete;
  if (publishAllowed) {
    const producer = await dependencies.createProducer({ env: dependencies.env, clientId: `${groupPrefix}-producer` });
    try {
      await producer.connect?.();
      publishMetadata = await producer.send({
        topic: outputTopic,
        messages: mapped.events.map((event) => ({ ...buildHwlabKafkaProducerMessage(event), partition: DEBUG_OUTPUT_PARTITION }))
      });
      publishedCount = mapped.events.length;
    } catch (error) {
      throw cliError("hwlab_debug_publish_failed", "failed to publish regenerated HWLAB events to the isolated debug topic", {
        topic: outputTopic,
        mappedCount: mapped.events.length,
        artifactPath,
        causeCode: (error as any)?.code ?? null,
        causeMessage: error instanceof Error ? error.message : String(error)
      });
    } finally {
      if (typeof producer.disconnect === "function") await producer.disconnect().catch(() => undefined);
    }
  }

  const first = mapped.events[0];
  const last = mapped.events.at(-1);
  const offsetRanges = publishOffsetRanges(publishMetadata, publishedCount, DEBUG_OUTPUT_PARTITION);
  if (publishAllowed && offsetRanges.length !== 1) {
    throw cliError("debug_publish_barrier_missing", "Kafka publish succeeded without a usable isolated replay offset barrier", {
      replayId,
      topic: outputTopic,
      partition: DEBUG_OUTPUT_PARTITION,
      publishedCount,
      metadataCount: Array.isArray(publishMetadata) ? publishMetadata.length : 0
    });
  }
  const sourceFile = text(readResult.sourceFile);
  const traceArgument = traceId ? ` --trace-id ${traceId}` : "";
  const groupArgument = groupPrefix ? ` --group-prefix ${groupPrefix}` : "";
  const baseCommand = sourceMode === "jsonl"
    ? `hwlab-cli kafka regenerate hwlab --from jsonl --session-id ${sessionId}${traceArgument} --replay-id ${replayId}${groupArgument} --jsonl-file ${shellArg(sourceFile || String(parsed.jsonlFile))}`
    : `hwlab-cli kafka regenerate hwlab --from kafka --session-id ${sessionId}${traceArgument} --replay-id ${replayId}${groupArgument} --input-topic ${inputTopic}`;
  const nextCommand = !sourceScanComplete
    ? `${baseCommand}${shouldPublish ? " --publish" : " --no-publish"} --output-topic ${outputTopic} --json`
    : shouldPublish
    ? `${baseCommand} --no-publish --output-topic ${outputTopic} --expect-count ${mapped.events.length} --json`
    : `${baseCommand} --publish --output-topic ${outputTopic} --expect-count ${mapped.events.length} --json`;
  return {
    ok: sourceScanComplete,
    action: "kafka.regenerate.hwlab",
    status: sourceScanComplete ? "succeeded" : "partial",
    error: sourceScanComplete ? undefined : {
      code: "source_scan_incomplete",
      message: "Kafka scan ended before the captured AgentRun topic end offsets were reached; debug publish was not attempted",
      details: {
        completionReason: readResult.completionReason ?? null,
        reachedEndOffsets: readResult.reachedEndOffsets ?? false,
        publishRequested: shouldPublish,
        artifactPath,
        valuesPrinted: false
      }
    },
    replayId,
    sourceMode,
    config: {
      inputTopic: inputTopicResolution,
      outputTopic: outputTopicResolution,
      groupPrefix: groupPrefixResolution
    },
    input: {
      topic: inputTopic,
      groupId: readResult.groupId ?? null,
      readCount: records.length,
      matchedCount: mapped.events.length,
      scannedCount: readResult.scannedCount ?? records.length,
      parsedCount: readResult.parsedCount ?? records.length,
      queryMatchedCount: readResult.matchedCount ?? records.length,
      scanComplete: sourceScanComplete,
      completionReason: readResult.completionReason ?? (sourceMode === "jsonl" ? "file-end" : null),
      reachedEndOffsets: readResult.reachedEndOffsets ?? (sourceMode === "jsonl"),
      endOffsetsAvailable: readResult.endOffsetsAvailable ?? false,
      endOffsets: readResult.endOffsets ?? [],
      lastScannedOffsets: readResult.lastScannedOffsets ?? [],
      sessionId,
      traceId: traceId || null
    },
    output: {
      schema: DEFAULT_HWLAB_DEBUG_EVENT_TOPIC,
      topic: outputTopic,
      count: mapped.events.length,
      published: publishAllowed,
      publishedCount,
      metadataCount: Array.isArray(publishMetadata) ? publishMetadata.length : 0,
      offsetRanges,
      artifact: {
        path: artifactPath,
        bytes: Buffer.byteLength(artifactText),
        sha256: `sha256:${sha256(artifactText)}`
      }
    },
    validation: {
      expectedCount: expectedCount ?? null,
      sourceScanComplete,
      oneToOne: mapped.inputCount === mapped.events.length,
      orderPreserved: mapped.orderPreserved,
      lineagePreserved: mapped.lineagePreserved,
      firstSourceSeq: first?.debugLineage?.sourceSeq ?? null,
      lastSourceSeq: last?.debugLineage?.sourceSeq ?? null,
      inputKinds: [...new Set(mapped.inputKinds)]
    },
    replay: {
      replayId,
      producerInvoked: publishAllowed,
      sourceMatched: true,
      sourceLineage: {
        traceId: first?.debugLineage?.inputTraceId ?? traceId ?? null,
        sessionId: first?.debugLineage?.inputSessionId ?? sessionId,
        hwlabSessionId: first?.debugLineage?.inputHwlabSessionId ?? null,
        runId: first?.debugLineage?.inputRunId ?? null,
        commandId: first?.debugLineage?.inputCommandId ?? null,
        inputTopic,
        sourcePartition: first?.sourceEvent?.partition ?? null,
        firstSourceOffset: first?.sourceEvent?.offset ?? null,
        lastSourceOffset: last?.sourceEvent?.offset ?? null,
        firstSourceSeq: first?.debugLineage?.sourceSeq ?? null,
        lastSourceSeq: last?.debugLineage?.sourceSeq ?? null,
        valuesPrinted: false
      },
      outputBarrier: offsetRanges.length === 1 ? offsetRanges[0] : null,
      valuesPrinted: false
    },
    runtimeDependencies: {
      cloudApi: false,
      database: false,
      transactionalProjector: false,
      kafka: sourceMode === "kafka" || publishAllowed
    },
    next: {
      command: nextCommand,
      reason: !sourceScanComplete
        ? "源 Kafka 扫描不完整，已保留 artifact 且未调用 producer；先扩大有界扫描并到达捕获上界，再重试发布。"
        : shouldPublish
        ? "用同一源码映射做一次无 Kafka 写入复验；随后 Workbench 调试模式按 traceId 消费独立 debug topic。"
        : "离线映射已固化为 JSONL；确认内容后可显式 --publish 写入独立 debug topic。"
    },
    valuesPrinted: false
  };
}

export async function renderHwlabKafkaTrace(parsed: ParsedArgs, dependencies: Required<KafkaCliOptions>) {
  const traceId = requiredTraceId(parsed.traceId);
  const sessionId = optionalSessionId(parsed.sessionId);
  const runId = optionalRunId(parsed.runId);
  const commandId = optionalCommandId(parsed.commandId);
  const sourceMode = enumValue(parsed.from ?? "kafka", "from", ["kafka", "jsonl"]);
  const format = enumValue(parsed.format ?? "text", "format", ["text", "markdown"]);
  const inputTopicResolution = resolveConfig(
    parsed.inputTopic,
    dependencies.env.HWLAB_KAFKA_EVENT_TOPIC,
    DEFAULT_HWLAB_EVENT_TOPIC,
    "--input-topic",
    "env:HWLAB_KAFKA_EVENT_TOPIC"
  );
  const inputTopic = inputTopicResolution.value;
  const limit = boundedInteger(parsed.limit, DEFAULT_LIMIT, 1, 5000, "limit");
  const timeoutMs = boundedInteger(parsed.timeoutMs, DEFAULT_TIMEOUT_MS, 250, 60000, "timeoutMs");
  const expectedCount = optionalPositiveInteger(parsed.expectCount, "expectCount");
  const readLimit = commandId || expectedCount === undefined ? limit : Math.min(5000, expectedCount + 1);
  const groupPrefixResolution = resolveKafkaGroupConfig(
    parsed.groupPrefix,
    dependencies.env.HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX,
    sourceMode === "kafka"
  );
  const groupPrefix = text(groupPrefixResolution.value);
  if (groupPrefix) assertDebugGroup(groupPrefix);

  const kafkaQuery = {
      env: dependencies.env,
      stream: "hwlab",
      topic: inputTopic,
      traceId,
      sessionId: sessionId || null,
      runId: runId || null,
      limit: readLimit,
      timeoutMs,
      fromBeginning: parsed.fromEnd !== true,
      groupIdPrefix: groupPrefix
    };
  const refreshHandoff = sourceMode === "kafka"
    ? await replayHwlabKafkaTraceThroughProductionHandoff({
        traceId,
        sessionId: sessionId || null,
        query: kafkaQuery,
        readKafka: dependencies.readKafka
      })
    : null;
  const readResult = sourceMode === "jsonl"
    ? await readJsonlInput(requiredText(parsed.jsonlFile, "jsonlFile"), dependencies.cwd, inputTopic)
    : refreshHandoff?.readResult ?? {};
  const records = sourceMode === "kafka" && refreshHandoff?.status === "succeeded"
    ? refreshHandoff.records
    : Array.isArray(readResult.events) ? readResult.events : [];
  const scoped = scopeHwlabTraceRecords(records, { traceId, sessionId, runId, commandId });
  const sourceScanComplete = sourceMode === "jsonl" || readResult.completionReason === "end-offset";
  const commandScopeScanComplete = sourceScanComplete;
  const matchingRecords = scoped.records;
  const stateId = `hwlab-trace-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
  const artifactPath = path.resolve(
    dependencies.cwd,
    text(parsed.outputMarkdown) || path.join(".state", "hwlab-cli", "kafka-render", stateId, `${traceId}.md`)
  );
  if (!sourceScanComplete && matchingRecords.length === 0) {
    const markdown = renderWorkbenchKafkaTraceMarkdown({
      traceId,
      sessionId,
      status: "unknown",
      userMessages: [],
      sourceEventCount: 0,
      rows: [],
      finalResponseText: null,
      sourceScanComplete: false,
      completionReason: readResult.completionReason ?? null
    });
    const artifact = `${markdown.trimEnd()}\n`;
    await mkdir(path.dirname(artifactPath), { recursive: true });
    await writeFile(artifactPath, artifact, "utf8");
    return {
      markdown,
      payload: {
        ok: false,
        action: "kafka.render.trace",
        status: "partial",
        error: {
          code: "source_scan_incomplete",
          message: "Kafka scan ended before the captured topic end offsets were reached",
          details: {
            completionReason: readResult.completionReason ?? null,
            reachedEndOffsets: readResult.reachedEndOffsets ?? false,
            artifactPath,
            valuesPrinted: false
          }
        },
        format,
        input: {
          mode: sourceMode,
          topic: inputTopic,
          topicSource: inputTopicResolution.source,
          groupId: readResult.groupId ?? null,
          groupPrefix: groupPrefix || null,
          groupPrefixSource: groupPrefixResolution.source,
          scannedCount: readResult.scannedCount ?? records.length,
          parsedCount: readResult.parsedCount ?? records.length,
          queryMatchedCount: readResult.matchedCount ?? records.length,
          matchedCount: 0,
          invalidJsonCount: readResult.invalidJsonCount ?? 0,
          filterRejectedCount: readResult.filterRejectedCount ?? 0,
          scopeRejectedCount: scoped.summary.baseRejectedCount + scoped.summary.commandScopeRejectedCount,
          commandScope: { ...scoped.summary, scanComplete: false },
          scanComplete: false,
          completionReason: readResult.completionReason ?? null,
          reachedEndOffsets: readResult.reachedEndOffsets ?? false,
          endOffsetsAvailable: readResult.endOffsetsAvailable ?? false,
          endOffsetsError: readResult.endOffsetsError ?? null,
          endOffsets: readResult.endOffsets ?? [],
          lastScannedOffsets: readResult.lastScannedOffsets ?? [],
          sourceFile: readResult.sourceFile ?? null,
          refreshHandoff: refreshHandoff?.evidence ?? null,
          valuesPrinted: false
        },
        projection: {
          pipeline: ["createWorkbenchKafkaRefreshHandoff", "decodeWorkbenchRealtimeEventFrame", "reduceWorkbenchRealtimeEvent", "planWorkbenchRealtimeApply", "workbenchLiveKafkaProjectionTarget", "projectWorkbenchLiveKafkaUserMessage|projectWorkbenchLiveKafkaMessage", "traceDisplayRows"],
          decodedCount: 0,
          plannedCount: 0,
          appliedCount: 0,
          rejectedCount: 0,
          valuesPrinted: false
        },
        output: {
          artifact: { path: artifactPath, byteCount: Buffer.byteLength(artifact, "utf8"), sha256: sha256(artifact) },
          valuesPrinted: false
        },
        validation: {
          sourceScanComplete: false,
          refreshHandoffComplete: false,
          allMatchedEventsApplied: true,
          sharedRowModel: true,
          valuesPrinted: false
        },
        runtimeDependencies: {
          cloudApi: false,
          database: false,
          transactionalProjector: false,
          kafka: sourceMode === "kafka"
        },
        next: {
          command: `hwlab-cli kafka render trace --from ${sourceMode} --trace-id ${traceId}${sessionId ? ` --session-id ${sessionId}` : ""}${runId ? ` --run-id ${runId}` : ""}${commandId ? ` --command-id ${commandId}` : ""} --format markdown`,
          reason: "源扫描未完成；使用新的隔离 debug group 重试，只有完整 barrier 后零匹配才判定 trace 不存在。"
        },
        valuesPrinted: false
      }
    };
  }
  if (matchingRecords.length === 0) {
    throw cliError(commandId && scoped.summary.baseMatchedCount > 0 ? "source_command_missing" : "source_trace_missing", commandId
      ? "no HWLAB Kafka events matched the requested command lifecycle"
      : "no HWLAB Kafka events matched the requested traceId/sessionId", {
      traceId,
      sessionId: sessionId || null,
      runId: runId || null,
      commandId: commandId || null,
      scannedCount: readResult.scannedCount ?? records.length,
      parsedCount: readResult.parsedCount ?? records.length,
      queryMatchedCount: readResult.matchedCount ?? records.length,
      commandScope: { ...scoped.summary, scanComplete: commandScopeScanComplete },
      completionReason: readResult.completionReason ?? null,
      reachedEndOffsets: readResult.reachedEndOffsets ?? null
    });
  }
  if (expectedCount !== undefined && matchingRecords.length !== expectedCount) {
    throw cliError("hwlab_trace_event_count_mismatch", "matched HWLAB event count does not equal --expect-count", {
      expectedCount,
      actualCount: matchingRecords.length,
      traceId,
      sessionId: sessionId || null
    });
  }

  const projection = projectHwlabRecordsThroughWorkbench(matchingRecords, {
    traceId,
    sessionId,
    observedAt: dependencies.now()
  });
  if (!projection.message?.runnerTrace) {
    throw cliError("workbench_trace_projection_empty", "HWLAB events did not produce a Workbench runner trace", {
      traceId,
      decodedCount: projection.decodedCount,
      plannedCount: projection.plannedCount,
      appliedCount: projection.appliedCount,
      rejectedCount: projection.rejections.length,
      firstRejection: projection.rejections[0] ?? null
    });
  }
  const finalResponseText = projection.message.traceAutoLifecycle === "terminal" ? text(projection.message.text) : "";
  const trace = projection.message.runnerTrace as Record<string, any>;
  const events = Array.isArray(trace.events) ? trace.events : [];
  const rows = traceDisplayRows(trace, events, {
    finalResponsePlacement: "outer",
    outerFinalResponseText: finalResponseText || null
  });
  const markdown = renderWorkbenchKafkaTraceMarkdown({
    traceId,
    sessionId: text(projection.message.sessionId),
    status: text(projection.message.status),
    userMessages: projection.userMessages,
    sourceEventCount: events.length,
    rows,
    finalResponseText: finalResponseText || null,
    sourceScanComplete,
    completionReason: readResult.completionReason ?? (sourceMode === "jsonl" ? "file-end" : null)
  });
  await mkdir(path.dirname(artifactPath), { recursive: true });
  await writeFile(artifactPath, `${markdown.trimEnd()}\n`, "utf8");
  const fullRows = parsed.full === true;
  const rowPreviewLimit = boundedInteger(parsed.rowLimit, 20, 1, 200, "rowLimit");
  const selectedRows = fullRows ? rows : rows.slice(-rowPreviewLimit);
  const sessionIds = uniqueText(matchingRecords.flatMap((record) => hwlabSessionCandidates(valueFromRecord(record))));
  const runIds = uniqueText(matchingRecords.flatMap((record) => runCandidates(valueFromRecord(record))));
  const commandIds = uniqueText(matchingRecords.flatMap((record) => commandCandidates(valueFromRecord(record))));
  const sourceEventIdentityValues = matchingRecords.map((record) => text(valueFromRecord(record)?.sourceEventId ?? valueFromRecord(record)?.event?.sourceEventId));
  const sourceEventIds = uniqueText(sourceEventIdentityValues);
  const sourceEventIdentityPresentCount = sourceEventIdentityValues.filter(Boolean).length;
  const rowPayload = selectedRows.map((row) => ({
    rowId: row.rowId,
    seq: row.seq,
    sequenceAuthority: row.sequenceAuthority,
    tone: row.tone,
    toolState: row.toolState ?? null,
    statusLabel: row.statusLabel ?? null,
    header: row.header,
    preview: row.preview ?? null,
    body: fullRows ? row.body : boundedText(row.body, 500),
    bodyTruncated: !fullRows && text(row.body).length > 500,
    terminal: row.terminal === true,
    bodyFormat: row.bodyFormat ?? null
  }));
  const toolRowCount = rows.filter((row) => row.rowId.startsWith("tool:")).length;
  const assistantRowCount = rows.filter((row) => /助手/u.test(row.header)).length;
  const finalResponseFingerprint = traceTextFingerprint(finalResponseText);
  const finalResponseInTrace = Boolean(finalResponseFingerprint && rows.some((row) =>
    /助手/u.test(row.header) && traceTextFingerprint(row.body) === finalResponseFingerprint
  ));
  const replayLineage = traceReplayLineageEvidence({
    sourceEvents: projection.sourceEvents,
    appliedEvents: events,
    userMessages: projection.userMessages,
    rows,
    finalResponseText
  });
  return {
    markdown,
    payload: {
      ok: sourceScanComplete,
      action: "kafka.render.trace",
      status: sourceScanComplete ? "succeeded" : "partial",
      error: sourceScanComplete ? undefined : {
        code: "source_scan_incomplete",
        message: "Kafka scan ended before the captured topic end offsets were reached",
        details: {
          completionReason: readResult.completionReason ?? null,
          reachedEndOffsets: readResult.reachedEndOffsets ?? false,
          artifactPath,
          valuesPrinted: false
        }
      },
      format,
      input: {
        mode: sourceMode,
        topic: inputTopic,
        topicSource: inputTopicResolution.source,
        groupId: readResult.groupId ?? null,
        groupPrefix: groupPrefix || null,
        groupPrefixSource: groupPrefixResolution.source,
        scannedCount: readResult.scannedCount ?? records.length,
        parsedCount: readResult.parsedCount ?? records.length,
        queryMatchedCount: readResult.matchedCount ?? records.length,
        matchedCount: matchingRecords.length,
        invalidJsonCount: (readResult.invalidJsonCount ?? 0) + scoped.summary.invalidTransportRecordCount,
        filterRejectedCount: readResult.filterRejectedCount ?? 0,
        scopeRejectedCount: scoped.summary.baseRejectedCount + scoped.summary.commandScopeRejectedCount,
        commandScope: { ...scoped.summary, scanComplete: commandScopeScanComplete },
        scanComplete: sourceScanComplete,
        completionReason: readResult.completionReason ?? (sourceMode === "jsonl" ? "file-end" : null),
        reachedEndOffsets: readResult.reachedEndOffsets ?? (sourceMode === "jsonl"),
        endOffsetsAvailable: readResult.endOffsetsAvailable ?? false,
        endOffsetsError: readResult.endOffsetsError ?? null,
        endOffsets: readResult.endOffsets ?? [],
        lastScannedOffsets: readResult.lastScannedOffsets ?? [],
        sourceFile: readResult.sourceFile ?? null,
        refreshHandoff: refreshHandoff?.evidence ?? null,
        valuesPrinted: false
      },
      identity: {
        requestedTraceId: traceId,
        requestedSessionId: sessionId || null,
        requestedRunId: runId || null,
        requestedCommandId: commandId || null,
        observedTraceId: text(projection.message.traceId),
        observedSessionIds: sessionIds,
        observedRunIds: runIds,
        observedCommandIds: commandIds,
        baseObservedRunIds: scoped.summary.baseObservedRunIds,
        baseCommandIdCounts: scoped.summary.commandIdCounts,
        missingCommandIdCount: scoped.summary.missingCommandIdCount,
        selectedSessionId: text(projection.message.sessionId),
        sourceEventIdentityCount: sourceEventIds.length,
        sourceEventIdentityMissingCount: matchingRecords.length - sourceEventIdentityPresentCount,
        sourceEventIdentityDuplicates: Math.max(0, sourceEventIdentityPresentCount - sourceEventIds.length),
        valuesPrinted: false
      },
      projection: {
        pipeline: ["createWorkbenchKafkaRefreshHandoff", "decodeWorkbenchRealtimeEventFrame", "reduceWorkbenchRealtimeEvent", "planWorkbenchRealtimeApply", "workbenchLiveKafkaProjectionTarget", "projectWorkbenchLiveKafkaUserMessage|projectWorkbenchLiveKafkaMessage", "traceDisplayRows"],
        decodedCount: projection.decodedCount,
        plannedCount: projection.plannedCount,
        appliedCount: projection.appliedCount,
        rejectedCount: projection.rejections.length,
        rejections: projection.rejections.slice(0, 20),
        eventCount: events.length,
        userMessageCount: projection.userMessages.length,
        userMessageIds: projection.userMessages.map((item: any) => text(item.messageId ?? item.id)).filter(Boolean),
        userTargetTraceIds: projection.userMessages.map((item: any) => text(item.targetTraceId)).filter(Boolean),
        terminalObserved: projection.message.traceAutoLifecycle === "terminal",
        status: projection.message.status,
        lastEventAt: projection.message.timing?.lastEventAt ?? null,
        valuesPrinted: false
      },
      render: {
        rowModel: "tools/src/hwlab-cli/trace-renderer:traceDisplayRows",
        markdownRenderer: "tools/src/hwlab-cli/trace-renderer:renderTraceRowsMarkdown",
        finalResponsePlacement: "outer",
        rowCount: rows.length,
        rowsReturned: rowPayload.length,
        rowsOmitted: Math.max(0, rows.length - rowPayload.length),
        rowWindow: fullRows ? "full" : "tail",
        rowPreviewLimit: fullRows ? null : rowPreviewLimit,
        toolRowCount,
        assistantRowCount,
        finalResponsePresent: Boolean(finalResponseText),
        finalResponseInTrace,
        rowModelSha256: sha256(JSON.stringify(rows)),
        rows: rowPayload,
        valuesPrinted: false
      },
      replayLineage,
      output: {
        artifact: { path: artifactPath, byteCount: Buffer.byteLength(`${markdown.trimEnd()}\n`, "utf8"), sha256: sha256(`${markdown.trimEnd()}\n`) },
        valuesPrinted: false
      },
      validation: {
        expectedCount: expectedCount ?? null,
        allMatchedEventsApplied: projection.appliedCount === matchingRecords.length,
        commandLifecyclePreserved: !commandId || (commandScopeScanComplete && scoped.summary.exactCommandCount > 0 && scoped.summary.ambiguousUnscopedLifecycleCount === 0),
        sourceScanComplete,
        refreshHandoffComplete: sourceMode !== "kafka" || refreshHandoff?.status === "succeeded",
        finalResponseOutsideTrace: !finalResponseInTrace && Boolean(finalResponseText),
        sharedRowModel: true,
        valuesPrinted: false
      },
      runtimeDependencies: {
        cloudApi: false,
        database: false,
        transactionalProjector: false,
        kafka: sourceMode === "kafka"
      },
      next: {
        command: `hwlab-cli kafka render trace --from ${sourceMode} --trace-id ${traceId}${sessionId ? ` --session-id ${sessionId}` : ""}${runId ? ` --run-id ${runId}` : ""}${commandId ? ` --command-id ${commandId}` : ""}${sourceMode === "jsonl" ? ` --jsonl-file ${shellArg(readResult.sourceFile)}` : ""} --format markdown`,
        reason: "使用同一持久事件和共享 row model 直接检查 Markdown Trace；浏览器验收只需核对最后的 HTML 分叉。"
      },
      valuesPrinted: false
    }
  };
}

export async function inspectKafkaSessionOrder(parsed: ParsedArgs, dependencies: {
  env: EnvLike;
  now: () => string;
  readKafka: (input: Record<string, any>) => Promise<Record<string, any>>;
}) {
  const requestedSessionId = optionalSessionId(parsed.sessionId);
  const containsText = text(parsed.containsText);
  if (!requestedSessionId && !containsText) {
    throw cliError("order_scope_required", "kafka inspect order requires --session-id or --contains-text", { fields: ["sessionId", "containsText"] });
  }
  if (containsText.length > 500) throw cliError("order_contains_text_too_long", "containsText must not exceed 500 characters", { length: containsText.length });

  const agentrunTopic = resolveConfig(parsed.agentrunTopic, dependencies.env.HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC, DEFAULT_AGENTRUN_EVENT_TOPIC, "--agentrun-topic", "env:HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC");
  const hwlabTopic = resolveConfig(parsed.hwlabTopic, dependencies.env.HWLAB_KAFKA_EVENT_TOPIC, DEFAULT_HWLAB_EVENT_TOPIC, "--hwlab-topic", "env:HWLAB_KAFKA_EVENT_TOPIC");
  const group = resolveKafkaGroupConfig(parsed.groupPrefix, dependencies.env.HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX, true);
  const groupPrefix = text(group.value);
  assertDebugGroup(groupPrefix);
  const limit = boundedInteger(parsed.limit, DEFAULT_LIMIT, 1, 5000, "limit");
  const timeoutMs = boundedInteger(parsed.timeoutMs, DEFAULT_TIMEOUT_MS, 250, 60000, "timeoutMs");
  const rowLimit = boundedInteger(parsed.rowLimit, 80, 1, 500, "rowLimit");

  let sessionId = requestedSessionId;
  let discovery: Record<string, any> | null = null;
  if (!sessionId) {
    const discoveredRead = await dependencies.readKafka({
      env: dependencies.env,
      stream: "hwlab",
      topic: hwlabTopic.value,
      limit,
      timeoutMs,
      fromBeginning: true,
      groupIdPrefix: `${groupPrefix}-order-discovery`
    });
    if (!kafkaReadComplete(discoveredRead)) return partialKafkaOrderResult({
      sessionId: null,
      containsText,
      agentrunTopic,
      hwlabTopic,
      group,
      reads: { discovery: discoveredRead }
    });
    const candidates = discoverKafkaOrderScopes(discoveredRead.events, containsText);
    const sessions = uniqueText(candidates.flatMap((candidate) => candidate.sessionIds));
    discovery = {
      containsTextChars: containsText.length,
      containsTextSha256: sha256(containsText),
      matchedEventCount: candidates.length,
      candidateSessionIds: sessions,
      candidates: candidates.slice(0, 20),
      valuesPrinted: false
    };
    if (sessions.length === 0) throw cliError("order_scope_not_found", "no retained HWLAB event matched --contains-text", {
      containsTextChars: containsText.length,
      containsTextSha256: sha256(containsText),
      scannedCount: discoveredRead.scannedCount ?? null
    });
    if (sessions.length !== 1) throw cliError("order_scope_ambiguous", "--contains-text matched more than one HWLAB session", {
      candidateSessionIds: sessions,
      matchedEventCount: candidates.length,
      valuesPrinted: false
    });
    sessionId = sessions[0];
  }
  if (!sessionId) throw cliError("order_scope_not_found", "Kafka order inspection did not resolve a sessionId");

  const [agentrunRead, hwlabRead] = await Promise.all([
    dependencies.readKafka({
      env: dependencies.env,
      stream: "agentrun",
      topic: agentrunTopic.value,
      sessionId,
      limit,
      timeoutMs,
      fromBeginning: true,
      groupIdPrefix: `${groupPrefix}-order-agentrun`
    }),
    dependencies.readKafka({
      env: dependencies.env,
      stream: "hwlab",
      topic: hwlabTopic.value,
      sessionId,
      limit,
      timeoutMs,
      fromBeginning: true,
      groupIdPrefix: `${groupPrefix}-order-hwlab`
    })
  ]);
  if (!kafkaReadComplete(agentrunRead) || !kafkaReadComplete(hwlabRead)) return partialKafkaOrderResult({
    sessionId,
    containsText,
    agentrunTopic,
    hwlabTopic,
    group,
    reads: { agentrun: agentrunRead, hwlab: hwlabRead },
    discovery
  });

  const refreshHandoff = await replayHwlabKafkaScopeThroughProductionHandoff({
    traceId: null,
    sessionId,
    query: { stream: "hwlab", topic: hwlabTopic.value, sessionId },
    readKafka: async () => hwlabRead
  });
  if (refreshHandoff.status !== "succeeded") {
    throw cliError("order_refresh_handoff_failed", "production Kafka refresh handoff rejected the retained session order", {
      sessionId,
      refreshHandoff: refreshHandoff.evidence,
      valuesPrinted: false
    });
  }

  const agentrunRecords = Array.isArray(agentrunRead.events) ? agentrunRead.events : [];
  const hwlabRecords = refreshHandoff.records;
  const projected = projectHwlabSessionRecordsThroughWorkbench(hwlabRecords, { sessionId, observedAt: dependencies.now() });
  const agentrunByEventId = new Map<string, { index: number; record: JsonRecord; value: JsonRecord }>();
  agentrunRecords.forEach((record, index) => {
    const value = valueFromRecord(record);
    const eventId = text(value.eventId ?? value.event?.id);
    if (eventId) agentrunByEventId.set(eventId, { index, record, value });
  });
  const orderRows = projected.rows.map((row, index) => {
    const hwlabRecord = hwlabRecords[index];
    const hwlabValue = valueFromRecord(hwlabRecord);
    const sourceEventId = text(hwlabValue.sourceEventId ?? hwlabValue.event?.sourceEventId);
    const source = agentrunByEventId.get(sourceEventId) ?? null;
    const expected = source ? projectAgentRunKafkaEventToHwlabEvent(source.value, {
      sourceTopic: source.record.topic,
      sourcePartition: source.record.partition,
      sourceOffset: source.record.offset,
      sourceKey: source.record.key,
      inputSha256: source.record.valueSha256
    }) : null;
    return {
      eventId: text(hwlabValue.eventId) || null,
      sourceEventId: sourceEventId || null,
      traceId: row.traceId,
      targetTraceId: row.targetTraceId,
      sourceSeq: row.sourceSeq,
      kind: row.kind,
      role: row.role,
      agentrun: source ? { index: source.index, topic: source.record.topic, partition: source.record.partition, offset: source.record.offset } : null,
      mapper: {
        matched: Boolean(expected
          && text(expected.eventId) === text(hwlabValue.eventId)
          && text(expected.sourceEventId) === sourceEventId
          && Number(expected.event?.sourceSeq) === Number(hwlabValue.event?.sourceSeq)
          && text(expected.event?.type) === text(hwlabValue.event?.type)),
        sourceTopic: text(hwlabValue.sourceEvent?.topic) || null,
        sourcePartition: nullableIntegerOrNull(hwlabValue.sourceEvent?.partition),
        sourceOffset: text(hwlabValue.sourceEvent?.offset) || null
      },
      hwlab: { index, topic: hwlabRecord.topic, partition: hwlabRecord.partition, offset: hwlabRecord.offset },
      sse: { index: row.sseIndex, delivery: "replay" },
      reducer: { index: row.reducerIndex, applied: row.applied },
      web: {
        messageId: row.messageId,
        messageIndexAtApply: row.messageIndexAtApply,
        finalMessageIndex: row.finalMessageIndex,
        domRowIndexAtApply: row.domRowIndexAtApply,
        finalDomRowIndex: row.finalDomRowIndex
      },
      valuesPrinted: false
    };
  });
  const turnPairs = kafkaOrderTurnPairs(orderRows);
  const firstDivergence = firstKafkaOrderDivergence(turnPairs);
  const mappedAgentRunIndices = orderRows.map((row) => row.agentrun?.index).filter((value): value is number => Number.isInteger(value));
  const validation = {
    agentrunScanComplete: true,
    hwlabScanComplete: true,
    refreshHandoffComplete: true,
    mapperOneToOne: orderRows.length === agentrunByEventId.size && orderRows.every((row) => row.agentrun && row.mapper.matched),
    sourceOrderPreserved: strictlyIncreasing(mappedAgentRunIndices),
    sseOrderPreserved: orderRows.every((row, index) => row.hwlab.index === index && row.sse.index === index),
    allEventsApplied: projected.rejectedCount === 0 && projected.appliedCount === orderRows.length,
    reducerOrderPreserved: orderRows.every((row, index) => row.reducer.applied && row.reducer.index === index),
    domTurnOrderPreserved: turnPairs.every((pair) => pair.dom.user < pair.dom.agent),
    noFirstDivergence: firstDivergence === null,
    valuesPrinted: false
  };
  const returnedRows = orderRows.slice(-rowLimit);
  return {
    ok: true,
    action: "kafka.inspect.order",
    status: "succeeded",
    scope: {
      requestedSessionId: requestedSessionId || null,
      resolvedSessionId: sessionId,
      discovery,
      valuesPrinted: false
    },
    input: {
      agentrun: kafkaOrderReadEvidence(agentrunRead),
      hwlab: kafkaOrderReadEvidence(hwlabRead),
      refreshHandoff: refreshHandoff.evidence,
      valuesPrinted: false
    },
    order: {
      pipeline: ["agentrun.event.v1", "HWLAB direct mapper", "hwlab.event.v1", "Kafka refresh handoff", "SSE frame", "Web decode/queue/reducer", "conversation DOM"],
      eventCount: orderRows.length,
      rowsReturned: returnedRows.length,
      rowsOmitted: Math.max(0, orderRows.length - returnedRows.length),
      rowWindow: orderRows.length > rowLimit ? "tail" : "full",
      rows: returnedRows,
      turnPairs,
      firstDivergence,
      valuesPrinted: false
    },
    projection: {
      decodedCount: projected.decodedCount,
      plannedCount: projected.plannedCount,
      appliedCount: projected.appliedCount,
      rejectedCount: projected.rejectedCount,
      finalMessageOrder: projected.messages.map((message: any, index: number) => ({
        index,
        role: message.role,
        traceId: text(message.traceId ?? message.runnerTrace?.traceId) || null,
        messageId: text(message.messageId ?? message.id) || null
      })),
      finalDomOrder: projected.timelineRows.filter((row: any) => row.message).map((row: any, index: number) => ({
        index,
        type: row.type,
        role: row.role ?? null,
        traceId: row.traceId,
        messageId: row.identity
      })),
      valuesPrinted: false
    },
    validation,
    runtimeDependencies: { cloudApi: false, database: false, transactionalProjector: false, kafka: true },
    next: {
      command: `hwlab-cli kafka inspect order --session-id ${sessionId} --group-prefix ${groupPrefix} --json`,
      reason: firstDivergence
        ? `首个顺序分歧位于 ${firstDivergence.layer}；使用同一 session 和稳定身份继续定点修复。`
        : "同一 session 在 source、mapper、SSE、reducer 与 DOM 的顺序一致。"
    },
    valuesPrinted: false
  };
}

export function projectHwlabSessionRecordsThroughWorkbench(records: JsonRecord[], options: { sessionId: string; observedAt: string }) {
  let state = createWorkbenchServerState();
  let decodedCount = 0;
  let plannedCount = 0;
  let appliedCount = 0;
  let rejectedCount = 0;
  const rows: any[] = [];
  const observedBase = Date.parse(options.observedAt);
  const baseMs = Number.isFinite(observedBase) ? observedBase : 0;
  for (const [index, record] of records.entries()) {
    const envelope = valueFromRecord(record);
    const decoded = decodeWorkbenchRealtimeEventFrame(JSON.stringify(envelope));
    if (!decoded.payload) {
      rejectedCount += 1;
      rows.push(rejectedKafkaOrderProjectionRow(index, envelope, `decode-${decoded.status}`));
      continue;
    }
    decodedCount += 1;
    const reduced = reduceWorkbenchRealtimeEvent(decoded.payload as any, "hwlab.event.v1");
    const plan = planWorkbenchRealtimeApply(reduced.action);
    const traceStep = plan.steps.find((step) => step.type === "apply-trace-event");
    if (!traceStep || traceStep.type !== "apply-trace-event" || !traceStep.event) {
      rejectedCount += 1;
      rows.push(rejectedKafkaOrderProjectionRow(index, envelope, reduced.action.type === "ignore" ? reduced.action.reason : "trace-step-missing"));
      continue;
    }
    plannedCount += 1;
    const traceId = text(envelope.traceId ?? traceStep.event.traceId);
    const sessionId = text(envelope.hwlabSessionId ?? envelope.sessionId ?? traceStep.event.sessionId);
    if (!traceId || sessionId !== options.sessionId) {
      rejectedCount += 1;
      rows.push(rejectedKafkaOrderProjectionRow(index, envelope, !traceId ? "trace-missing" : "session-mismatch"));
      continue;
    }
    const event = traceStep.event as any;
    const receivedAt = new Date(baseMs + index).toISOString();
    const role = workbenchLiveKafkaProjectionTarget(event) === "user" ? "user" : "agent";
    let message: any = null;
    if (role === "user") {
      const messageId = text(event.userMessageId ?? event.messageId);
      const previous = selectActiveMessages(state, sessionId).find((item) => text(item.messageId ?? item.id) === messageId) ?? null;
      message = projectWorkbenchLiveKafkaUserMessage({ previous, traceId, sessionId, event, receivedAt });
    } else {
      const previous = selectActiveMessages(state, sessionId).find((item) => item.role === "agent" && text(item.traceId ?? item.runnerTrace?.traceId) === traceId) ?? null;
      message = projectWorkbenchLiveKafkaMessage({ previous, traceId, sessionId, event, receivedAt });
    }
    if (!message) {
      rejectedCount += 1;
      rows.push(rejectedKafkaOrderProjectionRow(index, envelope, "projection-invalid"));
      continue;
    }
    state = reduceWorkbenchServerState(state, { type: "message.upsert", sessionId, message });
    appliedCount += 1;
    const messages = selectActiveMessages(state, sessionId);
    const messageId = text(message.messageId ?? message.id);
    const timelineRows = buildWorkbenchTimelineRows(messages);
    rows.push({
      sseIndex: index,
      reducerIndex: index,
      applied: true,
      traceId,
      targetTraceId: text(event.targetTraceId) || null,
      sourceSeq: Number.isInteger(Number(event.sourceSeq)) ? Number(event.sourceSeq) : null,
      kind: text(event.type ?? event.eventType) || "unknown",
      role,
      messageId,
      messageIndexAtApply: messages.findIndex((item) => text(item.messageId ?? item.id) === messageId),
      domRowIndexAtApply: timelineRows.findIndex((row) => row.message && workbenchMessageIdentity(row.message) === messageId),
      finalMessageIndex: null,
      finalDomRowIndex: null
    });
  }
  const messages = selectActiveMessages(state, options.sessionId);
  const timelineRows = buildWorkbenchTimelineRows(messages);
  const finalMessageIndex = new Map(messages.map((message, index) => [text(message.messageId ?? message.id), index]));
  const finalDomIndex = new Map(timelineRows.filter((row) => row.message).map((row, index) => [workbenchMessageIdentity(row.message as any), index]));
  for (const row of rows) {
    row.finalMessageIndex = finalMessageIndex.get(row.messageId) ?? -1;
    row.finalDomRowIndex = finalDomIndex.get(row.messageId) ?? -1;
  }
  return { rows, state, messages, timelineRows, decodedCount, plannedCount, appliedCount, rejectedCount };
}

function discoverKafkaOrderScopes(records: unknown, needle: string) {
  if (!needle || !Array.isArray(records)) return [];
  const normalizedNeedle = needle.toLocaleLowerCase();
  return records.flatMap((record: JsonRecord) => {
    const value = valueFromRecord(record);
    const event = recordObject(value.event) ?? {};
    const finalResponse = recordObject(event.finalResponse) ?? {};
    const visibleTexts = uniqueText([
      event.text,
      event.message,
      event.assistantText,
      finalResponse.text
    ]);
    if (!visibleTexts.some((candidate) => candidate.toLocaleLowerCase().includes(normalizedNeedle))) return [];
    return [{
      sessionIds: hwlabSessionCandidates(value),
      traceIds: traceCandidates(value),
      sourceEventId: text(value.sourceEventId ?? event.sourceEventId) || null,
      topic: text(record.topic) || null,
      partition: nullableIntegerOrNull(record.partition),
      offset: text(record.offset) || null,
      kind: text(event.type ?? event.eventType) || "unknown",
      valuesPrinted: false
    }];
  });
}

function kafkaOrderTurnPairs(rows: any[]) {
  const byTrace = new Map<string, { user?: any; agent?: any }>();
  for (const row of rows) {
    if (!row.traceId || !["user", "agent"].includes(row.role)) continue;
    const pair = byTrace.get(row.traceId) ?? {};
    if (!pair[row.role as "user" | "agent"]) pair[row.role as "user" | "agent"] = row;
    byTrace.set(row.traceId, pair);
  }
  return [...byTrace.entries()].flatMap(([traceId, pair]) => {
    if (!pair.user || !pair.agent) return [];
    return [{
      traceId,
      userMessageId: pair.user.web.messageId,
      agentMessageId: pair.agent.web.messageId,
      agentrun: { user: pair.user.agentrun?.index ?? -1, agent: pair.agent.agentrun?.index ?? -1 },
      hwlab: { user: pair.user.hwlab.index, agent: pair.agent.hwlab.index },
      sse: { user: pair.user.sse.index, agent: pair.agent.sse.index },
      reducer: { user: pair.user.reducer.index, agent: pair.agent.reducer.index },
      message: { user: pair.user.web.finalMessageIndex, agent: pair.agent.web.finalMessageIndex },
      dom: { user: pair.user.web.finalDomRowIndex, agent: pair.agent.web.finalDomRowIndex },
      valuesPrinted: false
    }];
  }).sort((left, right) => Math.min(left.hwlab.user, left.hwlab.agent) - Math.min(right.hwlab.user, right.hwlab.agent));
}

function firstKafkaOrderDivergence(turnPairs: any[]) {
  for (const pair of turnPairs) {
    if (pair.agentrun.user < 0 || pair.agentrun.agent < 0) return kafkaOrderDivergence(pair, "agentrun.event.v1 -> HWLAB direct mapper", "source-lineage-missing");
    if (pair.agentrun.user > pair.agentrun.agent) return kafkaOrderDivergence(pair, "agentrun.event.v1", "user-after-agent-source-event");
    if (pair.hwlab.user > pair.hwlab.agent) return kafkaOrderDivergence(pair, "HWLAB direct mapper -> hwlab.event.v1", "user-after-agent-hwlab-event");
    if (pair.sse.user > pair.sse.agent) return kafkaOrderDivergence(pair, "Kafka refresh handoff -> SSE frame", "user-after-agent-sse-frame");
    if (pair.reducer.user > pair.reducer.agent) return kafkaOrderDivergence(pair, "Web decode/queue/reducer", "user-after-agent-reducer-apply");
    if (pair.message.user > pair.message.agent) return kafkaOrderDivergence(pair, "Web message reducer", "user-after-agent-message-order");
    if (pair.dom.user > pair.dom.agent) return kafkaOrderDivergence(pair, "conversation DOM", "user-after-agent-dom-row");
  }
  return null;
}

function kafkaOrderDivergence(pair: any, layer: string, code: string) {
  return { layer, code, traceId: pair.traceId, evidence: pair, valuesPrinted: false };
}

function rejectedKafkaOrderProjectionRow(index: number, envelope: JsonRecord, reason: string) {
  const event = recordObject(envelope.event) ?? {};
  return {
    sseIndex: index,
    reducerIndex: index,
    applied: false,
    rejection: reason,
    traceId: text(envelope.traceId ?? event.traceId) || null,
    targetTraceId: text(event.targetTraceId) || null,
    sourceSeq: Number.isInteger(Number(event.sourceSeq)) ? Number(event.sourceSeq) : null,
    kind: text(event.type ?? event.eventType) || "unknown",
    role: workbenchLiveKafkaProjectionTarget(event as any),
    messageId: null,
    messageIndexAtApply: -1,
    domRowIndexAtApply: -1,
    finalMessageIndex: -1,
    finalDomRowIndex: -1
  };
}

function kafkaReadComplete(read: Record<string, any> | null | undefined) {
  return read?.completionReason === "end-offset"
    && read?.reachedEndOffsets === true
    && read?.completion?.complete === true;
}

function kafkaOrderReadEvidence(read: Record<string, any> | null | undefined) {
  return {
    topic: text(read?.topic) || null,
    groupId: text(read?.groupId) || null,
    scannedCount: read?.scannedCount ?? 0,
    parsedCount: read?.parsedCount ?? 0,
    matchedCount: read?.matchedCount ?? (Array.isArray(read?.events) ? read.events.length : 0),
    invalidJsonCount: read?.invalidJsonCount ?? 0,
    filterRejectedCount: read?.filterRejectedCount ?? 0,
    completionReason: text(read?.completionReason) || null,
    reachedEndOffsets: read?.reachedEndOffsets === true,
    endOffsets: Array.isArray(read?.endOffsets) ? read.endOffsets : [],
    lastScannedOffsets: Array.isArray(read?.lastScannedOffsets) ? read.lastScannedOffsets : [],
    valuesPrinted: false
  };
}

function partialKafkaOrderResult(input: {
  sessionId: string | null;
  containsText: string;
  agentrunTopic: Record<string, any>;
  hwlabTopic: Record<string, any>;
  group: Record<string, any>;
  reads: Record<string, Record<string, any>>;
  discovery?: Record<string, any> | null;
}) {
  const incomplete = Object.entries(input.reads).find(([, read]) => !kafkaReadComplete(read));
  const evidence = Object.fromEntries(Object.entries(input.reads).map(([name, read]) => [name, kafkaOrderReadEvidence(read)]));
  return {
    ok: false,
    action: "kafka.inspect.order",
    status: "partial",
    error: {
      code: "source_scan_incomplete",
      message: `Kafka ${incomplete?.[0] ?? "order"} scan ended before its captured topic barrier`,
      details: { stream: incomplete?.[0] ?? null, completionReason: incomplete?.[1]?.completionReason ?? null, valuesPrinted: false }
    },
    scope: {
      resolvedSessionId: input.sessionId,
      discovery: input.discovery ?? (input.containsText ? {
        containsTextChars: input.containsText.length,
        containsTextSha256: sha256(input.containsText),
        valuesPrinted: false
      } : null),
      valuesPrinted: false
    },
    config: { agentrunTopic: input.agentrunTopic, hwlabTopic: input.hwlabTopic, groupPrefix: input.group },
    input: { ...evidence, valuesPrinted: false },
    validation: { agentrunScanComplete: kafkaReadComplete(input.reads.agentrun), hwlabScanComplete: kafkaReadComplete(input.reads.hwlab), valuesPrinted: false },
    next: {
      command: input.sessionId
        ? `hwlab-cli kafka inspect order --session-id ${input.sessionId} --group-prefix ${input.group.value} --json`
        : `hwlab-cli kafka inspect order --contains-text <text> --group-prefix ${input.group.value} --json`,
      reason: "使用新的隔离 debug group 和足够的 YAML-owned limit/timeout 重试，只有完整 barrier 才能判定顺序。"
    },
    valuesPrinted: false
  };
}

function strictlyIncreasing(values: number[]) {
  return values.every((value, index) => index === 0 || value > values[index - 1]!);
}

function nullableIntegerOrNull(value: unknown) {
  if (value === null || value === undefined || value === "") return null;
  const parsed = Number(value);
  return Number.isInteger(parsed) && parsed >= 0 ? parsed : null;
}

export async function replayHwlabKafkaTraceThroughProductionHandoff(input: {
  traceId: string;
  sessionId: string | null;
  query: Record<string, any>;
  readKafka: (query: Record<string, any>) => Promise<Record<string, any>>;
}) {
  return replayHwlabKafkaScopeThroughProductionHandoff(input);
}

export async function replayHwlabKafkaScopeThroughProductionHandoff(input: {
  traceId: string | null;
  sessionId: string | null;
  query: Record<string, any>;
  readKafka: (query: Record<string, any>) => Promise<Record<string, any>>;
}) {
  let readResult: Record<string, any> | null = null;
  let summary: Record<string, any> | null = null;
  let liveSubscriptionInstalled = false;
  const records: JsonRecord[] = [];
  const sourceByTransport = new Map<string, JsonRecord>();
  const handoff = createWorkbenchKafkaRefreshHandoff({
    sessionId: input.sessionId,
    traceId: input.traceId,
    liveBufferLimit: 1,
    subscribeLive() {
      liveSubscriptionInstalled = true;
      return () => undefined;
    },
    async queryRetention({ signal }: { signal: AbortSignal }) {
      readResult = await input.readKafka({ ...input.query, signal });
      for (const record of Array.isArray(readResult.events) ? readResult.events : []) {
        sourceByTransport.set(kafkaTransportKey(record), record);
      }
      return readResult;
    },
    async deliverEvent(envelope: JsonRecord, transport: JsonRecord, delivery: string) {
      if (delivery !== "replay") throw cliError("kafka_refresh_cli_delivery_invalid", "CLI retention handoff must deliver replay records before its connected barrier", { delivery });
      records.push(sourceByTransport.get(kafkaTransportKey(transport)) ?? { ...transport, value: envelope });
      return true;
    },
    async deliverConnected(value: Record<string, any>) {
      summary = value;
      return true;
    }
  });
  try {
    summary = await handoff.start();
    return {
      status: "succeeded",
      readResult: readResult ?? {},
      records,
      evidence: {
        module: "internal/cloud/workbench-kafka-refresh-handoff:createWorkbenchKafkaRefreshHandoff",
        status: "succeeded",
        liveSubscriptionInstalled,
        phase: summary?.phase ?? "live",
        topic: summary?.topic ?? null,
        topicPartitions: summary?.topicPartitions ?? [],
        barrier: summary?.barrier ?? [],
        counts: summary?.counts ?? null,
        valuesPrinted: false
      }
    };
  } catch (error) {
    return {
      status: "failed",
      readResult: readResult ?? {},
      records: [],
      evidence: {
        module: "internal/cloud/workbench-kafka-refresh-handoff:createWorkbenchKafkaRefreshHandoff",
        status: "failed",
        liveSubscriptionInstalled,
        phase: text((error as any)?.phase) || handoff.phase(),
        error: {
          code: text((error as any)?.code) || "workbench_kafka_refresh_failed",
          message: error instanceof Error ? error.message : String(error),
          valuesRedacted: true
        },
        valuesPrinted: false
      }
    };
  } finally {
    handoff.stop("cli-complete");
  }
}

function kafkaTransportKey(value: Record<string, any>): string {
  return `${text(value?.topic)}:${nullableInteger(value?.partition) ?? ""}:${text(value?.offset)}`;
}

export function projectHwlabRecordsThroughWorkbench(records: JsonRecord[], options: { traceId: string; sessionId?: string; observedAt: string }) {
  let message: any = null;
  const userMessagesById = new Map<string, any>();
  const sourceEvents: Array<{ index: number; event: JsonRecord; transport: JsonRecord }> = [];
  let decodedCount = 0;
  let plannedCount = 0;
  let appliedCount = 0;
  const rejections: Array<{ index: number; code: string; detail?: string }> = [];
  const observedBaseMs = Date.parse(options.observedAt);
  const baseMs = Number.isFinite(observedBaseMs) ? observedBaseMs : 0;
  for (const [index, record] of records.entries()) {
    const value = valueFromRecord(record);
    const decoded = decodeWorkbenchRealtimeEventFrame(JSON.stringify(value));
    if (!decoded.payload) {
      rejections.push({ index, code: `decode-${decoded.status}` });
      continue;
    }
    decodedCount += 1;
    const schema = text(decoded.payload.schema);
    const realtimeEvent = schema === "hwlab.event.debug.v1"
      ? { ...decoded.payload, schema: "hwlab.event.v1" }
      : decoded.payload;
    if (realtimeEvent.schema !== "hwlab.event.v1") {
      rejections.push({ index, code: "schema-mismatch", detail: text(realtimeEvent.schema) });
      continue;
    }
    const reduced = reduceWorkbenchRealtimeEvent(realtimeEvent as any, "hwlab.event.v1");
    const plan = planWorkbenchRealtimeApply(reduced.action);
    const traceStep = plan.steps.find((step) => step.type === "apply-trace-event");
    if (!traceStep || traceStep.type !== "apply-trace-event" || !traceStep.event) {
      rejections.push({ index, code: reduced.action.type === "ignore" ? reduced.action.reason : "trace-step-missing" });
      continue;
    }
    plannedCount += 1;
    const eventTraceId = text(realtimeEvent.traceId ?? traceStep.event.traceId);
    if (eventTraceId !== options.traceId) {
      rejections.push({ index, code: "trace-mismatch", detail: eventTraceId });
      continue;
    }
    const eventSessionId = text(realtimeEvent.hwlabSessionId ?? realtimeEvent.sessionId ?? traceStep.event.sessionId ?? options.sessionId);
    if (!eventSessionId) {
      rejections.push({ index, code: "session-missing" });
      continue;
    }
    if (options.sessionId && !hwlabSessionCandidates(realtimeEvent).includes(options.sessionId)) {
      rejections.push({ index, code: "session-mismatch", detail: eventSessionId });
      continue;
    }
    sourceEvents.push({
      index,
      event: traceStep.event as JsonRecord,
      transport: {
        topic: text(record.topic),
        partition: nullableInteger(record.partition),
        offset: text(record.offset) || null
      }
    });
    if (workbenchLiveKafkaProjectionTarget(traceStep.event as any) === "user") {
      const userMessageId = text((traceStep.event as any).userMessageId ?? (traceStep.event as any).messageId);
      const userMessage = projectWorkbenchLiveKafkaUserMessage({
        previous: userMessageId ? userMessagesById.get(userMessageId) ?? null : null,
        traceId: options.traceId,
        sessionId: eventSessionId,
        event: traceStep.event as any,
        receivedAt: new Date(baseMs + index).toISOString()
      });
      if (!userMessage) {
        rejections.push({ index, code: "user-projection-invalid" });
        continue;
      }
      userMessagesById.set(userMessage.id, userMessage);
      appliedCount += 1;
      continue;
    }
    message = projectWorkbenchLiveKafkaMessage({
      previous: message,
      traceId: options.traceId,
      sessionId: eventSessionId,
      event: traceStep.event as any,
      receivedAt: new Date(baseMs + index).toISOString()
    });
    appliedCount += 1;
  }
  return { message, userMessages: [...userMessagesById.values()], sourceEvents, decodedCount, plannedCount, appliedCount, rejections };
}

function traceReplayLineageEvidence(input: {
  sourceEvents: Array<{ index: number; event: JsonRecord; transport: JsonRecord }>;
  appliedEvents: JsonRecord[];
  userMessages: JsonRecord[];
  rows: JsonRecord[];
  finalResponseText: string;
}) {
  const source = traceEventLayerSummary(input.sourceEvents.map((item) => ({
    event: item.event,
    position: item.index,
    identity: traceLayerEventIdentity(item.event),
    transport: item.transport
  })));
  const applied = traceEventLayerSummary([
    ...input.userMessages.map((message, index) => ({
      event: { ...message, type: "user", eventType: "user" },
      position: index,
      identity: text(message.messageId ?? message.id) || null,
      transport: null
    })),
    ...input.appliedEvents.map((event, index) => ({
      event,
      position: input.userMessages.length + index,
      identity: traceLayerEventIdentity(event),
      transport: null
    }))
  ]);
  const render = traceRenderLayerSummary(input.rows);
  const finalFingerprint = traceContentFingerprint(input.finalResponseText);
  const finalDuplicate = finalFingerprint
    ? render.assistantEntries.find((entry) => entry.contentFingerprint === finalFingerprint) ?? null
    : null;
  const firstVisibleDuplicateLayer = source.duplicate.first
    ? "source"
    : applied.duplicate.first
      ? "applied"
      : render.duplicate.first
        ? "render"
        : finalDuplicate
          ? "final"
          : null;
  return {
    counts: {
      source: source.counts.total,
      applied: applied.counts.total,
      assistant: render.counts.assistant,
      tool: render.counts.tool,
      terminal: render.counts.terminal,
      final: finalFingerprint ? 1 : 0
    },
    layers: {
      source: { ...source.counts, assistantDuplicate: source.duplicate },
      applied: { ...applied.counts, assistantDuplicate: applied.duplicate },
      render: { ...render.counts, assistantDuplicate: render.duplicate },
      final: {
        count: finalFingerprint ? 1 : 0,
        contentFingerprint: finalFingerprint,
        duplicatedInRender: Boolean(finalDuplicate),
        matchingRenderIdentity: finalDuplicate?.identity ?? null
      }
    },
    duplicatePolicy: "normalized non-final assistant text sha256; final/reply-authority events are compared only at the outer-final layer",
    firstVisibleDuplicateLayer,
    valuesPrinted: false
  };
}

function traceEventLayerSummary(entries: Array<{ event: JsonRecord; position: number; identity: string | null; transport: JsonRecord | null }>) {
  const counts = { total: entries.length, user: 0, assistant: 0, tool: 0, terminal: 0, final: 0, other: 0 };
  const assistantEntries: JsonRecord[] = [];
  for (const entry of entries) {
    const kind = traceLayerEventKind(entry.event);
    if (kind === "user") counts.user += 1;
    else if (kind === "assistant") counts.assistant += 1;
    else if (kind === "tool") counts.tool += 1;
    else counts.other += 1;
    const terminal = traceLayerEventTerminal(entry.event);
    const final = traceLayerEventFinal(entry.event);
    if (terminal) counts.terminal += 1;
    if (final) counts.final += 1;
    if (kind !== "assistant" || terminal || final) continue;
    const contentFingerprint = traceContentFingerprint(workbenchLiveKafkaAssistantText(entry.event));
    if (!contentFingerprint) continue;
    assistantEntries.push({
      position: entry.position,
      identity: entry.identity,
      sourceSeq: nullableInteger(entry.event.sourceSeq),
      contentFingerprint,
      transport: entry.transport
    });
  }
  return { counts, duplicate: traceAssistantDuplicateSummary(assistantEntries) };
}

function traceRenderLayerSummary(rows: JsonRecord[]) {
  const counts = { total: rows.length, assistant: 0, tool: 0, terminal: 0, other: 0 };
  const assistantEntries: JsonRecord[] = [];
  for (const [position, row] of rows.entries()) {
    const assistant = /助手/u.test(text(row.header));
    const tool = text(row.rowId).startsWith("tool:");
    const terminal = row.terminal === true || text(row.rowId).startsWith("trace-completion:");
    if (assistant) counts.assistant += 1;
    else if (tool) counts.tool += 1;
    else if (!terminal) counts.other += 1;
    if (terminal) counts.terminal += 1;
    if (!assistant || terminal) continue;
    const contentFingerprint = traceContentFingerprint(row.body);
    if (!contentFingerprint) continue;
    assistantEntries.push({ position, identity: text(row.rowId) || null, sourceSeq: nullableInteger(row.seq), contentFingerprint, transport: null });
  }
  return { counts, assistantEntries, duplicate: traceAssistantDuplicateSummary(assistantEntries) };
}

function traceAssistantDuplicateSummary(entries: JsonRecord[]) {
  const firstByFingerprint = new Map<string, JsonRecord>();
  const countsByFingerprint = new Map<string, number>();
  let first: JsonRecord | null = null;
  for (const entry of entries) {
    const fingerprint = text(entry.contentFingerprint);
    countsByFingerprint.set(fingerprint, (countsByFingerprint.get(fingerprint) ?? 0) + 1);
    const previous = firstByFingerprint.get(fingerprint);
    if (!previous) {
      firstByFingerprint.set(fingerprint, entry);
      continue;
    }
    first ??= {
      contentFingerprint: fingerprint,
      first: traceDuplicatePosition(previous),
      duplicate: traceDuplicatePosition(entry),
      valuesPrinted: false
    };
  }
  const duplicateCounts = [...countsByFingerprint.values()].filter((count) => count > 1);
  return {
    fingerprintCount: duplicateCounts.length,
    occurrenceCount: duplicateCounts.reduce((sum, count) => sum + count - 1, 0),
    first,
    valuesPrinted: false
  };
}

function traceDuplicatePosition(entry: JsonRecord) {
  return {
    position: nullableInteger(entry.position),
    identity: text(entry.identity) || null,
    sourceSeq: nullableInteger(entry.sourceSeq),
    transport: entry.transport ?? null
  };
}

function traceLayerEventKind(event: JsonRecord): "user" | "assistant" | "tool" | "other" {
  const type = text(event.type ?? event.eventType);
  const label = text(event.label);
  if (type === "user") return "user";
  if (type === "assistant" || type === "assistant_progress" || type === "assistant_message" || label === "agentrun:assistant:progress" || label === "agentrun:assistant:message" || label === "assistant:message") return "assistant";
  if (type === "tool" || type === "tool_call" || text(event.toolName) === "commandExecution" || /commandExecution|^agentrun:tool:/u.test(label)) return "tool";
  return "other";
}

function traceLayerEventTerminal(event: JsonRecord): boolean {
  const type = text(event.type ?? event.eventType);
  const label = text(event.label);
  return event.terminal === true || type === "result" || text(event.eventType) === "terminal" || label === "agentrun:terminal:completed" || label === "agentrun:result:completed";
}

function traceLayerEventFinal(event: JsonRecord): boolean {
  return event.final === true || event.replyAuthority === true || Boolean(text(recordObject(event.finalResponse)?.text));
}

function traceLayerEventIdentity(event: JsonRecord): string | null {
  const sourceEventId = text(event.sourceEventId);
  if (sourceEventId) return sourceEventId;
  const runId = text(event.runId);
  const sourceSeq = nullableInteger(event.sourceSeq);
  if (runId && sourceSeq !== null) return `${runId}:${sourceSeq}`;
  return text(event.itemId ?? event.messageId ?? event.id) || null;
}

function traceContentFingerprint(value: unknown): string | null {
  const normalized = text(value).replace(/\r\n|\r/gu, "\n").replace(/\s+/gu, " ").trim();
  return normalized ? sha256(normalized) : null;
}

function renderWorkbenchKafkaTraceMarkdown(input: { traceId: string; sessionId: string; status: string; userMessages: any[]; sourceEventCount: number; rows: any[]; finalResponseText: string | null; sourceScanComplete: boolean; completionReason: string | null }) {
  const lines = [
    `# Workbench Trace ${input.traceId}`,
    "",
    `- session: ${input.sessionId || "unknown"}`,
    `- status: ${input.status || "unknown"}`,
    `- source scan: ${input.sourceScanComplete ? "complete" : `partial (${input.completionReason ?? "unknown"})`}`,
    `- source events: ${input.sourceEventCount}`,
    `- user messages: ${input.userMessages.length}`,
    `- rendered rows: ${input.rows.length}`,
    `- final response placement: outer`
  ];
  if (input.userMessages.length > 0) {
    lines.push("", "## 用户输入");
    for (const message of input.userMessages) {
      lines.push(
        "",
        `### ${text(message.messageId ?? message.id) || "用户消息"}`,
        "",
        `- trace: ${text(message.traceId) || "unknown"}`,
        `- target trace: ${text(message.targetTraceId) || "none"}`,
        "",
        text(message.text)
      );
    }
  }
  lines.push("", "## 运行记录", "", renderTraceRowsMarkdown(input.rows));
  if (input.finalResponseText) lines.push("", "## 最终回复", "", input.finalResponseText);
  return lines.join("\n");
}

export function mapAgentRunRecordsToHwlabDebugEvents(records: JsonRecord[], options: { sessionId: string; traceId?: string; expectedCount?: number; producedAt?: string; replayId?: string }) {
  const matchingRecords = records.filter((record) => recordMatches(record, options.sessionId, options.traceId));
  if (matchingRecords.length === 0) {
    throw cliError("source_trace_missing", "no AgentRun events matched the requested sessionId/traceId", {
      sessionId: options.sessionId,
      traceId: options.traceId || null,
      readCount: records.length
    });
  }
  if (options.expectedCount !== undefined && matchingRecords.length !== options.expectedCount) {
    throw cliError("agentrun_debug_event_count_mismatch", "matched AgentRun event count does not equal --expect-count", {
      expectedCount: options.expectedCount,
      actualCount: matchingRecords.length
    });
  }
  const events = [];
  const inputKinds = [];
  let orderPreserved = true;
  let lineagePreserved = true;
  for (const [index, record] of matchingRecords.entries()) {
    const kafkaMessage = kafkaMessageFromRecord(record, index);
    const projected = projectAgentRunKafkaMessageToHwlabDebugEvent(kafkaMessage, {
      producedAt: options.producedAt,
      replayId: options.replayId,
      replayIndex: index + 1,
      replayCount: matchingRecords.length
    });
    if (!projected.ok) {
      throw cliError(projected.error?.code || "hwlab_debug_projection_failed", projected.error?.message || "AgentRun debug projection failed", {
        index,
        topic: kafkaMessage.topic,
        partition: kafkaMessage.partition,
        offset: kafkaMessage.message.offset
      });
    }
    const input = valueFromRecord(record);
    const sourceSeq = Number(input.sourceSeq ?? input.event?.seq);
    orderPreserved &&= projected.envelope.debugLineage?.sourceSeq === sourceSeq;
    lineagePreserved &&= projected.envelope.debugLineage?.inputTraceId === text(input.traceId ?? input.event?.payload?.traceId)
      && sessionCandidates(input).includes(options.sessionId)
      && projected.envelope.sourceEvent?.sha256 === kafkaMessage.inputSha256
      && (!options.replayId || projected.envelope.replayId === options.replayId);
    events.push(projected.envelope);
    inputKinds.push(projected.inputKind);
  }
  if (events.length !== matchingRecords.length || !orderPreserved || !lineagePreserved) {
    throw cliError("hwlab_debug_mapping_invariant_failed", "AgentRun -> HWLAB debug mapping did not preserve one-to-one order and lineage", {
      inputCount: matchingRecords.length,
      outputCount: events.length,
      orderPreserved,
      lineagePreserved
    });
  }
  return { events, inputKinds, inputCount: matchingRecords.length, orderPreserved, lineagePreserved };
}

function kafkaHelp() {
  return {
    ok: true,
    action: "kafka.help",
    status: "succeeded",
    commands: [
      "hwlab-cli kafka inspect order --session-id ses_... [--agentrun-topic agentrun.event.v1] [--hwlab-topic hwlab.event.v1] --group-prefix hwlab-...-debug [--row-limit 80] [--json]",
      "hwlab-cli kafka inspect order --contains-text <bounded-user-text> --group-prefix hwlab-...-debug [--json]",
      "hwlab-cli kafka render trace --from kafka --trace-id trc_... [--session-id ses_...] [--run-id run_...] [--command-id cmd_...] [--input-topic hwlab.event.v1] [--group-prefix hwlab-...-debug] [--format markdown] [--row-limit 20] [--json]",
      "hwlab-cli kafka render trace --from jsonl --trace-id trc_... [--run-id run_...] [--command-id cmd_...] --jsonl-file hwlab-events.jsonl [--format markdown] [--output-markdown trace.md] [--row-limit 20] [--json]",
      "hwlab-cli kafka regenerate hwlab --from kafka --session-id ses_... [--trace-id trc_...] [--replay-id rpl_...] [--input-topic agentrun.event.v1] [--group-prefix hwlab-...-debug] [--expect-count 35] [--json]",
      "hwlab-cli kafka regenerate hwlab --from kafka --session-id ses_... --input-topic agentrun.event.debug.v1 --group-prefix hwlab-...-debug [--replay-id rpl_...] [--json]",
      "hwlab-cli kafka regenerate hwlab --from jsonl --session-id ses_... [--replay-id rpl_...] --jsonl-file agentrun-events.jsonl --no-publish [--output-jsonl hwlab-events.jsonl] [--json]"
    ],
    defaults: {
      kafkaInputTopic: DEFAULT_AGENTRUN_EVENT_TOPIC,
      kafkaTraceInputTopic: DEFAULT_HWLAB_EVENT_TOPIC,
      jsonlInputTopic: DEFAULT_AGENTRUN_DEBUG_EVENT_TOPIC,
      outputTopic: DEFAULT_HWLAB_DEBUG_EVENT_TOPIC,
      groupPrefix: null,
      groupPrefixAuthority: "--group-prefix or env:HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX when Kafka is used",
      kafkaInputPublishes: false,
      jsonlInputPublishes: false
    },
    boundaries: {
      productTopicAllowed: false,
      explicitPublishRequired: true,
      cloudApi: false,
      database: false,
      transactionalProjector: false,
      reconstructionIdentityFabricated: false
    },
    next: {
      command: "hwlab-cli kafka regenerate hwlab --from jsonl --session-id ses_... --jsonl-file agentrun-events.jsonl --no-publish --json",
      reason: "先用完全离线 JSONL 验证一对一顺序映射，再决定是否发布到独立 debug topic。"
    },
    valuesPrinted: false
  };
}

async function defaultKafkaReader(input: Record<string, any>) {
  return withBunKafkaTimerClamp(() => queryKafkaEventStream({
    ...input,
    kafkaFactory(config: { brokers: string[]; clientId: string; dnsServers?: string[]; dnsSearchDomains?: string[] }) { return boundedKafka(config); }
  }));
}

async function withBunKafkaTimerClamp<T>(task: () => Promise<T>) {
  if (!process.versions.bun) return task();
  const originalSetTimeout = globalThis.setTimeout;
  // KafkaJS 2.2.4 schedules an idle queue check at 0 - Date.now(); Bun clamps it but emits an unbounded warning stack.
  const clampedSetTimeout = ((handler: Parameters<typeof globalThis.setTimeout>[0], timeout?: number, ...args: any[]) => originalSetTimeout(
    handler,
    typeof timeout === "number" && timeout < 0 ? 1 : timeout,
    ...args
  )) as typeof globalThis.setTimeout;
  globalThis.setTimeout = clampedSetTimeout;
  try {
    return await task();
  } finally {
    if (globalThis.setTimeout === clampedSetTimeout) globalThis.setTimeout = originalSetTimeout;
  }
}

async function defaultProducerFactory({ env, clientId }: { env: EnvLike; clientId: string }) {
  const brokers = csv(env.HWLAB_KAFKA_BOOTSTRAP_SERVERS);
  if (brokers.length === 0) throw cliError("kafka_brokers_missing", "HWLAB_KAFKA_BOOTSTRAP_SERVERS is required for Kafka publish", { field: "HWLAB_KAFKA_BOOTSTRAP_SERVERS" });
  return boundedKafka({
    clientId,
    brokers,
    dnsServers: csv(env.HWLAB_KAFKA_DNS_SERVERS),
    dnsSearchDomains: csv(env.HWLAB_KAFKA_DNS_SEARCH_DOMAINS)
  }).producer({ allowAutoTopicCreation: false });
}

function boundedKafka({ clientId, brokers, dnsServers = [], dnsSearchDomains = [] }: { clientId: string; brokers: string[]; dnsServers?: string[]; dnsSearchDomains?: string[] }) {
  const socketFactory = kafkaSocketFactory(dnsServers, dnsSearchDomains);
  return new Kafka({
    clientId,
    brokers,
    logLevel: logLevel.NOTHING,
    connectionTimeout: 5000,
    requestTimeout: 10000,
    retry: { retries: 1, maxRetryTime: 10000 },
    ...(socketFactory ? { socketFactory } : {})
  });
}

async function readJsonlInput(fileValue: string, cwd: string, defaultTopic: string) {
  const file = path.resolve(cwd, fileValue);
  const content = await readFile(file, "utf8");
  const events = [];
  for (const [index, line] of content.split(/\r?\n/u).entries()) {
    if (!line.trim()) continue;
    let value;
    try { value = JSON.parse(line); } catch (error) {
      throw cliError("jsonl_line_invalid", "input JSONL contains invalid JSON", { file, line: index + 1, message: error instanceof Error ? error.message : String(error) });
    }
    events.push(normalizeRecord(value, defaultTopic));
  }
  return {
    ok: true,
    topic: defaultTopic,
    groupId: null,
    count: events.length,
    scannedCount: events.length,
    parsedCount: events.length,
    matchedCount: events.length,
    invalidJsonCount: 0,
    filterRejectedCount: 0,
    completionReason: "file-end",
    reachedEndOffsets: true,
    events,
    sourceFile: file
  };
}

function normalizeRecord(record: any, defaultTopic: string) {
  if (!record || typeof record !== "object" || Array.isArray(record)) throw cliError("jsonl_record_invalid", "each JSONL row must be an object");
  if (Object.hasOwn(record, "value")) return { ...record, topic: text(record.topic) || defaultTopic };
  return { topic: defaultTopic, partition: null, offset: null, key: null, value: record };
}

function kafkaMessageFromRecord(record: JsonRecord, index: number) {
  const value = valueFromRecord(record);
  const valueText = typeof record.value === "string" ? record.value : JSON.stringify(value);
  return {
    topic: text(record.topic) || DEFAULT_AGENTRUN_DEBUG_EVENT_TOPIC,
    partition: nullableInteger(record.partition),
    inputSha256: text(record.valueSha256) || sha256(valueText),
    message: {
      offset: record.offset === null || record.offset === undefined ? null : String(record.offset),
      key: record.key === null || record.key === undefined ? null : Buffer.from(String(record.key)),
      value: Buffer.from(valueText),
      timestamp: record.timestamp === null || record.timestamp === undefined ? null : String(record.timestamp),
      headers: record.headers ?? undefined,
      debugIndex: index
    }
  };
}

function valueFromRecord(record: JsonRecord) {
  const candidate = Object.hasOwn(record, "value") ? record.value : record;
  if (typeof candidate === "string") {
    try { return JSON.parse(candidate); } catch { throw cliError("jsonl_value_invalid", "JSONL transport value must contain a JSON object"); }
  }
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) throw cliError("jsonl_value_invalid", "JSONL transport value must be a JSON object");
  return candidate;
}

function recordMatches(record: JsonRecord, sessionId: string, traceId?: string) {
  const value = valueFromRecord(record);
  if (!sessionCandidates(value).includes(sessionId)) return false;
  return !traceId || traceCandidates(value).includes(traceId);
}

function sessionCandidates(value: any) {
  return uniqueText([
    value.sessionId,
    value.hwlabSessionId,
    value.run?.sessionId,
    value.run?.hwlabSessionId,
    value.event?.payload?.sessionId,
    value.event?.payload?.hwlabSessionId,
    value.event?.sessionId,
    value.event?.hwlabSessionId
  ]);
}

function traceCandidates(value: any) {
  return uniqueText([value.traceId, value.run?.traceId, value.command?.traceId, value.event?.traceId, value.event?.payload?.traceId, value.event?.payload?.hwlabTraceId]);
}

function scopeHwlabTraceRecords(records: JsonRecord[], filters: { traceId: string; sessionId?: string; runId?: string; commandId?: string }) {
  const parsed: Array<{ record: JsonRecord; value: Record<string, any> }> = [];
  let invalidTransportRecordCount = 0;
  for (const record of records) {
    try {
      parsed.push({ record, value: valueFromRecord(record) });
    } catch {
      invalidTransportRecordCount += 1;
    }
  }
  const base = parsed.filter(({ value }) => {
    if (!traceCandidates(value).includes(filters.traceId)) return false;
    if (filters.sessionId && !hwlabSessionCandidates(value).includes(filters.sessionId)) return false;
    return !filters.runId || runCandidates(value).includes(filters.runId);
  });
  const baseObservedRunIds = uniqueText(base.flatMap(({ value }) => runCandidates(value)));
  const commandIdCountMap = new Map<string, number>();
  let missingCommandIdCount = 0;
  let unscopedLifecycleCount = 0;
  let unscopedNonLifecycleCount = 0;
  for (const { value } of base) {
    const commands = commandCandidates(value);
    if (commands.length === 0) {
      missingCommandIdCount += 1;
      if (hwlabCommandLifecycleEvent(value)) unscopedLifecycleCount += 1;
      else unscopedNonLifecycleCount += 1;
      continue;
    }
    for (const candidate of commands) commandIdCountMap.set(candidate, (commandIdCountMap.get(candidate) ?? 0) + 1);
  }
  const exactCommandRecords = filters.commandId
    ? base.filter(({ value }) => commandCandidates(value).includes(filters.commandId as string))
    : [];
  const soleBaseRunId = baseObservedRunIds.length === 1 ? baseObservedRunIds[0] : null;
  const explicitTargetRunIds = uniqueText([
    filters.runId,
    ...exactCommandRecords.flatMap(({ value }) => runCandidates(value))
  ]);
  const targetRunIds = explicitTargetRunIds.length > 0 ? explicitTargetRunIds : uniqueText([soleBaseRunId]);
  let selectedUnscopedLifecycleCount = 0;
  let attributableUnscopedLifecycleCount = 0;
  let ambiguousUnscopedLifecycleCount = 0;
  let otherRunUnscopedLifecycleCount = 0;
  let otherCommandRecordCount = 0;
  const selected = !filters.commandId
    ? base
    : exactCommandRecords.length === 0
      ? []
      : base.filter(({ value }) => {
    const commands = commandCandidates(value);
    if (commands.includes(filters.commandId as string)) return true;
    if (commands.length > 0) {
      otherCommandRecordCount += 1;
      return false;
    }
    if (!hwlabCommandLifecycleEvent(value)) return false;
    const runs = runCandidates(value);
    const attributable = runs.some((candidate) => targetRunIds.includes(candidate))
      || (runs.length === 0 && Boolean(soleBaseRunId) && targetRunIds.includes(soleBaseRunId as string));
    if (!attributable) {
      if (runs.length > 0 && targetRunIds.length > 0) otherRunUnscopedLifecycleCount += 1;
      else ambiguousUnscopedLifecycleCount += 1;
      return false;
    }
    attributableUnscopedLifecycleCount += 1;
    selectedUnscopedLifecycleCount += 1;
    return true;
      });
  return {
    records: selected.map(({ record }) => record),
    summary: {
      mode: filters.commandId ? "command-lifecycle" : "trace-run",
      requestedCommandId: filters.commandId || null,
      baseMatchedCount: base.length,
      selectedCount: selected.length,
      exactCommandCount: exactCommandRecords.length,
      missingCommandIdCount,
      unscopedLifecycleCount,
      attributableUnscopedLifecycleCount,
      selectedUnscopedLifecycleCount,
      ambiguousUnscopedLifecycleCount,
      otherRunUnscopedLifecycleCount,
      unscopedNonLifecycleCount,
      otherCommandRecordCount,
      commandScopeRejectedCount: Math.max(0, base.length - selected.length),
      baseRejectedCount: Math.max(0, records.length - invalidTransportRecordCount - base.length),
      invalidTransportRecordCount,
      baseObservedRunIds,
      targetRunIds,
      commandIdCounts: [...commandIdCountMap.entries()]
        .sort(([left], [right]) => left.localeCompare(right))
        .map(([commandId, count]) => ({ commandId, count })),
      valuesPrinted: false
    }
  };
}

function hwlabCommandLifecycleEvent(value: Record<string, any>) {
  const event = recordObject(value.event) ?? value;
  const finalResponse = recordObject(event.finalResponse);
  const types = uniqueText([event.type, event.eventType, value.type, value.eventType]).map((candidate) => candidate.toLowerCase());
  return event.terminal === true
    || event.final === true
    || event.replyAuthority === true
    || Boolean(text(finalResponse?.text))
    || types.some((candidate) => ["terminal", "terminal_status", "result"].includes(candidate));
}

function hwlabSessionCandidates(value: any) {
  return uniqueText([
    value?.hwlabSessionId,
    value?.sessionId,
    value?.event?.hwlabSessionId,
    value?.event?.sessionId,
    value?.context?.hwlabSessionId,
    value?.context?.sessionId
  ]);
}

function runCandidates(value: any) {
  return uniqueText([value?.runId, value?.run_id, value?.event?.runId, value?.event?.run_id, value?.context?.runId, value?.context?.run_id]);
}

function commandCandidates(value: any) {
  return uniqueText([value?.commandId, value?.command_id, value?.event?.commandId, value?.event?.command_id, value?.context?.commandId, value?.context?.command_id]);
}

function recordObject(value: unknown): Record<string, any> | null {
  return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, any> : null;
}

function parseOptions(argv: string[]): ParsedArgs {
  const out: ParsedArgs = { _: [] };
  for (let index = 0; index < argv.length; index += 1) {
    const item = argv[index] ?? "";
    if (!item.startsWith("--")) { out._.push(item); continue; }
    const eq = item.indexOf("=");
    const rawKey = eq >= 0 ? item.slice(2, eq) : item.slice(2);
    const key = rawKey.replace(/-([a-z])/gu, (_, character) => String(character).toUpperCase());
    if (eq >= 0) { out[key] = item.slice(eq + 1); continue; }
    const next = argv[index + 1];
    if (next && !next.startsWith("--")) { out[key] = next; index += 1; }
    else out[key] = true;
  }
  return out;
}

export function renderKafkaCliText(payload: Record<string, any>) {
  if (payload.status === "partial") return [
    "partial",
    `action=${payload.action}`,
    `code=${payload.error?.code ?? "source_scan_incomplete"}`,
    payload.input?.completionReason ? `completion=${payload.input.completionReason}` : null,
    payload.input?.reachedEndOffsets !== undefined ? `reachedEnd=${payload.input.reachedEndOffsets}` : null,
    payload.input?.scannedCount !== undefined ? `scanned=${payload.input.scannedCount}` : null,
    payload.input?.matchedCount !== undefined ? `matched=${payload.input.matchedCount}` : null,
    payload.projection?.appliedCount !== undefined ? `applied=${payload.projection.appliedCount}` : null,
    payload.output?.artifact?.path ? `artifact=${shellArg(payload.output.artifact.path)}` : null,
    payload.next?.command ? `next=${JSON.stringify(payload.next.command)}` : null
  ].filter(Boolean).join(" ");
  if (payload.ok === false) return `failed action=${payload.action} code=${payload.error?.code} message=${JSON.stringify(payload.error?.message)} next=${JSON.stringify(payload.next?.command)}`;
  const validation = payload.validation ?? {};
  return [
    "ok",
    `action=${payload.action}`,
    payload.scope?.resolvedSessionId ? `session=${payload.scope.resolvedSessionId}` : null,
    payload.input?.scannedCount !== undefined ? `scanned=${payload.input.scannedCount}` : null,
    payload.input?.parsedCount !== undefined ? `parsed=${payload.input.parsedCount}` : null,
    payload.input?.queryMatchedCount !== undefined ? `queryMatched=${payload.input.queryMatchedCount}` : null,
    payload.input?.matchedCount !== undefined ? `matched=${payload.input.matchedCount}` : null,
    payload.input?.commandScope?.exactCommandCount !== undefined ? `commandExact=${payload.input.commandScope.exactCommandCount}` : null,
    payload.input?.commandScope?.selectedUnscopedLifecycleCount !== undefined ? `commandLifecycle=${payload.input.commandScope.selectedUnscopedLifecycleCount}` : null,
    payload.input?.commandScope?.scanComplete !== undefined ? `scopeComplete=${payload.input.commandScope.scanComplete}` : null,
    payload.input?.readCount !== undefined ? `input=${payload.input.matchedCount}/${payload.input.readCount}` : null,
    payload.projection?.appliedCount !== undefined ? `applied=${payload.projection.appliedCount}` : null,
    payload.replayLineage?.counts?.assistant !== undefined ? `assistant=${payload.replayLineage.counts.assistant}` : null,
    payload.replayLineage?.counts?.tool !== undefined ? `tool=${payload.replayLineage.counts.tool}` : null,
    payload.replayLineage?.counts?.terminal !== undefined ? `terminalRows=${payload.replayLineage.counts.terminal}` : null,
    payload.replayLineage?.counts?.final !== undefined ? `final=${payload.replayLineage.counts.final}` : null,
    payload.replayLineage?.firstVisibleDuplicateLayer !== undefined ? `duplicateLayer=${payload.replayLineage.firstVisibleDuplicateLayer ?? "none"}` : null,
    payload.render?.rowCount !== undefined ? `rows=${payload.render.rowCount}` : null,
    payload.projection?.terminalObserved !== undefined ? `terminal=${payload.projection.terminalObserved}` : null,
    payload.output ? `output=${payload.output.count}` : null,
    payload.output ? `published=${payload.output.publishedCount}` : null,
    validation.orderPreserved !== undefined ? `order=${validation.orderPreserved ? "preserved" : "failed"}` : null,
    validation.noFirstDivergence !== undefined ? `order=${validation.noFirstDivergence ? "preserved" : "diverged"}` : null,
    payload.order?.firstDivergence?.layer ? `firstDivergence=${JSON.stringify(payload.order.firstDivergence.layer)}` : null,
    payload.output?.topic ? `topic=${payload.output.topic}` : null,
    payload.config?.outputTopic?.source ? `topicSource=${payload.config.outputTopic.source}` : null,
    payload.config?.groupPrefix?.source ? `groupSource=${payload.config.groupPrefix.source}` : null,
    payload.output?.artifact?.path ? `artifact=${shellArg(payload.output.artifact.path)}` : null,
    payload.next?.command ? `next=${JSON.stringify(payload.next.command)}` : null
  ].filter(Boolean).join(" ");
}

function result(exitCode: number, payload: Record<string, any>, now: () => string) {
  return { exitCode, payload: { generatedAt: now(), cli: CLI_NAME, version: VERSION, ...payload } };
}

function failure(error: any, action = "kafka.regenerate.hwlab") {
  return {
    ok: false,
    action,
    status: "failed",
    error: { code: error?.code ?? "hwlab_kafka_debug_cli_failed", message: error instanceof Error ? error.message : String(error), details: error?.details ?? undefined },
    next: { command: "hwlab-cli kafka help --json", reason: "检查 Kafka topic/group 的 YAML 注入、trace/session identity、JSONL 合同和扫描可见性后重试。" },
    valuesPrinted: false
  };
}

function assertDebugTopic(topic: string) {
  if (topic === "hwlab.event.v1" || !/(?:^|[.-])debug(?:[.-]|$)/u.test(topic)) {
    throw cliError("product_topic_forbidden", "output topic must be an isolated debug topic; hwlab.event.v1 is forbidden", { topic });
  }
}

function assertDebugGroup(groupPrefix: string) {
  if (!/(?:^|[.-])debug(?:[.-]|$)/u.test(groupPrefix)) throw cliError("debug_group_required", "Kafka consumer group prefix must identify an isolated debug group", { groupPrefix });
}

function requiredSessionId(value: unknown) {
  const result = requiredText(value, "sessionId");
  if (!/^ses_[A-Za-z0-9_.:-]+$/u.test(result)) throw cliError("invalid_session_id", "sessionId must start with ses_", { sessionId: result });
  return result;
}

function optionalSessionId(value: unknown) {
  const result = text(value);
  if (result && !/^ses_[A-Za-z0-9_.:-]+$/u.test(result)) throw cliError("invalid_session_id", "sessionId must start with ses_", { sessionId: result });
  return result;
}

function optionalRunId(value: unknown) {
  const result = text(value);
  if (result && !/^run_[A-Za-z0-9_.:-]+$/u.test(result)) throw cliError("invalid_run_id", "runId must start with run_", { runId: result });
  return result;
}

function optionalCommandId(value: unknown) {
  const result = text(value);
  if (result && !/^cmd_[A-Za-z0-9_.:-]+$/u.test(result)) throw cliError("invalid_command_id", "commandId must start with cmd_", { commandId: result });
  return result;
}

function requiredTraceId(value: unknown) {
  const result = optionalTraceId(value);
  if (!result) throw cliError("trace_id_required", "--trace-id is required", { field: "traceId" });
  return result;
}

function optionalTraceId(value: unknown) {
  const result = text(value);
  if (result && !/^trc_[A-Za-z0-9_.:-]+$/u.test(result)) throw cliError("invalid_trace_id", "traceId must start with trc_", { traceId: result });
  return result;
}

function optionalReplayId(value: unknown) {
  const result = text(value);
  if (result && !/^rpl_[A-Za-z0-9_.:-]+$/u.test(result)) throw cliError("invalid_replay_id", "replayId must start with rpl_", { replayId: result });
  return result;
}

function publishOffsetRanges(metadata: unknown, publishedCount: number, partition: number) {
  if (!Array.isArray(metadata) || publishedCount <= 0) return [];
  const item = metadata.find((entry: any) => Number(entry?.partition) === partition);
  const firstOffset = kafkaOffset(item?.baseOffset);
  if (!item || firstOffset === null) return [];
  return [{
    topic: text(item.topicName) || null,
    partition,
    firstOffset,
    lastOffset: (BigInt(firstOffset) + BigInt(publishedCount - 1)).toString(),
    count: publishedCount,
    valuesPrinted: false
  }];
}

function kafkaOffset(value: unknown) {
  const result = text(value);
  if (!result || !/^\d+$/u.test(result)) return null;
  try { return BigInt(result).toString(); } catch { return null; }
}

function enumValue(value: unknown, field: string, allowed: string[]) {
  const result = text(value);
  if (!allowed.includes(result)) throw cliError("invalid_option", `${field} must be one of: ${allowed.join(", ")}`, { field, value: result });
  return result;
}

function resolveConfig(cliValue: unknown, envValue: unknown, fallback: string, cliSource: string, envSource: string) {
  const explicit = text(cliValue);
  if (explicit) return { value: explicit, source: cliSource };
  const injected = text(envValue);
  if (injected) return { value: injected, source: envSource };
  return { value: fallback, source: "contract-default" };
}

function resolveKafkaGroupConfig(cliValue: unknown, envValue: unknown, required: boolean) {
  const explicit = text(cliValue);
  if (explicit) return { value: explicit, source: "--group-prefix" };
  const injected = text(envValue);
  if (injected) return { value: injected, source: "env:HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX" };
  if (required) {
    throw cliError(
      "kafka_debug_group_missing",
      "Kafka debug operations require --group-prefix or YAML-injected HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX",
      { field: "HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX" }
    );
  }
  return { value: null, source: "offline-no-kafka" };
}

function optionalPositiveInteger(value: unknown, field: string) {
  if (value === undefined) return undefined;
  return boundedInteger(value, 0, 1, 5000, field);
}

function boundedInteger(value: unknown, fallback: number, minimum: number, maximum: number, field: string) {
  const parsed = value === undefined ? fallback : Number(value);
  if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) throw cliError("invalid_integer", `${field} must be an integer between ${minimum} and ${maximum}`, { field, value });
  return parsed;
}

function nullableInteger(value: unknown) {
  if (value === null || value === undefined || value === "") return null;
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed < 0) throw cliError("invalid_partition", "partition must be a non-negative integer or null", { value });
  return parsed;
}

function requiredText(value: unknown, field: string) {
  const result = text(value);
  if (!result) throw cliError("missing_required_value", `${field} is required`, { field });
  return result;
}

function cliError(code: string, message: string, details: Record<string, unknown> = {}) {
  return Object.assign(new Error(message), { code, details });
}

function uniqueText(values: unknown[]) { return [...new Set(values.map(text).filter(Boolean))]; }
function text(value: unknown) { return String(value ?? "").trim(); }
function boundedText(value: unknown, limit: number) { const result = String(value ?? ""); return result.length <= limit ? result || null : `${result.slice(0, Math.max(0, limit - 3))}...`; }
function traceTextFingerprint(value: unknown) { return text(value).replace(/\s+/gu, " "); }
function csv(value: unknown) { return String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean); }
function sha256(value: string) { return createHash("sha256").update(value).digest("hex"); }
function shellArg(value: unknown) { const result = String(value ?? ""); return /^[A-Za-z0-9_./:@-]+$/u.test(result) ? result : `'${result.replace(/'/gu, `'"'"'`)}'`; }
function requestedFormat(argv: string[]) { const index = argv.findIndex((item) => item === "--format"); if (index >= 0) return text(argv[index + 1]); const inline = argv.find((item) => item.startsWith("--format=")); return inline ? text(inline.slice("--format=".length)) : "text"; }
