Conversation
…ng for API-key preset The `commandcode` (API-key) registry entry was missing the official model-profile reasoning-facts table that the OAuth `command-code` entry carries. Two symptoms followed: 1. The Codex catalog advertised no reasoning levels for API-key models (deepseek-v4-flash/pro, GLM-5.x), so clients forced effort none and requests failed with 400 'messages.content.type is invalid'. 2. The router's known-ids decode source missed the native slash ids, so Codex-facing slugs like `commandcode/deepseek-deepseek-v4-pro` were forwarded upstream verbatim and rejected with 400 'unsupported_model'. Also fix the GLM table keys to match the exact upstream ids (`zai-org/GLM-5.3` not `zai-org/glm-5.3`) and make the effort lookup case-insensitive, and add GLM-5 / 5.1 / 5.2-Fast (verified high/max from their official profiles).
…ical behavior Phase A1 of the FastWire umbrella (#1886): FastWire descriptors, FastPolicyAuthority capture with a precomputed modelWireOverrideAllowed guard, the shared resolveFastPolicy() four-level adapter resolver with capability/eligibility separation, and the TierDecision state machine. Fast-mode injection no longer mutates parsed._rawBody; the Responses adapter applies the settled decision to a detached outbound body. The legacy Chat serializer gate survives as legacyChatEligibility() until the B1 migration, and no registry provider declares a descriptor yet, so outbound wire bytes and catalog bytes are unchanged. The A0 characterization suites pass unmodified except the raw-body observation test whose A0 comment scheduled this exact update. Full suite at this commit: 12970 pass / 10 skip / 0 fail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…conversion chatCompletionsToResponsesBody dropped service_tier, so a tier declared by a /v1/chat/completions caller vanished before the responses pipeline could see it — fast override, the capability gate, and serialization all behaved as if no tier was requested, while the same request through /v1/responses worked. Copy the field under the converter's existing optional-scalar convention and let the downstream pipeline keep owning the semantics. Flips the A0 known-bug characterization for this exact behavior (FastWire umbrella #1886, independent bug-fix unit), and adds converter + end-to-end regressions including the fail-closed strip on a supportsServiceTier:false route. Full suite at this commit: 12750 pass / 10 skip / 0 fail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
806/806 green locally is not the same as stable for a Windows user, and the reason is structural: platform-windows is gated on workflow_dispatch (.github/workflows/ci.yml:547-552), the aggregation job accepts skipped, and release.yml:181-201 asks for a push-event CI run that Windows never joins. Every release so far published without executing a Windows test. Three independent Pro audits were run against a zip of the v2.24.2 tree with the GitHub connector, on orthogonal briefs: platform primitives, runtime and distribution, and user-visible failure modes plus CI coverage. Every finding carried into this unit was reproduced against the working tree in the same session; the rest were dropped, including two that turned out to be already fixed. Seven verified findings, nine dependency-ordered phases. The sharpest one is src/service.ts:2361, which uses the exact PowerShell argv that src/codex/user-identity.ts:222-224 forbids under #1589 -- it survived because the regression test at tests/windows-deploy-close-regressions.test.ts:43 is bound to src/update/job.ts alone. The icacls/CIM request-path latency class is deliberately excluded: both audits rank it first, but this session measured nothing, and an unverified claim next to seven verified ones devalues all of them. It is recorded at the end of 001 so the next cycle inherits it. No production code changes.
Round r1-20260817113441 returned FAIL with six blockers and four citation defects. All verified before acting on them; the auditor was right on every one. Citations corrected in 001: the updater's bare -like match is job.ts:1381 not :1377, and the service copy's token boundaries are service.ts:2350-2355 not :2330. Two overstatements withdrawn -- "every release ran zero Windows tests" ignores that service-shaped releases separately require service-lifecycle.yml, which does include a Windows job (release.yml:224-234); and service.ts:1983 argues the ACL-is-authoritative principle for an elevation staging directory, not for credential writers generally. Sequencing (002) was mostly wrong and is rewritten. Only 030->031 and 060 stage 1->070 are structural; the rest was file-overlap. 010->020 was backwards, 050->051 was fake and had produced an impossible verification claim, and "everything before 060" was false since nothing here makes the suite red today. 060 stage 1 and 051 now start immediately. 040 no longer writes a redacted table or a scratch pointer when it finds an exposure -- per AGENTS.md that is itself disclosure. It goes silent in the tree and reports verbally. 060 stops claiming a merge gate it cannot create: dev has no branch protection (MAINTAINERS.md:121,125). Stage 3 is a convention gate, stage 4 is the real one because release.yml reads conclusions directly. The hosted-vs-self-hosted contradiction is now a decision to make before stage 1 rather than two incompatible sentences. 031 names its module, counters, retrieval path and the constraint that the publisher label can never be path-derived. 030 specifies a new neutral module because config.ts:47 already imports config-ownership, so the obvious placement would close a cycle. 050 picks 600s and spells out the %TIME% midnight-wrap and space-pad traps. 080 drops to five implementable jobs plus two marked investigate-and-record. Missing verification commands added: typecheck on the source phases, full suite on 030, prepush on the workflow phases per .github/AGENTS.md:25.
Round r2 returned FAIL with five findings. Verified each; all five were real. The 050 %TIME% advice was wrong in a way that would have failed at runtime. set /a reads a leading zero as octal, so a minute or second component of 08 or 09 is a hard error -- confirmed on this machine, "set /a a=08" prints Invalid number. The doc now names four traps (octal, space padding, midnight wrap, delayed expansion inside the parenthesized branch given the wrapper's plain setlocal at service.ts:1522) and promotes the state-file approach from fallback to recommendation, since four traps plus an expansion-mode change is not arithmetic worth defending in review. 080's self-update job was not testing self-update. ocx update resolves its target from the registry (update/index.ts:167) and installs a resolved version (update/index.ts:106); there is no tarball injection seam, so a locally packed candidate cannot drive the real command. Renamed to a package replacement smoke, which is still worth having because npm replacing a live global install is the step that produced #1849, and the doc now says what covering ocx update would actually require. 031 named a producer but not a consumer. It now names system-routes.ts:49, a sibling endpoint rather than appending to /api/system/memory, the response shape, and the route test. 060 no longer says stage 1 depends on nothing and then gates it on the runner decision. Option 2 is downgraded honestly: the cleanup step removes stale checkout files, not installed services or registry state, which this product writes as normal behavior. Option 1 is the only one that closes the contradiction. Phase headers on 020, 050 and 080 still claimed dependencies that 002 had already withdrawn. Synchronized, and 002 now states it is authoritative on what is structural.
tests/system-routes.test.ts does not exist -- confirmed. Current handleSystemRoutes coverage sits in memory-watchdog.test.ts:171 and codex-restart-route.test.ts:11. 031 now says to create the file rather than extend it, and its verify block runs it. The state file was oversold in 050. It removes the delayed-expansion problem because the counter is read fresh each iteration, but it does not remove the elapsed-time arithmetic: a stored start timestamp still has to be parsed and subtracted, so the octal, padding and midnight-wrap rules apply either way. The doc now splits it -- state file for the retry counter, documented arithmetic for the 600s uptime reset -- and names the file's own questions: location, what happens when the write fails (treat as a fresh counter, never fail the restart), and removal on uninstall alongside the wrapper and launcher.
Independent final-gate review returned FAIL with three findings and three citation corrections. All verified; all correct. 031 claimed privacy:scan enforces the fixed-literal publisher label. It does not -- privacy-scan.ts:187 is a textual scanner over file content matching home paths, emails and token shapes, and it cannot see that a runtime value was path-derived. Replaced with a closed union type so a path-derived string fails typecheck instead, plus a test asserting the snapshot keys are a subset of it. privacy:scan stays in the verify block as a backstop, not as the mechanism. 031 also claimed CI would assert the counters stay zero across the Windows suite. The counters are process-local and the suite runs across four sharded runners in many short-lived processes with no endpoint to query, so that assertion needs a suite finalizer and a collection step -- a design of its own. The CI claim is withdrawn rather than left as an instruction nobody could follow, and the evidence section now says plainly that local runs and voluntary bug reports are the only sources. 040's seed list missed config.ts:3937, the invalid-config backup, which copies the whole config including any secrets in it. Added, and the phase now says to re-derive every chmodSync call rather than trust the seeds -- an incomplete seed list is exactly the false negative that phase exists to avoid. Citations: the updater's bare -like match is job.ts:1383 not :1381; the skipped allowance is the jq filter at ci.yml:769-772; release.yml service enforcement runs to :241 with the failure at 235-239.
3937 declares backupInvalidConfig; the chmodSync(backupPath, 0o600) call is line 3942. Corrected in 001 F5 and in 040's seed list, with the declaring function named so the citation stays legible if the file shifts.
src/service.ts spawned PowerShell with "-WindowStyle", "Hidden" as argv elements. src/codex/user-identity.ts:222-224 already forbids exactly that: Bun 1.3.14 can fail the direct CLI pair before the command runs (#1589), and the process-level windowsHide flag is what actually suppresses the window. The call is not decorative. stopServiceIfInstalled() uses it because schtasks /end can leave the wscript.exe/cmd.exe wrapper alive to respawn the proxy, and it ignores spawnSync's exit status. Under #1589 wrapper termination silently does nothing, so ocx stop, restart and update report success without sticking. The invariant survived as prose plus a single-file assertion: tests/windows-deploy-close-regressions.test.ts:43 pins the argv only for src/update/job.ts (the variable is bound at line 13), so service.ts was never covered. Replaced with a sweep over every src/**/*.ts, which found exactly one offender. The sweep matches the argv form only. Six call sites legitimately pass -WindowStyle Hidden inside a PowerShell script string handed to Start-Process (windows-elevation.ts 622/660/687/736, tray/windows.ts:489, update/job.ts:574); Bun never parses those. A second test pins that discrimination in both directions so the sweep cannot quietly become vacuous or start failing correct code. Driven red first: the sweep reported ["service.ts"] before the fix. Verification: bun test tests/windows-popup-fix.test.ts (7 pass), bun test tests/service.test.ts tests/windows-deploy-close-regressions.test.ts (131 pass), bun run typecheck clean.
Grok CLI was pinned to api_backend = "chat_completions" because opencodex emitted response.heartbeat as a typed SSE event. That is not a valid Responses variant, so Grok-build's strict enum deserializer crashed with "unknown variant response.heartbeat". The keep-alive now emits an SSE comment line instead, which re-arms the idle timer without triggering deserialization on any client. With heartbeats fixed, Grok can finally use the Responses passthrough path. This gives Grok clients the same protocol fidelity Codex already enjoys and removes the chat to responses translation layer from the hot path. Some upstream relays (e.g. sub2api) omit annotations on output_text content parts even though the Responses spec marks it as a required Vec field. Strict clients, including Grok-build's async-openai fork, fail with "missing field annotations". A new stateless SSE/JSON backfill adds annotations: [] on any output_text part that lacks it, on both the streaming and bounded-JSON passthrough paths. The rewrite is unconditional and safe for all clients because the field is always valid on the wire. The /v1/responses handler now surfaces grok-tagged requests as surface=grok in the log context, matching the chat-completions handler. Stale comments referencing grok-build's decoder and the old chat_completions pin have been corrected in the tests.
src/service.ts and src/update/job.ts each carried a copy of the same teardown logic, and the copies drifted in both directions. service.ts matched canonical full paths as complete command-line tokens; update/job.ts matched the bare filenames with -like '*name*'. Meanwhile update/job.ts had received the #1589 argv cleanup that service.ts had not. The bare-filename matcher is the defect. Two OpenCodex homes under one Windows account means a dashboard update for home A can force-terminate home B's scheduler wrapper, and any unrelated process whose command line contains either filename matches as well. Extracted the service.ts implementation, which was the correct one, into src/lib/windows-service-wrappers.ts and pointed both callers at it. The updater now passes its own config dir instead of bare names. windowsWrapperKillScript is exported because the matching rule is the entire point of the module and the spawn reports nothing: the script it builds is the only observable surface, which is the same source-level convention windows-deploy-close-regressions.test.ts already uses. New tests/windows-service-wrappers.test.ts pins that another home's path is not among the patterns, that matching is token-bounded rather than substring, that the caller excludes itself, and that neither file keeps a private matcher. The last assertion was driven red first: both files failed it before the extraction. windows-deploy-close-regressions.test.ts asserted "$_.ProcessId -eq $PID" against update/job.ts. That string moved, so the assertion follows it to the shared module rather than being dropped. Verification: bun test over service, windows-deploy-close-regressions, windows-popup-fix and the new file (143 pass), bun run typecheck clean.
Keep-alives are now SSE comment lines (': opencodex heartbeat') instead of
response.heartbeat events, so the bridge-lifecycle RC3 test and the transport
architecture docs no longer describe a parser-ignored response.heartbeat event.
The grok-build guides' api_backend examples were still chat_completions; they
now match the Responses backend the proxy emits.
…er owning it src/config.ts:102-123 knew that Windows can refuse rename with EBUSY, EPERM or EACCES while a scanner or sync client still holds the target, and retried twice (25ms then 50ms). Nothing else did. Eight durable publishers called renameSync directly: - src/codex/prompt-journal.ts, whose journal carries full config.toml bytes -- losing that publish is what breaks journal restore; - src/lib/config-ownership.ts, the uninstall manifest; - src/claude/agents-inject.ts, the generated agent definitions; - src/lab/automation/persistence.ts and config-persistence.ts; - src/lab/ledger/purge.ts, the rewritten ledger; - src/storage/cleanup.ts, both the satellite backup (1094) and the restore-pending state file (2438); - src/tray/windows.ts, the tray's owned-file publisher. None corrupts anything on failure; they throw rather than publish a partial file. But under a real-time scanner holding the target they turn a momentary hold into a user-visible failure, and the tolerance to survive it already existed one module away. Three renameSync calls in src/storage/cleanup.ts are deliberately NOT converted. 1639 and 2616 move directories between staging and trash, and 1667 moves one back on rollback: these relocate a directory rather than publishing a temp file over a destination. Windows directory-move failures are a different problem with a different fix, and the callers already handle them. The loop moves to src/lib/windows-atomic-replace.ts rather than becoming an export of config.ts: config-ownership.ts is one of the callers and config.ts already imports config-ownership.ts, so the obvious placement would close an import cycle. config.ts re-exports renameAtomicFile because callers use it; renameAtomicFileAsync stays internal, as it was before. The envelope is unchanged at two retries, and tests/windows-atomic-replace.test.ts now pins it: which codes count as transient, that POSIX never retries, and that the bound is two rather than hopeful. The extracted module had no test of its own before -- config.test.ts exercises it only through atomicWriteFile -- so an accidental widening would have gone unnoticed. That matters because the next commit adds counters specifically to decide whether widening is justified. Verification: bun run typecheck clean; bun test over claude-agents-inject, windows-atomic-replace, config, storage-cleanup and windows-tray.
…gued from evidence Both plan audits wanted the 75ms retry envelope widened. Neither could show it failing in the field, and one explicitly declined to raise its severity for exactly that reason. Counting is the honest next step: if these stay at zero across a release the envelope is fine, and if they do not, the change cites numbers. Counters are keyed by publisher AND error code. The code is the diagnostic half: EBUSY from a scanner, EACCES from a permissions problem and EPERM from a lock are three different stories, and collapsing them would leave the counters unable to answer the question they exist for. Separate retried and exhausted totals per key. Exposed as GET /api/system/windows-replace-retries -- a sibling of /api/system/memory rather than a field on it, because that payload is memory-shaped and appending filesystem counters would make both harder to consume. The publisher label is a closed union (ReplacePublisher), and that union is the privacy enforcement, not privacy:scan. The scanner reads file text (scripts/privacy-scan.ts:187) and cannot tell that a runtime string came from a path -- a path could carry a username. A closed union makes the same mistake a typecheck failure instead, which the new test pins with @ts-expect-error. Every converted publisher carries its own label, so nothing reports under the "config" default it does not belong to: storage-cleanup for the satellite backup replace and tray for the tray's owned-file publisher, alongside the four from the previous commit. Scope note, stated because the plan originally overreached here: these counters are process-local. Asserting they stay zero across the Windows suite would need a finalizer aggregating many short-lived sharded processes, which does not exist. Evidence comes from local runs and voluntary bug reports. tests/system-routes.test.ts is new; handleSystemRoutes coverage previously lived scattered in memory-watchdog.test.ts and codex-restart-route.test.ts. Verification: bun test tests/system-routes.test.ts (10 pass), bun test tests/windows-atomic-replace.test.ts (8 pass), bun run typecheck clean, bun run privacy:scan passed.
Six blockers across three rounds, all verified before acting. The two worth remembering: the counters first collapsed three error codes into one number, and the wrapper tests asserted the generated script contained IndexOf/before/after - which a broken substring matcher would also satisfy. Also records that 030's instruction to sweep for remaining renameSync calls read as complete and was not: six more publishers were left behind, two of them found only in the second review round.
The public OAuth error projection from #1842 collapsed the fixed reauth-identity remediation messages (identity mismatch, unverifiable legacy identity) into the generic authentication failure, so the dashboard could no longer tell the user to sign in with the selected account. Represent both outcomes as bounded typed errors (OAuthReauthIdentityMismatchError, OAuthReauthIdentityUnverifiedError) whose messages carry no account, token, or email data, allowlist them in publicOAuthAuthenticationErrorMessage, and cover them in the projector allowlist and management status-polling regressions. Resolves the unresolved P2 review on #1842. Credit: original redaction work by @luvs01 in #1842.
fix(oauth): redact public authentication errors (#1842)
structure/04's native Chat passthrough paragraph claimed canonical Fast follows the resolved Fast policy without chatServiceTier. The code does not do that: chat-native.ts has no tier wiring at all — tier resolution lives only in the Responses pipeline feeding adapter buildRequest, which the passthrough bypasses. On the native path every caller service_tier is forwarded raw and only under chatServiceTier: true, and fastMode injects nothing (openai-chat.ts:121, chat-native.ts:54-72). Campaign wp9 docs-drift fix; provenance and code-verification record in devlog/_plan/260818_bug_pr_resolution/040.
docs(structure): scope canonical Fast injection to the bridged Chat path
- 090 (windows program): transactional update with rollback — stage-to-side, verification manifest, swap protocol, failure-mode table, boot probe (#1942/#1849 remaining half, diff-level). - 051 (campaign): thought-signature credential scope (keyFor v4 + preferred discriminator) and the six-site emit-after-commit barrier design (#1926 remaining half, diff-level). - 010-050: campaign phase records (WP-V audit, wp6 redesigns, wp7 dispositions, wp9 sweep + docs drift, wp10 plan), all audit-reviewed.
docs(devlog): wp10 follow-up designs + campaign phase records
24/24 disposition rows terminal or recorded-hold; WP-V stabilization (12 failures fixed forward, no reverts), wp6/wp7/wp9/wp10 evidence, and the wp11 outcome ledger with final gates (lidge decisive run 13316 pass / 0 fail on a5ec641; push CI success on e446607 with docs-only delta). Security gate: prior public disclosure via issue #1926 (r10-corrected rationale).
docs(devlog): close the 260818 bug-PR resolution campaign to _fin
feat(clients): ZCode (Z.ai desktop client) managed integration — ocx zcode
#1897 inserted a CCA display-name slug at the TOP of pickerModelIdForDiscoveredWireId, which returned before every collapse rule below it. CCA labels a tier row "Gemini 3.7 Flash (High)", and that slugs to `gemini-3.7-flash-high` — so the picker published one row per reasoning tier, none of which is a key in ANTIGRAVITY_MODEL_EFFORTS. The effort ladder was never deleted; nothing could reach it anymore, and live discovery stamped the split rows with `reasoningEfforts: []`. Demote the label to a last resort and guard it with collapsesIntoKnownPickerModel: a slug that reduces to a known base model's tier is a rung, not a model. The label still resolves an id Google renamed on the wire while keeping a stable public name, which is the one case no other rule can answer. Restore two suppressions the same commit dropped: - Compatibility aliases must not be admitted as independently discovered rows. CCA still serves retired 3.5/3.6 Flash tiers, so admitting them republishes exactly the dead ids the alias map exists to retire. - `gemini-3.1-flash-image` is agent-callable but grouped under image generation, so it never appears in agentModelSorts and needs its explicit registration back. Also stop live discovery from overriding a model that owns an effort ladder. A collapsed row reports ONE representative wire id, so it can name a rung but never a ladder: gemini-3.1-pro low and high both collapsed onto gemini-pro-agent, and gemini-3.7-flash sent thinkingLevel=low against the `-high` wire id — a request that contradicts itself. Verified against the live CCA :fetchAvailableModels response, not a fixture: 3.7 Flash low/medium/high each reach gemini-3.7-flash-tiered with the matching thinkingLevel, 3.1 Pro splits low/high across its two wire ids, and a saved gemini-3.6-flash-high still routes to 3.7 carrying its tier. The PR had frozen the regression into three tests (including renaming "stale discovery cannot republish" to "live discovery follows"), so those expectations are restored to the original contract.
Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
…ever survives nameless (#2061) * fix(codex): strip app-rewritten provider sub-tables so the provider never survives nameless A Codex app config rewrite re-serializes the provider's inline env_http_headers table into a separate [model_providers.opencodex.env_http_headers] sub-table. removeOcxSection() ends its removal scope at the next table header, and the sub-table header is one, so an inject/restore removed the main table (with name/base_url) but kept the sub-table: model_providers.opencodex then exists with only env_http_headers and no name, and Codex rejects the whole config on startup ('provider name must not be empty'). Because every cleanup guard matched the exact string '[model_providers.opencodex]', the orphan never matched again, was journaled as baseline, and re-persisted on every inject/restore cycle. Recognize [model_providers.opencodex.*] sub-table headers in removeOcxSection() and in the three cleanup guards (injectCodexConfig, stripOpencodexConfigResult, hasOpencodexRouting) so the main table and its sub-tables are removed together. A user's similarly named table ([model_providers.opencodex_backup]) stays out of scope: the match requires the 'opencodex.' dot boundary. * fix(codex): recognize trailing comments on provider table headers TOML v1.0 permits a comment after a table header, so [model_providers.opencodex] # comment is a valid root header form. The exact string compare missed it, skipping provider cleanup on inject and restore and letting hasOpencodexRouting miss the table. Match the root header by regex with optional whitespace + trailing comment; the sub-table prefix check already tolerates trailing comments by construction. Regression tests for commented root and sub-table headers. --------- Co-authored-by: jzli <jzli@alauda.io>
Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
…t wire (#2042) `noStructuredOutputModels` is documented, in every locale, as "Exact model IDs whose `openai-chat` endpoint rejects `response_format`. Only an exact requested-model match omits the field; structured-output translation stays enabled for every other `openai-chat` model." The Responses ingress enforces that, and tests/openai-chat-hardening.test.ts already pins a `:tag` sibling keeping the field there. The native Chat passthrough added in #1467 matched through `modelInList` instead, which also matches the pre-colon prefix. On a provider that serves Ollama-style tags -- ollama-cloud ships `gpt-oss:120b`, `qwen3-coder:480b`, `qwen3.5:397b`, `gemma4:31b` -- a `noStructuredOutputModels: ["gpt-oss"]` entry therefore stripped `response_format` from `gpt-oss:120b` on /v1/chat/completions while /v1/responses kept it. The caller asked for JSON and silently got prose, on a model the operator never opted out. That is the failure #1424 called out when it chose the exact boundary: a wider match "would silently return prose for siblings that support JSON Schema". The sibling gates on the lines above keep `modelInList` -- `noVisionModels` is documented as tolerating an Ollama `:size` tag, this one is not -- so the comment now says why this gate differs. Four tests, next to the existing Responses-side assertions so the two ingresses read as a pair: exact id opts out, a `:tag` sibling does not, the full `:tag` id does when listed, and an unrelated model is untouched. The `:tag` sibling case fails on current dev. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…them (#2059) `resolveProductionBehaviorValues` is documented as "authoritative effective values emitted by the production route/model/adapter resolver", and its hash is the behavior fingerprint that keys Lab evidence. Ten of its rows are membership tests over the provider's `no*Models`-style lists, and they went through a local function includesModel(list, modelId) { return Array.isArray(list) && list.includes(modelId); } while every runtime gate those rows describe matches through `modelInList`, which also accepts a bare entry for a tagged id. ollama-cloud serves `gpt-oss:120b`, `qwen3-coder:480b`, `qwen3.5:397b` and `gemma4:31b`, and the same registry row writes the bare `gpt-oss` into noVisionModels -- the bare-prefix form is how these lists are meant to be written. So with `noTemperatureModels: ["gpt-oss"]` the adapter omitted temperature from the request for `gpt-oss:120b` while the report said `sampling.omitTemperature: false`. Same for omitTopP, omitPenalties, reasoning.budgetMode, reasoning.splitMode, reasoning.toggleMode, reasoning.supported, reasoning.replayMode's two flags, and tools.choiceRestrictions. The blast radius is confined to subjects that were being described wrongly. Fingerprints measured on the same config, before and after: gpt-oss:120b 54154e19bd2c8ee4 -> 45a73577c5c257f9 (was wrong) gpt-oss 45a73577c5c257f9 -> 45a73577c5c257f9 (unchanged) glm-5.3 54154e19bd2c8ee4 -> 54154e19bd2c8ee4 (unchanged) Note the first line against the third: a model whose sampling gates were applied hashed identically to one where they were not. It now hashes with `gpt-oss`, the id it actually behaves like. Recorded evidence for unaffected subjects keeps its fingerprint, so `resolverVersion` is left at 2 rather than invalidating every recorded subject globally -- say the word if you would rather draw a clean generation boundary and I will bump it. Tests assert the wire the adapter really builds first, then hold the report to that same wire, so the pair cannot drift apart silently. The five report cases fail on current dev. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
* feat(cursor): add HTTP/1.1 compatibility transport * feat(gui): expose Cursor HTTP transport setting * fix(cursor): harden HTTP/1.1 failure handling * fix(http): reject unsupported version pin targets * fix(cursor): close HTTP/1.1 transport races * fix(cursor): pace HTTP/1.1 compatibility requests * docs(cursor): document HTTP/1.1 alias
Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
…#2076) * docs(devlog): 260819 triage-execution campaign roadmap (r2-audited) * fix(codex): emit modern env_key admission line on non-loopback inject codex-cli 0.146+ documents env_key as the bearer auth form for custom model providers: it sends Authorization: Bearer $VAR and hard-errors on a missing variable instead of silently omitting the header. The server side already admits and substitutes this bearer (#1686 chain), so the injector was the stale half. Loopback Design B still emits no auth line; the #2061 orphan sub-table cleanup keeps handling historic env_http_headers configs. Closes #2073 * test(codex): pin non-loopback legacy env_http_headers -> env_key upgrade
…d persist visibility (#2078) * docs(devlog): #2064 RCA — fixed on dev by #2016, version-ancestry proven * fix(replay): scope durable thought signatures per credential and bound persist visibility The durable thought-signature store keyed entries by thread + destination + model but not by credential, so account A's Gemini signatures could replay under account B on the same destination (#1926 gap 1). Keys now carry a salted-HMAC credential identity (installation-local salt persisted beside the store, full 256-bit output — never an unsalted digest of key material) derived from the persisted OAuth account-slot id, the API key, or the Codex account handle; a scope that cannot produce one fails closed instead of sharing a durable slot. STORE_VERSION 3 -> 4 drops old rows on load (not upgradable — no credential info was recorded). Gap 2: terminal frames could become externally visible before the queued signature persist settled. All async terminal paths (completed, truncation and adapter-EOF incompletes, failed) and both buffered JSON returns now await a bounded (250ms) durability barrier; the sync stall-timeout kill path keeps the pre-existing best-effort behavior, documented in place. Closes #1926 * fix(replay): wire durable lookup at adapter serialization; trust only pool account handles; harden salt perms Security-review fold-back: (1) the parser's durable lookup ran before the credential scope was bound, so the store was write-only at runtime — the google adapter now falls back to the durable store at serialization time, when the scope identity exists (regression-pinned); (2) the codex-forward durable handle no longer accepts the client-supplied chatgpt-account-id header (trusted pool context only; direct-forward fails closed); (3) the salt file re-asserts 0600 on every load.
…2079) * fix(update): stage, verify, and swap npm self-updates with rollback The npm self-update installed straight into the live global tree, so any failure after npm removed the old files left a file-less package skeleton with no recovery (#1849) and nothing verified the new tree before it went live (#1942). The launcher now stages the target version into a sibling directory (npm --prefix, same volume), verifies a manifest inside the stage (package.json version, launcher integrity, sentinel deps), moves live aside to a sibling backup, swaps the staged tree in, re-verifies, and rolls back by reverse rename on failure — writing a recovery marker with a one-line restore on double fault. A boot probe restores the newest backup over a broken live tree (power loss mid-swap) and reaps stale backups once live verifies. Staging and backup are SIBLINGS of the package dir, never children — a child would travel with the live rename and the live tree cannot move into its own subtree (design defect caught by the plan audit and fixed here). Closes #1942 Closes #1849 * fix(update): harden transactional swap per adversarial review - verify the bundled Bun binary by size (>=10MB), not just its package.json, so the boot probe can never call a Bun-less tree healthy and reap the only backup - bounded EPERM/EBUSY/EACCES rename retry for the Windows AV/indexer class - staging/backup mkdir failures return phase errors with live untouched; an unexpected throw now reports and stops instead of falling back to the destructive in-place install - the Windows service wrapper (outside the package tree) restores the newest .ocx-backup-* sibling before declaring the install incomplete — the exact power-loss window the in-launcher probe cannot reach
… contract (#2081) * docs(devlog): 260819 triage-execution campaign records (010/020/030) * test(update): re-pin launcher invariants to the transactional install contract #2079 replaced the direct global npm install spawn with the staged transactionalNpmUpdate call, breaking three source-invariant pins that anchored on the removed spawn line (dev-head CI run 32204396229). The invariants themselves still hold — stop precedes the destructive step, the history-restore warning precedes it, and every npm spawn goes through the hardened npmInvocation resolver — so the pins now anchor on the transactional call and the runNpm callback's invocation spawn.
…ge (#2087) Release-audit blocker: local-style npm install hoists bun/zod to stageRoot/node_modules, so the swapped-in package directory shipped without its dependencies and every post-upgrade 'ocx update' failed verification. Stage with npm install -g --prefix instead — the global layout nests dependencies inside the package exactly like the live tree — and resolve the staged path across the POSIX lib/node_modules and Windows node_modules shapes. Proven against the real registry: staging @bitkyc08/opencodex@2.25.0 verifies and swaps end-to-end with dependencies inside the package.
|
Important Review skippedToo many files! This PR contains 407 files, which is 107 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (407)
You can disable this status message by setting the 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. Comment |
|
✅ Deterministic PR hygiene checks passed. |
⏳ DRAFT
What to do
Its title has been prefixed with |
Promote dev (f2e5e76) to main for the 2.26.0 stable release.
Validation: lidge full isolate suite 13393/0 on the campaign head; Cross-platform CI green; 3-lane release-regression audit clear after the staging-hoist fix (#2087); preview 2.26.0-preview.20260819 published from this source tree (release run 32211336930).
Release: bun scripts/release.ts 2.26.0 --publish (OIDC).