Merge pull request #2480 from pikasTech/fix/2474-product-trace-lifecycle
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Success

fix: 保持纯 Kafka Trace 运行中与终态可读
This commit is contained in:
Lyon
2026-07-10 22:35:01 +08:00
committed by GitHub
3 changed files with 98 additions and 3 deletions
+1 -1
View File
@@ -372,7 +372,7 @@ lanes:
secretPlaneRef: config/hwlab-v03/secrets.yaml#secretPlane
workbench:
traceTimeline:
autoExpandRunning: false
autoExpandRunning: true
autoCollapseTerminal: false
rawHwlabEventWindow:
enabled: true
@@ -16,6 +16,71 @@ describe("TraceTimeline sequence authority", () => {
Object.defineProperty(HTMLElement.prototype, "scrollTo", { configurable: true, value: () => undefined });
});
it("auto-opens a running Trace without persisting programmatic state", async () => {
const storageKey = "trace-lifecycle.running";
const wrapper = mount(TraceTimeline, {
props: {
autoExpanded: true,
storageKey,
trace: liveTrace("running")
}
});
await nextTick();
expect(wrapper.get<HTMLDetailsElement>(".trace-disclosure").element.open).toBe(true);
expect(window.localStorage.getItem(storageKey)).toBeNull();
wrapper.unmount();
});
it("keeps an auto-opened Trace expanded when terminal policy has no collapse request", async () => {
const wrapper = mount(TraceTimeline, {
props: {
autoExpanded: true,
storageKey: "trace-lifecycle.keep-open",
trace: liveTrace("running")
}
});
await nextTick();
expect(wrapper.get<HTMLDetailsElement>(".trace-disclosure").element.open).toBe(true);
await wrapper.setProps({ autoExpanded: null, trace: liveTrace("completed") });
await nextTick();
expect(wrapper.get<HTMLDetailsElement>(".trace-disclosure").element.open).toBe(true);
wrapper.unmount();
});
it("collapses on an explicit terminal request while keeping a manual stored value authoritative", async () => {
const storageKey = "trace-lifecycle.collapse";
const wrapper = mount(TraceTimeline, {
props: {
autoExpanded: true,
storageKey,
trace: liveTrace("running")
}
});
await nextTick();
expect(wrapper.get<HTMLDetailsElement>(".trace-disclosure").element.open).toBe(true);
await wrapper.setProps({ autoExpanded: false, trace: liveTrace("completed") });
await nextTick();
expect(wrapper.get<HTMLDetailsElement>(".trace-disclosure").element.open).toBe(false);
wrapper.unmount();
window.localStorage.setItem(storageKey, "1");
const storedWrapper = mount(TraceTimeline, {
props: {
autoExpanded: false,
storageKey,
trace: liveTrace("completed")
}
});
await nextTick();
expect(storedWrapper.get<HTMLDetailsElement>(".trace-disclosure").element.open).toBe(true);
storedWrapper.unmount();
});
it("renders sourceSeq-only live Kafka rows while terminal and does not label them projected", async () => {
const wrapper = mount(TraceTimeline, {
props: {
@@ -70,6 +135,16 @@ describe("TraceTimeline sequence authority", () => {
});
});
function liveTrace(status: "running" | "completed") {
return {
traceId: "trc_live_lifecycle",
eventSource: "hwlab-kafka-sse" as const,
status,
eventCount: 1,
events: [{ sourceEventId: "evt_live_lifecycle", runId: "run_live", sourceSeq: 1, source: "agentrun.kafka", type: "assistant", label: "agentrun:assistant:message", message: "live answer" }]
};
}
class FakeResizeObserver {
observe(): void {}
unobserve(): void {}
@@ -46,10 +46,30 @@ watch(() => [eventCount.value, props.trace?.status, readableRows.value.length, e
void keepBottomAfterUpdate();
});
watch(() => [props.trace?.traceId, props.storageKey, props.defaultExpanded, props.autoExpanded] as const, () => {
setExpandedProgrammatically(readStoredExpanded() ?? props.autoExpanded ?? props.defaultExpanded ?? false);
watch(() => [traceExpansionScope(), props.defaultExpanded, props.autoExpanded] as const, (current, previous) => {
const stored = readStoredExpanded();
if (stored !== null) {
setExpandedProgrammatically(stored);
return;
}
const [scope, defaultExpanded, autoExpanded] = current;
if (!previous || scope !== previous[0]) {
setExpandedProgrammatically(autoExpanded ?? defaultExpanded ?? false);
return;
}
if (typeof autoExpanded === "boolean") {
setExpandedProgrammatically(autoExpanded);
return;
}
if (defaultExpanded !== previous[1]) setExpandedProgrammatically(defaultExpanded ?? false);
}, { immediate: true });
function traceExpansionScope(): string {
if (props.storageKey) return `storage:${props.storageKey}`;
return `trace:${String(props.trace?.traceId ?? "")}`;
}
function rowKey(row: TraceEventRow, index: number): string {
return `${row.rowId}-${row.sequenceAuthority}-${row.seq ?? "no-seq"}-${index}`;
}