diff --git a/scripts/lib/codex_comments.jq b/scripts/lib/codex_comments.jq index 3930d93fbb..50769c483d 100644 --- a/scripts/lib/codex_comments.jq +++ b/scripts/lib/codex_comments.jq @@ -49,8 +49,8 @@ def codex_comment_is_informational($bot): ) catch false) // false else ($body | test("Didn.t find any major issues|usage limits have been reached|create a Codex account")) - or ($body | test("^(### 🛡️ Codex Security Review · _[^_\n]+_\n+)?" - + "Security review completed\\. No security issues were found in this pull request\\." + # The heading was stripped once above; a second heading is an unknown envelope. + or ($body | test("^Security review completed\\. No security issues were found in this pull request\\." + "\n+\\*\\*Reviewed commit:\\*\\* `[0-9a-f]{7,40}`" + "\n+\\[View security finding report\\]\\(https://chatgpt\\.com/codex/cloud/tasks/[A-Za-z0-9_-]+\\)" + "\n+_Only the user who started this review can view the report in Codex\\._$")) diff --git a/scripts/wait_pr_codex.sh b/scripts/wait_pr_codex.sh index 957d1c14bd..dd98607e3c 100755 --- a/scripts/wait_pr_codex.sh +++ b/scripts/wait_pr_codex.sh @@ -466,7 +466,7 @@ CHECK_CODEX_STATUS_ONCE() { include "codex_comments"; [.[] | select(.author.login == $bot and .createdAt > $request_at) | select( - ((.body | startswith("") or startswith("Security review completed.")) + ((.body | codex_without_help | startswith("") or startswith("Security review completed.")) and codex_comment_is_informational($bot)) | not )] | length ') diff --git a/src/browser/components/ChatPane/ChatPane.tsx b/src/browser/components/ChatPane/ChatPane.tsx index 17e3f24c77..501a7fa260 100644 --- a/src/browser/components/ChatPane/ChatPane.tsx +++ b/src/browser/components/ChatPane/ChatPane.tsx @@ -1691,9 +1691,10 @@ const ChatPaneContent: React.FC = (props) => { )} {/* Replay feedback must not resize the in-flow dock: even a brief catch-up would otherwise shift cached transcript rows on workspace switches. + An active turn does not mean history has loaded, so retain feedback alongside it. Keep it above the dock for both editable and read-only transcripts, yielding to Jump to bottom while scrolled up so they cannot overlap on phones. */} - {isHydratingTranscript && !shouldMountStreamingBarrier && autoScroll && ( + {isHydratingTranscript && autoScroll && (
{ + await step("Running init finishes before replay feedback; active turns retain it", async () => { emitChat({ type: "init-start", hookPath: "/project/.xum/init", @@ -276,7 +276,8 @@ function createHydrationStory(workspaceId: string): AppStory { phase: "preparing", hadAnyOutput: false, }); - await waitFor(() => expect(getLoadingStatus(canvasElement), "preparing stream").toBeNull()); + await expect(await canvas.findByText(/starting\.\.\./)).toBeVisible(); + await checkLoadingLayout(canvasElement); emitChat({ type: "stream-start", workspaceId: workspace.id, @@ -286,7 +287,7 @@ function createHydrationStory(workspaceId: string): AppStory { startTime: STABLE_TIMESTAMP, }); await expect(await canvas.findByText(/streaming\.\.\./)).toBeVisible(); - await expect(getLoadingStatus(canvasElement)).toBeNull(); + await checkLoadingLayout(canvasElement); emitChat(history); emitChat({ type: "caught-up", @@ -319,12 +320,12 @@ function createHydrationStory(workspaceId: string): AppStory { }); }); - await step("A monitor barrier suppresses duplicate replay status", async () => { + await step("A monitor barrier retains replay feedback", async () => { await switchWorkspace(canvasElement, monitorWorkspace.id); await expect( await canvas.findByText(/Waiting on background bash monitor/, {}, { timeout: 5000 }) ).toBeVisible(); - await expect(getLoadingStatus(canvasElement)).toBeNull(); + await checkLoadingLayout(canvasElement); }); await step("Read-only cached transcripts retain aligned replay feedback", async () => {