authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-04 19:38:30-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 19:38:30-07:00
log1246146f59a5a0f7a6f2da46bdd91f79b231d6a6
tree707282302136babe68d34034477b033fb59c6be6
parent1665d81bb5d78f33be6734e8962297b369396ac3
signature Signed by PGP key B5690EEEBB952194

fix(web): show retained runtime diagnostics in the work log (#9870)


2 files changed, 83 insertions(+), 0 deletions(-)

apps/web/src/session-logic.runtime-diagnostics.test.ts created+75
......@@ -0,0 +1,75 @@
1import { EventId, TurnId, type OrchestrationThreadActivity } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { deriveWorkLogEntries } from "./session-logic";
5import { workEntryDisplayLabel } from "./components/chat/MessagesTimeline.logic";
6
7// These are the already-truncated fields emitted by ProviderRuntimeIngestion
8// for the malformed-skill diagnostic reported in issue 1084.
9const retainedMessage =
10 "2026-03-14T16:11:12.550224Z ERROR codex_core::codex: failed to load skill /home/sebherrerabe/repos/devsuite/.agent/skills/monorepo-scaffolding/SKILL.md: invalid YAML: mapping va...";
11const warningSummary =
12 "2026-03-14T16:11:12.550224Z ERROR codex_core::codex: failed to load skill /home/sebherrerabe/repos/devsuite/.agent/sk...";
13
14function makeActivity(
15 overrides: Partial<OrchestrationThreadActivity> = {},
16): OrchestrationThreadActivity {
17 return {
18 id: EventId.make("diagnostic"),
19 createdAt: "2026-09-05T00:00:00.000Z",
20 kind: "runtime.error",
21 tone: "error",
22 summary: "Runtime error",
23 payload: { message: retainedMessage },
24 turnId: TurnId.make("turn-1"),
25 ...overrides,
26 };
27}
28
29describe("runtime diagnostics in the work log", () => {
30 it("shows the retained error message in place of its generic row label", () => {
31 const [entry] = deriveWorkLogEntries([makeActivity()]);
32
33 expect(entry).toMatchObject({ label: "Runtime error", detail: retainedMessage });
34 expect(entry && workEntryDisplayLabel(entry, undefined)).toBe(retainedMessage);
35 });
36
37 it("shows the retained warning message beyond its truncated label", () => {
38 const [entry] = deriveWorkLogEntries([
39 makeActivity({ kind: "runtime.warning", tone: "info", summary: warningSummary }),
40 ]);
41
42 expect(entry).toMatchObject({ label: warningSummary, detail: retainedMessage });
43 expect(entry && workEntryDisplayLabel(entry, undefined)).toBe(retainedMessage);
44 });
45
46 it("keeps an existing diagnostic detail when one is provided", () => {
47 const [entry] = deriveWorkLogEntries([
48 makeActivity({ payload: { message: retainedMessage, detail: "Run claude auth login." } }),
49 ]);
50
51 expect(entry?.detail).toBe("Run claude auth login.");
52 });
53
54 it("does not repeat a short warning already visible in the label", () => {
55 const [entry] = deriveWorkLogEntries([
56 makeActivity({
57 kind: "runtime.warning",
58 tone: "info",
59 summary: "Reconnecting... 2/5",
60 payload: { message: "Reconnecting... 2/5" },
61 }),
62 ]);
63
64 expect(entry?.label).toBe("Reconnecting... 2/5");
65 expect(entry?.detail).toBeUndefined();
66 });
67
68 it("does not interpret an unrelated activity message as a runtime diagnostic", () => {
69 const [entry] = deriveWorkLogEntries([
70 makeActivity({ kind: "tool.completed", tone: "tool", summary: "Read file" }),
71 ]);
72
73 expect(entry?.detail).toBeUndefined();
74 });
75});
apps/web/src/session-logic.ts+8
......@@ -964,6 +964,14 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
964964 const viewedImagePath = asTrimmedString(asRecord(payload?.data)?.imagePath);
965965 if (detail) {
966966 entry.detail = detail;
967 } else if (activity.kind === "runtime.error" || activity.kind === "runtime.warning") {
968 const message = asTrimmedString(payload?.message);
969 if (
970 message &&
971 normalizePreviewForComparison(message) !== normalizePreviewForComparison(activity.summary)
972 ) {
973 entry.detail = message;
974 }
967975 }
968976 if (viewedImagePath) {
969977 entry.viewedImagePath = viewedImagePath;