test: document workbench timeline opencode parity
This commit is contained in:
@@ -151,6 +151,9 @@ assert.doesNotMatch(workbenchStoreSource, /function applyRealtimeEvent[\s\S]{0,9
|
||||
assertIncludes(workbenchTimelineRuntimeSource, "TurnGap", "Timeline runtime must preserve OpenCode TurnGap row parity");
|
||||
assertIncludes(workbenchTimelineRuntimeSource, "AssistantPart", "Timeline runtime must preserve OpenCode AssistantPart row parity");
|
||||
assertIncludes(workbenchTimelineRuntimeSource, "unwrapErrorMessage", "Timeline runtime must preserve OpenCode readable error row behavior");
|
||||
assertIncludes(workbenchTimelineRuntimeSource, "WORKBENCH_TIMELINE_OPENCODE_PARITY", "Timeline runtime must carry an explicit OpenCode row parity table");
|
||||
assertIncludes(workbenchTimelineRuntimeSource, "not-applicable", "Timeline parity table must document HWLAB-not-applicable OpenCode rows");
|
||||
assertIncludes(workbenchTimelineRuntimeSource, "Permission", "Timeline parity table must explicitly account for OpenCode permission rows");
|
||||
assertIncludes(workbenchScrollRuntimeSource, "createWorkbenchScrollPersistenceRuntime", "Scroll runtime must preserve OpenCode session/tab scroll persistence");
|
||||
assertIncludes(conversationPanelSource, "sessionKey: scrollSessionKey", "ConversationPanel must use session-scoped scroll persistence in production");
|
||||
assertIncludes(workbenchErrorRuntimeSource, "formatServerError", "Error runtime must preserve OpenCode readable server error formatting");
|
||||
|
||||
@@ -15,7 +15,7 @@ import { composeWorkbenchScopedKey, splitWorkbenchScopedKey, workbenchPathKey, w
|
||||
import { createSafeStorageRuntime, isStorageQuotaError, migrateLegacyStorage, normalizePersistedValue, readJsonStorage, removePersistedTarget, removeStorageKey, writeJsonStorage, type StorageLike } from "../src/utils/safe-storage.ts";
|
||||
import { checkWorkbenchHealth, createWorkbenchHealthProbeCache } from "../src/utils/workbench-health.ts";
|
||||
import { messageDiagnosticView } from "../src/utils/workbench-error-runtime.ts";
|
||||
import { buildWorkbenchTimelineRows, normalizeWorkbenchTimelineMessages, workbenchTimelineSignature } from "../src/stores/workbench-timeline-model.ts";
|
||||
import { WORKBENCH_TIMELINE_OPENCODE_PARITY, buildWorkbenchTimelineRows, normalizeWorkbenchTimelineMessages, workbenchTimelineSignature } from "../src/stores/workbench-timeline-model.ts";
|
||||
import { reduceWorkbenchRealtimeEvent } from "../src/stores/workbench-event-reducer.ts";
|
||||
import { planWorkbenchRealtimeApply, planWorkbenchRealtimeRecovery } from "../src/stores/workbench-realtime-plan.ts";
|
||||
import { cleanupWorkbenchServerStateDroppedSessions, cleanupWorkbenchServerStateSessions, createWorkbenchServerState, reduceWorkbenchServerState } from "../src/stores/workbench-server-state.ts";
|
||||
@@ -215,6 +215,21 @@ test("timeline model preserves OpenCode turn gap, thinking, retry and error row
|
||||
assert.ok(rows.some((row) => row.type === "Error" && row.text === "Provider: offline"));
|
||||
});
|
||||
|
||||
test("timeline model documents OpenCode row parity and preserves comment/diff rows", () => {
|
||||
assert.ok(WORKBENCH_TIMELINE_OPENCODE_PARITY.some((row) => row.openCodeRows === "Permission" && row.status === "not-applicable"));
|
||||
assert.ok(WORKBENCH_TIMELINE_OPENCODE_PARITY.some((row) => row.openCodeRows === "Tool" && row.status === "adapted"));
|
||||
|
||||
const rows = buildWorkbenchTimelineRows([
|
||||
{ id: "msg_user", messageId: "msg_user", role: "user", title: "用户", text: "review", status: "sent", createdAt: "2026-06-30T00:00:00.000Z", sessionId: "ses_a", traceId: "trc_a", comments: [{ text: "note" }] } as ChatMessage,
|
||||
{ id: "msg_agent", messageId: "msg_agent", role: "agent", title: "Code Agent", text: "done", status: "completed", createdAt: "2026-06-30T00:00:01.000Z", sessionId: "ses_a", traceId: "trc_a", summary: { diffs: [{ file: "a.ts" }, { file: "a.ts" }, { file: "b.ts" }] } } as ChatMessage
|
||||
]);
|
||||
|
||||
assert.ok(rows.some((row) => row.type === "CommentStrip"));
|
||||
const diff = rows.find((row) => row.type === "DiffSummary");
|
||||
if (!diff || diff.type !== "DiffSummary") throw new Error("missing DiffSummary row");
|
||||
assert.equal(diff?.diffs?.length, 2);
|
||||
});
|
||||
|
||||
test("session cache trim keeps retained sessions and trims messages by explicit policy", () => {
|
||||
const result = trimWorkbenchSessionCache({
|
||||
sessionOrder: ["ses_1", "ses_2", "ses_3"],
|
||||
|
||||
@@ -11,6 +11,29 @@ import { firstScopePart, workbenchTimelineRowScopeKey } from "@/utils/workbench-
|
||||
|
||||
export type WorkbenchTimelineRowType = "TurnGap" | "CommentStrip" | "UserMessage" | "TurnDivider" | "AssistantPart" | "Thinking" | "Retry" | "DiffSummary" | "Error";
|
||||
|
||||
export type WorkbenchTimelineOpenCodeParityStatus = "preserved" | "adapted" | "not-applicable";
|
||||
|
||||
export interface WorkbenchTimelineOpenCodeParityRow {
|
||||
openCodeRows: string;
|
||||
openCodeSource: string;
|
||||
hwlabRows: WorkbenchTimelineRowType | "MessageTraceDebugPanel" | "none";
|
||||
status: WorkbenchTimelineOpenCodeParityStatus;
|
||||
note: string;
|
||||
}
|
||||
|
||||
export const WORKBENCH_TIMELINE_OPENCODE_PARITY: readonly WorkbenchTimelineOpenCodeParityRow[] = Object.freeze([
|
||||
{ openCodeRows: "TurnGap", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "TurnGap", status: "preserved", note: "User turn boundaries are rebuilt from authoritative ChatMessage order." },
|
||||
{ openCodeRows: "UserMessage", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "UserMessage", status: "preserved", note: "User text remains a first-class row keyed by message identity." },
|
||||
{ openCodeRows: "AssistantPart", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "AssistantPart", status: "adapted", note: "HWLAB uses read-model ChatMessage projection instead of OpenCode message parts." },
|
||||
{ openCodeRows: "Thinking", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "Thinking", status: "adapted", note: "Running agent rows with no visible text keep the thinking placeholder and reasoning heading." },
|
||||
{ openCodeRows: "Retry", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "Retry", status: "preserved", note: "Retry status produces an explicit retry row." },
|
||||
{ openCodeRows: "Error", openCodeSource: "pages/session/timeline/rows.ts:284-329", hwlabRows: "Error", status: "preserved", note: "Provider/config JSON errors are unwrapped to readable error rows." },
|
||||
{ openCodeRows: "DiffSummary", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "DiffSummary", status: "adapted", note: "HWLAB projects summary.diffs from read model and deduplicates by file." },
|
||||
{ openCodeRows: "Tool", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "MessageTraceDebugPanel", status: "adapted", note: "Tool call details live in the trace timeline/debug panel; collapsed command previews are guarded separately." },
|
||||
{ openCodeRows: "Permission", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "none", status: "not-applicable", note: "HWLAB Workbench does not expose an interactive OpenCode permission prompt row in the current read model." },
|
||||
{ openCodeRows: "Question", openCodeSource: "pages/session/timeline/rows.ts:107-242", hwlabRows: "none", status: "not-applicable", note: "HWLAB Workbench has no OpenCode question row authority; user prompts remain UserMessage rows." }
|
||||
]);
|
||||
|
||||
interface WorkbenchTimelineRowBase {
|
||||
type: WorkbenchTimelineRowType;
|
||||
key: string;
|
||||
|
||||
Reference in New Issue
Block a user