Skip to content

[WRONG BRANCH] promote dev onto preview for v2.35.0-preview - #2825

Merged
lidge-jun merged 112 commits into
previewfrom
codex/promote-preview-2350
Aug 28, 2026
Merged

[WRONG BRANCH] promote dev onto preview for v2.35.0-preview#2825
lidge-jun merged 112 commits into
previewfrom
codex/promote-preview-2350

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Summary

Promote the dev integration line onto preview for the v2.35.0-preview release train. This is a maintainer promotion, not a feature PR: the branch is origin/dev at d7a82a8fc verbatim, so git diff origin/dev HEAD is empty.

dev had accumulated 111 commits ahead of both release branches while npm latest still served 2.34.0 and preview served 2.34.0-preview.20260827, so the in-tree 2.35.0 line had never been published on either channel.

The head carries the Kiro turn-termination work merged in #2819. Its operationally significant fix: turn suppression had been keyed off a Responses phase field that clients are not obliged to echo back, so localTerminalReason fired 0 times across 1,534 logged Kiro turns despite being deployed. The proxy now remembers the final answer it emitted, scoped to the normalized conversation digest, so suppression no longer depends on client cooperation.

enforce-target will fail wrong_baseALLOWED_BASES is ["dev"], so every promotion to a release branch trips it by construction. Precedent: #2757, #2760, #2553, #2507.

Verification

  • dev Cross-platform CI at d7a82a8fc — completed: success on the exact commit this branch points at (ci, gates, hygiene, all 4 test shards, macos, windows, npm-global on all three OSes, keyring on all three OSes, linux-systemd, storage policy, api usage, react-doctor all green).
  • Promotion identity: branch created directly from origin/dev with no additional commits; git diff origin/dev codex/promote-preview-2350 is empty.
  • Open CodeQL alerts are pre-existing on main (oldest 2026-08-13, newest 2026-08-27) and are not introduced by this delta.
  • GUI dist rebuilt from current source and verified in a live browser against the running 2.35.0 proxy: the dashboard loads on loopback with no admin-token prompt (the stale 2.34.0 bundle was prompting because it predated the silent session-renewal fix).

Checklist

  • Local CI green for the promoted content
  • Branch is origin/dev verbatim
  • No new CodeQL findings attributable to this delta
  • Ready for maintainer promotion

Summary by CodeRabbit

  • New Features

    • Added the ocx capabilities command for machine-readable command and route discovery.
    • Added storage cleanup, trash, policy, inspection, integration, and system management commands.
    • Added account pause/resume, quota, strategy, and sticky controls.
    • Added Cursor umbrella model selection and improved model discovery.
    • Added per-account OAuth usage attribution and version-skew reporting.
  • Bug Fixes

    • Improved Kiro completion handling, Cursor error classification, and provider error status reporting.
    • Added safer storage cleanup metadata handling and credential-collision protection.
    • Improved CLI JSON output, exit codes, filtering, and error details.
  • Documentation

    • Added comprehensive ocx agent skill documentation and CLI references.

olddonkey and others added 30 commits August 26, 2026 23:01
…blic API

`normalizeXaiResponsesWebSearch` was gated on `isXaiPublicApi`, so it ran for
api.x.ai and skipped cli-chat-proxy.grok.com — the OAuth lane. That gate
contradicted the evidence already in this file: the 2026-08-22 probe recorded in
`normalizeToolGroup` and in `isXaiResponsesDestination` had found the two hosts
to be one dialect. Re-probed 2026-08-27 against the CLI proxy, one field per
request: `web_search_preview` -> 422 `unknown variant`, `external_web_access` ->
400 on every value including `true`, `search_context_size` -> 400, while
`user_location` and `search_content_types` -> 200. Identical to the public API.

Two consequences, both reproduced at the adapter before the fix.

A cached/index-only declaration became a LIVE web search. This normalizer omits
the whole tool when `external_web_access` is not `true`, precisely so dropping
the flag cannot silently widen network access. On the CLI proxy the normalizer
never ran, and the downstream capability strip only DELETES that flag and keeps
the tool — so a caller asking for no network reached xAI with an ordinary
`{type:"web_search"}`. That is the widening this file exists to refuse, on the
lane OAuth users actually take.

A legacy `web_search_preview` reached the proxy verbatim and 422'd the entire
turn, because the type conversion also lives behind the same gate.

Both are fixed by scoping the gate to `isXaiResponsesDestination`, which covers
both hosts, and `isXaiPublicApi` is removed with its last caller.
`responses-routed-web-search-fields` had a case asserting that an unclassified
row leaks the fatal fields at the CLI adapter; the host-scoped normalizer now
strips them regardless of classification, which was already true for api.x.ai,
so that expectation is corrected rather than adjusted. Reverting the gate turns
the new coverage red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fills in the outcome sections for the four release phases: the preview
prerelease, the main promotion, the stable publish, and the deploy proof.

Two plan pages had to change on contact rather than merely be annotated.

030 planned to keep main's stale 2.33.0 through the promotion conflict,
following #2553 and #2507. CI failed that correctly: tests/release-version-line.test.ts
arrived in this very delta and asserts the in-tree version is never behind the
highest release tag, and once v2.34.0-preview.20260827 existed, 2.33.0 ordered
behind it. The promotion was rebuilt to carry dev's 2.34.0, which makes the
promotion merge itself the release commit.

040 therefore lost its only hand step. It is now a pure workflow dispatch
against the promotion merge, since release.ts would skip the bump anyway.

050 carries the full record: every sha, every run id, the docs deploy and
live-site proof, the unpacked-tarball and installed-binary proof, and the two
disclosures - #2745 still unmerged pending security review, and the +11 open
CodeQL alerts this train carries onto latest.
docs(devlog): record the v2.34.0 release train outcome
…scan-2762

fix(ci): restore post-release dev gates
fix(cli): neutralize usage report terminal controls
fix(xai): normalize web search on the Grok CLI proxy, not just the public API
fix(integrations): ignore JSON object key order in ownership
…ma-bounds-2763

fix(moonshot): intersect nested schema bounds
…he-options-2765

fix(openai): strip unsupported forward cache options
Phase-0 docs-only cycle for the CLI-operability unit: research the real
management API surface (183 reachable routes, 108 mutating), the CLI surface
(52 runners, 20 disconnected help sources), the GUI capability map (13
GUI-only classes), and per-issue root causes for #2696-#2705.

The finding that shapes the roadmap: nothing binds the CLI surface to the API
surface. Help lives in a hand-written banner plus 20 module USAGE constants
with zero consumers outside their own files, so it is free to drift, and no
test fails when a route lands with no verb.

Records diff-level docs for wp2-wp9 and the decisions deliberately deferred.
The A-gate reviewer verified ~40 file:line anchors and found the diagnosis
sound but three proposed patches unbuildable and one guard vacuous. Each
finding re-verified against source before amending:

- wp6 patched a function that does not exist; the real gate is
  accountLabelForAttribution (summary.ts:687), and widening the Codex label
  predicate would have made the edit a no-op.
- wp6's stamp site sits inside isGenericFailoverProvider, which needs oauth
  authMode, and the rotation paths need >=2 accounts. A single-account xai user
  would never have been attributed while every test passed. Stamp moved outside
  the gate with a named activation scenario.
- The recurrence-guard regex matched handleLogin and neither target, so it would
  have greened over the regression it guards. Now red-first.
- Command counts were 49/43/52; they are 58/52/57.
- The new-verb sketch had four wrong helper signatures.
- accounts[] is blanked under any filter (summary.ts:943), so the natural agent
  query returned an empty table; it now says why.
- auto-switch and reset-credits already have verbs.

Also splits wp3's overloaded contract work into wp3b and adds the third parity
check without which an under-declared route registry was undetectable.
The audit split wp3's contract work into its own phase but left the stack
diagram and phase table describing the old eight-branch chain. Records why
wp3b is a dependency-ordered successor rather than an effort bucket, and why
wp6 deliberately follows wp4.
The update-recovery case starts a real detached proxy, and its cleanup ran the
reap only when `node ocx.mjs stop` exited non-zero. That exit code is a claim,
not proof: `stop` also reports success when it finds no live runtime to stop,
which is indistinguishable here from one it failed to stop. On that path nothing
killed the proxy, and the block went straight on to `rmSync` the fixture tree.

The survivor was reparented to init with its package tree, `src` symlink, and
config deleted underneath it — unable to serve, unable to exit. One escapee sat
at 99% of a core for three and a half hours, 202 minutes of CPU time, listening
on nothing. It also held the test machine lock, which is how it surfaced.

Cleanup now resolves the pid before anything destroys the record of it, runs
`stop` for its graceful path, then verifies liveness and reaps regardless of what
`stop` claimed. `rmSync` moves inside a nested finally so it still always runs,
but strictly after the reap: deleting the tree out from under a live detached
process is what turned a missed kill into a permanently spinning orphan. Reaping
the recorded pid is enough because bin/ocx.mjs mirrors its Bun child's exit, so
the node launcher follows it down.

Reaping alone would still fail silently, because the case passed the whole time
it was leaking. An afterAll now audits the pid, so a future regression is red
instead of invisible. Verified by A/B: with `stop` stubbed to exit 0 without
killing, the old cleanup reports 15 pass / 0 fail and leaves the orphan, the new
one reports 15 pass / 0 fail and leaves nothing, and the old cleanup under the
new guard turns red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nnot match prose

The PR hygiene gate failed with `empty_catch` on a documentation-only PR. The
scanner is right about what it saw and wrong about what it means: it reads added
lines with a text regex, and two devlog lines quote the very construct they are
arguing should be removed. A prose citation of a defect is indistinguishable from
the defect when the check is textual.

Rewording the citations is the honest fix here. Loosening the regex to ignore
markdown would weaken a real guard for every future PR, and a `suppression-approved`
label would record an exception that does not exist -- there is no empty catch in
this diff to approve.

Verified against the gate's own exported helper rather than by eye: hasEmptyCatch
now returns false for every file in this unit, while still returning true for
`try{}catch{}` and false for a handled catch, so the detector is intact.
WP9MSG && git log --oneline -1
Three defects made unattended agent operation impossible.

The provider and models dispatch runners awaited their handler and returned a
literal 0, so index.ts called process.exit(0) and erased the exit code the
handler had set. 'ocx provider quota' against a stopped proxy printed an error
and exited 0 (#2697).

responseMessage read only error/message/detail, so the reason and hint the
server sets on a refusal never reached the operator: a fenced management plane
was indistinguishable from a generic failure (#2698). The account client was
worse, collapsing every transport error into a bare sentinel that discarded the
cause, and exiting 1 for everything including 404 and 409.

The service exports its token file as OPENCODEX_API_AUTH_TOKEN, and nothing
refused a management token there. When the two match the server fences the whole
management plane closed at boot, so every /api/* returns 503 even on a loopback
install that never needed a data-plane secret (#2696). The write path now
refuses it, and doctor names an install already in that state.

The recurrence guard for the exit-code defect corrected its own plan: the
allowlist is debug and login, verified because both exit(1) from inside the
handler, not the three commands the plan guessed. login reporting success for a
failed OAuth flow is left to wp3b's exit-code contract.

Closes #2696
Closes #2697
Closes #2698
Reading process.exitCode only reports the current command's outcome if nothing
earlier in the process set it. Four handlers in index.ts write it (handleEnsure,
handleTrayProxyStart, handleProxyRestart, handleStop), and while only one
command runs per process today, the service runner already resets first for
exactly this reason. Mirror it rather than depending on that invariant holding.
Two independent reviewers found the same defect: the fix was half-inert. apiError
gained a status parameter and apiJson gained transportError, and no production
caller passed or read either, so the 404 to 4 and 409 to 5 mapping never ran and
the retained transport cause was never printed. The behavior existed only in this
phase's own tests while the commit message claimed it shipped. Status is now
threaded through all 19 apiError call sites and the cause through the 14
status === 0 guards; two new tests assert the call sites rather than the helpers,
so it cannot go inert again.

tray had the identical #2697 defect: windowsTrayCommand reports failure through
process.exitCode and returns void, so ocx tray install printed an error and exited
0. The recurrence guard could not see it because the pattern was anchored on
'await handle\w+(', scoping it to a naming convention rather than to the defect
class. Broadened to 'await [\w.]+(', which surfaced four more candidates; each was
verified in its handler before being allowlisted with its own reason.

Also corrects the login allowlist reason (it throws rather than exits 1), passes
the env seam through to configuredAdminToken so doctor's check no longer reads real
machine state for half its comparison, and extends assertNotAdminToken with the
equality arm that catches an operator-set admin token without the ocx_admin_ prefix.

The #180 matrix test asserting a 404 exits 1 now expects 4. That issue is a feature
request, not an exit-code contract; the test described then-current behavior, and
the change is the one 010.3 specified and the PR disclosed.
A review of the #2696 work noted that doctor announced a total management outage
at the same level as a directory-permission note. OAuthDoctorCheck gains a FAIL
level for a condition that makes the surface unusable rather than degraded, and
the credential-collision check uses it. Doctor's exit code remains wp3b's scope.
Four parallel read-only reviews of 020 against the current tree all returned
FAIL, on one shared root cause: the doc counts grep line hits and calls them
routes. Nine amendments in 021.

The load-bearing one is check 3. 020 calls it "the unit's central claim -- if
this gate can pass vacuously, nothing else in the unit holds", and specifies it
as registryRoutes == literalCount + allowlistCount. That identity cannot
balance against the real source: one literal line can register two routes
(PUT || PATCH), 19 literal lines decide their method in an enclosing block, and
two live routes use a negated guard the prescribed === scan never sees. One
module -- routing-analytics-routes.ts -- has one route and zero literals, so a
literal-keyed test omits it entirely. For GET /api/storage the scan finds only
the dead shadowed copy and never the live one; the gate would have mistaken the
corpse for the patient. It now reconciles on distinct (method, path) pairs and
fails loudly rather than guessing a method it cannot resolve.

The 188-literal figure was measured over a wider file set than the "40+
invisible" claim it justifies: 159 in management scope, and the 29-line
difference is exactly the codex-auth and native-profile files counted as
invisible. Real non-literal count is 18, now enumerated with mechanism.

The lab exemption claimed 20 reads. The family is 21 routes, 7 of them
mutating, and a local SQLite read cannot start an automation run -- so accept
criterion 2 was violated on day one by routes that already exist. Those seven
get a bounded deferred-verb exemption naming wp7 as owner, and the parity test
asserts the owner phase still exists, so an exemption cannot outlive its debt.

020.4 aimed the version field at isOpencodexHealthz, which returns boolean and
cannot carry a value; the body it validates is discarded one frame up in
proxyIdentityAt. It also omitted tests/proxy-liveness.test.ts, where 12
exhaustive toEqual assertions break, and asked for a push into a warnings array
that does not exist -- the only candidate funnels into codexRuntime.warning,
which would file a stale-PATH warning under the Codex runtime heading.

020.2's premise was that 20 USAGE constants are dead. 20 is the file count; 37
constants exist, 12 are exported, and the 12 exports are one-line aliases whose
targets carry 243 live references threaded through rejectArgs into
CliUsageError. Deleting them is a contract change to how the CLI reports
argument errors, not a deletion sweep.

Also: 020 never mentioned the Lab boundary, whose protected set includes
src/server/management-api.ts -- the file that would import the new route
registry. 021 gives the data-only mechanism and the guard command that proves it.

Accept criterion 5 is new: the reconciliation gate must be driven red before it
counts as evidence. 020's original gate would have failed on correct code while
believing itself rigorous, which is what a gate nobody has attacked looks like.
The A-gate reviewer for 021 died twice on provider rate limits, so the audit was
done directly. That substitution is recorded in A10 rather than glossed: an
unaudited amendment correcting an unaudited plan is the same failure one level up.

Four defects, two blocking.

A1 told the scanner to resolve a route's method from the same line, the next two
lines, or the enclosing block. All three miss how lab-routes.ts actually works:
a preceding sibling guard at :352 (`if (req.method !== "GET") return null`)
narrows the eight path literals that follow it. storage-log-guard-routes.ts
inverts the nesting again, with the path outer at :112 and the method as an
inner early return at :113. A forward-and-outward scanner resolves neither, so
the fix for 020's arithmetic was itself unimplementable. Method resolution is
now a small ordered walk over guard statements that maintains a narrowing
context -- which means admitting the fail-loud requirement cannot be satisfied
by a regex.

A3 asked the parity test to check that every deferred-verb owner still exists in
the goalplan. The goalplan is gitignored and untracked, so that test passes here
and cannot find the file in CI -- a vacuous gate introduced by the fix rather
than by the original. The exemption now names a tracked devlog doc instead, so
the assertion means the same thing in both places.

A5 had each module's const USAGE become a table lookup. Those constants are
top-level, and ESM answers a top-level cycle with undefined rather than an
error, so a future back-import would silently empty the usage text in every
rejectArgs call -- a quiet regression in the exact error-reporting surface these
issues are about. capabilities.ts is now a leaf data module with a guard test
and a non-empty assertion.

A6 proposed capability entries for help and --version. Neither reaches the
dispatch table: both exit in the CLI head at root.ts:28, so --version has no
runner key to parity-check against. And help's absence is not a gap --
tests/cli-registry.test.ts:14-21 excludes it in a comment and a headHandled set
as a deliberate decision. A6 read that decision as an oversight. Head-handled
surfaces now live in a separate list that feeds the banner and the JSON output
without touching runner-key parity.

Also verified rather than assumed: agent-settings-routes.ts and
oauth-account-routes.ts are correctly absent from A2's non-literal table. Their
startsWith/slice hits manipulate payloads, not paths.
…ofile test

CI failed in `tests/cli-native-profile.test.ts`: a 409 from `stage/finish` returned 5
where the test expected 1. Both cannot be right, so this is a contract question rather
than a broken assertion, and it gets answered rather than silenced.

5 is correct. `apiError` maps 404 to 4 and 409 to 5 so a script can tell a missing
account from a concurrent mutation, which is the whole point of #2698; the plan for this
unit declares that vocabulary explicitly. The test asserted 1 only because every
account-family failure used to exit 1 regardless of status -- it was pinning the defect,
not the intent. What the test actually covers, the idempotent cancel fallback and the
absence of a spurious cleanup warning, is unchanged and still asserted.

The expectation is updated with that reasoning recorded inline, so the next reader sees
why 5 is not a regression. The comment citing the old bare-catch defect is reworded for
the same reason as the devlog lines: the hygiene scanner matches the construct in prose.
WP9MSG && git log --oneline -1
… source

Nothing in this repository related the CLI surface to the management API. This
adds the declared inventory that later phases register against, plus the gate
that keeps it honest.

The registry is declared, not harvested, because 18 routes are unreachable by
any text scan of their own file: four regexes over model paths, three over lab
ids, an endsWith, a pathname.slice, a prefix decode, three path constants, and
two negated pathname !== guards. The negated pair matters most. GET
/api/routing-analytics has no equality literal anywhere, so a scan built on ===
never sees it. And for GET /api/storage a === scan finds only the dead shadowed
copy in logs-usage-routes.ts, never the live guard in
storage-log-guard-routes.ts -- it would have reconciled against the corpse and
called the surface covered.

The reconciliation counts (method, path) pairs, never grep line hits. Line
counting cannot balance against this source: /api/codex-auth/pool-strategy
registers PUT and PATCH from one guard spanning four lines, and nineteen path
guards decide their method somewhere other than their own line. Both nesting
orders occur and neither is inferable from the other -- lab-routes.ts:352
narrows to GET before eight path literals, while storage-log-guard-routes.ts:112
opens with the path and puts the method guard inside at :113. So the scanner
walks statements in order maintaining a method narrowing context, and when it
cannot resolve a method it returns null and the test fails. It never assumes
GET; a scanner that guesses produces a number nobody can trust, which is how the
earlier route figures became unreproducible.

Resolved surface: 192 pairs from source plus 18 declared non-literal, 108 of
them mutating.

The gate was driven red before being trusted. Injecting POST
/api/combos/red-first-probe into a real handler failed two checks independently
and named the route in both. Two further failures found during construction were
also real: the generator emitted the negated-guard routes twice once the scanner
learned to resolve them, and the inertness assertion flagged this file's own
header, which explains why it must not import Lab.

That inertness is load-bearing. route-registry.ts is imported by
src/server/management-api.ts, which tests/core-lab-boundary.test.ts protects: a
user with one provider and no Lab must execute no Lab code. Route paths are
strings, so declaring /api/lab/status creates no module edge. The module imports
nothing at all, and a test asserts it.

Exemptions carry a mandatory reason. The seven mutating /api/lab routes are the
interesting case: they were previously going to be waved through as
"local-transport" alongside the reads, but ocx lab reads local SQLite and cannot
start an automation run or import a community bundle. They now carry
deferred-verb with an owner phase and a tracked devlog doc the test verifies
exists -- deliberately a repository file and not the goalplan, which is
gitignored, so the assertion means the same thing in CI as it does locally.
An agent driving ocx had no way to discover what it could do except by parsing
help text, and help text was twenty per-module USAGE constants plus a banner a
test explicitly licensed to drift from the command registry. This adds the table
that relates each CLI capability to the management routes it drives, and the verb
that emits it.

  ocx capabilities                      human tree
  ocx capabilities --json               full table with routes and flags
  ocx capabilities --json --mutating-only
  ocx capabilities --route /api/usage   which commands drive this route

capabilities.ts is a leaf module and a test enforces it. Every command module
declares its usage text as a top-level const USAGE evaluated at import time, so
a cycle back into this table would resolve to undefined under ESM rather than
throwing -- silently emptying the usage string rejectArgs hands to
CliUsageError, degrading the very error reporting these issues are about. The
test asserts no relative import and no dynamic import, plus that every rendered
invocation and summary is non-empty, so a cycle fails loudly.

Head-handled surfaces stay out of CLI_COMMANDS. --version, -v, and version exit
in the CLI head at root.ts before dispatch, so none is a runner key; help is
excluded there deliberately, documented in cli-registry.test.ts as a
head-handled pseudo-case. Listing either as an ordinary command entry would
break the runner-key parity assertion, so they live in HEAD_CAPABILITIES and
contribute to the banner and JSON output without touching parity.

--route on a route no verb drives exits 4 rather than printing an empty list and
returning 0. Reporting success for a request that accomplished nothing is the
same dishonesty the transport phase just removed; it does not get reintroduced
at the discovery layer.

The registry cross-check earned its place immediately: the first version of this
table declared ocx status as driving GET /api/status, and the test proved that
route does not exist. collectStatus identity-probes /healthz and reads local
config, so the capability now correctly declares no route and says so.

Flag parsing is order-independent by construction, scanning argv for the flag
rather than reading a fixed position -- positional reading is why
ocx restore back --json ignores its flag today.
lidge-jun and others added 13 commits August 28, 2026 21:40
docs(skill): add the repo-owned ocx operating skill
Co-authored-by: Cursor <cursoragent@cursor.com>
…sktop

`--json=true` and Unicode dashes still ran doctor and printed prose as
success. Desktop status/show threw a generic Error so usage exited 1.

Co-authored-by: Cursor <cursoragent@cursor.com>
fix(cli): close the gap-audit findings where the CLI misreported its own outcome
…Kiro again

A turn whose replayed history already ENDS with a delivered final answer had
nothing to ask upstream, but the adapter asked anyway. `buildKiroPayload`
appends a trailing user turn because the protocol requires one, and cf1a572
made that turn a neutral acknowledgement rather than a resume instruction. What
it could not remove is the inference itself: a neutral prompt is still a prompt,
so the model answered the closed task again and a finished turn kept behaving
like a still-open goal. 60537f0 additionally stopped advertising the
completion tool for that shape, which narrowed the loop without ending it.

The boundary is now that no request happens at all. `ProviderAdapter` gains an
optional `localTerminal` hook, consulted in `handleResponsesInner` after adapter
resolution and BEFORE the request is built: no build (so no token estimate), no
send (so `sendCount` stays 0), and the turn completes from a locally constructed
terminal.

Placement is load-bearing in three ways, each of which ruled out a simpler spot:

`buildRequest` cannot express it — the contract returns an `AdapterRequest`, and
events only exist once a `Response` reaches `parseStream`. Faking a `Response`
inside `fetchResponse` is worse: it records a physical send.

An outputless `done` from the ordinary event path cannot express it either.
`guardEmptyCompletionEventStream` treats a terminal with no content event as a
failed turn, suppresses it, and re-invokes the identical request; a second empty
terminal becomes `empty_completion_retry_failed`. Routing the fix through that
path would have re-sent the very request it exists to prevent — which is why the
regression runs with `emptyCompletionRetry` both enabled and disabled rather than
only in the default configuration.

`runTurn` was considered and rejected: it routes every Kiro request into the
custom transport branch, which waits on provider pacing and increments
`sendCount` before any local decision, still meets the empty-completion guard
afterwards, and would force the adapter to re-own transport, retries, failover,
cancellation, and accounting it already gets from the standard path.

The predicate is the existing `hasTrailingDeliveredFinalAnswer`, so there is one
notion of "delivered": it keys off role and `phase`, which is why a user message
merely QUOTING the acknowledgement is unaffected, and it returns false at the
first non-assistant message, so a genuine follow-up still reaches Kiro. The
adapter-owned bounded retry is untouched because it builds with a forced
`text_fallback` mode and never passes through this hook.

`localTerminalReason` is recorded on the request log so a zero-send request is
explainable rather than looking like a lost one. It is a fixed identifier, never
conversation-derived.

Scope: this fixes the repeated inference only. The user-visible duplicate answer
— ordinary text released as `commentary` and the completion answer as
`final_answer` in the SAME inference, split into two messages by the bridge —
is a separate mechanism and is planned as wp2 in
devlog/_plan/260828_kiro_turn_termination/020.

Verification: 214 tests pass across kiro-adapter, kiro-stream,
server-kiro-completion-e2e, core-lab-boundary and repo-hygiene; 113 more across
the empty-completion and request-log suites; `bun x tsc --noEmit` clean;
`privacy:scan` green. All four new short-circuit assertions were driven red by
reverting the hook call, so none is vacuous. Full-suite validation was skipped at
the user's explicit instruction.
…r SSE return

The local-terminal short-circuit returned its SSE body raw. Every other
streaming return in handleResponsesInner wraps the bridge output in
trackStreamLifetime, which is what releases the turn admission lease when the
body finishes or the client disconnects. A turn that already holds all of its
output would otherwise keep a lease alive until something else reclaimed it —
the one resource the no-inference path had no excuse to hold.

Found while checking the short-circuit against the neighbouring return sites
rather than by a failing test, which is why no assertion changes here: the
existing local-terminal regressions still pass, and a lease leak is not
observable through them.
The merged stack left GET /api/providers off the undeclared-route ratchet,
apiError sites coalescing status to 1, a dispatch source scan that still
expected console on sync-cache, and a stale generated skill surface.

Co-authored-by: Cursor <cursoragent@cursor.com>
fix(cli): unblock dest CI after the agentic-control stack
…timate

The C-phase verifier failed wp1 criterion 3 on evidence, and it was right. The
local terminal constructed explicit zero usage, but `usageForFinalLog` marks all
Kiro and Cursor usage estimated provider-wide, so the row came out as
`{inputTokens: 0, outputTokens: 0, estimated: true}` with
`usageStatus: "estimated"`. Zero counts from a turn that made no inference are
exact. A row claiming otherwise is indistinguishable from a real turn whose usage
frame never arrived — which is the wrong signal on the one path whose whole point
is that nothing was sent.

The provider-wide marking exists because those adapters can only guess a real
inference's usage. There is nothing to guess when there was no inference, so
`usageForFinalLog` takes an explicit `locallyAnswered` argument rather than
inferring it from the numbers: a genuine turn that happened to consume zero
tokens is still an estimate, and a locally answered turn whose adapter DID
estimate something still says so. It defaults false, so no existing caller
changes behaviour.

The second finding is fixed too: `localTerminalReason` lived only on the internal
context and never reached `RequestLogEntry`, so the field could not actually make
a zero-send row explainable as its own comment claimed. It is now copied into the
entry, sanitized like the other metadata strings.

Both gaps existed because the committed regressions asserted behaviour — nothing
was sent — and never read the resulting log. `tests/server-kiro-completion-e2e.test.ts`
now inspects the request-log row for both response modes: the reason field,
`usageStatus: "reported"`, absent `estimated`, zero counts, `sendCount === 0`,
and no `inputTokenEstimate`. Reverting the `locallyAnswered` argument turns
exactly those two red.

Verification: 363 tests pass across the kiro, usage-log, request-log,
empty-completion, core-lab-boundary and repo-hygiene suites;
`bun x tsc --noEmit` clean; `privacy:scan` green. Full-suite validation remains
skipped per the user's instruction.
Re-verification failed criterion 3 again, one level down. The parent request-log
row read exact, but `addFinalRequestLog` finalizes its active attempt first
through `finishRequestAttempt`, which ran `finalizedUsage` without the new
`locallyAnswered` argument. So the row said `usageStatus: "reported"` while its
own embedded attempt still said `estimated: true` — and the attempt is the
detailed accounting a maintainer actually reads for a zero-send turn.

The flag travels ON the attempt rather than as a `finishRequestAttempt`
parameter. That function is called from six places (core, collaboration, compact,
encrypted-payload, policy-fallback, and the request-log module itself), and a new
positional argument would silently default to the wrong answer at any call site
that was missed. Setting it once where the local terminal is decided cannot be
forgotten by a later caller.

The gap survived the first fix for the same reason it existed: the accounting
tests asserted the row and the attempt's `sendCount`/`inputTokenEstimate`, but
not the attempt's usage provenance. They now assert `usageStatus` and absent
`estimated` on every embedded attempt.

Verification: 205 tests pass across server-kiro-completion-e2e, usage-log,
request-log, kiro-adapter, tool-catalog-nudge and cursor-exec-empty-result;
`bun x tsc --noEmit` clean. Full-suite validation skipped per instruction.
…after a wasted one

`EMPTY_EXEC_OUTPUT_MESSAGE` is a repair: it fires only once a model has already
spent a call and read a blank result. That recovers the turn but cannot recover
the round trip, and it leaves the model guessing whether its command failed or
its output was merely dropped. Live 2026-08-28, a routed Kiro session did exactly
that — reported a blank exec result as possible context loss, and learned the echo
rule only from the repair text.

The rule now also appears in the tool-catalog nudge, so a code-mode turn is told
up front that nothing in the isolate is echoed automatically and that a bare
trailing `await tools.<name>(...)` is discarded. The wording is one exported
constant shared by both surfaces and by the Cursor guidance note: two surfaces
describing the same isolate must not drift, because that is how a model gets told
two different things about whether its output survived.

Scope is gated on an actual code-mode exec being advertised. A flat shell bridge
echoes stdout on its own, so the same sentence there would be false — asserted
directly rather than left to the reader.

Verification: 205 tests pass across server-kiro-completion-e2e, usage-log,
request-log, kiro-adapter, tool-catalog-nudge and cursor-exec-empty-result;
`bun x tsc --noEmit` clean. The kiro-adapter assertion checks the ACTUAL wire
prompt rather than the builder alone, since the session that misread a blank
result was a routed Kiro turn. Full-suite validation skipped per instruction.
…client to echo phase

Suppressing a finished turn keyed off the inbound `phase: "final_answer"` alone, which is a
field the proxy emits but no client is obliged to send back. Measured against the running
proxy: the same history WITH phase sent nothing upstream, and WITHOUT it spent 7686 input
tokens redoing the closed task. `localTerminalReason` appeared 0 times across 1534 logged
Kiro turns even though the suppression code was deployed and loaded, so the feature had
never once fired in production.

The proxy now records the final answer it actually emitted, keyed by the normalized
conversation digest it already computes for logging, and consults that record when the
inbound phase is missing. An explicit phase still wins; the record is an additional source.

Scope is the load-bearing constraint. Only the 32-hex normalized digest may key the map, so
no raw header or account identifier can be retained here, and an unresolvable scope means "no
record" rather than a global fallback. Matching requires the remembered answer to still be
the trailing content-bearing message: every legitimate next turn contains a new user message
somewhere, so "a later user message exists" would suppress nothing, while keying off the
trailing turn distinguishes a re-sent closed history from a genuine follow-up.

A local terminal emits no output and therefore writes no record, which is what keeps
suppression alive across the repeated identical histories the live loop actually produced.
…and-turn-termination

fix(kiro): explain empty exec output and stop reopening a delivered final answer
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 28, 2026 14:19
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b3d67d06-98dd-467c-b591-5b0b1b382246

📥 Commits

Reviewing files that changed from the base of the PR and between 809a06b and 6667e34.

📒 Files selected for processing (219)
  • .github/workflows/ci.yml
  • AGENTS.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/000_plan.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/010_phase1.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/011_wp1_outcome.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/020_phase2.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/021_wp2_outcome.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/030_phase3.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/040_phase4.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/041_wp2b_2729_supersede.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/050_phase5.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/060_phase6.md
  • devlog/_plan/260827_igwanu_bug_pr_merge_round/070_outcome.md
  • devlog/_plan/260827_release_train/020_preview_release.md
  • devlog/_plan/260827_release_train/030_main_promote.md
  • devlog/_plan/260827_release_train/040_stable_release.md
  • devlog/_plan/260827_release_train/050_deploy_and_verify.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/000_plan.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/010_phase1.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/020_phase2.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/021_wp2_wp3_outcome.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/030_phase3.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/040_phase4.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/050_phase5.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/060_phase6.md
  • devlog/_plan/260828_bugpr_fix_and_reimplement/070_outcome.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/000_plan.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/001_backlog_abort_rca.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/002_cursor_open_defect_inventory.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/003_roadmap_lock.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/010_backlog_abort_fix.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/020_macmini_probe_round.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/021_probe_results_round1.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/030_cursor_fixes.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/031_midstream_echo_fix.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/040_closure_round.md
  • devlog/_plan/260828_cursor_ndjson_backlog_train/050_merge_train.md
  • devlog/_plan/260828_cursor_umbrella_catalog/000_plan.md
  • devlog/_plan/260828_cursor_umbrella_catalog/001_reference_analysis.md
  • devlog/_plan/260828_cursor_umbrella_catalog/002_current_surface.md
  • devlog/_plan/260828_cursor_umbrella_catalog/003_design.md
  • devlog/_plan/260828_cursor_umbrella_catalog/004_roadmap_lock.md
  • devlog/_plan/260828_cursor_umbrella_catalog/010_capability_core.md
  • devlog/_plan/260828_cursor_umbrella_catalog/020_catalog_integration.md
  • devlog/_plan/260828_cursor_umbrella_catalog/030_closure.md
  • devlog/_plan/260828_kiro_turn_termination/000_research.md
  • devlog/_plan/260828_kiro_turn_termination/010_wp1_terminal_boundary.md
  • devlog/_plan/260828_kiro_turn_termination/011_audit_round1.md
  • devlog/_plan/260828_kiro_turn_termination/012_audit_round2.md
  • devlog/_plan/260828_kiro_turn_termination/020_wp2_duplicate_answer.md
  • devlog/_plan/260828_ocx_agentic_control/000_plan.md
  • devlog/_plan/260828_ocx_agentic_control/001_api_route_inventory.md
  • devlog/_plan/260828_ocx_agentic_control/002_cli_surface_inventory.md
  • devlog/_plan/260828_ocx_agentic_control/003_gui_capability_map.md
  • devlog/_plan/260828_ocx_agentic_control/004_issue_root_cause.md
  • devlog/_plan/260828_ocx_agentic_control/005_audit_record.md
  • devlog/_plan/260828_ocx_agentic_control/010_phase_transport_honesty.md
  • devlog/_plan/260828_ocx_agentic_control/011_wp2_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/020_phase_capability_registry.md
  • devlog/_plan/260828_ocx_agentic_control/021_wp3_stale_check_amendment.md
  • devlog/_plan/260828_ocx_agentic_control/025_phase_uniform_cli_contract.md
  • devlog/_plan/260828_ocx_agentic_control/026_wp3b_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/030_phase_dto_fidelity.md
  • devlog/_plan/260828_ocx_agentic_control/031_wp4_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/040_phase_new_verbs.md
  • devlog/_plan/260828_ocx_agentic_control/041_wp5_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/050_phase_account_attribution.md
  • devlog/_plan/260828_ocx_agentic_control/051_wp6_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/060_phase_gui_parity.md
  • devlog/_plan/260828_ocx_agentic_control/061_wp7_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/070_phase_agent_skill.md
  • devlog/_plan/260828_ocx_agentic_control/071_wp8_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/080_phase_rebase_and_ci.md
  • devlog/_plan/260828_ocx_agentic_control/081_deferred_decisions.md
  • devlog/_plan/260828_ocx_agentic_control/081_wp9_implementation_record.md
  • devlog/_plan/260828_ocx_agentic_control/090_phase_gap_closure.md
  • devlog/_plan/260828_ocx_agentic_control/091_wp10_implementation_record.md
  • docs-site/src/content/docs/reference/cli.md
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/fr.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/tr.ts
  • gui/src/i18n/zh-TW.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Storage.tsx
  • gui/tests/storage-policy-metadata-warning.test.tsx
  • package.json
  • scripts/generate-ocx-skill-surface.ts
  • skills/ocx/SKILL.md
  • skills/ocx/references/01_management_surface.md
  • skills/ocx/references/02_json_shapes.md
  • skills/ocx/references/03_recipes.md
  • skills/ocx/references/04_failure_semantics.md
  • src/adapters/base.ts
  • src/adapters/cursor.ts
  • src/adapters/cursor/catalog.ts
  • src/adapters/cursor/cursor-errors.ts
  • src/adapters/cursor/discovery.ts
  • src/adapters/cursor/envelope-echo.ts
  • src/adapters/cursor/request-builder.ts
  • src/adapters/cursor/tool-definitions.ts
  • src/adapters/cursor/tool-result-normalize.ts
  • src/adapters/exec-tool-result-normalize.ts
  • src/adapters/google-antigravity-replay.ts
  • src/adapters/google-antigravity-wire.ts
  • src/adapters/google.ts
  • src/adapters/kiro-constants.ts
  • src/adapters/kiro.ts
  • src/adapters/openai-chat.ts
  • src/adapters/openai-responses.ts
  • src/adapters/run-turn-queue.ts
  • src/adapters/tool-catalog-nudge.ts
  • src/adapters/xai-web-search.ts
  • src/claude/outbound.ts
  • src/cli/access.ts
  • src/cli/account-api.ts
  • src/cli/account-extended.ts
  • src/cli/account-main.ts
  • src/cli/account.ts
  • src/cli/agent.ts
  • src/cli/capabilities-command.ts
  • src/cli/capabilities.ts
  • src/cli/claude-desktop.ts
  • src/cli/dispatch.ts
  • src/cli/doctor.ts
  • src/cli/help.ts
  • src/cli/index.ts
  • src/cli/inspect.ts
  • src/cli/observe.ts
  • src/cli/registry.ts
  • src/cli/runtime-api.ts
  • src/cli/status.ts
  • src/cli/storage.ts
  • src/cli/system-command.ts
  • src/cli/usage-report.ts
  • src/cli/version-skew.ts
  • src/codex/account-label.ts
  • src/codex/catalog/provider-fetch.ts
  • src/codex/transition-state.ts
  • src/compatibility/openai-responses.ts
  • src/integrations/ownership-policy.ts
  • src/integrations/ownership.ts
  • src/integrations/state.ts
  • src/integrations/writer.ts
  • src/lib/admin-secrets.ts
  • src/lib/errors.ts
  • src/lib/service-secrets.ts
  • src/oauth/store.ts
  • src/providers/label.ts
  • src/responses/turn-termination.ts
  • src/server/management/logs-usage-routes.ts
  • src/server/management/route-registry.ts
  • src/server/proxy-liveness.ts
  • src/server/request-log.ts
  • src/server/responses/core.ts
  • src/service.ts
  • src/storage/policy-job.ts
  • src/storage/policy.ts
  • src/usage/log.ts
  • src/usage/summary.ts
  • structure/02_config-and-codex-home.md
  • structure/04_transports-and-sidecars.md
  • structure/08_openai-provider-tiers.md
  • structure/09_client-integrations.md
  • tests/abort-race.test.ts
  • tests/claude-desktop-cli.test.ts
  • tests/claude-outbound.test.ts
  • tests/cli-account-pool-verbs.test.ts
  • tests/cli-account.test.ts
  • tests/cli-capabilities.test.ts
  • tests/cli-dispatch.test.ts
  • tests/cli-dto-fidelity.test.ts
  • tests/cli-json-contract.test.ts
  • tests/cli-native-profile.test.ts
  • tests/cli-storage-inspect.test.ts
  • tests/cli-transport-honesty.test.ts
  • tests/cli-usage-report.test.ts
  • tests/cli-version-skew.test.ts
  • tests/codex-app-server-processes.test.ts
  • tests/codex-transition-state-adoption.test.ts
  • tests/cursor-catalog.test.ts
  • tests/cursor-discovery.test.ts
  • tests/cursor-effort-suffix.test.ts
  • tests/cursor-envelope-echo-retry.test.ts
  • tests/cursor-errors.test.ts
  • tests/cursor-exec-empty-result.test.ts
  • tests/cursor-hardening.test.ts
  • tests/cursor-static-catalog.test.ts
  • tests/cursor-ultra-mode.test.ts
  • tests/cursor-umbrella-rows.test.ts
  • tests/cursor-uncallable-quarantine.test.ts
  • tests/fixtures/compatibility/openai-codex-forward-gpt56-sol-v1.json
  • tests/google-antigravity-replay.test.ts
  • tests/google-antigravity-wire.test.ts
  • tests/google-signature-history-roundtrip.test.ts
  • tests/google-vertex-thought-signature.test.ts
  • tests/helpers/management-route-scan.ts
  • tests/integrations-state.test.ts
  • tests/integrations-writer.test.ts
  • tests/kiro-adapter.test.ts
  • tests/local-management-direct-transport.test.ts
  • tests/management-route-registry.test.ts
  • tests/moonshot-tool-schema.test.ts
  • tests/oauth-account-attribution.test.ts
  • tests/openai-responses-passthrough.test.ts
  • tests/proxy-liveness.test.ts
  • tests/request-log.test.ts
  • tests/responses-routed-web-search-fields.test.ts
  • tests/run-turn-queue.test.ts
  • tests/server-kiro-completion-e2e.test.ts
  • tests/skill-ocx.test.ts
  • tests/storage-policy-config-race.test.ts
  • tests/tool-catalog-nudge.test.ts
  • tests/update-stop-first.test.ts
  • tests/usage-log.test.ts
  • tests/xai-web-search-compat.test.ts

📝 Walkthrough

Walkthrough

The version is bumped to 2.35.0. The changeset adds a generated CLI capability registry, honest exit codes, new ocx verbs, a generated agent skill, a Cursor umbrella model catalog with echo/backlog diagnostics, a Kiro local-terminal short-circuit for delivered final answers, storage-policy metadata-persistence reporting, ownership semantic fingerprints, OAuth per-account usage attribution, and provider compatibility fixes.

Changes

OCX CLI Agentic Control Platform

Layer / File(s) Summary
Capability registry and route inventory
src/cli/capabilities.ts, src/server/management/route-registry.ts, src/cli/help.ts, src/cli/registry.ts, tests/cli-capabilities.test.ts, tests/management-route-registry.test.ts, tests/helpers/management-route-scan.ts
Adds a declared capability table and a management route registry, generates CLI help from the registry, and adds bidirectional reconciliation tests.
Transport honesty and exit-code vocabulary
src/cli/dispatch.ts, src/cli/doctor.ts, src/cli/index.ts, src/cli/runtime-api.ts, src/cli/status.ts, src/cli/version-skew.ts, src/lib/admin-secrets.ts, src/lib/service-secrets.ts, src/service.ts, src/server/proxy-liveness.ts, tests/cli-dispatch.test.ts, tests/cli-transport-honesty.test.ts, tests/cli-json-contract.test.ts
Propagates real exit codes, renders reason/hint on errors, maps 404/409 to exit codes 4/5, refuses an admin-token collision, and reports version skew.
New verbs and DTO fidelity
src/cli/account*.ts, src/cli/access.ts, src/cli/storage.ts, src/cli/inspect.ts, src/cli/system-command.ts, src/cli/observe.ts, src/cli/agent.ts, src/cli/usage-report.ts, tests/cli-account-pool-verbs.test.ts, tests/cli-dto-fidelity.test.ts, tests/cli-storage-inspect.test.ts
Adds account pause/resume/strategy/sticky, storage, inspect, and system verbs; restores paused, five-hour quota, and per-account usage fields dropped by the CLI.
Claude Desktop CLI exit codes
src/cli/claude-desktop.ts, tests/claude-desktop-cli.test.ts
Adds a status subcommand and switches usage errors to exit code 2 via CliUsageError.
Generated agent skill and docs
scripts/generate-ocx-skill-surface.ts, skills/ocx/*, docs-site/.../cli.md, package.json, .github/workflows/ci.yml, AGENTS.md, tests/skill-ocx.test.ts
Generates the ocx capability surface into a repo-owned skill, adds recipes and failure-semantics docs, and gates staleness in CI.
Planning documentation
devlog/_plan/260828_ocx_agentic_control/*
Devlog documents recording the wp1-wp10 design and audit history for this platform.

Estimated code review effort: 4 (Complex) | ~75 minutes

Cursor Adapter Umbrella Catalog and Diagnostics

Layer / File(s) Summary
Umbrella catalog and resolver
src/adapters/cursor/catalog.ts, src/adapters/cursor/discovery.ts, src/adapters/cursor/request-builder.ts, src/codex/catalog/provider-fetch.ts, tests/cursor-catalog.test.ts, tests/cursor-*.test.ts
Introduces a single per-base capability table replacing the effort-map, with byte-equal back-compat resolution and Max Mode evidence gating.
Error precedence and result normalization
src/adapters/cursor/cursor-errors.ts, src/adapters/exec-tool-result-normalize.ts, src/adapters/cursor/tool-result-normalize.ts, src/adapters/cursor/tool-definitions.ts, src/adapters/tool-catalog-nudge.ts, tests/cursor-errors.test.ts
Fixes failed_precondition classification precedence and shares empty/failed exec-result guidance across adapters.
Midstream envelope-echo diagnostics
src/adapters/cursor/envelope-echo.ts, src/adapters/cursor.ts, tests/cursor-envelope-echo-retry.test.ts
Adds a bounded observer that detects mid-stream tool-result echoes and corrupted call ids.
Backlog coalescing
src/adapters/run-turn-queue.ts, tests/run-turn-queue.test.ts, tests/abort-race.test.ts
Coalesces adjacent deltas/heartbeats at push time to stop false backlog-exceeded aborts on slow-but-attached consumers.
Planning documentation
devlog/_plan/260828_cursor_umbrella_catalog/*, devlog/_plan/260828_cursor_ndjson_backlog_train/*
Devlog documents for the catalog design/closure and the backlog-abort RCA/probe/fix rounds.

Estimated code review effort: 4 (Complex) | ~60 minutes

Kiro Local-Terminal Turn Suppression

Layer / File(s) Summary
Adapter contract
src/adapters/base.ts
Adds the optional localTerminal hook and AdapterLocalTerminal interface to ProviderAdapter.
Kiro detection and payload changes
src/adapters/kiro.ts, src/adapters/kiro-constants.ts, tests/kiro-adapter.test.ts
Detects a trailing delivered final answer and implements localTerminal to withhold a repeated inference.
Server integration and usage accounting
src/responses/turn-termination.ts, src/server/responses/core.ts, src/server/request-log.ts, src/usage/log.ts, tests/server-kiro-completion-e2e.test.ts, tests/usage-log.test.ts
Binds turn scope, records delivered answers, returns a zero-usage local response, and marks it not-estimated.
Planning documentation
devlog/_plan/260828_kiro_turn_termination/*
Devlog research, plans, and audit rounds for the fix.

Estimated code review effort: 4 (Complex) | ~40 minutes

Storage Cleanup Policy Metadata Persistence

Layer / File(s) Summary
Metadata commit and error classification
src/storage/policy.ts, src/storage/policy-job.ts, tests/storage-policy-config-race.test.ts, structure/02_config-and-codex-home.md
Adds PolicyMetadataPersistenceError and field-scoped config commits so a metadata write failure does not discard the cleanup outcome.
GUI metadata-save warning
gui/src/pages/Storage.tsx, gui/src/i18n/*, gui/tests/storage-policy-metadata-warning.test.tsx
Surfaces a warning alert when cleanup succeeds but scheduling metadata fails to save.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Ownership Semantic Fingerprinting

Layer / File(s) Summary
Semantic fingerprint helpers
src/integrations/ownership.ts, src/integrations/ownership-policy.ts, src/integrations/state.ts, src/integrations/writer.ts, tests/integrations-state.test.ts, tests/integrations-writer.test.ts, structure/09_client-integrations.md
Adds key-order-independent fingerprints paired with exact fingerprints so reordered JSON keys are not misread as protected edits.

Estimated code review effort: 3 (Moderate) | ~20 minutes

OAuth Account Attribution and Provider Compatibility Fixes

Layer / File(s) Summary
Per-account OAuth attribution
src/codex/account-label.ts, src/providers/label.ts, src/usage/log.ts, src/usage/summary.ts, src/server/responses/core.ts, tests/oauth-account-attribution.test.ts
Adds o<hex6> labels and stamps them at resolve/rotation so xai/cursor spend is attributed per account.
Claude terminal status fix
src/lib/errors.ts, src/claude/outbound.ts, tests/claude-outbound.test.ts
Fixes status-less server_error classification so Claude Code keeps retrying transient failures.
Antigravity signature bypass
src/adapters/google-antigravity-replay.ts, src/adapters/google-antigravity-wire.ts, src/adapters/google.ts, tests/google-antigravity-*.test.ts
Injects and rejects a skip_thought_signature_validator sentinel for Gemini turns lacking a real signature.
Provider request compatibility fixes
src/adapters/openai-chat.ts, src/adapters/openai-responses.ts, src/adapters/xai-web-search.ts, src/oauth/store.ts, src/codex/transition-state.ts, src/compatibility/openai-responses.ts, structure/04_transports-and-sidecars.md, structure/08_openai-provider-tiers.md
Strips prompt_cache_options for canonical forwarding, intersects Moonshot schema bounds, normalizes both xAI Responses hosts, and reports removeCredential outcomes.

Estimated code review effort: 4 (Complex) | ~50 minutes

Historical Merge and Release Process Records

Layer / File(s) Summary
Bug PR merge round, release train, and fix-reimplement records
devlog/_plan/260827_igwanu_bug_pr_merge_round/*, devlog/_plan/260827_release_train/*, devlog/_plan/260828_bugpr_fix_and_reimplement/*
Devlog documents recording prior merge and release rounds, unrelated to code changed elsewhere in this diff.

Estimated code review effort: 1 (Trivial) | ~10 minutes

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/promote-preview-2350

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.

@github-actions github-actions Bot changed the title promote dev onto preview for v2.35.0-preview [WRONG BRANCH] promote dev onto preview for v2.35.0-preview Aug 28, 2026
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • wrong target branch (preview); retarget to dev.

What to do

  • Retarget this PR to dev — all contributions go to dev.

UI screenshot waived by the gui-screenshot-waived label.
Its title has been prefixed with [WRONG BRANCH].
Automatic draft conversion failed (token cannot change draft status). Please convert this pull request to a draft manually. The required enforce-target check will keep failing until every issue above is resolved.

@github-actions
github-actions Bot marked this pull request as draft August 28, 2026 14:20
@lidge-jun
lidge-jun marked this pull request as ready for review August 28, 2026 14:22
@lidge-jun lidge-jun added suppression-approved Maintainer approved a new type or lint suppression gui-screenshot-waived Maintainer waiver for false-positive GUI screenshot requirements labels Aug 28, 2026
@github-actions
github-actions Bot marked this pull request as draft August 28, 2026 14:22

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6667e34517

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

* static `maxModeVerified` gate so ultra generalizes automatically as evidence
* arrives — never from window size (devlog 260828 blocker-4 fold).
*/
let liveCursorMaxModeBases: ReadonlySet<string> = new Set();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope live Max Mode evidence to each Cursor provider

When multiple Cursor providers or failover accounts expose different maxModeModels, this process-global set makes request behavior depend on whichever discovery completed last: provider-fetch.ts records one authenticated provider's roster here, while every later resolveCursorSelection reads the same value. An Ultra request can therefore enable unsupported Max Mode for another account, or lose valid evidence after another provider reports none. Store this evidence per provider/account and resolve requests against the selected credential's discovery result.

Useful? React with 👍 / 👎.

Comment thread src/providers/label.ts
Comment on lines +49 to +50
const base = baseProviderLabel(providerName);
if (base === "openai" || base === "anthropic") return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve configured provider identity in OAuth account labels

For a custom OAuth provider whose valid name ends in -main or -p<hex6>, baseProviderLabel strips that suffix before hashing; two distinct configured providers using the same upstream account ID then receive the same label and are merged by usage/summary.ts, which keys account rows only by label. Names such as anthropic-main are worse because the normalized value triggers the attribution skip entirely. Hash the actual configured provider identity, applying only explicit built-in aliases rather than legacy persisted-log normalization.

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun marked this pull request as ready for review August 28, 2026 14:35
@lidge-jun
lidge-jun merged commit e0e3234 into preview Aug 28, 2026
27 of 33 checks passed
@lidge-jun
lidge-jun deleted the codex/promote-preview-2350 branch August 28, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gui-screenshot-waived Maintainer waiver for false-positive GUI screenshot requirements suppression-approved Maintainer approved a new type or lint suppression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants