Skip to content

Merge beta into main: v3.0.5 release - #143

Merged
GeorgiK77 merged 2 commits into
mainfrom
beta
May 26, 2026
Merged

GeorgiK77 merged 2 commits into
mainfrom
beta

Conversation

@KrasimirKralev

@KrasimirKralev KrasimirKralev commented May 26, 2026

Copy link
Copy Markdown
Contributor

Promotes the v3.0.5 release from beta to production. All commits have been live on beta and validated end-to-end on physical Jetson hardware (simon.local). CodeRabbit + CI green on each component PR before squash-merge into beta.

v3.0.5 highlights (PRs #142 + #144)

  • feat(install): Pin OpenClaw via config/openclaw-target.txt. ClawBox now controls which OpenClaw release the fleet runs, instead of every device racing to whatever npm last published. Bumped in a PR → beta → main, fleet follows on next update.
  • feat(install): After every core install, force-reinstall non-bundled plugins so they bump alongside the core. Without this, a 5.12 → 5.22 core leaves @openclaw/codex stuck at 5.12 and the in-UI updater silently reports 'Up to date'.
  • feat(install): IPv4-first DNS drop-in for the gateway systemd unit. Fixes a 2-min event-loop starvation on networks where IPv6 is advertised but not routed — was surfacing to users as 'Failed to change effort: Request timeout' on model switches.
  • fix(chat): Double-rAF wrap around scrollIntoView so a new message + dropdown re-render don't race the scroll target. Extracted to src/lib/scroll.ts so ChatPopup and ChatApp can't drift.
  • fix(chat): Dedupe-guarded 3s chat.history refetch after ack-only finals (delivery-mirror persona pipeline) with single-flight timer ref + unmount cleanup. Ack-only text suppressed from the rendered transcript so no 'Sent.' bubble flashes.
  • fix(chat) [PR fix(chat): use shared isSentinel() in ChatApp ack-only detection #144 follow-up]: ChatApp now uses the shared isSentinel() helper (was using a local NO_REPLY-only regex). Brings it to parity with ChatPopup so every protocol sentinel from chat-sentinels.ts is handled uniformly — no sentinel can sneak past as a visible assistant bubble or skip the deferred history refetch.
  • feat(ui): Removed standalone 'OpenClaw Update' button (Settings) and OpenClaw ComponentCard (System Update window) so OpenClaw can only be upgraded as part of a full ClawBox release. Endpoint kept for SSH/MCP/admin triggers, no UI surface.
  • chore(release): bump to 3.0.5.

Customer-impact wins shipping with this release

  • Customer reports of Unrecognized key: "supportedReasoningEfforts" (caused by their OpenClaw being older than 4.26) auto-resolve as soon as the device runs System Update — the pin converges every device on the tested 5.22.
  • 'Failed to change effort: Request timeout' on model switches eliminated on dual-stack-but-broken-IPv6 networks.

Test plan

  • Pull main on a Jetson, run the standard update flow, verify version reports 3.0.5.
  • Open System Update window: confirm only ClawBox card shows; no standalone OpenClaw card.
  • Open Settings → confirm 'OpenClaw Update' tile is gone; 'System Update' tile remains.
  • Confirm /proc/<gateway-MainPID>/environ contains NODE_OPTIONS=--dns-result-order=ipv4first.
  • In Chat: send a Cyrillic prompt — confirm the reply arrives in real time without manual refresh.
  • In Chat: switch between Free/Pro and Max tier model — confirm no 'Failed to change effort: Request timeout' error.

Reminders on merge

…142)

* feat(install): pin OpenClaw + refresh plugins + IPv4-first DNS

Three install.sh + updater changes that together let ClawBox control
the OpenClaw runtime on customer Jetsons, instead of devices racing to
whatever npm published.

**OpenClaw version pin**

- New file: config/openclaw-target.txt — single source of truth, one
  line, bumped via PR + beta→main.
- install.sh::step_openclaw_install reads the pin (with OPENCLAW_PIN_VERSION
  env override for QA flows). Falls back to the hardcoded OPENCLAW_VERSION
  constant if the pin file is missing.
- src/lib/updater.ts::getVersionInfo reads the same pin file for the
  'Latest' column in the System Update UI. Flattened the env-override
  branch into plain async/await per CLAUDE.md guidelines.

**Plugin refresh after core install**

Parses 'openclaw plugins list --json' via inline python3 to find non-
bundled plugins, then force-reinstalls each. Without this a 5.12→5.22
core bump leaves @openclaw/codex stuck at 5.12 and the in-UI updater
silently reports 'Up to date'. Runs even when core is already at target
because plugins can drift independently.

**IPv4-first DNS drop-in for the gateway**

step_gateway_setup now writes
/etc/systemd/system/clawbox-gateway.service.d/dns-ipv4first.conf:

  [Service]
  Environment="NODE_OPTIONS=--dns-result-order=ipv4first"

Without this, on networks where the ISP advertises an IPv6 prefix but
doesn't actually route public v6 traffic (common on home/SMB networks),
every Node fetch to a dual-stack host (Telegram polling, OAuth, npm
registry, model providers) hangs ~2 minutes on the dead AAAA before
falling back. The hung socket starves Node's event loop and makes every
WS request slow — surfacing to the user as 'Failed to change effort:
Request timeout' on model switches. No-op on networks where v6 works.

* fix(chat): rAF scroll race + gated history refetch for deferred replies

Two related fixes for the chat UX:

**Double-rAF scroll** (new src/lib/scroll.ts)

When a chat message is appended while another component in the header
re-renders (e.g. the model-picker dropdown becoming visible as the
catalog loads), the scroll target was reading the pre-reflow layout and
landing above the freshly-added message — user thought their send
'disappeared' until they refreshed. The new scrollToBottomAfterLayout
helper waits for two animation frames so the scroll fires after the
next paint regardless of which order React batches the renders in.
Shared by ChatPopup and ChatApp so they can't drift apart.

**Gated 3s history refetch on deferred replies**

OpenClaw can ack a turn with 'Sent.' while the real reply is generated
server-side a moment later via the delivery-mirror persona pipeline.
That reply is persisted to chat history but never streamed back over
the WS — the client used to see only 'Sent.' until the user refreshed.
After 'final' arrives we now schedule one chat.history refetch 3s
later, but ONLY when the final's text was empty or 'Sent.'-shaped.
Normal streamed replies (which arrive via delta+final) skip the
refetch so they don't pay an extra round-trip per turn.

Both ChatPopup and ChatApp get the same fallback path so the windowed
chat app stops being silently broken in the same way the popup was.

* feat(ui): remove standalone OpenClaw upgrade entry points

Both the 'OpenClaw Update' tile in Settings and the OpenClaw
ComponentCard in System Update were customer-facing standalone
upgrade triggers that bypassed the ClawBox-pinned version. With the
pin in place, customers should only upgrade OpenClaw as part of a full
ClawBox release — that way the OpenClaw bump rides through
beta → main alongside any client code changes that accompany it.

- SettingsApp.tsx: dropped the blue 'OpenClaw Update' button. Current
  OpenClaw version is still surfaced in the version-info section.
- SystemUpdateApp.tsx: dropped the OpenClaw ComponentCard; the
  grid-cols-1 sm:grid-cols-2 collapsed to grid-cols-1 since ClawBox
  is the only component card now.
- SystemUpdateApp.tsx::triggerUpdate narrowed to take no parameter
  (only the 'full' path is exposed; the 'openclaw' branch is dead).
  /setup-api/update/openclaw endpoint and startOpenclawUpdate helper
  are kept as a server-side hook for SSH/MCP/admin triggers — they
  just have no UI surface anymore.
- Dropped the dead outer-scope openclawAvail and the openclawAvail
  branch from the status useMemo. Status now flips to 'available' on
  ClawBox deltas only, matching what the UI actually offers.

* chore(release): bump version to 3.0.5

* fix: address CodeRabbit review on #142

Three findings from CodeRabbit's first pass:

- install.sh PIN_FILE parse switched from `tr -d '[:space:]'` to
  `awk '{print $1}'`. The old one stripped ALL whitespace and would
  concat tokens on a multi-field line ("2026.5.22 beta" →
  "2026.5.22beta"). awk matches updater.ts's
  `raw.trim().split(/\s+/)[0]` exactly so the two parsers stay
  identical if the pin format ever grows beyond a single token.

- ChatPopup + ChatApp ack-only 3s history-refetch timer is now
  single-flight: the timer id is stored in ackOnlyHistoryTimerRef
  and any pending timer is cleared before scheduling a new one.
  Also cleared in the component unmount cleanup so a tab close
  during the 3s window doesn't leave a dangling timer.

- updater.ts adds OPENCLAW_VERSION_FALLBACK = '2026.5.3-1' (mirrors
  install.sh::OPENCLAW_VERSION) so a missing pin file no longer
  desyncs the UI from install.sh — both report the same target
  version the device would actually install.

* fix(chat): suppress ack-only finals from rendered transcript

Follow-up to the CodeRabbit outside-diff comment for ChatApp.tsx.

The previous ack-only handling scheduled a 3s chat.history refetch but
still appended the 'Sent.' / NO_REPLY text as an assistant bubble. On
legacy delivery-mirror configurations that produced a brief 'Sent.'
flash before the real reply replaced it.

Both ChatPopup and ChatApp now compute a shared isAckOnly check (empty
text, /^Sent.$/, or protocol sentinel) once, use it to skip the
setMessages append, AND use the same flag to drive the dedupe-guarded
3s refetch. Single source of truth in each component.

On the pinned OpenClaw 5.22 this branch never fires (codex responds
directly without the Sent. dance), but the defensive path is now
correct for any fleet device still on legacy delivery-mirror configs.
@KrasimirKralev
KrasimirKralev requested a review from a team as a code owner May 26, 2026 08:38
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR establishes OpenClaw version pinning via a config file, updates both the install script and client code to read from it rather than npm, implements chat ack-only response suppression with deferred history refetch, simplifies update UI to show only ClawBox updates, configures gateway DNS to prefer IPv4, and bumps the package version.

Changes

OpenClaw pinning, chat, and update flow

Layer / File(s) Summary
OpenClaw version pinning infrastructure
config/openclaw-target.txt, install.sh, src/lib/updater.ts, package.json
Config file establishes pinned target (2026.5.22). Install script reads pin to install/refresh OpenClaw core and plugins. Updater reads pin instead of querying npm registry. Package version incremented from 3.0.4 to 3.0.5.
Post-layout scroll helper
src/lib/scroll.ts
New scrollToBottomAfterLayout utility delays scroll until after layout repaint using double requestAnimationFrame tick, with SSR fallback to immediate scrollIntoView.
Chat ack-only response handling and scroll integration
src/components/ChatApp.tsx, src/components/ChatPopup.tsx
Both components detect ack-only gateway responses (empty, "Sent.", or sentinel content), suppress rendering those messages, and schedule deferred chat.history refetch after 3 seconds (debounced). Both integrate new scroll helper to control auto-scroll timing.
Update UI simplification for pinned OpenClaw
src/components/SystemUpdateApp.tsx, src/components/SettingsApp.tsx
Removes OpenClaw-specific update controls from both components. SystemUpdateApp simplifies triggerUpdate to a parameterless handler that always posts full ClawBox update. SettingsApp removes OpenClaw update button tile.
Gateway DNS IPv4-first configuration
install.sh
Adds systemd drop-in for clawbox-gateway.service that sets NODE_OPTIONS=--dns-result-order=ipv4first to force IPv4-first DNS resolution.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ID-Robots/clawbox#142: Parent PR establishing the same OpenClaw pinning, chat ack-only suppression, DNS configuration, and UI simplification changes.

Suggested labels

enhancement

Suggested reviewers

  • yalexx
  • GeorgiK77

Poem

🐰 A rabbit hops through versions pinned,
No more npm's fickle winds!
Chat says "Sent." then whispers low,
While scrolls dance soft, not steal the show—
Updates flow, one bright path clear. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Merge beta into main: v3.0.5 release' clearly and concisely summarizes the primary change—promoting v3.0.5 from beta to production.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description is comprehensive with clear summary, detailed highlights, customer-impact wins, test plan, and merge reminders, meeting the template structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch beta

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown

CI Summary

✅ Tests

  • Result: passed
  • View run
  • Coverage: statements 70.94%, branches 60.32%, functions 66.78%, lines 73.12%

✅ E2E

✅ E2E Install

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ChatApp.tsx`:
- Around line 256-286: The ack-only detection currently sets isAckOnly using a
NO_REPLY regex which misses other protocol sentinels; update the condition that
defines isAckOnly (the variable near the setMessages/prettifyAssistantText
block) to use the shared sentinel checker isSentinel(text) instead of (or in
addition to) the /^\s*NO_REPLY\s*$/ test so the ack-only branch (including the
ackOnlyHistoryTimerRef logic and deferred loadHistory call) fires for all
sentinel finals; keep the other checks (empty text and the "Sent." regex) intact
and ensure any references to prettifyAssistantText, loadHistory, and
ackOnlyHistoryTimerRef remain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36ad3621-c592-4bf4-a4f4-4e4022392a99

📥 Commits

Reviewing files that changed from the base of the PR and between 6e7907c and 8c91dc0.

📒 Files selected for processing (9)
  • config/openclaw-target.txt
  • install.sh
  • package.json
  • src/components/ChatApp.tsx
  • src/components/ChatPopup.tsx
  • src/components/SettingsApp.tsx
  • src/components/SystemUpdateApp.tsx
  • src/lib/scroll.ts
  • src/lib/updater.ts

Comment thread src/components/ChatApp.tsx Outdated
Addresses CodeRabbit finding on #143: the local NO_REPLY regex misses
other protocol sentinels (still here scuttling around, all good boss,
etc. — see chat-sentinels.ts::PROTOCOL_SENTINEL_REPLIES). Without this
the ack-only branch wouldn't fire for those sentinels, and the deferred
chat.history refetch wouldn't run, so a real reply could be missed.

- isAckOnly now uses isSentinel(text) (matches ChatPopup behaviour).
- The aborted/error streaming-flush branch and the loadHistory message
  filter also switched from inline NO_REPLY regex to isSentinel, so
  every protocol sentinel is handled uniformly across the file.
@KrasimirKralev
KrasimirKralev requested a review from GeorgiK77 May 26, 2026 09:29

@GeorgiK77 GeorgiK77 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Works!

@GeorgiK77
GeorgiK77 merged commit 99a14a9 into main May 26, 2026
7 checks passed
@GeorgiK77
GeorgiK77 deleted the beta branch May 26, 2026 09:32
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.

2 participants