Skip to content

Merge main into beta — reconverge history after the v3.1.11 promotion - #282

Open
yalexx wants to merge 4 commits into
betafrom
sync/main-into-beta
Open

Merge main into beta — reconverge history after the v3.1.11 promotion#282
yalexx wants to merge 4 commits into
betafrom
sync/main-into-beta

Conversation

@yalexx

@yalexx yalexx commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Content-neutral. main and beta already have identical trees, so beta's tree does not change — this only records the ancestry.

Why

The v3.1.11 promotion (#281) landed on main as a single-parent commit: MERGE_HEAD was dropped while creating the promotion branch, so the merge was committed without beta as a second parent. My mistake, caught right after tagging.

The release itself is unaffected — v3.1.11 points at a tree byte-identical to beta, and the updater has no ancestry guard, so every box resolves the tag correctly.

What it would break is the next promotion: with beta not an ancestor of main, beta → main would re-conflict on the same files, for exactly the reason #264 caused today's conflicts. This merge stops that from recurring.

Verification

  • git diff origin/beta after the merge: empty
  • beta's tree is unchanged; only history changes

yalexx and others added 4 commits July 22, 2026 21:46
* Harden gateway recovery after updates (#263)

* fix: migrate legacy openai/<gpt> model+fallbacks to codex on startup (#266)

ChatGPT-subscription boxes (Codex OAuth, no OpenAI API key) that stored
their active model or a fallback as `openai/gpt-5.5` (etc.) before the setup
UI routed ChatGPT picks through Codex hit `401 Missing bearer or basic
authentication in header` on api.openai.com/v1/responses — often only as a
FailoverError days into use, once the OAuth token first refreshes and the
failover chain reaches the keyless `openai/*` fallback.

The chat-model pick route already rewrites openai/<gpt> -> codex/<gpt>, but
only when the user re-picks the model; existing configs never re-pick, so an
updated box stays broken until manually re-selected. Migrate primary +
fallbacks in gateway-pre-start.sh on gateway start, guarded on "codex OAuth
present AND no OpenAI API key" so keyed / dual-auth boxes (where openai/* is a
valid route) are left untouched. Mirrors CODEX_SUPPORTED_MODEL_RE /
hasOpenAiApiKeyProfile / hasCodexOauthProfile in
src/app/setup-api/chat/model/route.ts.

Verified against 6 fixtures: openai->codex primary+fallback migrate; keyed
box untouched; non-supported (gpt-4o) primary left as-is; already-codex no-op;
no-codex-auth untouched; composes with the retired-Sonnet migration. bash -n
and py_compile pass.

Co-authored-by: Mike (IDRobots) <mike@idrobots.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(3.1.10): responsive chat header pills (wrap instead of overlap on narrow chat) (#267)

* fix: chat header pills squeeze + truncate cleanly on narrow panels

On a narrow chat the provider / model / thinking selector pills overlapped
into an unreadable strip. Two parts:

1. .header-dropdown-trigger gets width:100% so the button fills its
   flex-shrinking .header-dropdown parent. Previously the button kept its
   content width and spilled past the shrunk parent, so overflow:hidden on
   .chat-header-pills clipped / overlapped the pills instead of the labels
   truncating. Now every pill gives ground evenly and its label ellipsizes
   (the chevron stays — it's reserved in the 24px right padding).

2. Single row (no wrap) + overflow:hidden, and the chat window clamps to
   MIN_CHAT_WIDTH (340px) on both resize paths + the rendered width, so the
   window stops shrinking once the pills reach a readable minimum instead of
   smashing them.

The open menu is portaled to <body> (HeaderDropdown), so clipping the row
can't hide it. Verified at 320-420px: even truncation, carets visible, zero
overlap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: open chat from mascot with a macOS-style animation, no corner flash

- Stop streaming the frozen mascot's position into mascotX while the chat is
  open (page.tsx). That nudged mascotX for a frame right after opening, so the
  popup flashed to the wrong corner before settling. mascotX is now captured
  once from the tap.
- Grow the popup OUT of the mascot: transform-origin pinned to the popup's
  bottom edge, aligned horizontally with the mascot, and scale 0.82 -> 1 on an
  easeOutExpo curve (cubic-bezier(0.16,1,0.3,1)) over 0.36s. Smooth, premium,
  emanates from where you tapped instead of scaling from the popup centre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: bump SW cache clawbox-v3 -> v4 to invalidate stale assets on the 3.1.10 chat-UI changes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: keep chat popup header on-screen on short/zoomed viewports

The un-dragged popup anchors from the bottom (bottom:170 above the mascot,
bottom:65 in tray mode) but its maxHeight budget was a flat 100vh-60px, so on
viewports shorter than ~680px (small windows, browser zoom) a 500px-tall popup
shoved its whole header — pills, status dot, close button — off the TOP of the
screen (rect.y = -76 measured on a 594px viewport). Subtract the bottom anchor
from the height budget per mode (+12px top margin) so the header is always
visible and the popup just gets shorter instead.

Found by driving the real desktop over CDP and sampling the popup rect during
open; the same probe confirmed the mascot-open animation runs and there is no
left-corner flash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Mike (IDRobots) <mike@idrobots.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Mike (IDRobots) <mike@idrobots.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Merges beta into main for the 3.1.11 release. The conflicts came from #264
(the 3.1.10 gateway-recovery squash landing on main) diverging from the same
work as it evolved on beta; every one resolved to the beta side. The
resulting tree is byte-identical to origin/beta -- the exact code verified
on hardware today.

What ships:

- codex: route codex turns through the app-server harness (#280). Without
  agentRuntime, core posts to /backend-api/responses -- a browser endpoint
  Cloudflare challenges -- and every turn dies with an HTML error page.
- codex: restore the credential #278 broke, and migrate legacy auth profiles
  into the sqlite store core actually reads (#279).
- codex: stop mirroring the rotating refresh token into every codex-home, so
  a single-use refresh token cannot be spent twice (#278).
- codex: default ChatGPT auth to gpt-5.5; unblock GPT-5.6 for entitled
  accounts (#275, #276).
- gateway: stop update/boot from bricking the box; parameterised health and
  recovery waits.
- memory: local embeddings so semantic memory works without an API key.
- docs/site: point references at clawbox.com; measured Wi-Fi and local-model
  performance figures.

Hardware verification (2026-07-28, boxes .52 and todor):

- HTML/Cloudflare failure reproduced on the old build, fixed by the update
- 401 profile=- auth failure reproduced, fixed by the update
- forced OAuth token rotation x2 on both boxes -- codex survived both
- 3.1.10 -> 3.1.11 through the shipped updater: pass
- 3.1.5 -> 3.1.11 through the shipped updater incl. reboot: pass, ~4 min
- CI: 1499 unit tests, E2E, E2E-install in a real systemd container

Known, not a regression (present in 3.1.10, fix queued for 3.1.12): the Codex
model picker offers gpt-5.4/gpt-5.4-mini, which free-tier ChatGPT accounts
cannot run -- upstream 400s with no failover. The default is gpt-5.5, which
every tier runs, so this only bites a user who picks an older model by hand.
Release v3.1.11 — promote beta to main
The v3.1.11 promotion (#281) landed on main as a single-parent commit --
MERGE_HEAD was dropped while creating the promotion branch -- so beta is
not recorded as an ancestor of main even though the trees are identical.
Left alone, the next beta -> main promotion would re-conflict on the same
files for the same reason #264 caused today's conflicts.

This merge is content-neutral: main and beta already have identical trees,
so beta's tree does not change. It only records the ancestry.
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

CI Summary

✅ Tests

  • Result: passed
  • View run
  • Coverage: statements 70.1%, branches 60.1%, functions 65.41%, lines 72.07%

✅ E2E

✅ E2E Install

@github-actions

Copy link
Copy Markdown

🦀 ClawReview

Poked my eyestalks out for this one. Quick tour:

A content-neutral merge commit that reconnects beta as an ancestor of main after the v3.1.11 promotion (#281) was committed with only one parent. No files change — the tree on beta is byte-identical before and after. This is purely a history repair to prevent the next beta→main promotion from re-conflicting on the same files.

At a glance

  • 🧹 Chore · touches git branch lineage / release promotion workflow
  • Base branch: beta · +0 source / +0 tests across 0 files
  • ✅ base beta matches the beta-first convention
  • 🟡 title doesn't follow type: description (feat/fix/chore/docs/…)

Good to know

  • ℹ️ Zero file changes — diff is empty. This only writes a new merge commit into git history.
  • 🟡 Without this fix, the next promotion from beta would conflict on the same files, repeating today's Promote v3.1.10 gateway recovery to main #264 incident.

— ClawReview 🦀, your resident reef crab. Just orientation — CodeRabbit does the line-by-line, humans do the merge. Conventions: docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant