Skip to content

fix(native-chat): drop the Codex error path #23682 removed - #23801

Merged
nwparker merged 1 commit into
mainfrom
nwparker/codex-provider-verdict-import
Sep 29, 2026
Merged

nwparker merged 1 commit into
mainfrom
nwparker/codex-provider-verdict-import

Conversation

@nwparker

@nwparker nwparker commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 0 0 0 0
Prod 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​6 $\color{#cf222e}{\Huge{\mathbf{−}}}$​14 $\color{#cf222e}{\Huge{\mathbf{−}}}$​8

ELI5

A recent change decided that Codex only finishes a task when Codex says "turn completed". It deleted the old helper that used to finish a task early when Codex reported an error — but one file still tried to use that deleted helper. Nothing could compile, so every open pull request went red. This deletes the leftover line.

What Changed

Before: main was red. Every PR failed typecheck and static analysis, and because the unit-test matrix only runs when typecheck succeeds, no unit tests ran on any PR at all. Bun profile persistence also silently ran all six platforms on every run instead of the platforms it selected.

After: all three go green again and the unit matrix runs.

The mechanism: #23682 deleted src/main/codex/codex-structured-journal-provider-verdicts.ts but left codex-background-task-frames.ts importing readCodexProviderVerdict from it and branching on its turn-failed verdict. An import of a module that no longer exists is a hard tsc failure, and it also throws inside the esbuild bundle that Bun profile scope detection uses to work out which platforms a change can affect — that failure path fails closed to all six platforms.

readCodexChildTurnEnding now has a single case: a closed thread is the only way a child's turn ends without turn/completed. The turn-ended frame narrows to turnId: null and state: 'unverifiable' accordingly, since nothing produces failed through this path any more, and the now-unused CodexChildTurnEnding alias is gone.

Why

This is the deletion #23682 intended, not a workaround for it. That PR captured real app-server behaviour on 0.141.0 and 0.158.0 across eight failure scenarios: Codex follows every turn-ending error (willRetry: false) with a failed turn/completed for the same turn 0-32 ms later, and only the completion carries the turn's duration and receipt time. Ending the turn on the error threw that away and ended the turn twice.

The alternative — restoring the deleted module — would reinstate exactly the double-ending #23682 removed, and would have to be justified against that PR's captured evidence. No test asserted the error path, which is consistent with it being intentionally removed.

Linked Issue

No pre-existing issue; this is a merge-time regression from #23682 found while auditing CI runner demand.

Visual Proof

N/A — no UI change. The user-visible effect is a child agent's failed turn now settling on Codex's turn/completed rather than on its error, which #23682 already established as correct.

Testing

Local, macOS:

  • pnpm tc — exit 0 (was failing on TS2307 for the missing module)
  • pnpm exec vitest run --config config/vitest.config.ts src/main/codex/codex-background-task-tracker.test.ts src/main/codex/codex-structured-session-background-tasks.test.ts — 32 passed
  • pnpm exec vitest run --config config/vitest.config.ts config/scripts/bun-profile-change-scope.test.mjs — 37 passed, including the real-dependency-graph test that was failing on main
  • oxlint and pnpm run check:code-quality:changed — clean

Cross-platform: pure TypeScript control flow, no platform-dependent behaviour. SSH/mobile unaffected.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Existing coverage in codex-background-task-tracker.test.ts and codex-structured-session-background-tasks.test.ts exercises the child-turn reader; no test asserted the removed error path, so there is no assertion to update. Added none: this restores the state #23682 intended, and the regression's real gate is tsc, which now passes.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Security: none. Cross-platform: none. SSH/remote: none. Mobile: none. Backwards compatibility: none — internal translator only. Performance: removes a per-event call.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

#23682 established that only `turn/completed` ends a Codex turn and deleted
`codex-structured-journal-provider-verdicts.ts`, but the child-turn reader kept
importing `readCodexProviderVerdict` and branching on its `turn-failed` verdict.
The module is gone, so the import resolves to nothing: typecheck and static
analysis fail on every pull request, which skips the whole unit matrix behind
them, and the esbuild pass behind Bun profile scope detection throws, so every
run falls back to all six platforms.

A closed thread is now the only child-turn end without `turn/completed`, which
is what #23682 intended: Codex follows a turn-ending `error` with a failed
`turn/completed` for the same turn 0-32 ms later, and that completion carries
the duration and receipt time the error does not.
@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Removes error handling path from background task processor.

The PR is not ready to merge because existing child-work unit tests assert the removed behavior.

Findings

  1. P1 Existing child-work tests fail ▶

Summary

The PR removes an obsolete Codex error-based child-turn ending path and narrows the remaining ending frame to thread closure.

  • The existing child-work tests still assert the removed error-only behavior and need to be brought into line with the completion-based sequence.

Reviews (1) · Last reviewed commit: "fix(native-chat): drop the Codex error p..."

Comment on lines 50 to 52
return event.method === 'thread/closed'
? { kind: 'turn-ended', threadId: event.threadId, turnId: null, state: 'unverifiable' }
: null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Existing child-work tests fail

Two existing tests send a non-retryable error without a turn/completed event and expect the child to settle as failed. With this branch removed, those errors produce no frame, so the child stays working and both assertions fail when the full unit suite runs. Update the tests to use the intended completion-based sequence.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The production change is correct, but dropping the error path left three existing child-settlement assertions stale, so the unit suite now fails on this branch.

Reviewed changes

  • Drops the dangling import — codex-background-task-frames.ts no longer imports readCodexProviderVerdict from the module #23682 deleted, which is what broke tsc and the Bun profile change-scope bundle on main.
  • Collapses readCodexChildTurnEnding to one case — a non-retried error no longer ends a child turn; only thread/closed does, so turn-ended frames always carry turnId: null / state: 'unverifiable' and the CodexChildTurnEnding alias is gone.

⚠️ Three child-settlement assertions still encode the removed error path — the unit suite is red

The PR body says "no test asserted the removed error path", but three assertions do, and they fail now that the error branch is gone. On this branch pnpm tc:node passes, yet pnpm exec vitest run --config config/vitest.config.ts src/main/codex/ reports three AssertionErrors in codex-child-work-evidence.test.ts and codex-structured-child-work-producer.test.ts — so the unit matrix this PR is meant to unblock will itself fail. The fix has to update those files alongside the frames change.

Technical details
# Update the child-turn tests to the post-#23682 rule

## Affected sites
- `src/main/codex/codex-child-work-evidence.test.ts:181-199` — the two error cases (`childError('c1', false)` and `childError(undefined, false)`) expect `{ membership: 'settled', outcome: 'failed' }`; after this change the `error` frame is ignored, so the child stays `live`/`working`. The `thread/closed` case in the same `it.each` is still correct.
- `src/main/codex/codex-structured-child-work-producer.test.ts:336-344` — mid-step asserts `settled`/`failed` on the non-retried error; it should stay `working`.
- `src/main/codex/codex-structured-child-work-producer.test.ts:402-406` — final expected list has `lint` as `settled`/`failed`; under the new rule it is only settled at session close, as `unknown`.
- `src/main/codex/codex-background-task-tracker.test.ts` — passes; no change needed.

## Required outcome
- A non-retried `error` no longer settles a child: assert it is ignored (stays `working`) and that the child settles only on `turn/completed` or, absent one, `unknown` at session close.
- `pnpm exec vitest run --config config/vitest.config.ts src/main/codex/` is green apart from the sandbox-only integration tests (`codex-app-server-*.integration.test.ts` fail in this environment regardless of the change).

## Suggested approach
Reuse the shape of the existing "keeps a child working through a retried error" case (`codex-child-work-evidence.test.ts:201`) for the non-retried variant rather than inventing a new fixture.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: ba5c7a28-074c-40e3-952d-d120ee37ce16

📥 Commits

Reviewing files that changed from the base of the PR and between ceffecf and 7e4fd51.

📒 Files selected for processing (1)
  • src/main/codex/codex-background-task-frames.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The turn-ended frame type now requires a null turnId and an unverifiable state. readCodexChildTurnEnding no longer emits a failed ending from a provider verdict. It still emits an unverifiable ending for thread/closed.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 7e4fd

No actionable issue is established with the narrowed child-turn ending behavior. The change is ready for normal merge checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 7e4fd

The change narrows an internal completion path without adding a new privileged action or broader entry point. One event-ordering assumption remains unverified, so the risk is low rather than minimal.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed ending path acts on an existing child execution selected by thread identity; the examined route does not add an event method, privileged sink, or primary-thread settlement path.

Trust Boundaries and Controls

  • observed — Provider event fields enter the frame reader as a method, thread ID, and unknown parameters. Child-ending translation requires a non-primary thread and a thread/closed method; ordinary completion takes a separate turn-ID-reading path.

Resilience and Maintainability Implications

  • observed — The execution owner rejects a working update after terminal settlement and keeps terminal history keyed by child thread and turn, limiting stale updates to the wrong current turn.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: removing the obsolete Codex error path. The trailing reference to #23682 is slightly awkward but does not make the title misleading.
Description check ✅ Passed The description is complete and closely follows the repository template. It explains the change, rationale, linked regression context, lack of UI impact, testing, compatibility considerations, and che…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker
nwparker merged commit 643f93f into main Sep 29, 2026
33 of 35 checks passed
@nwparker
nwparker deleted the nwparker/codex-provider-verdict-import branch September 29, 2026 06:14
brennanb2025 added a commit that referenced this pull request Sep 29, 2026
#23801 already dropped the error branch and the deleted import. Kept this PR's
thread-closed frame; the tests that still expected an error to end a child's
turn are what remains.
nwparker added a commit that referenced this pull request Sep 29, 2026
…ot retry

#23801 removed the child-path reading of Codex's turn-ending `error` along with
the import of the module #23682 deleted. That was the wrong half to remove: the
primary journal path can rely on Codex's failed `turn/completed` arriving within
~32 ms, which is what #23682 established, but a child turn has no such
guarantee, and without the error as its end the child's lifecycle row latches on
`working` for the life of the session. Three tests assert exactly that and could
not run, because the unresolved import had been skipping the unit matrix since
#23682 merged.

The reading is restored inline against `readCodexErrorWillRetry`, itself restored
to `codex-structured-thread-facts.ts`, rather than by reviving the deleted
module: its `thread-stopped-running` arm lost its only consumer when #23682
rewrote the primary path, so restoring the file would re-add dead code.

Also drops `pr-workflow-parallelism.test.mjs`'s read of
`.github/workflows/track-community-prs.yaml`, which #23796 deleted while leaving
the assertion behind. Same failure class, and it fails the same shard.
nwparker added a commit that referenced this pull request Sep 29, 2026
…ot retry (#23808)

#23801 removed the child-path reading of Codex's turn-ending `error` along with
the import of the module #23682 deleted. That was the wrong half to remove: the
primary journal path can rely on Codex's failed `turn/completed` arriving within
~32 ms, which is what #23682 established, but a child turn has no such
guarantee, and without the error as its end the child's lifecycle row latches on
`working` for the life of the session. Three tests assert exactly that and could
not run, because the unresolved import had been skipping the unit matrix since
#23682 merged.

The reading is restored inline against `readCodexErrorWillRetry`, itself restored
to `codex-structured-thread-facts.ts`, rather than by reviving the deleted
module: its `thread-stopped-running` arm lost its only consumer when #23682
rewrote the primary path, so restoring the file would re-add dead code.

Also drops `pr-workflow-parallelism.test.mjs`'s read of
`.github/workflows/track-community-prs.yaml`, which #23796 deleted while leaving
the assertion behind. Same failure class, and it fails the same shard.
brennanb2025 added a commit that referenced this pull request Sep 29, 2026
…3700, #22943, #23720, #23793, #23776, #23796) into #23059

#23573 (keep a turn's bar on the prompt that opened it) is ported onto the
turn-record membership instead of kept as a second owner lookup:
- The running turn's bar comes from nativeChatTurnMembership, which now
  returns barTurnKey beside liveTurnKey. Where the host states each row's
  turn they are the same key: the running root turn's anchor, and a
  message handed over mid-turn is already scoped to that turn. Where the
  host states no scope, rows still group by position, so barTurnKey is the
  user entry the running record names (through the same anchors) while
  liveTurnKey keeps the positional live group, as #23573 kept row liveness
  on the newest prompt.
- activeTurnOpenedBy, selectStructuredAgentTurnBars' openedBy and
  selectNativeChatActiveTurnKey are removed; the desktop list, the slot
  builder and both turn-status hooks read barTurnKey.
- The queued-send clock (queuedUntil: a turn counts from the previous
  turn's end when its send waited behind it) is kept, read in the same
  one-pass walk over root turn records that keys timing by anchor.
- Its tests are adapted to pass a journal: each runs on a host that
  states no scope (the original expectations), and the desktop and mobile
  bar tests also run on a scoped host.

#23621 (a message Claude folds into its running turn no longer splits the
turn) is host-only and merged as is: the translator contract carries both
openTurnInLiveProviderCycle and #23059's command-turn members.

Both sides grew the Claude translator past its line budget, so its result
branch moves unchanged into claude-result-journaling.ts, beside
claude-message-journaling.ts (the pre-commit lint refuses the merge
without it).

#23801's Codex child path (no error-driven end) and #23059's Codex /compact
guard (a command turn ends only on turn/completed) both stand.
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