chore(upstream): sync stable v1.24.0 - #39
Merged
Merged
Conversation
* fix(proxy): durably recover HTTP bridge operations * fix(proxy): close durable bridge recovery review findings * fix(proxy): harden transcript retention and replay spools * fix(proxy): preserve operation ambiguity across bridge failures * fix(proxy): fence cross-session operation retention * fix(proxy): gate indefinite recovery streams * fix(proxy): preserve recovery event continuity * fix(proxy): harden durable recovery fencing * fix(proxy): finish terminal recovery handling * fix(proxy): fence cleanup health and frame size * fix(proxy): fence active same-session recovery * fix(http-bridge): close recovery ledger review gaps * test(http-bridge): cover terminal spools and migration round trips * fix(http-bridge): gate recovery and preflight metadata * fix(http-bridge): clean up pre-dispatch and abandoned spools * fix(http-bridge): settle replay reservations * fix(http-bridge): reserve operation metadata * fix(http-bridge): normalize reserved identity before hashing * fix(http-bridge): fence local recovery replay * fix(http-bridge): retain durable recovery fences * fix(http-bridge): discard detached operation contexts * fix(http-bridge): preserve same-request recovery operation * fix(http-bridge): complete recovery validation paths * fix(http-bridge): normalize account-neutral operation fingerprints * fix(http-bridge): retain rebind fences across retries * fix(http-bridge): hand off recovery operation leases * fix(http-bridge): fence ambiguous recovery attempts * fix(http-bridge): claim one-shot recovery operations * fix(http-bridge): spool detached terminal events * fix(http-bridge): re-fence recovery race paths * fix(http-bridge): restore security retry fences * fix(cleanup): retain operation transcripts when sticky cleanup is disabled * fix(http-bridge): mark security retry dispatch after preflight * fix(ci): restore hard bridge recovery pipeline * fix(websocket): keep downstream alive during terminal relay * test(websocket): leave margin for delayed upstream events * fix(db): converge recovery migration with latest main * fix(proxy): detach retained sessions across process epochs * fix(proxy): detach retained rows from prior process epoch * fix(proxy): normalize lease timestamps during recovery handoff * fix(http-bridge): persist one-shot recovery budget * fix(bridge): recover startup disconnects safely * fix(bridge): preserve rollback before frame dispatch * fix(bridge): normalize lease timestamps at lookup boundary * fix(bridge): retain recovery owner through replay handoff * fix(bridge): fence ambiguous operation before cleanup * fix(bridge): refund stale replay claims before dispatch * fix(bridge): recover consumed checkpoints safely * fix(bridge): terminalize recovery admission failures * fix(bridge): fence pre-admission terminal recovery * fix(bridge): keep failed rows fenced when spool overflows * fix(bridge): recover stale owners before continuation admission * fix(bridge): fence hard turn-state recovery retries * fix(websocket): bound upstream cleanup close * fix(bridge): advance repeated hard turns * fix(bridge): walk repeated hard-turn chain * test(bridge): cover repeated hard turns * fix(bridge): preserve incomplete hard turns * test(bridge): isolate reconnect affinity key * test(bridge): align reconnect fixture with ring identity * fix(bridge): continue race-path hard-turn chain * fix(bridge): retain anchored hard-turn retry body * fix(bridge): mark fallback anchor as injected * test(bridge): keep quarantine fixture on ring instance * test(bridge): align unsafe resend fixture identity * fix(proxy): damp stale websocket anchor retries * fix(proxy): isolate stale anchor cache per service * fix(proxy): revalidate stale owners across replicas * fix(bridge): settle terminal recovery outcomes * fix(bridge): fence discarded event spools * fix(bridge): preserve consumed recovery checkpoints * ci: rerun flaky integration bridge * fix(shutdown): bound recovery settlement pre-drain * fix(bridge): close remaining Codex review gaps * fix(websocket): cancel timed-out upstream closes * fix(websocket): cancel closes with no drain budget * fix(bridge): preserve acknowledged recovery state * fix(bridge): defer recovery until parent proof * test(bridge): cover recovery parent proof path * test(bridge): exercise durable parent advancement * fix(bridge): roll back unsent durable operations * fix(bridge): clear rolled back operation identity * fix(bridge): retain advanced body across capacity retries * fix(bridge): clear restored operation identity * test(bridge): cover unknown reclaim capacity retry * fix(db): merge recovery and upstream migration heads * test(bridge): assert reclaim precedes dispatch * fix(bridge): persist terminal state before spool finalization * fix(bridge): make takeover and terminal spooling recoverable * fix(bridge): mark atomic terminal spools replayable * fix(bridge): preserve dropped spool safety on takeover * fix(bridge): settle dropped terminal operations * test(bridge): cover dropped failure settlement --------- Co-authored-by: Syakur Rahman <shaqman2004@yahoo.com> Co-authored-by: shaqman <shaqman@users.noreply.github.com> Co-authored-by: Codex <codex@localhost> Co-authored-by: Darafei Praliaskouski <me@komzpa.net>
…1677) * fix(http-bridge): keep idle retirements out of retry circuit Advance a hard-key retry circuit only when bridge retirement owns at least one pending request and that request has emitted no response event. Routine idle socket retirement remains diagnostic but no longer creates phantom failures or premature cooldowns. * fix(http-bridge): derive circuit evidence on retirement --------- Co-authored-by: Soju06 <qlskssk@gmail.com>
* feat(reset-credits): add refresh scheduler enable toggle Expose rate_limit_reset_credits_refresh_enabled (default true) so operators can disable background reset-credit polling per replica. Scheduler start() becomes a no-op when disabled; the factory wires the setting. Updates the rate-limit-reset-credits OpenSpec delta, which previously mandated no toggle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(tests): raise settings ratchet to 127 and regenerate settings reference Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(reset-credits): warn when disabled polling starves persisted auto-redeem The refresh loop is the sole driver of automatic redemption, so rate_limit_reset_credits_refresh_enabled=false would silently disable a persisted auto_redeem_reset_credits_before_expiry opt-in. Disabled start() now reads the dashboard settings and logs a configuration-conflict warning naming both settings; the delta spec documents the precedence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(settings): link rate-limit-reset-credits spec from the settings reference Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(settings): reject new auto-redeem opt-in while reset-credit polling is disabled The startup conflict warning cannot guard a runtime PUT that enables auto_redeem_reset_credits_before_expiry after boot. The settings update now rejects a NEW opt-in with reset_credit_polling_disabled while the polling toggle is off; an already-persisted opt-in stays re-savable so full-payload dashboard saves of unrelated fields are not blocked. Route-level integration coverage for both paths; delta spec documents the contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ent (Soju06#1618) * docs(openspec): add add-anonymous-telemetry change proposal * feat(telemetry): add anonymous telemetry with informed opt-out consent Implements OpenSpec change add-anonymous-telemetry (backend): - snapshot builder over request_logs aggregates with strict field allowlist - client-family mapping table and model catalog allowlist (raw values never transmitted) - consent tri-state (env > persisted > default-active) with dialog-facing API - SHM sender (Ed25519 identity, 5s timeout, single retry, debug-only failures) - startup + 24h scheduler with undecided-consent startup notice - Alembic migration for consent state, instance id, and encrypted signing key * feat(dashboard): telemetry consent dialog and settings toggle - one-time consent dialog while consent is undecided (suppressed under env override): exact payload JSON preview, equal-weight enable/disable actions, dismiss persists nothing - settings row with telemetry toggle (env-controlled state disables the toggle with an explanatory notice) and collected-data preview dialog - i18n keys for en/ko/zh-CN, msw handlers, unit + integration tests * fix(db): rebase telemetry migration onto current alembic head * chore(telemetry): switch collection endpoint to telemetry.tokmaxxing.com * fix(telemetry): enforce outbound telemetry contracts Use typed models for every outbound body and one shared snapshot-envelope builder for consent previews and transmission. Gate snapshot work on shared leader election. The undecided-consent startup notice is leader-only so multi-replica deployments emit one operator notice instead of one per replica. * docs(telemetry): publish collection and consent contract Document every transmitted body, the on-demand preview API, leader-owned cadence, fail-honest request kinds, and the currently unspecified collector retention duration. * fix(dashboard): render transmitted envelope and harden telemetry contract - consent dialog and collected-data preview render the exact transmitted envelope (instance_id + metrics + timestamp), matching the backend's single envelope source of truth - preview is fetched lazily: base consent query never carries include_preview; the settings affordance fetches ?include_preview=true on demand only - TelemetrySnapshot declared as a full strict zod schema (every layer strictObject) so backend drift fails parsing in tests - settings loading skeleton gains a telemetry-shaped card to prevent layout shift * fix(db): re-parent telemetry migration and settings ratchet after rebase onto current main * fix(ci): scope stat monkeypatch to target db and teach browser smoke the consent dialog - the unmeasurable-db-size test monkeypatched Path.stat globally without accepting follow_symlinks, crashing the pytest runner itself outside -k telemetry runs; scope the failure to the telemetry db path and delegate everything else to the real stat - the dashboard browser smoke now exercises the first-run consent dialog as a first-class scenario: assert the transmitted envelope is rendered, keep telemetry enabled via a real PUT, then verify the dashboard underneath; /api/settings/telemetry joins the required API paths * fix(ci): type-safe stat monkeypatch and re-parent telemetry migration onto current head * fix(telemetry): exclude cancelled terminals from usage metrics; fix consent docs link - success_rate now subtracts total_cancelled: cancellations are neither errors nor successes (NON_ERROR_STATUSES), so disconnect-heavy workloads no longer inflate the transmitted rate. - top_upstream_errors restricts to actual error statuses; cancelled rows retain upstream_error_code='client_disconnected' and were displacing genuine upstream failures. - consent dialog now links the published /telemetry/ docs page (same target as the backend startup notice) instead of a nonexistent openspec path. Codex review threads on Soju06#1618; leader-election gating was already in place via run_if_leader. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): bucket priority tier, gate guest preview, publish stable capability spec - service_tier_mix gains a priority bucket: 'fast' normalizes to 'priority' at write time, so lumping it into default hid fast-mode traffic. Backend schema, zod mirror, fixtures, and context field lists updated. - Read-only guests no longer fire the consent preview aggregation: the consent query is disabled when the session cannot persist a decision. - Synced the telemetry delta spec to openspec/specs/telemetry (spec.md + context.md) and pointed docs/telemetry.md source-of-truth links at the stable capability path instead of the change folder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): mark telemetry T6/T8 complete Both are implemented on this head: the consent dialog (T6) ships with live payload preview and equal-prominence actions, and the delta spec is synced to the stable openspec/specs/telemetry capability (T8). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…es (Soju06#1709) On Windows, mimetypes merges HKCR mappings where third-party software commonly remaps .js to text/plain; FileResponse then serves every /assets/*.js as text/plain and strict browser MIME checking blanks the dashboard. Register text/javascript (+css/svg/json/woff/woff2/html) via mimetypes.add_type at app import, which overrides the merged registry table on all platforms and is a no-op where defaults are already correct. Fixes Soju06#1698 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…6#1713) * docs(openspec): archive 90 landed changes and sync their specs Bulk archive of merged-and-implemented changes: 89 archived via 'openspec archive' (delta specs applied to main specs, 7 new capability specs created) plus preserve-historical-compact-side-effects archived --skip-specs (its delta is already a subset of the current main spec). 'openspec validate --specs' passes (57 items). 22 completed changes stay active: their deltas contain requirements or scenarios absent from the current main specs (spec drift from merging without sync), so blind archival would drop normative content. Tracked for per-change manual merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): write real Purpose sections for the seven new capability specs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ch (Soju06#1729) * fix(dashboard): distinguish first-run empty states from filter mismatch Empty Accounts, APIs, and request logs told first-run operators to adjust filters. Reports drew a zero line when there was no daily data. /firewall landed on Settings with Advanced collapsed. Use first-run copy when the source list is empty, keep filter-empty copy when something is hidden, add a dashboard CTA to /accounts, show no-data on empty Reports line charts, and expand Advanced for /firewall. * fix(dashboard): satisfy eslint on Advanced settings deeplink Move the URL helper out of the component module and remount the collapsed group from the route instead of setting open state in an effect. * fix(dashboard): keep nonempty request-log totals out of first-run empty copy A deep-linked later page can have requests=[] while total>0. That is not a first-run fleet; treat a positive total as existing logs when choosing empty copy. * test(dashboard): align API first-run integration expectation * fix(dashboard): stabilize empty-state transitions and firewall scroll * fix(dashboard): preserve firewall deeplink through async layout * fix(dashboard): settle firewall deeplink scroll * fix(dashboard): stabilize async navigation transitions
ChatCompletionsRequest.tools uses default_factory=list. The tools validator assigned the default back onto the model, marking the field as set, and to_responses_request() always wrote tools onto the converted request. That synthesized "tools": [] for clients that omitted the field and bypassed the Responses omit path. Keep explicit [] intact.
…06#1722) * fix(http-bridge): refuse foreign claims on live DRAINING leases Shutdown marks durable HTTP-bridge rows DRAINING before releasing the lease. claim_session treated DRAINING as takeover-eligible, so a foreign claim_live_session(allow_takeover=False) stole the live owner. Align claim refuse and local-create takeover with the turn-state live-owner predicate. Expired, released, and CLOSED rows stay recoverable. * fix(http-bridge): do not force-steal live DRAINING leases Missing ring endpoints set force_durable_takeover and bypassed the live DRAINING claim guard. Mask force when the durable lookup still has a live DRAINING owner. Active-lease recovery after a missing endpoint is unchanged. * fix(http-bridge): refuse live DRAINING on the locked claim row A stale ACTIVE lookup plus force_durable_takeover still authorized allow_takeover=True. If the owner marked DRAINING before the claim lock, the previous guard would steal. Refuse live DRAINING against the locked row even when takeover is forced.
* fix(proxy): keep stream idle timeouts account-neutral A first-event HTTP SSE stream_idle_timeout still failovers for this request, but must not increment account error_count. * test(proxy): stop expecting idle-timeout health writes Existing stream_idle_timeout failover tests still require exclude/failover and request-log codes, but no longer expect record_error.
* fix(proxy): settle compact failover before account health Keep the API-key reservation across compact failover_next and write the failed account's health only after the next settlement. * fix(proxy): defer compact 500 and route-error health writes HTTP 500 retry exhaustion and UpstreamProxyRouteError after failover must settle the reservation before account-health updates. * fix(proxy): defer compact refresh/connect health writes Retryable freshness/connect and post-401 forced-refresh failures now queue account health until the reservation settles. * fix(proxy): settle before second-401 and permanent refresh health A repeated 401 after forced refresh now defers _handle_proxy_error, and a permanent RefreshError settles before mark_permanent_failure. * fix(proxy): flush compact health after fallback reservation release If compact finalize fails but fail-safe release succeeds, drain deferred failover health before surfacing usage_settlement_failed. Leave health unapplied when the reservation is still held. * fix(proxy): keep compact success when deferred health fails A finalized compact response now survives a later deferred health-write failure. Cancellation and other non-proxy exits settle first, then flush the deferred failover health queue. * fix(proxy): finish compact health flush after cancellation Keep the deferred health task alive with asyncio.shield so a cancel that arrives mid-write still penalizes the failed account before the request exits. * fix(proxy): keep flushing remaining compact health writes A single deferred health-persistence failure no longer drops later failed-account penalties from the same compact request. * fix(proxy): flush compact health after selection timeout A later account-selection budget timeout is a ProxyResponseError, so it now goes through the same settle-and-flush cleanup as other unsettled compact exits.
…6#1712) Non-streaming /v1/chat/completions returned on the first response.failed event without draining the upstream generator, so reservation finalizers did not run, and collected errors were forced to HTTP 502. Drain collect like /v1/responses, aclose the stream afterwards, and use the shared Responses status map (429 for rate_limit_exceeded).
…6#1747) * fix(proxy): sweep idle bridge sessions without request traffic _prune_http_bridge_sessions_locked is reached from exactly one place — _get_or_create_http_bridge_session — so the idle sweep is request-driven. A replica that stops receiving bridge requests never evicts its idle sessions and holds their upstream WebSockets until the process restarts. Expose the existing sweep as prune_idle_http_bridge_sessions() and drive it from the per-replica ring heartbeat, beside the durable-ownership reconcile that already runs there. No new selection logic and no new timing: the same eligibility, the same idle TTL that protects a freshly-handed session, and the same bounded close path. This is the residual leg of Soju06#1354; the cap-exhaustion symptom was addressed by turn-scoped leases in Soju06#1476, which already release an idle session's slot when its last turn detaches. Refs Soju06#1354 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(proxy): cover the heartbeat wiring, not just the sweep The sweep tests called prune_idle_http_bridge_sessions() directly, so they would still pass with the heartbeat wiring removed — leaving the quiet-replica leak the change exists to fix. Extract the heartbeat's bridge upkeep into run_http_bridge_heartbeat_maintenance() and test it: both passes run, a failing reconcile does not skip the sweep (nor stop the heartbeat), and a missing service or pass is tolerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(proxy): host the idle sweep in the session-registry mixin main moved mixin.py to its 2436-line architecture ratchet, and the sweep is registry maintenance, so it belongs next to the other registry operations rather than pushing mixin.py over the limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
) * docs(proxy): document cluster-wide account cap partitioning Fix (c) of issue Soju06#1354. proxy_account_stream_limit reads like a per-replica value but is a cluster-wide target partitioned across the bridge ring (floor(cap/R) + remainder by rank, floored at 1), so the default 8 becomes 3/3/2 slots on three replicas — the amplifier behind the original report. Adds an Account Cap Sizing section to the proxy-admission-control context (SSOT) and an operator-facing subsection on the Kubernetes multi-replica page, covering the recovery reserve's selection-time-only subtraction, the share-growth hysteresis, the turn-scoped-lease interaction, the undersizing signature, and the one-process-per-pod requirement. Refs Soju06#1354 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(proxy): correct four cap-partitioning details against the code - A cap of 0 stays unlimited on every replica; the floor-of-1 applies only to positive caps. - Partitioning is the default scope; proxy_account_caps_scope=replica is the supported opt-out where every replica enforces the full cap. - On an initialized deployment the dashboard-persisted caps are authoritative over the environment values, so operators must change the cap in the dashboard, not by restarting with a new env var. - The stale-lease bound for streams is not the raw 900s TTL: a long stream must not be reclaimed mid-flight, so the sweep uses max(TTL, longest stream budget) + 60s — 7260s with default budgets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(proxy): fix the cap<replicas aggregate and the env-reseed misconception - When the cap is below the replica count, the floor of 1 makes the aggregate equal the replica count and grow with each added replica; the 'adding replicas only re-partitions' rule holds only at cap >= replicas. - There is no dashboard path back to an unset cap, so drop the 'keep it unset' alternative: env values only seed the initial dashboard row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(cache): restore a pending bump whose write is cancelled or raises _flush_pending_bumps clears each pending marker before awaiting its write — deliberately, so a request_bump() arriving mid-write re-queues rather than being coalesced into the version already being written. But it only restored the marker when bump() returned False. A write that was cancelled or raised left the namespace neither written nor pending. stop() cancels the polling task by design, so this silently dropped whatever namespace the poll loop was mid-write on at shutdown: a mutation that had already committed never reached peer replicas, leaving their caches stale until an unrelated later bump. It affects every namespace on the bus. Found while investigating Soju06#1354; independent of that fix. Co-Authored-By: Darafei Praliaskouski <me@komzpa.net> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): scope the change to the aborted-write fix The invalidation-bus spec already requires coalesced namespaces to remain pending until a bump succeeds; an aborted write silently dropping one violated that. Record the clarification rather than inventing a new shutdown-delivery contract — 'a lost bump still converges within the fallback TTL' is the documented bound and stays so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): state the ambiguous-abort preference A cancellation can reach the bump write after the database accepted its commit. The restore is unconditional there by design: a redundant bump only re-runs peers' idempotent invalidation callbacks, while dropping an unconfirmed write leaves them stale until the fallback TTL. The bus already tolerates extra increments — a request_bump arriving mid-flush produces one deliberately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): qualify the no-version assertion for the unambiguous abort The aborted-write scenario asserted no version is written, which the ambiguous-abort scenario immediately below it contradicts. Scope it to a cancellation arriving before the database accepts the commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): scope both abort branches by commit acceptance The qualifier only covered cancellation, but a driver can raise after the server accepted COMMIT too. Both scenarios now split on whether the database accepted the commit rather than on how the write aborted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(cache): prove the poller retries an aborted bump The unit-level tests asserted the marker is restored; the product behavior is that the background poller then writes the version. Cover it end to end: first write raises, the running poller retries, the version lands. Also moves the ambiguous-abort rationale out of the normative requirement into the proposal, per the spec/context split, and trims the code comment to what is true now that shutdown delivery is out of scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): finish removing rationale from the requirement My previous edit left the original clause fragment in place, so the rationale I said I had moved was still there, duplicated. The requirement now states only the testable outcome; the reasoning lives in the proposal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(cache): drop the false shutdown-delivery rationale from the docstring The restore matters where a later cycle exists to retry; at process stop no cycle remains either way — shutdown delivery is explicitly out of scope and the restore there only keeps the pending set honest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cache): keep flushing other namespaces after an abnormal raise bump() reports normal failure by returning False, so a raise is abnormal — but re-raising aborted the sorted flush loop, and a persistently raising namespace that sorts first would starve every namespace after it on every cycle. Cancellation still restores and re-raises (teardown must abort); an abnormal raise now restores, logs at warning, and continues. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(openspec): sync the proposal with the split abort handling Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(cache): wait for the retry to settle before stopping the poller The committed row can become visible while bump() is still in its shielded session cleanup; stopping the poller at that instant cancels the retry mid-flight. Gate the stop on an event set only after bump() fully returns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Darafei Praliaskouski <me@komzpa.net> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…out (Soju06#1752) * feat(db): report SQLite write transactions that outlive the busy timeout Issue Soju06#1682's ~17-minute 'database is locked' stall self-recovers and the holder's identity never reaches any log, so it cannot be attributed — and any teardown or deadline fix is guesswork until it is. Every SQLite engine now reports, at WARNING when the transaction ends, any write transaction that held the writer slot past the busy timeout: duration, outcome, owning task, and the first and last write statements. Measured from the first write statement (WAL takes the writer slot there, not at BEGIN) so read-only transactions never report. Post-hoc by design: the stall recovers on its own, so identifying the holder when it ends is sufficient and needs no sampler. No new settings — the threshold is the existing busy timeout. Refs Soju06#1682 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(db): start the watchdog clock only after a write acquires the slot before_cursor_execute fires before the driver executes, so a statement that spends the busy timeout waiting for the writer slot and fails with 'database is locked' would have been reported as the holder it was a victim of. Time from after_cursor_execute — the slot is only held once a write statement has succeeded. Regression test pins a holder, lets a victim fail its wait, and asserts the victim's rollback produces no report. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(db): defer the watchdog report until the transaction actually ends ConnectionEvents.commit/rollback fire before the DBAPI call, and a wedged rollback is exactly the holder this watchdog hunts — reporting at the event excluded the wedge itself from the measured hold. The report is stashed at the event and finalized at the first proof the DBAPI transaction is over: the connection's next begin, or its return to the pool. Regression test stalls the commit listener past the threshold and fails on the report-at-event code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(db): track BEGIN IMMEDIATE holders; report failed commits truthfully - BEGIN IMMEDIATE/EXCLUSIVE acquire the writer slot with no DML at all (the accounts merge lock does exactly this), so a holder that never runs a write statement was invisible. The write-prefix filter now includes them; a plain deferred BEGIN stays untracked. - A DBAPI commit that raises still ends the transaction, but by rollback; the pending report marked at the commit event claimed a durable commit that never happened. handle_error (the real DBAPI failure path) and a bare rollback-after-commit both flip the outcome to commit_failed_rollback. Test wraps the aiosqlite adapter's commit to raise for real. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…essor (Soju06#1751) * fix(proxy): fence successor bridge claims against the retiring predecessor Root cause of the Soju06#1695 CI flake (409 bridge_instance_mismatch on a single instance). When an upstream closes cleanly, the retiring session's teardown releases its durable row while the next request's successor session claims the same row. Two defects let the race corrupt the claim: 1. A same-owner reclaim kept the owner epoch, so the predecessor's fenced release still matched after the successor's claim and closed the row out from under it. The epoch was historically kept because reused sessions re-claimed; today a reused session renews instead, so every claim comes from a successor and nothing needs epoch stability across claims. Every claim of an existing row now advances the epoch. 2. The claim's update mutated ORM attributes, and SQLAlchemy omits fields whose values match the transaction's read. On SQLite (with_for_update is a no-op) a release committing between the claim's SELECT and its write survived the claim — owner=None/state=CLOSED — and the refresh handed the claimant a closed, ownerless row. The update is now an explicit UPDATE setting every ownership field unconditionally. Both regression tests fail on the pre-fix code: the fence test and a deterministic interleave reproduction that injects the release between the claim's read and its writer section. Fixes Soju06#1695 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(proxy): route-level fence regression; gate restart_takeover explicitly - Deterministic route-level regression through POST /v1/responses: the retiring session's durable release is held captive while the successor claims, then landed late — pre-fix it closed the row under the live successor and the next turn 409'd; verified failing on the pre-fix repository code. - The restart_takeover metric was gated on allow_takeover, and with every claim now advancing the epoch it would have counted ordinary local successor claims (headerless requests have no pre-claim lookup, so allow_takeover defaults permissive). The claim call site now passes an explicit record_restart_takeover flag: a pre-claim durable lookup existed and its owner was genuinely not live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): serialize racing successor claims with an epoch CAS Two successor claims can both read epoch N (with_for_update is a no-op on SQLite); both would write N+1 and hold colliding fences. The claim's update now lands only if the row's epoch still matches the read; the loser rolls back and retries against fresh state, landing on its own distinct epoch. Alias clearing moves after the CAS so a losing attempt clears nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): return the CAS-written snapshot; bound CAS retries - A post-commit refresh could read a LATER claim's commit and hand this claimant the winner's epoch as its own — colliding fences through the return value. The snapshot is now built from the values this CAS wrote; no post-commit read exists at all. - Three-way contention exhausted the two-attempt loop: the insert race and the epoch CAS now share a bounded budget of 5, and every round has a winner, so losers converge on fresh reads. - getattr rowcount idiom for ty, matching the rest of the repository. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): revalidate takeover permission after losing a claim race allow_takeover is decided by the caller against a pre-claim lookup. Once another claimant has demonstrably written the row under us — a lost CAS or a lost insert race — that decision is stale: the row we re-read may now carry the winner's live lease, and reusing the permission let the loser steal it. Two replicas recovering the same released row hit this directly. A contended retry now drops the takeover permission, so a live foreign owner fails closed exactly like a non-takeover claim and the real owner is reported — which the bridge surfaces as the correct cross-replica retry response. Same-instance successor races are unaffected: the owner matches, so the epoch simply advances again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): stop the claim retry at a live foreign owner The repository drops takeover permission after losing a claim race, but the service's own retry loop issued a fresh claim with the caller's original permission — restoring it and stealing the winner's live lease, undoing the repository-level fix one layer up. A live foreign owner now ends the retry, so the fail-closed outcome stands and the bridge returns its cross-replica 409 as intended. The retry still runs for a genuinely takeable owner (expired, released, or closed), which is the transient contention it exists for. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): a rejected creator must not release the winner's durable row After an inflight evict, a replacement can win the registry slot while the stale creator is still claiming. The stale creator claims LAST, so its epoch is current and its fenced release succeeds — closing the durable row out from under the registered session, which then 409s. Distinct epochs alone did not prevent this: the unregistered claimant's cleanup was still the current fence. The failure cleanup now reports whether another session holds the registry slot, and skips the durable release when it does; the row belongs to the winner. Cleanup moved to helpers to stay under the mixin line ratchet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): abort a superseded creation before it claims the durable row Skipping the stale creator's release stopped the row being closed, but its claim had already advanced the epoch past the registered winner — fencing that winner's own renewals out of a row it legitimately owns. A creator that has already lost its inflight registry slot now aborts before claiming, so the winner's epoch is untouched. The skip-release path remains for the narrower window where eviction lands during the claim itself. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): hand a mid-claim-superseded epoch to the registered winner The pre-claim guard covers the reported ordering, but eviction can still land during the claim itself — the creator then advances the shared row's epoch past the session that won the slot, fencing that winner's own renewals out of a row it owns. Both sessions belong to this instance and point at the same row, so the failure cleanup now hands the claimed epoch to the registered winner instead of leaving it stranded. Guarded on same-row identity and a strictly newer epoch, so an unrelated winner is untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): adopt a same-instance epoch advance instead of evicting the owner A fenced renewal evicts the session and raises 409 — it does not self-heal — so the epoch handoff alone still lost to a winner already inside _renew_durable_http_bridge_lease with its old epoch. A renewal fenced by an advance from THIS instance now adopts the newer epoch when the renewing session still holds the registry slot for its key: that is a creator superseded mid-claim, not an ownership loss. A session whose slot a different local session holds still falls through to eviction with the existing retryable contract, and a foreign instance is untouched — so whoever loses the race self-heals on its own renewal regardless of ordering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): protect an unpublished replacement; snapshot the inserted claim - A replacement that has claimed but not yet published its session holds the in-flight slot and is as much the winner as a registered one. Releasing there closed the row beneath it, and it then registered with an older epoch and was fenced out on its first renewal. The settle path now treats an in-flight replacement as superseding too; a sole creator with neither still releases rather than leaking the row. - The insert path still returned a post-commit refresh, so a concurrent same-instance claimant advancing the brand-new row handed both claimants the same fence. It now builds its snapshot from what it inserted, matching the CAS path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): require a matching process epoch before adopting a newer fence Two incarnations can share a configured instance ID across a graceful restart. The adoption predicate checked only the instance ID, so the old process's registered session would adopt the successor's epoch and keep renewing a row the successor owns — defeating the process-incarnation fence the durable design already establishes. Adoption now requires the row's owner_process_epoch to match this process. A successor incarnation therefore still evicts the predecessor with the existing retryable contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): never carry ownership across an account change A claim rewrites the durable row's account_id and clears its continuity aliases. Handing the epoch to a winner that selected a different account — or adopting an advance that rebound the row — would leave a session renewing and dispatching on a row bound to someone else's account. Both paths now require the account to match: the handover is skipped and the adoption falls through to the existing eviction + retryable 409, which is the correct fail-closed outcome for a row that genuinely changed hands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(proxy): release a row the claim rebound away from the winner Declining the epoch handoff across an account change left the row preserved AND bound to the stale creator's account, so the registered winner kept dispatching against a row that was no longer its own until its next renewal. A winner on a different account no longer shares the row — the claim already rewrote its account binding and cleared its continuity aliases — so the creator releases it. The winner is then fenced promptly and retries cleanly. An in-flight replacement, whose account is not yet knowable, still preserves the row conservatively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(client): add explicit Daybreak Blue profile
* fix(proxy): fail closed on Daybreak transport fallback
* fix(proxy): guard all Daybreak capability ingress
* fix(auth): keep capability header on validate_proxy_api_key
Authenticate X-Codex-LB-Required-Capability through the existing
proxy API-key dependency so FastAPI overrides and auth-first upload
tests keep working, instead of introducing a parallel Security identity.
* fix(proxy): deny Daybreak JSON routes before Body decode
Capability-bearing Responses, compact, chat, image-generation, and
reset-credit consume requests now authenticate and return
required_capability_transport_unsupported in middleware, before FastAPI
parses the JSON body. Malformed payloads no longer mask the transport
denial or skip the API-key check.
* fix(proxy): keep Daybreak JSON deny inside firewall
Register the capability HTTP guard after body-decoding middleware and
before request-id/firewall/admission so blocked clients still get
ip_forbidden and invalid keys cannot skip the concurrency layers.
* fix(proxy): deny Daybreak warmup before JSON body decode
POST /v1/warmup and /v1/warmup/{mode} now use the same pre-body
capability allowlist as the other typed JSON routes, so a malformed
WarmupRequest cannot return 422 ahead of the transport denial.
* fix(review): keep Codex review sessions resumable * fix(review): support current Codex CLI invocation * docs(review): clarify non-interactive session recovery * fix(review): validate resumable wrapper invocation
* fix(accounts): recover Free accounts after reset * fix(accounts): require post-block reset baseline Rows recorded exactly at blocked_at can carry the stale reset marker and shadow later valid evidence after restart. Document the threshold removal follow-up and staggered-idle cooldown scope. Refs Soju06#1340 * fix(accounts): fall back to anchored reset evidence A newer unanchored reset pair must not mask persisted evidence tied to the active block marker. --------- Co-authored-by: Hulian Felipe Muller Buligon <hulian@MacBook-Pro-de-Hulian.local>
* fix(proxy): keep file-pin owner on soft 1011 reconnect A live input_file pin is hard ownership. Soft HTTP-bridge reconnect after upstream 1011 was treating that owner as skippable prompt-cache locality. * fix(proxy): stay under http-bridge mixin line ratchet Move reconnect owner resolution out of mixin.py so the architecture check stays green, and update the remaining fresh-upstream call-shape assertion. * fix(proxy): surface file-pin owner-unavailable on soft reconnect Fresh-upstream retry swallowed the required-owner envelope and the submit-on-closed path replaced it with generic upstream_unavailable. * fix(proxy): map required-owner reconnect miss to owner-unavailable Terminal reconnect selection failures (no_accounts, preferred miss, local cap) were still emitted as generic selection errors even when a live file pin made the owner required. * test(proxy): expect owner-unavailable on file-owner reconnect miss Precreated retry already fail-closes when the required file owner cannot be selected; the envelope is now previous_response_owner_unavailable. * fix(proxy): map required-owner connect miss to owner-unavailable Selecting the file-pin owner and then failing to open its replacement socket still collapsed submit-on-closed into generic upstream_unavailable. * fix(proxy): map required-owner transport miss to owner-unavailable A budget-expired ClientError or TimeoutError while opening the file-pin owner's replacement socket still collapsed submit-on-closed to a generic upstream_unavailable envelope. * fix(proxy): map required-owner refresh miss to owner-unavailable A budget-expired RefreshError for the file-pin owner still collapsed submit-on-closed into generic upstream_unavailable.
* fix(proxy): persist file ownership across replicas Rebase persist-file-account-pins onto current main: store file_id owner pins in a shared table, revalidate forwarded owners against that table, and keep one API-key reservation cleanup owner across direct, compact, and HTTP-bridge paths. * fix(proxy): keep startup reservation cleanup outside heartbeat Make the forwarded cleanup guard the outermost stream wrapper so a client close after the initial SSE heartbeat still cancels the probe task and releases an API-owned reservation. Also stop pinning the telemetry migration test to an outdated Alembic head. * test(proxy): satisfy ty on startup cleanup guard regression Use the real reservation-cleanup type and close the stream via getattr so the heartbeat-disconnect test typechecks. * fix(proxy): keep owner-forward cleanup fail-closed Treat connector failures as not-dispatched so origin can recover, and keep HTTP 200 after a successful compact fallback settlement so origin does not replay an already-settled forwarded compact. * test(proxy): import ConnectionKey so ty can see connector fixtures * fix(proxy): keep forwarded compact handoff fail-closed After cleanup-ready, keep HTTP 200 and put usage_settlement_failed on the SSE body so origin cannot replay an already-settled compact. Treat cancellation after owner-forward transport begins as dispatched. * fix(proxy): retry a failed reservation release If the first persistence write fails during startup or disconnect cleanup, schedule one follow-up release instead of abandoning quota. * fix(proxy): close remaining reservation-handoff gaps Signal cleanup-ready after any compact settlement attempt, close the service stream when the startup probe already completed, retry header cleanup through the reservation owner, and keep file_owner_unavailable when settlement also fails. * fix(proxy): keep owner non-200 as a definitive rejection A later body-read or timeout failure must not overwrite an already observed non-200 into dispatch-ambiguous and block local recovery. * fix(proxy): restore compact forced-refresh transport failover * test(proxy): fix PR1521 rebase follow-up expectations * fix(proxy): remove dead compact settlement flag --------- Co-authored-by: Darafei Praliaskouski <me@komzpa.net>
…oju06#1680) * fix(proxy): recover goal restarts from unavailable owners Permit an explicit, account-neutral Codex goal restart to retire an unchanged legacy owner only while its persisted account status is unavailable. Preserve fail-closed routing for all other continuity evidence and guard retirement with one compare-and-set tombstone. * fix(proxy): preserve scoped affinity ownership * fix(proxy): close restarted affinity selection gaps * fix(proxy): preserve typed legacy affinity ownership * test(proxy): update typed sticky lookup doubles * fix(db): re-parent sticky abandonment migration * fix(db): re-parent sticky scope onto telemetry head * test(db): allow telemetry migration descendants * fix(proxy): preserve scoped retirement compatibility * fix(db): re-parent sticky scope onto file pin head * fix(http-bridge): route verified restarts through selection * fix(review): scope restart authority to bridge request * refactor(http-bridge): preserve architecture ratchet * fix(http-bridge): keep restart replacement canonical * fix(http-bridge): scope post-handoff retirement * fix(http-bridge): close restart lifecycle gaps * fix(http-bridge): retain detached restart generations * fix(http-bridge): finalize detached restart lifecycles * fix(http-bridge): fence replacement generation cleanup * fix(http-bridge): close remaining lifecycle gaps * fix(http-bridge): preserve lifecycle ownership after retries * fix(http-bridge): retain retry cleanup ownership * fix(http-bridge): own LRU cleanup before overload * test(http-bridge): expect scoped restart markers * fix(http-bridge): preserve restart lifecycle boundaries * refactor(http-bridge): keep capacity policy within ratchet * fix(rebase): restore sorted imports after conflict resolution * fix(rebase): keep restart takeover default implicit * style(rebase): format restart takeover claim kwargs * refactor(http-bridge): extract capacity close planning helper --------- Co-authored-by: Darafei Praliaskouski <me@komzpa.net>
* fix(models): correct GPT-5.6 context windows * fix(models): correct GPT-5.6 context windows * docs: add @kidclone3 as a contributor * docs(models): cite corrected GPT-5.6 catalog * fix(models): address GPT-5.6 catalog review * docs(openspec): exclude persisted snapshots from gpt-5.6 bootstrap scenarios --------- Co-authored-by: Darafei Praliaskouski <me@komzpa.net>
… ownership (Soju06#1652) * fix(auth): guard refresh singleflight successor settlement * test(auth): cover refresh successor settlement race * test(auth): exercise delayed refresh completion callback
…06#1699) * test(proxy): add property tests for response payload invariants * test(proxy): harden hypothesis property test setup
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(frontend): add model-source reasoning effort controls * ci: rerun cancelled frontend checks * Preserve case when normalizing model-source reasoning efforts * style(frontend): remove trailing test whitespace
* fix(compact): absorb active recovery replay semantics * fix(proxy): reject mixed post-compact tool suffix replays
* fix(db): repair retired identity/warmup stamp * fix(db): repair partial file-account-pin migration
A reattach that proved silent/wedged quarantines its session key, but the quarantine gate only cleared two booleans. `bridge_session_key` was still re-adopted from `durable_lookup.canonical_kind/canonical_key`, and the session was still created under that same poisoned key, so for the whole 600s `_HTTP_BRIDGE_QUARANTINE_TTL_SECONDS` window every request rebuilt a fresh wedged bridge on the key that was already known to be dead. Demote the key instead of only dropping the anchor. When a quarantined key carries a sealed durable full-resend proof that matches the payload, the request dispatches on a soft account-neutral replay key with the client's own full conversation, and the original key's continuity is advanced only after that recovery actually completes. - `quarantine.py`: entries carry a `generation`, and clearing takes a key plus an expected generation, so a late recovery cannot clear a quarantine that was re-armed underneath it. - `upstream_events.py`: on a completed response, rebind and renew the original durable row before clearing its quarantine, so continuity is not stranded on the throwaway recovery key. - `streaming.py`: the quarantine gate demotes the key; the recovery key is named after the body this dispatch actually sends. `durable_recovery_attempt_fingerprint` deliberately keeps hashing the unprojected body. It is the key of persisted `http_bridge_recovery_attempts` rows, so hashing the projected body instead would mint a different fingerprint, a row journalled before a restart would stop matching, and the one-shot replay fence would silently open once. Covered by `test_quarantined_full_resend_recovery_fence_survives_restart_fingerprint`.
An HTTP bridge model transition could return `continuity_owner_conflict` indefinitely when a durable model owner and a stale hard alias pointed at different accounts, even though the request was safe to start on a fresh child bridge. Add a narrowly gated account-neutral model-transition fork, limited to the exact `continuity_owner_conflict` error, local requests and an account-neutral effective Responses payload. Forwarded requests, previous-response continuations, resolved file owners, account-scoped hosted references, and post-compaction payloads whose compacted context is not carried in the request remain fail-closed. The forked child key is pinned `hard`, and the child request state drops the parent affinity policy, hard continuity anchor and reused parent turn state so the submit and clean-close paths do not classify it as the parent's owner-bound turn.
…Soju06#1897) Production re-starved its event loop after ~91h on 1.24.0-beta.3 (py-spy: GIL dominated by asyncio shield callback cleanup) because the usage-refresh singleflight still awaited shared tasks via asyncio.shield — the one many-waiter site Soju06#1842 did not convert. Route both _UsageRefreshSingleflight wait paths through wait_on_shared_future so waiter attach/cancel/timeout is O(1) on the shared task, preserving result/cancellation/exception and join_existing=False sequencing semantics. Add regression coverage for concurrent joins, cancellation isolation, bounded callback fan-out (sabotage-proven: shield yields 201 callbacks for 100 waiters vs bounded 2), and successor sequencing. OpenSpec: fix-usage-refresh-shared-future-waiters extends the proxy-admission-control shared-future contract to usage-refresh waiters. Fixes Soju06#1896
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#1915) Bumps the python-minor-patch group with 7 updates: pygments 2.21.0, python-dotenv 1.2.3, uvicorn 0.52.4, ruff 0.16.4, ty 0.0.73, openai 3.3.1, hypothesis 6.165.10. Includes source-contract fixes for ty 0.0.73 (new implicit-return diagnostics on shielded cancellation-deferral helpers, plus restored isinstance narrowing for the service_tier lookup). Verified: ty 0.0.73/0.0.72 both clean, ruff check/format clean, targeted pytest (unit + proxy integration + SDK e2e) 6486 passed, CI green at 5599da0, current-head Codex review clean.
## [1.24.0](Soju06/codex-lb@v1.23.0...v1.24.0) (2026-08-26) ### Features * **api-keys:** allow per-key reasoning effort policies ([Soju06#1642](Soju06#1642)) ([ed31b7d](Soju06@ed31b7d)) * **config:** timeout-invariant linter — validate deadline/TTL inequalities at startup and in CI ([Soju06#1622](Soju06#1622)) ([d148dd9](Soju06@d148dd9)) * **db:** report SQLite write transactions that outlive the busy timeout ([Soju06#1752](Soju06#1752)) ([6464e96](Soju06@6464e96)) * **frontend:** configure model-source reasoning efforts ([Soju06#1848](Soju06#1848)) ([eab7155](Soju06@eab7155)) * **model-sources:** advertise operator-declared reasoning efforts ([Soju06#1661](Soju06#1661)) ([f1c8d5c](Soju06@f1c8d5c)) * **model-sources:** embeddings source capability ([Soju06#1776](Soju06#1776)) ([4d0f0ff](Soju06@4d0f0ff)) * **proxy:** report websocket cleanup phase ([Soju06#1726](Soju06#1726)) ([0c8d921](Soju06@0c8d921)) * **proxy:** support Ultrafast service tier ([Soju06#1734](Soju06#1734)) ([d522a4d](Soju06@d522a4d)) * **reports:** Add API Key Filtering to Reports Dashboard ([Soju06#1728](Soju06#1728)) ([1f65f80](Soju06@1f65f80)) * **reset-credits:** add refresh scheduler enable toggle ([Soju06#1701](Soju06#1701)) ([6509dd0](Soju06@6509dd0)) * **telemetry:** anonymous usage telemetry with informed opt-out consent ([Soju06#1618](Soju06#1618)) ([debd7cf](Soju06@debd7cf)) * **telemetry:** report consent state and send a decision-time opt-out signal ([Soju06#1835](Soju06#1835)) ([1541ee8](Soju06@1541ee8)) * **ui:** customize dashboard request-log columns ([Soju06#1503](Soju06#1503)) ([138aa9f](Soju06@138aa9f)) * **ui:** surface reasoning token usage ([Soju06#1801](Soju06#1801)) ([8e7589e](Soju06@8e7589e)) ### Bug Fixes * **accounts:** recover Free accounts after reset ([Soju06#1700](Soju06#1700)) ([b43d0c8](Soju06@b43d0c8)) * **auth:** guard the refresh singleflight negative cache by successor ownership ([Soju06#1652](Soju06#1652)) ([4ace71e](Soju06@4ace71e)) * **cache:** keep an aborted invalidation bump queued ([Soju06#1748](Soju06#1748)) ([7148810](Soju06@7148810)) * **chat:** omit unset tools on mapped Responses payloads ([Soju06#1725](Soju06#1725)) ([db5776c](Soju06@db5776c)) * **compact:** omit oversized non-state tool tail ([Soju06#1235](Soju06#1235)) ([edb3734](Soju06@edb3734)) * **compact:** recover previous-response-pinned compaction from quota-excluded owners ([Soju06#1780](Soju06#1780)) ([120af75](Soju06@120af75)) * **dashboard:** distinguish first-run empty states from filter mismatch ([Soju06#1729](Soju06#1729)) ([e439043](Soju06@e439043)) * **dashboard:** exclude cancelled/client_disconnected from error rate ([Soju06#1696](Soju06#1696)) ([8a7d956](Soju06@8a7d956)) * **dashboard:** pin web asset MIME types against poisoned OS registries ([Soju06#1709](Soju06#1709)) ([2164b8c](Soju06@2164b8c)), closes [Soju06#1698](Soju06#1698) * **dashboard:** preserve cancelled request count ([Soju06#1766](Soju06#1766)) ([ef9c68e](Soju06@ef9c68e)) * **dashboard:** separate quota and purchased credits ([Soju06#1670](Soju06#1670)) ([4a08d96](Soju06@4a08d96)) * **dashboard:** show cancellation totals in reports ([Soju06#1772](Soju06#1772)) ([5d27f7f](Soju06@5d27f7f)) * **dashboard:** show cancelled request logs ([Soju06#1769](Soju06#1769)) ([5f2f726](Soju06@5f2f726)) * **dashboard:** surface upstream route metadata ([Soju06#1767](Soju06#1767)) ([e359d49](Soju06@e359d49)) * **db:** add postgres shm_size and raise default pool headroom ([Soju06#1791](Soju06#1791)) ([539cf93](Soju06@539cf93)) * **db:** bound wedged SQLite session teardown and reclaim the connection ([Soju06#1778](Soju06#1778)) ([9eedb2c](Soju06@9eedb2c)) * **db:** repair retired identity/warmup migration stamp ([Soju06#1847](Soju06#1847)) ([b6c217f](Soju06@b6c217f)) * **docker:** upgrade util-linux family in runtime image for CVE-2026-53615 ([Soju06#1796](Soju06#1796)) ([0a4c0a1](Soju06@0a4c0a1)) * **helm:** bind TTFT dashboard SQL datasource ([Soju06#1827](Soju06#1827)) ([8abd507](Soju06@8abd507)) * **http-bridge:** classify recovery error frames and poison same-anchor eventless failures ([Soju06#1841](Soju06#1841)) ([01f089c](Soju06@01f089c)) * **http-bridge:** dedupe retry circuit failures per send ([Soju06#1743](Soju06#1743)) ([5780a27](Soju06@5780a27)) * **http-bridge:** keep idle retirements out of retry circuit ([Soju06#1677](Soju06#1677)) ([7c46719](Soju06@7c46719)) * **http-bridge:** preserve goal-restart recovery across reconnects ([Soju06#1680](Soju06#1680)) ([5dc6081](Soju06@5dc6081)) * **http-bridge:** refuse foreign claims on live DRAINING leases ([Soju06#1722](Soju06#1722)) ([b50cb86](Soju06@b50cb86)) * **models:** apply context-window overrides to /v1 input context fields ([Soju06#1808](Soju06#1808)) ([c750dcf](Soju06@c750dcf)) * **models:** correct GPT-5.6 context windows ([Soju06#1691](Soju06#1691)) ([8488bc4](Soju06@8488bc4)) * **models:** raise GPT-5.6 bootstrap max_context_window to 872k ([Soju06#1813](Soju06#1813)) ([1add104](Soju06@1add104)) * **proxy:** abandon unavailable owner on thread-scoped goal restart ([Soju06#1764](Soju06#1764)) ([17ae866](Soju06@17ae866)) * **proxy:** absorb replay-safe compaction recovery ([Soju06#1849](Soju06#1849)) ([c597226](Soju06@c597226)) * **proxy:** add explicit Daybreak capability routing ([Soju06#1742](Soju06#1742)) ([0031e3d](Soju06@0031e3d)) * **proxy:** bind account-bound retries to dispatch owner ([Soju06#1829](Soju06#1829)) ([3381938](Soju06@3381938)) * **proxy:** classify parameterless previous response errors ([Soju06#1818](Soju06#1818)) ([eeab46a](Soju06@eeab46a)) * **proxy:** close non-stream chat collect and map error status ([Soju06#1712](Soju06#1712)) ([a85f71d](Soju06@a85f71d)) * **proxy:** compact transport switch + trigger canonicalization (supersedes [Soju06#1749](Soju06#1749)) ([Soju06#1809](Soju06#1809)) ([0481ed9](Soju06@0481ed9)) * **proxy:** complete disconnect cleanup — pool leak, charged reservation, mutable terminal reason ([Soju06#1645](Soju06#1645)) ([6cf7e61](Soju06@6cf7e61)) * **proxy:** demote quarantined bridge reattach keys ([Soju06#1730](Soju06#1730)) ([5e1f568](Soju06@5e1f568)) * **proxy:** do not rewrite thread locality for a file-pin owner ([Soju06#1765](Soju06#1765)) ([34ef7b2](Soju06@34ef7b2)) * **proxy:** drop malformed compact item ids ([Soju06#1815](Soju06#1815)) ([812265d](Soju06@812265d)) * **proxy:** durably recover hard HTTP bridge operations ([Soju06#1657](Soju06#1657)) ([7a0b671](Soju06@7a0b671)) * **proxy:** fence successor bridge claims against the retiring predecessor ([Soju06#1751](Soju06#1751)) ([2c0dc5b](Soju06@2c0dc5b)) * **proxy:** guard model-transition owner-conflict fork ([Soju06#1619](Soju06#1619)) ([52092bc](Soju06@52092bc)) * **proxy:** hold fenced hard turns through cooldown ([Soju06#1739](Soju06#1739)) ([6ff51cd](Soju06@6ff51cd)) * **proxy:** keep abrupt eventless websocket drops account-neutral ([Soju06#1777](Soju06#1777)) ([6c97ad6](Soju06@6c97ad6)) * **proxy:** keep file-pin owner on soft 1011 reconnect ([Soju06#1761](Soju06#1761)) ([f694c44](Soju06@f694c44)) * **proxy:** keep stream idle timeouts account-neutral ([Soju06#1718](Soju06#1718)) ([64da340](Soju06@64da340)) * **proxy:** normalize single-account warmup failures ([Soju06#1774](Soju06#1774)) ([f92bc90](Soju06@f92bc90)) * **proxy:** O(1) shared-future admission waits + event-loop lag watchdog ([Soju06#1842](Soju06#1842)) ([ed2c94d](Soju06@ed2c94d)) * **proxy:** persist file ownership across replicas ([Soju06#1521](Soju06#1521)) ([2cd52e4](Soju06@2cd52e4)) * **proxy:** preserve compact terminal error type ([Soju06#1824](Soju06#1824)) ([78d63e5](Soju06@78d63e5)) * **proxy:** reject truncated chat completion streams ([Soju06#1833](Soju06#1833)) ([6ba083d](Soju06@6ba083d)) * **proxy:** release the API-key reservation on all exits of the models endpoints ([Soju06#1653](Soju06#1653)) ([7007885](Soju06@7007885)) * **proxy:** report suppressed duplicate tool-call terminals ([Soju06#1706](Soju06#1706)) ([25d6374](Soju06@25d6374)) * **proxy:** retain image reservation recovery ownership ([Soju06#1822](Soju06#1822)) ([bd67c64](Soju06@bd67c64)) * **proxy:** route source-owned models off the WebSocket transport ([Soju06#1659](Soju06#1659)) ([08b84a9](Soju06@08b84a9)) * **proxy:** scope backend Codex affinity by thread identity ([Soju06#1703](Soju06#1703)) ([35bbb00](Soju06@35bbb00)) * **proxy:** separate websocket scope cleanup budget ([Soju06#1723](Soju06#1723)) ([fd97cb8](Soju06@fd97cb8)) * **proxy:** settle compact failover before account health ([Soju06#1717](Soju06#1717)) ([3093203](Soju06@3093203)) * **proxy:** settle terminal spool append failures ([Soju06#1775](Soju06#1775)) ([4e48f35](Soju06@4e48f35)) * **proxy:** stop abandoning an unresolved inflight session-creation future ([Soju06#1644](Soju06#1644)) ([57618c8](Soju06@57618c8)) * **proxy:** sweep idle bridge sessions without request traffic ([Soju06#1747](Soju06#1747)) ([3159ebe](Soju06@3159ebe)) * **proxy:** wait on usage-refresh singleflight without asyncio.shield ([Soju06#1897](Soju06#1897)) ([798203f](Soju06@798203f)), closes [Soju06#1896](Soju06#1896) * **quota-planner:** compare warmup reset epochs in UTC ([Soju06#1623](Soju06#1623)) ([e4fa3f2](Soju06@e4fa3f2)) * **reports:** format full Cost values with grouping separators ([Soju06#1814](Soju06#1814)) ([028a75c](Soju06@028a75c)) * **review:** keep Codex review sessions resumable ([Soju06#1678](Soju06#1678)) ([e34db2d](Soju06@e34db2d)) * **server:** serve h2c upgrade offers as plain HTTP/1.1 instead of rejecting them ([Soju06#1782](Soju06#1782)) ([8d265c3](Soju06@8d265c3)) * **usage:** fence leaked live-usage-ingestor tasks and settle their failures deterministically ([Soju06#1783](Soju06#1783)) ([66fd103](Soju06@66fd103)) * **usage:** settle live snapshots after account consolidation ([Soju06#1773](Soju06#1773)) ([3f66c28](Soju06@3f66c28)) * **warmup:** warm paid-to-free transitions ([Soju06#1825](Soju06#1825)) ([68892e7](Soju06@68892e7)) ### Performance Improvements * **accounts:** bound the account-listing live tail with a 2h fold lag and a 30s summary cache ([Soju06#1792](Soju06#1792)) ([c1caa44](Soju06@c1caa44)) * **accounts:** make account deletion a fast mark + background batch drain ([Soju06#1795](Soju06#1795)) ([d4f9e23](Soju06@d4f9e23)) * **api-keys,proxy:** shape ORM hot-path queries ([Soju06#1788](Soju06#1788)) ([7dacb04](Soju06@7dacb04)) * **api-keys:** skip usage reservations when no limit applies ([Soju06#1789](Soju06#1789)) ([8a2d066](Soju06@8a2d066)) * coalesce same-owner sticky session TTL refresh upserts ([Soju06#1790](Soju06#1790)) ([076aab8](Soju06@076aab8)) * **dashboard:** cap projections bulk usage-history read per account ([Soju06#1779](Soju06#1779)) ([d4c43ef](Soju06@d4c43ef)) * **middleware:** convert BaseHTTPMiddleware layers to pure ASGI ([Soju06#1787](Soju06#1787)) ([94057cc](Soju06@94057cc)) * **proxy:** disable permessage-deflate on direct-egress upstream websockets ([Soju06#1786](Soju06#1786)) ([2e4a580](Soju06@2e4a580)) * **proxy:** relay unmodified SSE frames verbatim ([Soju06#1785](Soju06#1785)) ([980572e](Soju06@980572e)) * **proxy:** validate stream payloads only for lifecycle events ([Soju06#1784](Soju06#1784)) ([9d9f099](Soju06@9d9f099)) ### Documentation * **dashboard:** clarify routing, sticky affinity, quota thresholds, warm-up, and eligibility copy ([Soju06#1781](Soju06#1781)) ([6ff22e0](Soju06@6ff22e0)) * **openspec:** archive 90 landed changes and sync their specs ([Soju06#1713](Soju06#1713)) ([c3f0c56](Soju06@c3f0c56)) * **openspec:** archive landed performance and reliability changes ([Soju06#1694](Soju06#1694)) ([6b3db74](Soju06@6b3db74)) * **proxy:** document cluster-wide account cap partitioning ([Soju06#1750](Soju06#1750)) ([560fb50](Soju06@560fb50)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Merge Soju06/codex-lb v1.24.0 while retaining CHEK PostgreSQL schema isolation, already-applied migration compatibility, and internal immutable image publishing. The request runtime returns to the upstream implementation; legacy CHEK recovery tables remain inert schema contracts only. Upstream: 84fde5a
Author
|
Local follow-up evidence while GitHub Actions is in a major outage:
Official incident: https://www.githubstatus.com/incidents/y1t7p9fzrlj2 No CI bypass or merge was performed during the outage. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Soju06/codex-lb@v1.24.0(84fde5a1ed5e0d5a58ccb3ec4b82938b059bf8c5) as an auditable second parent20260826_010000_merge_v1240_and_chek_headsdb_setupfixtureReconciliation boundary
The legacy rowless recovery tables and columns remain mapped only so production schema drift checks and rollback-compatible deployments recognize already-applied DDL. The upstream runtime does not consume them. The former custom rowless/admin/transition request paths and DB-pool experiment are removed.
Post-tag upstream changes, including
Soju06/codex-lb#1863, are intentionally excluded from this stable-tag sync and should be evaluated as a separate hotfix.Validation
make lintmake typechecktests/unit/test_proxy_http_bridge.py: 640 passedmake migration-check: single head, policy ok, schema drift noneconfig.databasePostgresSchema=codex_lb_prodUpstream CI for the exact
v1.24.0tag is green: https://github.com/Soju06/codex-lb/actions/runs/32946623887