Skip to content

Commit ee4d584

Browse files
committed
Stop pinning startup transcript copy and key dedupe on writer
Tests asserted other modules' wording, so a brand or notice rename broke them with no behavior change; the suite now uses synthetic strings and the imported wordmark. Back-to-back system rows from different writers or with different labels no longer collapse, keeping the second-voice relabel.
1 parent 3041994 commit ee4d584

4 files changed

Lines changed: 135 additions & 38 deletions

File tree

‎src/tui/components/prompt-action-bar-label.test.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,35 @@ describe("yoloModeLabel", () => {
7979
expect(yoloModeLabel(false)).toBeUndefined();
8080
});
8181
});
82+
83+
describe("toggle label preservation", () => {
84+
test("an effort update keeps the yolo mode segment", () => {
85+
expect(
86+
composePromptActionBarModelLabel({
87+
profile: "work",
88+
model: "gpt-5",
89+
effort: "high",
90+
mode: yoloModeLabel(true),
91+
}),
92+
).toBe("work · gpt-5 · high · yolo");
93+
});
94+
95+
test("a yolo toggle keeps the effort segment", () => {
96+
expect(
97+
composePromptActionBarModelLabel({
98+
profile: "work",
99+
model: "gpt-5",
100+
effort: "high",
101+
mode: yoloModeLabel(true),
102+
}),
103+
).toBe("work · gpt-5 · high · yolo");
104+
expect(
105+
composePromptActionBarModelLabel({
106+
profile: "work",
107+
model: "gpt-5",
108+
effort: "high",
109+
mode: yoloModeLabel(false),
110+
}),
111+
).toBe("work · gpt-5 · high");
112+
});
113+
});
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import { describe, expect, test } from "bun:test";
2+
import { LOCKUP_WORDMARK } from "../lockup.js";
3+
import { ESSENTIALS_SEPARATOR } from "./prompt-action-bar-label.js";
24
import { composeSessionHeader } from "./session-header.js";
35

46
describe("composeSessionHeader", () => {
57
test("wordmark alone when no essentials apply", () => {
6-
expect(composeSessionHeader()).toBe("corbits code");
7-
expect(composeSessionHeader({})).toBe("corbits code");
8-
expect(composeSessionHeader({ essentials: "" })).toBe("corbits code");
8+
expect(composeSessionHeader()).toBe(LOCKUP_WORDMARK);
9+
expect(composeSessionHeader({})).toBe(LOCKUP_WORDMARK);
10+
expect(composeSessionHeader({ essentials: "" })).toBe(LOCKUP_WORDMARK);
911
});
1012

1113
test("wordmark leads the quiet essentials line", () => {
12-
expect(
13-
composeSessionHeader({ essentials: "thegreataxios · muse-spark" }),
14-
).toBe("corbits code · thegreataxios · muse-spark");
15-
expect(
16-
composeSessionHeader({
17-
essentials: "thegreataxios · muse-spark · yolo",
18-
}),
19-
).toBe("corbits code · thegreataxios · muse-spark · yolo");
14+
expect(composeSessionHeader({ essentials: "profile · model" })).toBe(
15+
`${LOCKUP_WORDMARK}${ESSENTIALS_SEPARATOR}profile · model`,
16+
);
17+
expect(composeSessionHeader({ essentials: "profile · model · yolo" })).toBe(
18+
`${LOCKUP_WORDMARK}${ESSENTIALS_SEPARATOR}profile · model · yolo`,
19+
);
2020
});
2121
});

‎src/tui/shell/chrome.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ import {
7171
steerCount,
7272
type RunState,
7373
} from "../session-queue.js";
74-
import { agentVoicesIn, isCollapsibleRow, type StreamRow } from "../stream.js";
74+
import {
75+
agentVoicesIn,
76+
isCollapsibleRow,
77+
MAIN_AGENT,
78+
type StreamRow,
79+
} from "../stream.js";
7580
import { UI } from "../theme.js";
7681
import {
7782
isDecisionOverlay,
@@ -1098,7 +1103,13 @@ const systemPushSequence = new WeakMap<AppShell, number>();
10981103
function isDuplicateSystemEcho(shell: AppShell, row: StreamRow): boolean {
10991104
if (row.role !== "system") return false;
11001105
const top = shell.streamLog[shell.streamLog.length - 1];
1101-
if (top === undefined || top.role !== "system" || top.text !== row.text) {
1106+
if (
1107+
top === undefined ||
1108+
top.role !== "system" ||
1109+
top.text !== row.text ||
1110+
(top.agent ?? MAIN_AGENT) !== (row.agent ?? MAIN_AGENT) ||
1111+
top.meta !== row.meta
1112+
) {
11021113
return false;
11031114
}
11041115
// The in-flight call already advanced the sequence, so the top row is

‎src/tui/startup-transcript.test.ts‎

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
/**
22
* CL-7938: consecutive duplicate system echoes collapse instead of painting
33
* twice, and a deferred session header flushes first when the landing clears.
4+
*
5+
* The duplicate-collapse and FIFO flush-order contracts hold with synthetic
6+
* strings here: the wording of other modules' notices (model picker, wiring)
7+
* is their own copy to pin, not this suite's.
48
*/
59
import { describe, expect, test } from "bun:test";
10+
import { composeSessionHeader } from "./components/session-header.js";
611
import { withTestRenderer } from "./harness";
712
import { appendStreamRow } from "./shell/chrome";
813
import { createAppShell } from "./shell/index";
@@ -15,24 +20,27 @@ const OPTIONS = {
1520
wireKeys: false,
1621
};
1722

23+
const DUPLICATE_TEXT = "synthetic duplicate notice.";
24+
const OTHER_TEXT = "synthetic second startup notice.";
25+
1826
describe("startup transcript", () => {
1927
test("consecutive duplicate system rows paint once", async () => {
2028
await withTestRenderer(async (h) => {
2129
const shell = createAppShell(h.renderer, OPTIONS);
2230
try {
2331
appendStreamRow(shell, {
2432
role: "system",
25-
text: "Chose muse-spark.",
26-
meta: "model picker",
33+
text: DUPLICATE_TEXT,
34+
meta: "synthetic source",
2735
});
2836
appendStreamRow(shell, {
2937
role: "system",
30-
text: "Chose muse-spark.",
31-
meta: "model picker",
38+
text: DUPLICATE_TEXT,
39+
meta: "synthetic source",
3240
});
3341
expect(streamRowCount(shell)).toBe(1);
3442
expect(shell.streamLog.map((row) => row.text)).toEqual([
35-
"Chose muse-spark.",
43+
DUPLICATE_TEXT,
3644
]);
3745
} finally {
3846
shell.dispose();
@@ -47,13 +55,13 @@ describe("startup transcript", () => {
4755
for (let i = 0; i < 3; i += 1) {
4856
appendStreamRow(shell, {
4957
role: "system",
50-
text: "Chose muse-spark.",
51-
meta: "model picker",
58+
text: DUPLICATE_TEXT,
59+
meta: "synthetic source",
5260
});
5361
}
5462
expect(streamRowCount(shell)).toBe(1);
5563
expect(shell.streamLog.map((row) => row.text)).toEqual([
56-
"Chose muse-spark.",
64+
DUPLICATE_TEXT,
5765
]);
5866
} finally {
5967
shell.dispose();
@@ -65,16 +73,65 @@ describe("startup transcript", () => {
6573
await withTestRenderer(async (h) => {
6674
const shell = createAppShell(h.renderer, OPTIONS);
6775
try {
68-
appendStreamRow(shell, { role: "system", text: "Chose muse-spark." });
76+
appendStreamRow(shell, { role: "system", text: DUPLICATE_TEXT });
6977
appendStreamRow(shell, { role: "user", text: "hi" });
70-
appendStreamRow(shell, { role: "system", text: "Chose muse-spark." });
71-
appendStreamRow(shell, { role: "system", text: "Chose muse-spark." });
72-
appendStreamRow(shell, { role: "tool", text: "Chose muse-spark." });
78+
appendStreamRow(shell, { role: "system", text: DUPLICATE_TEXT });
79+
appendStreamRow(shell, { role: "system", text: DUPLICATE_TEXT });
80+
appendStreamRow(shell, { role: "tool", text: DUPLICATE_TEXT });
7381
expect(shell.streamLog.map((row) => row.text)).toEqual([
74-
"Chose muse-spark.",
82+
DUPLICATE_TEXT,
7583
"hi",
76-
"Chose muse-spark.",
77-
"Chose muse-spark.",
84+
DUPLICATE_TEXT,
85+
DUPLICATE_TEXT,
86+
]);
87+
} finally {
88+
shell.dispose();
89+
}
90+
});
91+
});
92+
93+
test("same-text rows from different writers both paint", async () => {
94+
await withTestRenderer(async (h) => {
95+
const shell = createAppShell(h.renderer, OPTIONS);
96+
try {
97+
appendStreamRow(shell, {
98+
role: "system",
99+
text: DUPLICATE_TEXT,
100+
agent: "synthetic-agent-a",
101+
});
102+
appendStreamRow(shell, {
103+
role: "system",
104+
text: DUPLICATE_TEXT,
105+
agent: "synthetic-agent-b",
106+
});
107+
expect(shell.streamLog.map((row) => row.text)).toEqual([
108+
DUPLICATE_TEXT,
109+
DUPLICATE_TEXT,
110+
]);
111+
expect(shell.agentVoices.size).toBe(2);
112+
} finally {
113+
shell.dispose();
114+
}
115+
});
116+
});
117+
118+
test("same-text rows with different meta both paint", async () => {
119+
await withTestRenderer(async (h) => {
120+
const shell = createAppShell(h.renderer, OPTIONS);
121+
try {
122+
appendStreamRow(shell, {
123+
role: "system",
124+
text: DUPLICATE_TEXT,
125+
meta: "synthetic source a",
126+
});
127+
appendStreamRow(shell, {
128+
role: "system",
129+
text: DUPLICATE_TEXT,
130+
meta: "synthetic source b",
131+
});
132+
expect(shell.streamLog.map((row) => row.text)).toEqual([
133+
DUPLICATE_TEXT,
134+
DUPLICATE_TEXT,
78135
]);
79136
} finally {
80137
shell.dispose();
@@ -86,20 +143,17 @@ describe("startup transcript", () => {
86143
await withTestRenderer(async (h) => {
87144
const shell = createAppShell(h.renderer, { ...OPTIONS, run: "idle" });
88145
try {
146+
const header = composeSessionHeader({
147+
essentials: "synthetic profile · synthetic model",
148+
});
89149
expect(isLanding(shell)).toBe(true);
90-
surfaceSystemNotice(
91-
shell,
92-
"corbits code · thegreataxios · muse-spark · yolo",
93-
);
94-
surfaceSystemNotice(
95-
shell,
96-
"Permission prompts are disabled by your saved default (/yolo off to re-enable).",
97-
);
150+
surfaceSystemNotice(shell, header);
151+
surfaceSystemNotice(shell, OTHER_TEXT);
98152
expect(streamRowCount(shell)).toBe(0);
99153
appendStreamRow(shell, { role: "user", text: "first prompt" });
100154
expect(shell.streamLog.map((row) => row.text)).toEqual([
101-
"corbits code · thegreataxios · muse-spark · yolo",
102-
"Permission prompts are disabled by your saved default (/yolo off to re-enable).",
155+
header,
156+
OTHER_TEXT,
103157
"first prompt",
104158
]);
105159
} finally {

0 commit comments

Comments
 (0)