fix(queue): harden TUI dequeue display trim and override submit guard (#1183)

This commit is contained in:
Charles Packer
2026-02-26 18:12:54 -08:00
committed by GitHub
parent 1ca8cf9433
commit 70fac1d1f3
2 changed files with 45 additions and 3 deletions

View File

@@ -1750,7 +1750,14 @@ export default function App({
"queue-lifecycle",
`dequeued batch_id=${batch.batchId} merged_count=${batch.mergedCount} queue_len_after=${batch.queueLenAfter}`,
);
setQueueDisplay((prev) => prev.slice(batch.mergedCount));
// queueDisplay only tracks displayable items. If non-display barrier
// kinds are ever consumed, avoid over-trimming by counting only
// message/task_notification entries in the batch.
const displayConsumedCount = batch.items.filter(
(item) =>
item.kind === "message" || item.kind === "task_notification",
).length;
setQueueDisplay((prev) => prev.slice(displayConsumedCount));
},
onBlocked: (reason, queueLen) =>
debugLog(
@@ -6502,6 +6509,7 @@ export default function App({
async (message?: string): Promise<{ submitted: boolean }> => {
const msg = message?.trim() ?? "";
const overrideContentParts = overrideContentPartsRef.current;
const hasOverrideContent = overrideContentParts !== null;
if (overrideContentParts) {
overrideContentPartsRef.current = null;
}
@@ -6512,7 +6520,7 @@ export default function App({
taskNotifications.length > 0 && userTextForInput.length === 0;
// Handle profile load confirmation (Enter to continue)
if (profileConfirmPending && !msg) {
if (profileConfirmPending && !msg && !hasOverrideContent) {
// User pressed Enter with empty input - proceed with loading
const { name, agentId: targetAgentId, cmdId } = profileConfirmPending;
const cmd = commandRunner.getHandle(cmdId, `/profile load ${name}`);
@@ -6534,7 +6542,7 @@ export default function App({
// Continue processing the new message
}
if (!msg) return { submitted: false };
if (!msg && !hasOverrideContent) return { submitted: false };
// If the user just cycled reasoning tiers, flush the final choice before
// sending the next message so the upcoming run uses the selected tier.

View File

@@ -37,6 +37,40 @@ describe("queue ordering wiring", () => {
expect(segment).toContain("queuedOverlayAction,");
});
test("queue display trim uses displayable-item count, not mergedCount", () => {
const source = readAppSource();
const start = source.indexOf("onDequeued: (batch) => {");
const end = source.indexOf("onBlocked: (reason, queueLen) =>");
expect(start).toBeGreaterThan(-1);
expect(end).toBeGreaterThan(start);
const segment = source.slice(start, end);
expect(segment).toContain("const displayConsumedCount =");
expect(segment).toContain('item.kind === "message"');
expect(segment).toContain('item.kind === "task_notification"');
expect(segment).toContain("prev.slice(displayConsumedCount)");
});
test("onSubmit allows override-only queued submissions", () => {
const source = readAppSource();
const start = source.indexOf("const onSubmit = useCallback(");
const end = source.indexOf(
"// Process queued overlay actions when streaming ends",
);
expect(start).toBeGreaterThan(-1);
expect(end).toBeGreaterThan(start);
const segment = source.slice(start, end);
expect(segment).toContain(
"if (!msg && !hasOverrideContent) return { submitted: false };",
);
expect(segment).toContain(
"if (profileConfirmPending && !msg && !hasOverrideContent)",
);
});
test("queued overlay effect only runs when idle and clears action before processing", () => {
const source = readAppSource();
const start = source.indexOf(