Skip to content

🤖 ci: accept resolved Codex advisories on the review summary board - #4223

Merged
ibetitsmike merged 1 commit into
mainfrom
mike/codex-gate-resolved-advisories
Sep 11, 2026
Merged

🤖 ci: accept resolved Codex advisories on the review summary board#4223
ibetitsmike merged 1 commit into
mainfrom
mike/codex-gate-resolved-advisories

Conversation

@ibetitsmike

Copy link
Copy Markdown
Contributor

Summary

The Codex Comments gate treats Codex's review summary board as a blocking comment whenever it carries a "Security findings" section, even after every advisory thread is resolved and both reviews are complete. This whitelists the section when each advisory bullet carries Codex's own Resolved marker, so PRs that once had a security advisory can pass Required again. Unblocks #4211.

Background

Codex keeps resolved security advisories listed on the board and only adds the · **Resolved** marker when a later review completes (verified from the board's edit history on #4211: the marker appeared at the next review completion, not when the thread was resolved). The gate's line whitelist (#4149, #4158) does not know the section, so a completed board with resolved advisories is reported as an unresolved Codex comment and Required can never go green. On #4211 the gate log at 17:24Z shows exactly that: status: completed, both rows Completed, two **Resolved** advisories, still counted as blocking. The two most recent merged PRs with such boards (#4170, #4176) only passed because the section was added after their last gate run.

Implementation

scripts/lib/codex_comments.jq accepts three more line shapes inside a completed board: ### Security findings, #### Advisory findings (N), and a bullet that links a review thread on a PR (.../pull/N#discussion_r<id>), names a severity, and ends with · **Resolved**. A bullet without the marker is a live finding and keeps blocking, as do unknown sections, non-thread links, and trailing text.

Validation


Generated with xum • Model: anthropic:claude-fable-5-1 • Thinking: xhigh • Cost: $16.42

Codex keeps resolved security advisories listed on its summary board and
only adds the Resolved marker once a later review completes, so a
completed board with the "Security findings" section failed the Codex
Comments gate forever. Whitelist the section header, the advisory count
line and bullets that link a review thread on this PR and end with
Codex's Resolved marker; bare bullets, unknown sections and non-thread
links keep blocking.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T18:23:23.835702Z ce3fcd1 PR opened
🔒 Security Review Completed 2026-09-11T18:29:31.954822Z ce3fcd1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ibetitsmike
ibetitsmike added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 0328815 Sep 11, 2026
35 of 38 checks passed
@ibetitsmike
ibetitsmike deleted the mike/codex-gate-resolved-advisories branch September 11, 2026 19:02
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Sep 12, 2026
…4211)

## Summary

Replaces the always-expanded init banner pinned at the top of the
transcript with a Codex-style workspace creation card: it renders
directly after the user message that created the workspace, shows a step
checklist with a live checkout progress bar while running, collapses to
a one-line **Workspace created in Ns** header on success, and stays
expanded with the exit code and stderr on failure.

## Background

The `workspace-init` row was prepended to `getDisplayedMessages` with
`historySequence: -1` and rendered fully expanded forever, so every new
chat opened with a wall of setup output above the first message. Runtime
step markers (`InitLogger.logStep`) and raw hook stdout were flattened
into identical `init-output` lines, and local worktree creation ran `git
worktree add` buffered, so no checkout progress existed to display.

## Implementation

- **Placement:** the card is inserted after the first `user` row of the
displayed transcript (index 0 when there is none). Chronology is not
usable because `init-start` fires before the first message is persisted.
- **Contract:** `init-output` gains optional `step: true` for `logStep`
lines (persisted and replayed; older `init-status.json` files simply
render without a checklist). A new ephemeral `init-progress { label,
percent }` event and `InitLogger.logProgress?` carry checkout progress;
it is never persisted or replayed.
- **Local checkout progress:** `WorktreeManager.createWorkspace` now
runs `git worktree add --no-checkout`; `materializeWorkspace` then
populates the files with `git -c core.hooksPath=/dev/null checkout
--quiet --progress --no-recurse-submodules` while HEAD still holds the
branch, using `GIT_PROGRESS_DELAY=0` (no `--force`: the checkout runs in
an announced workspace, so anything written there meanwhile fails the
checkout instead of being overwritten), streaming stderr through a small
`GitProgressParser` that splits on `\r`/`\n` and emits deduplicated
percent updates. Git's diagnostics are held until the exit status is
known and classified once: plain output on success, error output on
failure. Once the files are in place HEAD moves to an unborn placeholder
ref and a second, write-free `git checkout` switches back, which keeps
the `post-checkout` hook contract identical to a plain `git worktree
add` (`<null> <new> 1`) while the branch stays claimed for the whole
streamed checkout; and `--no-recurse-submodules` mirrors what `worktree
add` does internally (linked-worktree submodule repos do not exist yet;
`syncLocalGitSubmodules` materializes them). Remote runtimes are
unchanged; their existing `logStep` calls become checklist items
automatically.
- **Deferred local materialization:** the renderer only subscribes once
`create()` has announced the workspace, so progress emitted inside
`runtime.createWorkspace` could never reach the card (Codex caught this;
the new IPC test that subscribes after `create()` resolves fails on the
previous head). `WorkspaceService.create` now asks the worktree runtime
for `deferMaterialization`: creation reserves the worktree (`add
--no-checkout`, unborn HEAD, branch mapping) and returns, the workspace
is announced, and the streamed checkout, `.xumignore` sync, fast-forward
and submodule sync run in `Runtime.materializeWorkspace()` at the start
of the background init, ahead of the init hook. This matches the
`Runtime` contract (create is fast, init streams) and how SSH runtimes
already sync in `initWorkspace`. The reserved worktree keeps HEAD on the
branch through the reservation gap and the streamed checkout itself (the
placeholder flip happens only after the files have landed), so no other
worktree can claim the branch until the workspace is complete.
Plugin-override sanitization for deferred worktrees runs on every
materialization exit, success or failure, and before the hook, exactly
like task worktrees; a sanitize failure still tears the creation down. A
checkout failure fails the init like a remote sync failure (red card,
workspace stays for inspection) and puts HEAD and the index back on the
workspace branch, so the retained worktree shows a stray file as a
modification rather than committing to the placeholder ref or staging
every file as deleted. Only removal may interrupt the file checkout
itself (`checkoutAbortSignal`, which kills the whole git process tree,
smudge filters included); archive aborts init but keeps the checkout and
never reruns it, so it waits for the files to land and parks a complete,
sanitized worktree, while everything after them (hook switch,
`.xumignore` sync, fast-forward, submodules) honours its abort,
`.xumignore` sync included (its `git ls-files` runs with the signal and
each copy checks it). If another worktree claims the branch in the
instant between the placeholder flip and the hook switch, the failure
path detaches HEAD at the branch tip instead of re-attaching, so the
rival stays the sole holder and the card shows git's error. `startInit`
persists the running init record and `replayInit` finalizes a running
record that no live init owns as a failed creation (exit -1 plus an
interruption line), so a creation that died with the app opens as a
failed card instead of a complete-looking workspace; archive deletes the
record it orphans so a cancelled init is not reported as an app exit.
Fork, restore, sub-agent tasks, multi-project and devcontainer creation
stay eager, and `task(kind="workspace")` opts out via `create(..., {
awaitMaterialization: true })` because its agentId validation reads the
checkout under the task mutex.
- **UI:** `InitMessage.tsx` rewritten in place: header button
(`aria-expanded`) with shimmer while running, step checklist (check /
spinner / error icon), new shared `ProgressBar` (`role="progressbar"`,
no animation), and a **More details** toggle for the raw log and project
path. Expand/collapse defaults derive from status (collapsed only on
success; details open once finished) with user toggles winning; the only
effect keeps the newest log line in view.
- **Store:** init output and progress events coalesce UI bumps through
`scheduleIdleStateBump`, which now runs a pre-bump prelude that flushes
the aggregator's throttled cache so a bump never renders the stale row.

## Validation

- Remote dogfood UAT (Coder Agents) on the pre-polish head `c900320a6f`
against a real 12k-file `coder/coder` worktree with success and failing
`.xum/init` fixtures: placement after the user bubble, running checklist
and streaming details, auto-collapse on success and header toggling,
failure stays expanded with red stderr, reload persistence, legacy
persisted data without step flags, and 375px width with no horizontal
overflow all passed. Not observed there: the live percentage bar,
because git suppresses progress under 2s and that checkout took about
2s. The follow-up commit sets `GIT_PROGRESS_DELAY=0`, guarded by a
real-git `WorktreeManager` test that fails without it (red-green
verified). Not covered: sub-agent child transcripts and the packaged
Electron shell (renderer only).
- Real-git `WorktreeManager` tests guard each checkout invariant and
were red-green verified: progress is reported for a one-file checkout
(fails without `GIT_PROGRESS_DELAY=0`), `post-checkout` receives
`<null-oid> <new> 1` (fails without the unborn HEAD, also across the
deferred split), a repo with `submodule.recurse=true` still checks out
(fails without `--no-recurse-submodules`), routine git chatter lands in
stdout with an empty stderr, a failed checkout is rolled back with its
diagnostics classified once and no `\r` in any logged line, a deferred
worktree is reserved empty and populated by `materializeWorkspace`, a
competing `git worktree add` for the reserved branch is refused during
the gap (red when the placeholder HEAD is set at reservation time) and
while a gated smudge filter holds the checkout mid-stream (red when the
placeholder HEAD is set before the checkout), a rival that claims the
branch in the placeholder instant (injected through the exec spy) leaves
this worktree detached at the tip with the rival as sole holder (red on
d28e33a: two holders), and a reserved-but-never-materialized worktree
force-deletes cleanly (what Cancel creation does).
- Jest `tests/ipc/workspace/init.test.ts` through the real ORPC path: a
subscriber attaching after `create()` resolves receives `init-progress`
and sees the checkout complete before the hook (red on the previous
head); a repository that tracks `.xum/mcp.local.jsonc` with a committed
`plugin:` enable has it pruned after the deferred checkout and before
the hook runs (red when the deferred sanitize is removed); a failing
checkout ends init with exit code -1, reports the git error once, skips
the hook, and keeps the workspace; a broken submodule gitlink that fails
materialization after the checkout still gets its committed `plugin:`
enable pruned (red when the sanitize only runs on success); archiving
while a slow smudge filter stalls the checkout parks a complete checkout
with the enable pruned and HEAD on the branch (red with the abort-signal
guard); archiving once the files have landed while a trusted
post-checkout hook sleeps returns promptly with a complete, clean
checkout (times out when archive is not forwarded past the file
checkout); `serverUpdateRestartBlockers` waits for the deferred init to
settle before enabling the updater, since the checkout is a restart
blocker until then. `xumignore.test.ts`: a cancelled signal rejects and
copies nothing (copied on the previous head).
`initStateManager.test.ts`: a running init record with no live init
replays as start, error line, end(-1) and is finalized on disk (nothing
was persisted before `endInit` on the previous head). WorktreeManager:
cancelling a deferred checkout stalled in a smudge filter shim settles
and leaves no helper processes behind (times out without
killTreeOnTermination). WorktreeManager: a file written into the
reserved worktree before the checkout survives and fails the checkout by
name, with HEAD and index restored (red with `--force`).
- Restart blocker: `collectRestartBlockers` keeps counting an init until
its final status write has landed (`endInit` turns the in-memory status
final only after the write); the `serviceContainer` blocker inventory
fails without it (red-green verified), and an `initStateManager` test
holds the workspace file lock to check the window itself.
- Not yet re-run after the deferral: the remote dogfood UAT above
(renderer behaviour is unchanged; the bar now has a window to appear
during the checkout).
- `make static-check`, targeted Bun suites (aggregator, messageUtils,
initStateManager, WorktreeManager, gitProgress, WorkspaceStore including
a bump-timing test that fails without the pre-bump flush), Jest
`tests/ipc/workspace/init.test.ts` and
`tests/ui/chat/initMessage.test.ts`, and the InitMessage /
App.chatLoading Storybook plays including the pinned phone viewport.

## Declined review findings

- Cross-process pending marker for deferred registrations: needs a
second Xum process to register the same half-populated worktree
directory as a project-dir local workspace during the checkout; task
worktrees already have this shape today, and holding the cross-process
registration lock across whole checkouts would block every other
registration. Left as a known tradeoff.
- Checklist failure marker lands on the most recent step (a
completion-style step such as "Fetched latest from origin" can be marked
failed when the next operation fails): fixing it needs explicit per-step
status in the init-output contract; the git error is shown in the red
output right below. Follow-up candidate.
- Resuming or quarantining a deferred checkout after an app exit: the
working tree may hold partially written files, so a resumed populate
would need `--force` (removed to protect files the user wrote there),
and the workspace may already carry the user's first prompt, so it is
neither deleted nor gated at startup. The interrupted creation is
reported as a failed card ("Check the checkout before using it, or
recreate the workspace") and recreate is the recovery; before this PR
the same crash left an orphaned directory with no workspace at all.
- Closing the placeholder instant between the symbolic-ref and the hook
switch: two consecutive local git commands with nothing in between;
removing it means dropping the null-oid post-checkout contract (round 2)
or a cross-process lock (declined above). A lost race now fails loudly
with a single holder instead of a silent double claim. The holder check
and the restore inside that failure path are likewise two consecutive
git commands (round 11); the same tradeoff applies and no further
narrowing is planned.
- Aborting an in-flight `.xumignore` copy: the sync now checks the
archive signal between files and its `git ls-files` subprocess is
killable; a single in-flight `fs.copyFile` is bounded by that one file
and completes rather than leaving a torn destination. Chunked or
stream-based abortable copying would add machinery to a best-effort
phase for an archive that already waits on the file checkout by design
(rounds 8 and 9); no further narrowing of the archive-abort contract is
planned in this PR.
- Cross-process owner or lease for init records: two Xum processes
sharing one config root is not a supported configuration, so replay
treats a running record with no live init in this process as an
interrupted creation.

## Review record

Codex code + security review on every pushed head; each round's findings
were reproduced first, fixed or declined with an inline reply, and
resolved.

| Head | Findings | Disposition |
| --- | --- | --- |
| `4348f56` | 3 (P1, P2, P2) | fixed in `02ec2c2` |
| `02ec2c2` | 2 (P1, P2) | fixed in `2267420` |
| `2267420` | 2 (P2, P2) | fixed in `c68f1dd` |
| `c68f1dd` | none ("Didn't find any major issues") | |
| `8704b61` | 2 (P2, P2) | fixed in `92baaf9` (deferred materialization)
|
| `92baaf9` | 3 (P2, P2, security) | fixed in `0cda234` |
| `0cda234` | 5 (P1, P1, P1, P2, security) | 3 fixed in `a422dae`, 2
declined (below) |
| `a422dae` | 3 (P1, P1, P1) | fixed in `9d65557` |
| `9d65557` | 2 (P1, P1) | fixed in `d28e33a` |
| `d28e33a` | 3 (P1, P2, P2) | 2 fixed in `e501fbb`, 1 fixed in
consequence and instant declined (below) |
| `e501fbb` | 4 (P1, P2, P2, P2) | 1 fixed in `0668eb8` (restart
blocker), 3 declined (below) |
| `0668eb8` | 1 (P2) | declined (below) |
| `0668eb8` (re-review) | none ("Didn't find any major issues"),
security review clean | |

The `Codex Comments` gate rejected the review summary board once it
listed resolved security advisories; coder#4223 fixed the gate on main.

## Risks

- Medium, local workspace creation: the two-step checkout changes how
every local worktree is materialized, and UI-created worktrees are now
announced before their files exist. Anything that reads the checkout
right after `create()` must wait for init like it already does for
SSH/Coder workspaces (tools, sends, attachments and skills already gate
on `waitForInit`); `task(kind="workspace")` keeps the eager path. A
crash between announcement and materialization is reported on the next
load as an interrupted creation (failed card) that the user recreates
like any failed workspace. Covered by real-git tests for new-branch,
existing-branch, deferred, and rollback paths; the resulting worktree,
branch, and clean status are asserted.
- Low, transcript ordering: only the init row moves; other rows keep
array order. Reconnect replay idempotency is unchanged and tested.

---

_Generated with `xum` • Model: `anthropic:claude-fable-5-1` • Thinking:
`xhigh` • Cost: `$127.35`_

<!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh
costs=127.35 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant