Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 070 — wp4b: land the exactly-once terminal recorder for PR #2433

## Why this is its own work-phase

wp4 closed with #2433 held, not merged. The blocker was real and the PR was
otherwise complete, so the correct move was to build the missing piece rather
than hand a maintainer's finished work back over a seam they had no reason to
suspect. That build is this work-phase.

## The change

Branch `codex/fix-2433-exactly-once-terminal`, based on `origin/dev` at
`ed719b568`. Three commits: the two original #2433 commits cherry-picked
(`56275ac50`, `3cc24816a`), then the fix `abdeaf8cc`.

The fix moves the once-guard off the exported callback and onto the recorder at
its creation site:

- `src/server/responses/core.ts` — `handleComboResponses` no longer wraps
`setTerminalOutcomeRecorder` in a local `terminalOutcomeRecorded` closure.
Instead `handleResponsesInner` guards `codexForwardTerminalOutcomeRecorder`
where it is constructed, so the preflight callback and the eager/tee
inspectors all receive the same guarded function.
- `tests/server-combo-failover-e2e.test.ts` — new regression asserting exactly
one account-health failure per streamed attempt.

21 lines changed in the runtime, 43 added in the test.

## Evidence

Red-green, from the implementer:

- `bun test tests/server-combo-failover-e2e.test.ts --test-name-pattern "records one account-health failure"`
- RED before the source fix: 0 pass, 1 fail, observed `consecutiveFailures: 2`, expected `1`.
- GREEN after: 1 pass, 0 fail.

Full focused run after rebasing onto current `origin/dev`:
`bun test tests/combos.test.ts tests/combo-stream-preflight.test.ts tests/server-combo-failover-e2e.test.ts tests/core-lab-boundary.test.ts`
-> 127 pass, 0 fail, 697 assertions. `bun run typecheck` exit 0.

## The question that decides this phase

Moving a guard to a wider scope trades one bug for a possible worse one. The
guard must be once-per-streamed-attempt, not once-per-request. A combo failing
over across three targets must still record three terminals, one per attempt.
If the recorder is created once per `handleResponsesInner` call and combo retries
happen inside that scope, the new guard would swallow later attempts' terminals —
which would be a quieter and more damaging defect than the one being fixed.

An independent reviewer was dispatched specifically on that lifetime question.
This phase does not land until that verdict is in.


## Landing record

Merged as PR #2449, squashed onto `dev` as `88b7cc057`, with @Ingwannu's two
original commits preserved in the branch history.

The independent lifetime audit answered the question this phase turned on:
each combo target calls `handleResponses` afresh at `core.ts:1915`, each call
builds a new `handleResponsesInner` at `:2163`, so each attempt gets its own
guard at `:3579`. Per attempt, not per request — a three-target failover still
records three terminals. The reviewer also confirmed the regression is
load-bearing: remove the guard and the tee inspector plus preflight both record,
putting `consecutiveFailures` back at 2.

CI 23 pass on the exact head. Issue #2431 closed; PR #2433 closed as superseded
with the reasoning posted for its author.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# 080 — wp7: issue #2392, centralize Codex auth-context error mapping

## Item

`Ingwannu` issue #2392, "centralize Codex auth-context error mapping across
Responses and compact". No PR exists; this work-phase builds one.

## Investigation verdict

`gpt-5.6-sol` high, read-only, against `origin/dev`:

- STATUS REPRODUCES (structural), disposition FIX_SMALL, effort small.
- `src/server/responses/core.ts:1537` — regular Responses carries a full local
exception matrix through `:1576`.
- `src/server/responses/compact.ts:397` — compact carries the same common matrix
through `:414`, with its own `ForwardAdmissionCredentialError` at `:337`.
- `d52032ebe` (PR #2390) already fixed the user-visible half of this — the compact
substitution failure — by adding one local 401 branch. What remains is
duplication, not a defect, so the refactor must change no observable behavior.
- `tests/codex-envkey-admission-substitution.test.ts` 4/4 confirms both
substitution paths return before upstream I/O.
- `tests/core-lab-boundary.test.ts` 13/13 confirms the protected-core guard on
`src/server/responses/core.ts`.

## Why this one is riskier than it looks

The existing tests check status codes on both paths but not byte-level parity
(`tests/server-auth.test.ts:1889`, `:1900`). A refactor that folds two error
matrices into one can pass those tests while quietly changing a response body or
a `Retry-After`. So the characterization test comes first in the definition of
done, not last: it must assert identical status, serialized body, content type,
cooldown and drain `Retry-After`, thread-affinity 409, zero upstream I/O for
substitution, regular-only safe logging, and rejection of unknown errors.

The new module also becomes a dependency of a protected core file, so it has to
be a pure leaf — builtins and local types only.

## Plan

New `src/server/responses/codex-auth-error.ts` exporting a pure
`mapCodexAuthContextErrorToResponse(error, { accountSelector, now })` returning
`Response | undefined`. Core and compact delegate the common classes and keep
their endpoint-specific logging and admission handling local. Unmapped errors
rethrow rather than being swallowed. `structure/01_runtime.md` records the new
owner.


## Execution record

Built on `codex/fix-2392-auth-error-mapping`, rebased onto `81bf4b9a4`, opened as
PR #2450.

New pure leaf `src/server/responses/codex-auth-error.ts`. Seven error classes
moved to it; four things deliberately left local, each for a stated reason:

| Left local | Reason |
|---|---|
| regular-Responses pseudonymous reauth log | compact has no equivalent, folding it in would add a log line |
| `ForwardAdmissionCredentialError` (both paths) | not an auth-context resolution error |
| compact alternate-account `CodexMainProfileDrainingError` | returns `null` to preserve the first account's rejection |
| unmapped errors | rethrown in both handlers, never swallowed |

Verification: 147 pass / 0 fail on the four focused suites, `typecheck` exit 0,
`privacy:scan` pass, and a full `bun run test` at 14,521 pass / 11 skip / 0 fail
across 906 files. Independently re-run by the main session after rebase:
61 pass / 0 fail on routing, admission-substitution, and core-lab-boundary.

The full suite was run here despite the session's focused-proof instruction
because this is the authentication surface and `AGENTS.md` requires security
review for it.

## Landing record

PR #2450 merged to `dev` as `9cebfc64e`, CI 20 pass / 0 fail. Issue #2392 closed.

This clears the entire Ingwannu queue: six PRs and five issues, all with a
recorded terminal disposition.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 090 — wp8: issue #2443, Codex Desktop wait integer coercion

## Item

`lidge-jun` issue #2443, "Codex Desktop wait.yield_time_ms / max_tokens still
rejected as 120000.0 after #2316".

## Investigation verdict

STATUS REPRODUCES, disposition FIX_SMALL. `src/lib/tool-argument-integers.ts:77`
allowlists only `timeout_ms`; commit `a9cb7661b` fixed #2316 alone, so the two
`wait` fields still forward as `120000.0` and `8000.0` through both bridge paths
at `src/bridge.ts:627` and `:1657`.

## The scoping decision

The obvious fix — adding both names to the global allowlist — is wrong.
`yield-time_ms` and `max_tokens` are ordinary names: Cursor has its own
`yield_time_ms` at `src/adapters/cursor/tool-definitions.ts:42`, and any
third-party tool may legitimately want a fractional `max_tokens`. The repair is
therefore keyed to the bare `wait` tool, with tool identity threaded through the
coercer and passed only when no namespace is present.

`tests/tool-argument-integers.test.ts:328` asserted the opposite contract for
`yield-time_ms`. It was written for #2316's narrower scope, and is updated here
deliberately rather than quietly deleted.

## Evidence

Red-green: 33 pass / 2 fail before the source change, 35 pass / 0 fail after.
`bun run typecheck` exit 0.

## Landing record

PR #2448, squashed onto `dev` as `81bf4b9a4`. Issue #2443 closed.

One CI wrinkle worth recording: the first macOS run failed on
`Codex autostart shim > Unix install rejects a recursive dynamic launcher`, a
test with nothing to do with integer coercion. Rather than merge past it, the
test was run on clean `dev` (1 pass) and on the PR branch worktree itself
(69 pass / 0 fail), which established the failure as environmental. The rerun
then came back green across all 23 checks.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 100 — wp9: issues resolved without new code (#2292, #1587)

Two of the nine owner issues needed a verdict, not a patch. Both claims were
re-verified against `origin/dev` by the main session rather than taken from the
reviewer's report.

## #2292 — Windows model picker stays stale

Fixed by PR #2382, merged as `84ee2e284` (`a3bbcdb03 feat(cli): add an opt-in
Windows desktop-app restart for a stale model picker`). Verified present in
`origin/dev` history.

The fix is opt-in by design. On Windows the desktop UI caches `model/list` and
invalidates only on `codex-app-server-initialized`, so killing the app-server
child does not refresh the picker — a full app restart does. Making that restart
automatic would kill a user's UI without asking, so it stays behind a flag.

## #1587 — routed first-turn tool catalog 3-5x native Sol

Already fixed by `fcbef381e`, confirmed an ancestor of `origin/dev` via
`git merge-base --is-ancestor`. That commit restored deferred discovery after
measuring the regression: 258,929 characters down to 96,699, recorded at
`structure/03_catalog-and-subagents.md:231`, with MCP reachability preserved.

`normalizeRoutedCatalogEntry()` at `src/codex/catalog/parsing.ts:526` now pairs
`tool_mode: "code_mode_only"` with `supports_search_tool: true`, and
`src/codex/catalog/sync.ts:371` gives template-less routed entries the same
contract. `bun test tests/catalog-cursor-search.test.ts` re-run by the main
session: 5 pass / 0 fail.

One honest caveat: a later comment on the issue describes a Claude Desktop
cache-tail comparison. That is a different client and a different, smaller
question — it is not a reproduction of the original Codex App defect. Closing
#1587 on the original report; the Claude Desktop question earns its own issue if
anyone wants to pursue it, rather than keeping a fixed bug open as a placeholder.


## Execution record

#2292 and #1587 closed 2026-08-23 with the verification evidence posted on each.
The #2152 repair went out as PR #2452 in the same work-phase, since it is the
third issue whose resolution needed no product change.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 110 — wp10: the four roadmap issues (#1478, #1049, #1048, #820)

Four of the nine owner issues are accepted architecture work, not defects. The
honest disposition for each is to stay open with a stated reason, not to be
closed for tidiness and not to be half-implemented inside a backlog sweep.

Closing an accepted roadmap item because a cleanup pass wanted a zero would
destroy the record of a decision the project already made. Each is re-confirmed
against `origin/dev` and annotated so its next reader knows where it stands.

## #1478 — config rebase provenance for deletion vs unseen keys

Labelled `roadmap`. The rebase-on-save from #1273 cannot distinguish "this
writer deleted the key" from "this writer never saw the key". That is a data
model gap in the merge's inputs, not a bug in its code: no amount of care in the
merge function recovers information the writer never recorded.

Fixing it means adding provenance to persisted config — a schema change with a
migration and a compatibility story for every existing install. That is its own
cycle. **Retain.**

## #1049 — adopt pre-substrate Codex homes into the write coordinator

Already given a deferral verdict in
`devlog/_plan/260822_backlog_disposition_program/060` and `061` during the prior
program, and nothing since has changed the calculus. Coordination covers clean
first applies and homes that already carry a valid coordinator; a home routed
before the substrate existed keeps its old uncoordinated path — which is every
install predating the substrate.

The reason it stays deferred is that adoption has to be safe on a home that may
be mid-write by an older binary, and that safety argument is the actual work.
**Retain, deferral standing.**

## #1048 — WP13 composed acceptance at the production boundary

Partially implemented: PR #1106 landed the workstation-safe composed acceptance
suite and six production-path scenarios on `dev`. What remains is the Windows
leg, which is exactly what #2152 addresses — so this issue's remaining scope is
now tracked by concrete work rather than being open-ended. **Retain, linked to
#2152.**

## #820 — 32 concurrent tool-recall sessions, protocol-safe and memory-bounded

Labelled `roadmap`, `architecture`. Defines a concurrency and memory
architecture for 32 sustained sessions with a 64-session burst, with no
OpenCodex-imposed serialization and no loss of Codex, Responses, Chat
Completions, Anthropic, or MCP compatibility. That is a program, not an issue.
**Retain.**

## Why this is a real disposition

Every one of these gets an annotation comment on the issue recording its current
state against `dev` today. "Still open" with a dated reason is a verdict; "still
open" with silence is a backlog.


## Execution record

All four annotated 2026-08-23 with a dated state check. Comments posted, then
rewritten via the API: the first attempt was assembled through a shell argument
and had its backticks and newlines eaten. Worth noting rather than hiding — the
repaired bodies are the ones now live.

| Issue | Comment |
|---|---|
| #1478 | 5386880900 |
| #1049 | 5386880991 |
| #1048 | 5386881069 |
| #820 | 5386881161 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 120 — wp11: issue #1702, per-target quota state in the combo workspace

## Item

`lidge-jun` issue #1702, "surface per-target quota state in the combo workspace —
disable actions when all targets have 0 credits".

## Investigation verdict

STATUS REPRODUCES, disposition FIX_NOW, effort medium.
`gui/src/pages/Combos.tsx:110-117` loads combos, config, and models but never
calls `/api/provider-quotas`. `combo-workspace-detail-panel.tsx:189-190` disables
Save/Create only for clean edits or busy state.

An earlier attempt exists — `c8c4358a1`, PR #1704, closed unmerged — and is not
an ancestor of `dev`. It added presentation without action gating, so it was not
resurrected.

The backing endpoint already exists at
`src/server/management/provider-routes.ts:372-378`, so this is GUI-only: no
runtime change.

## The design rule this hangs on

Quota state is tri-state: available, exhausted, **unknown**. Missing, stale,
malformed, failed, conflicting, or incomplete aggregate evidence all resolve to
unknown, and unknown must never disable a control. Only "every usable target is
KNOWN exhausted" disables Save and Create, and recovery re-enables automatically.

The inverse — treating absent evidence as exhausted — would turn a dropped poll
into a locked workspace, which is a worse bug than the one being fixed. Disabled
targets are excluded from the all-exhausted decision, since a disabled target is
not a target the combo can use.

## Evidence

- `bun test tests/combo-workspace-data.test.ts` -> 35 pass / 0 fail
(USD, percentage, custom window, unlimited, stale/unknown, trimmed provider,
incomplete aggregation, disabled target, mixed state, all-exhausted, recovery).
- `cd gui && bun test tests/combo-workspace-empty.test.tsx tests/combo-workspace-dirty.test.tsx` -> 5 pass.
- `bun run typecheck`, `bun run lint:gui`, `cd gui && bun run lint:i18n`,
`bun run build:gui` -> all pass.
- `cd docs-site && bun run build` -> 393 pages.
- Browser QA at 1440x813 and 500x757; badges wrap without clipping.

31 files: GUI components and data derivation, all nine locales
(`gui/AGENTS.md:14-18` requires every one), CSS, three test files, and the combo
guide in English plus its seven translations.

Re-verified by the main session after rebase onto `6b0f61f64`: 35 pass / 0 fail.


## Execution record

Opened as PR #2454 against `dev`, rebased onto `6b0f61f64`.

The repository's `enforce-target` gate rejects any PR mentioning `gui` without a
screenshot in the description, so the capture was committed to a throwaway
branch (`codex/asset-1702`, not for merge) purely to give the image a stable raw
URL. That branch gets deleted once the PR lands.
Loading
Loading