Fix plain openclaw startup on trusted POSIX Bun-only global installs by
pinning the installed Bun executable. Keep paths as inert launcher data so
released updaters can relocate them safely, with existing ownership, Doctor
consent, and rollback handling.
Validated 114 focused tests per runtime, package/tarball integrity, native
published-9.7 plain/apostrophe upgrades, and byte-exact rollback controls.
Document the inherited 9.7 readiness wait and the 9.6 upgrade limitation.
CI exception: the only failing test job reproduces an inherited browser
mock-cache defect already fixed on main by #162796; all other selected
checks passed. Exact-head ClawSweeper found no actionable code issue.
Keep ordinary session browsing available without unauthorized external catalog requests, and retire catalog state when read authority is lost.
Fixes#162900.
OpenAI's gpt-6.1 generation publishes no dated snapshots either, so the Reef
guard rejected the Team configuration that pinned gpt-6.1-sol and the channel
could not start. Admit the exact id beside the gpt-5.6 ids, with the same
documented residual risk; bare family aliases stay rejected.
A retained legacy session transcript truncated to 0 bytes (an earlier process left two .jsonl.bak-<pid>-<timestamp> backups beside it) put Doctor into an unresolvable retained_plugin_source_conflict loop: with the file present recovery reported the source incomplete and protected, with it moved recovery reported the source changed, and every Gateway start logged a degraded state. The retained-source verifier required a transcript header and ignored the backup siblings. Recovery now verifies those backups, imports missing history through the existing importer with identity and ownership checks, and reversibly archives the empty original; canonical edits and deletions remain authoritative, and unrecoverable sources stay protected with exact manual restoration instructions.
Closes#162817
Doctor's own inspection workers and cached shared-state readers kept the conservative holder census from reclaiming abandoned updater runtimes.
Settle those resources through their existing owners while retaining maintenance authority, then run the unchanged census. Preserve independent holders, runtime-only service boundaries, and the refusal to restart after resource settlement fails.
Validated 53 focused/regression cases on Node and fork Bun, published 2026.9.7 upgrades and positive/negative custody on both runtimes, scoped-clean P2 review, and green exact-head CI/security checks.
Preserve explicitly typed empty strings in DeepSeek DSML tool calls, including a final empty duplicate argument. Keep non-string rejection and terminal-completion checks unchanged.
Verified the current-head CI gate and 46 isolated scalar/HTTP-streaming regression tests; fresh independent review found no actionable issues.
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Await buffered progress receipts in the fresh candidate receiver before finalization, preserving captured database context and live executor/requester admission. Refused receipts stop replay; uncertain writes retain the existing cleanup failure. This candidate-side change preserves published drivers, schemas, stored state, and recovery policy.
Tests across ACP abort-cause, cron command running, the Windows Startup-fallback environment, the Gmail watcher, link-understanding transport, secrets resolution, install policy, the skills library persistence, and the worker runtime polled PID/marker files and the process table or raced child completion against fixed timers, so a fixture child that outlasted the bound on a loaded host failed the test.
The test owner now supplies readiness and completion through retained IPC/close promises, existing fixture receipts, or the worker's idle-ready callback; durable PID/profile records are written before a receipt wherever operation settlement can win the transport race. The shared src/test-utils/process-tree.ts forking fixture reports readiness through a fixture receipt (both callers updated), and an unused local PID-file polling helper is removed. Five files keep one deadline-free, signal-bound exact-PID check where the cleanup owner exposes no adopted-descendant reap promise. Native-platform waits without native proof here, release-file barriers that need a host-to-child release channel, and existing TERM/KILL rescue escalations stay unchanged. No product source, test timeout, product timeout, or assertion meaning changed.
Part of the polling audit from #162274. Proof on Blacksmith Testbox: nine delayed-work original-fail/candidate-pass pairs, four forced-abort cleanup probes, 220 standalone runs, 27/27 Node owner-shard replays, Gateway E2E shards 2/4 and 3/4 3/3 each on the rebased head, tsgo core and core-test, OpenGrep, type-aware lint, base-aware timeout-race ratchet; Codex autoreview clean.
* fix(ui): localize onboarding welcome in Chinese
Use the selected Control UI locale in the existing connection metadata and translate onboarding prose and question labels through the wizard catalogs. Preserve command reply payloads and English fallback. Covers simplified and traditional Chinese without a protocol or configuration change.
* fix(ui): preserve onboarding locale during catalog loading
Use the i18n owner’s requested locale for the existing Gateway connection metadata, including while the lazy translation chunk is pending. Preserve rendering fallback and offline retry behavior. Add Gateway locale forwarding coverage, a held-chunk browser regression, and operator documentation. Remote proof passes 173 owner tests and four browser cases; the new browser case takes 734ms. No protocol or config surface changes.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Fixes#162777.
The claude-cli transcript writer now records `__openclaw.runId` on the terminal assistant row, matching the field other writers set and the completion reader expects. CLI-backed subagents now deliver their complete final answer instead of the truncated-by-retention fallback.
Proof: real isolated Gateway with a fake claude-cli backend. main delivered the fallback and dropped the tail of a 14,025-character child answer; with this change the parent received all 400 lines including the tail marker. A failed CLI child still reports its error with no fabricated final. Live control: one real OpenAI embedded subagent turn delivered its exact answer. Regression tests fail before and pass after.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Repair runtime-sensitive test fixtures by awaiting owned completion and cleanup, preserving pinned-read and cancellation assertions, comparing unordered membership, and selecting the runtime required by the native preflight contract.
No production code or runtime partitioning changes. All 12 affected files pass on Node 24; 11/12 pass on checksum-verified Bun 17c9, with the known CI-owner runtime failures documented separately. Exact-head CI passed on its first attempt. Type, lint, format and import-cycle checks pass; the local graph-budget failure reproduces unchanged on main.
Reuse existing record, string, listener and path-containment helpers in core recovery and preparation. Inline two single-use forwarding constructs while preserving property-read, authority, cleanup and notification ordering.
No config, persisted format, protocol, error text or public SDK changes. Removes 25 net production lines. Remote proof passed 809 tests across 37 files, both cycle checks and byte-identical SDK declarations; independent P2 review found no actionable issues. Shared validation blockers and their owning fixes are recorded in the PR evidence.
When the candidate migration rehearsal is terminated at its budget, the update reported "The target configuration could not provide a usable inference route", a message from secondary repair setup, instead of what actually ran out of time. validateUpdateCandidateCanary now records the rehearsal step, elapsed seconds, and the last sanitized output in the failure fact and the update report. The flat 300 s cap in the report belongs to the installed 2026.9.3 driver; main already derives the rehearsal budget from database size (a 420 MB fixture rehearses in 24 s within an 802 s budget) and uses the responsive integrity child from #162213.
Refs #162737
Since #162586 package-activation admission captures the executable identity before npm staging; the LaunchAgent fixture in update-cli.candidate-activation.test.ts supplied a nonexistent bin/node, so admission exited early and the asserted command was undefined on main. The fixture now uses the suite's real Node executable; every assertion is unchanged.
Refs #162586
Share PixVerse's mode capability construction while preserving exact property order and fresh per-mode and per-provider allocations. Remove Comfy's private workflow-source wrapper and discarded configuration-probe clone; keep execution cloning before request mutation.
Remove 29 net production lines. Exact capability serialization/reference parity, both full provider suites, plugin contracts, SDK comparison, import boundaries and zero-cycle checks passed. Fresh isolated review found no actionable issue.
* test(android): drain managed image decoding in chat preview tests
ChatImagePreviewTest warmed decodeImageBytes and then raced Compose's
one-second waitUntil against ChatManagedImage, which decoded on a hardcoded
Dispatchers.Default. Compose idleness and its clock do not own that work, so
a slow decode under CI load could time out before the image appeared. This is
the latent sibling of the reader race fixed in d3fbeaac8f.
Rename that commit's LocalBase64ImageDecodeDispatcher to
LocalChatImageDecodeDispatcher and route ChatManagedImage through it, keyed on
the dispatcher like the Base64 path. Production still defaults to
Dispatchers.Default. The preview fixture injects a StandardTestDispatcher on
its own TestCoroutineScheduler and drains it explicitly: zero images before
the drain, exactly one after. The same drain replaces the post-restore poll,
whose dialog assertion is now assertIsDisplayed. The warm-up is gone; the
post-drain count proves the fixture decodes.
Proof: a temporary env-gated 1500 ms sleep inside the managed decode failed
all 9 original cases with ComposeTimeoutException at the openManagedImage
waitUntil, and passed 9/9 after the migration (the restore case took 3.24 s,
two delayed decodes, confirming the restored composition decodes through the
injected dispatcher). The probe is absent from this change. Probe-free
:app:testPlayDebugUnitTest passed preview 9, reader 26, codec 15, media
layout 10, and media transcript 1; :app:ktlintCheck and git diff --check pass.
Test cost: preview class 53.9 s in Gradle, of which 49.2 s is the first
case's Robolectric SDK 34 bootstrap; the other eight cases total 4.6 s.
* test(android): drain chat image decodes in media, transcript, and composer layout tests
ChatMessageMediaLayoutTest, ChatMediaTranscriptLayoutTest, and the
composer camera/Photos case still polled decoded images with a 1000 ms
wall-clock waitUntil while decoding ran on Dispatchers.Default. Provide a
StandardTestDispatcher through LocalChatImageDecodeDispatcher and drain it
explicitly, matching ChatImagePreviewTest. An env-gated 1500 ms decode
delay failed 10 cases before and none after.
## What Problem This Solves
Native chat lowercased complete session keys when admitting Gateway events. Distinct Matrix rooms/threads, Signal groups, or catalog conversations whose opaque IDs differed only by case could therefore appear in the selected transcript.
## User Impact
Native chat keeps those conversations separate while continuing to accept structural routing aliases. Stored keys, Gateway wire formats, main/global routing, and composer identity are unchanged.
## Why This Change Was Made
The sidebar already implemented the comparison contract used by the Control UI. Move that implementation to the existing shared session-key owner and use it for sidebar, native event filtering, and the public default-main matcher. Catalog bodies remain fully opaque; the canonical Control UI contract normalizes only their agent prefix. Matrix/Signal routing words retain their existing normalization.
A five-case regression passes actual `session.message` frames through the payload codec and registered dispatcher. Each case rejects a different opaque ID and accepts its structural alias. The existing public-matcher table also covers its separate Talk-facing API contract.
## Evidence
- Remote `check-changed` passed on Blacksmith Testbox.
- Both import-cycle checks passed with **0 cycles**.
- Focused source review and isolated independent P2 review completed. The one review concern about lowercasing the catalog discriminator was rejected against the existing Control UI source contract, which deliberately preserves the complete catalog body.
- Swift formatting and `git diff --check` passed.
- Exact-head hosted OpenClawKit CI passed 2,000 tests in 168 suites (35.259s), plus 21 NativeState tests. The new five-case frame/dispatch regression passed in 5.206s including concurrent-suite scheduling. Isolated-file wall time was not measured. The regression has not been executed against the original implementation: Blacksmith supports Linux only, and the AWS existing-host Mac route returned no available Dedicated Host. The original whole-key lowercase path and its event-dispatch effect were traced directly; this is source evidence, not an observed baseline test failure.
- Tests add no sleeps, polling, process boots, or production seams. The full macOS app and iOS smoke jobs remain separate hosted evidence.
Found during the sibling duplication investigation. The production change removes one net line by sharing the existing comparison owner; tests add 52 lines and documentation adds three.
## Fixes found along the way
The macOS cloud-worker fixture signaled readiness through file existence while `printf` was still writing its argument capture. Hosted run 36887822355 observed output ending at `--ephemeral`, before the final display-name arguments. Publish the complete capture with a same-directory temporary file and atomic rename; every original assertion and timeout stays intact. This fixture-only repair has a clean independent P2 review. Linux Testbox proof passed 25 runs per enrollment mode (50 total), two gated atomic-publication controls, and two original-publication adverse controls. The proof verified the exact committed fixture bytes and preserved every argument, including a Unicode path. Both cycle checks again reported zero. This proves shell fixture publication; updated-head Mac ProcessIdentity/AppKit and full native CI remain separate evidence.
That run also encountered `AXError.attributeUnsupported (-25205)` while the test helper requested the application's accessibility windows, before GatewayInstallerView's text/action assertions. The same helper failure is documented in #158049 without a proven fix. The affected owners are unchanged by this PR, recent inspected main runs passed, and no matching current-main failure has been established. The assertion is retained; this earlier failure is not claimed fixed or bypassed.
* fix(daemon): report the selected launchd job state
Decode runtime facts through the existing root-field parser so nested coalition states cannot overwrite the selected job state. Reuse those facts for native observation and cleanup PID selection, remove the flat parser, and preserve decoded string trimming.
Regression reproduced before repair. Validation: 396 sibling cases, 15 final affected cases, selected checks and independent P2 review pass. A read-only native macOS probe matches independently extracted root state and PID. Production delta: -6 lines; no schema or service mutation change.
* test(daemon): use native launchd framing in update fixtures
Complete the transport fixture migration in update, Doctor, retained-policy and managed-handoff tests. Keep all production code and safety assertions unchanged.
Reproduced both CI failure groups before repair; all 13 original failures, additional fixture siblings, affected checks and full-candidate P2 review pass. Preserve the broad macOS systemd-mode failures and bounded baseline attribution without claiming all those local cases pass.
## What Problem This Solves
A few config, protocol and tool definitions still duplicate defaults, field shapes and validation predicates already owned elsewhere.
## User Impact
No behavior or schema change. This removes 38 net production lines across eight files while preserving config defaults, validation results, protocol output and model-facing descriptors.
## Why This Change Was Made
Reuse the existing Voice Call context schemas for outer defaults, with `parse({})` preserving the existing independence of nested default arrays. Reuse protocol union/property owners for cron, coverage, reload metadata and `agents_list`, and use the existing closed-object helper for two remaining placement schemas. Config lookup and plugin-path matching use their established predicates.
No dependencies, public SDK exports, configuration options, protocol bumps or tests are added.
## Evidence
- Fresh independent Codex review through P2 found no actionable findings.
- Exact committed tree verified on an isolated AWS lease from the pinned base plus a verified Git bundle. Before/after captures preserve Voice Call manifest bytes, input/output schemas, raw and registered config parsing, cross-route identity and three mutation sequences; protocol schemas/validators, `agents_list` descriptors/compact hint/Code Mode API text, and config lookup results also match.
- Both cycle checks report zero. All 48 plugin-contract files / 1,132 tests passed; full `check-changed` passed in 961 seconds. Config schema and generated Voice Call manifest checks passed.
- SDK surface/API checks passed: no entrypoint or direct-export changes; 126 reachable declaration-reference changes are recorded separately from runtime/schema parity.
- The initial broad test selection referenced two retired test paths, and the isolated protocol check initially lacked its base-ref metadata. The corrected selection passed all eight shards in 155.01 seconds, including the full Voice Call suite (53 files / 614 tests), and protocol:check passed with zero generated drift. These were proof-harness repairs, not product changes.
- Hosted CI on the published head is the final test gate. No tests, checks or builds ran on the overloaded Mac.
Keep pending attachment claims with the submit guard until durable admission. Retire only unchanged files in the captured current composer scope while preserving newer drafts and overlapping submissions.
Related: #137427. Broader pre-admission handoff work remains separate.
Reuse existing cache, timeout, error-code, and method-policy owners across Gateway flows. Share lifecycle registration preparation while preserving live-generation guards and terminal-only persistence.
Remove unused heartbeat return state and test-only shutdown overrides; retain the real-child cleanup regression through its existing module mocks. No wire, config, CLI, or persisted-state changes.
* fix(test): load compiled-subprocess declarations at collection
The first load of a compiled-subprocess declaration in a Vitest invocation
prepares the whole compiled worker generation (tens of seconds warm, minutes
cold). 387 test files first reached a declaration through an await import()
inside a test body or hook, so their first test or hook absorbed that
preparation and could time out.
Move those loads to collection at their owner: static subject imports,
static imports in the shared helpers that owned the lazy load, and a named
side-effect preload (src/test-utils/prepare-compiled-subprocesses.ts) for
suites that re-import their subject per test or whose first load happens in
production code they call. plugin-test-runtime keeps the host-capability
fixture lazy so its other consumers do not start loading that graph.
Document the rule in docs/help/testing/writing-tests.md.
335 of the 387 files now load their first declaration before collection
ends; 52 remain (48 extension tests needing an SDK preload, one package
test, three files over the line cap).
* fix(test): retain npm install fixture reuse
Preserve the existing failedSpawn reuse after merging main so the collection-time preload stays within the line-cap ratchet. Assertions and import ordering are unchanged.
* fix(test): keep memory-core facade cold-import assertion meaningful
The static subject import ran before beforeEach reset the loader mock, so the cold-import assertion passed vacuously. Restore the in-test subject imports after mock setup and preload the compiled-subprocess declaration during collection instead.
* fix: prevent delegated work from stopping silently
* fix: preserve frozen bootstrap metadata and refresh task prompts
* test: retain a foreign manager in frozen hydration proof
* test: exercise Corepack bootstrap warnings during hydration
* test: align completion fixtures with required private replies
Verify meaningful private outcomes, retained completion authority after inline waits, and one real write with no recovery replay. Start the browser fixture completion deadline when its held model is released.
* fix(node): recheck upload cancellation after opening snapshots
* test: group subagent typechecks with session ownership
* refactor(plugins): deslop feature plugin helpers
Share existing request, transcript, approval-completion, timer, and normalization owners across feature plugins. Remove unreachable FaceTime legacy PID discovery and private forwarding aliases while preserving admitted contracts.
Fix Workboard rejected-upload cleanup so metadata-budget rejection deletes the blob once and preserves its original error. Extend real SQLite boundary coverage and retain sibling-specific extraction, release, and lifecycle policies.
* fix(plugins): finish shared helper boundary checks
Keep Google API errors private and exercise their bounded response and cleanup ordering through the public Meet request. Use the SDK deferred owner for the supported TypeScript library and remove the Slack contracts runtime import.
* style(onnx): omit the default deferred type argument
* test: preserve POSIX backslashes at home path boundaries
* fix: preserve literal backslashes in POSIX home path displays
* test: format POSIX home boundary regression
The session-store fixture cleanup in 57e0aaa1c1 removed the chat abort
persistence test's max-lines suppression but left its baseline entry.
Canonically prune that one obsolete entry, shrinking the list from 650 to
649 and restoring the suppression ratchet. No allowance is added.
Closes#162802
## What Problem This Solves
Fixes Gateway heap exhaustion during Codex session discovery on large agent fleets. Thanks to @609NFT for the report and allocation profiles.
## User Impact
Large fleets can finish startup and retain their native Codex session catalog without retaining a whole fleet configuration for every agent/home pair. No schema, stored-data, configuration, or catalog-output changes are required.
## Why This Change Was Made
The existing config-identity cache now owns one captured configuration per generation. Agent/home entries keep their separate directories and cloned connection options, but share that captured configuration. Config reload isolation and source-specific backoff remain unchanged.
No overlap with Pash/Sarah changes.
Codex source inspection: `codex-rs/app-server/src/request_processors/thread_processor.rs:2524–2608` in the sibling Codex checkout confirms that `thread/list` consumes pagination and filter parameters; the fleet configuration capture being repaired belongs to OpenClaw, not the native listing protocol.
## Evidence
Compared baseline `7ef388bac8` with candidate `f0ae922f60922f2b357bc3d5af22a2142799fbe0` in isolated Linux arm64 Docker containers, Node 24.21.0. The synthetic profile contained 739 agents, eight explicit Codex homes, and three native sessions (380,439 bytes of configuration).
| Check | Baseline | Candidate |
| --- | --- | --- |
| Real Gateway, 2,560 MiB heap | Heap OOM at 100.3 s | Survived the six-minute window; clean shutdown |
| Sampled catalog allocations under `resolveRequestOptions` → `structuredClone` | 2.248 GB | 5.48 MB |
| Post-startup heap, 150–360 s | Process already terminated | Bounded at 377–566 MiB |
| Complete startup, separate 6,144 MiB control without profiling | 98.34 s | 93.83 s |
| HTTP listening, same startup control | 50.52 s | 49.56 s |
The larger-heap startup control lets the baseline complete startup rather than comparing a successful candidate against an OOM.
- Real `sessions.catalog.list` Gateway RPCs returned exactly the three expected native session IDs for agents `agent000`, `agent001`, and `agent738`.
- Exact-head live gate: a real OpenAI `gpt-5.4-mini` turn through the candidate Gateway's Codex harness returned `CATALOG_LIVE_OK` on the 739-agent profile (5.449 s). Its temporary read-only credential file and container were removed.
- Regression failed on baseline: four fleet config clones instead of one across multiple agents/homes. It passes on the candidate and also checks reload allocation isolation; the regression itself took 5 ms.
- All 38 focused tests passed across `session-catalog-listing-cache.test.ts`, `session-catalog-request-lifetime.test.ts`, `session-catalog-homes.test.ts`, and `session-catalog-backoff.test.ts` (38.67 s wall).
- Standalone `pnpm test extensions/codex/src/session-catalog-listing-cache.test.ts --maxWorkers=1` passed (32.61 s wall, including runner preparation).
- Built and exercised the real candidate runtime with `pnpm build`. Broader static and project-wide validation is left to CI.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026.9.7 lengthened the compile-cache marker from <mtime>-<size> to build-<buildId>, which pushed cache paths on Windows into the directory-length window where Node 24's module.enableCompileCache() never returns (nodejs/node#66438); every OpenClaw process start could then spin at full CPU. The shared cache owner now uses a 16-character hash of the full build id as the marker, refuses Windows cache paths over 200 characters with a recorded warning (cache off for that process), and no longer propagates an unsafe inherited cache path to children. Legacy namespace handling and POSIX behavior are unchanged; native Windows/Node 24.18 completes the packaged CLI with a 220-character TEMP in 2.4 s.
Closes#162821
Keep blocked work visible in a Markdown-only card when no authorized step can proceed, rather than repeatedly saving unfinished checklist steps. Preserve the bounded completion check unchanged.\n\nRefs #162878
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: RomneyDa <6581799+RomneyDa@users.noreply.github.com>
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
* perf(ui): pause session run rings outside the sidebar viewport
Problem: Running sessions kept 11 ring elements in per-frame restyles,
including rows outside the sidebar viewport.
Fix: Use one sidebar-owned IntersectionObserver to toggle a paused class
on ring and paired-avatar trace indicators. Preserve queued and
reduced-motion behavior, and release observations on row removal or
sidebar disconnection.
Proof:
- Native browser regression: 1 passed, 0 failed; 4.05s wall with
--maxWorkers=1. Covers scrolling, zero-ratio edge contact, queued state,
indicator replacement, run completion, and reconnection.
- Full ui/src/components and ui/src/pages/chat/components suites under
ui/vitest.config.ts: 449 files, 7276 passed, 0 failed; 53.20s wall.
- chat-composer and chat-composer-microphone suites: 35 passed, 0 failed;
11.06s wall with --maxWorkers=1.
- UI E2E: mobile-chat-session-menu, chat-flow.streaming,
sidebar-interactions, chat-composer-focus, sidebar-session-stability,
sidebar-cached-list-stability, and sidebar-restart-recovery:
7 files, 38 passed, 0 failed; 77.68s wall.
- pnpm tsgo:ui passed in 10.80s; node scripts/check-changed.mjs passed
in 427.82s; git diff --check passed.
- Coordinator Codex autoreview of the uncommitted tree: scoped-clean at P2.
* fix(ui): register session run rings for visibility from the row render
Problem: The sidebar host scanned before its roster child committed new
rows. The real-roster regression rendered 11 running rows, but the
host-level registration observed only 1, leaving offscreen rings active.
Fix: Register actual ring and trace elements through a Lit directive
supplied by the native and catalog row renderers. Share one lazy
IntersectionObserver per scroll root through a WeakMap, unobserve on
directive disconnection, and disconnect empty groups. Only observer
callbacks toggle the paused class. Remove the host scanner and retain
the existing CSS, queued state, and reduced-motion behavior.
Proof:
- Real-roster regression fails on b0c90ed56391 with 1 of 11 rows observed;
the correction observes all 11, pauses ten offscreen indicators, and
resumes them after scrolling into view.
- Updated browser file: 2 passed, 0 failed; 4.19s wall with --maxWorkers=1.
Retains edge-contact, indicator replacement, completion, and
reconnection coverage.
- Owner suites: 126 passed across 4 files; 42.66s wall.
- Full ui/src/components and ui/src/pages/chat/components suites under
ui/vitest.config.ts: 449 files, 7277 passed; 45.28s wall.
- chat-composer and chat-composer-microphone suites: 35 passed;
11.56s wall with --maxWorkers=1.
- UI E2E: mobile-chat-session-menu, chat-flow.streaming,
sidebar-interactions, chat-composer-focus, sidebar-session-stability,
sidebar-cached-list-stability, and sidebar-restart-recovery:
7 files, 38 passed; 54.47s wall.
- pnpm tsgo:ui passed in 9.90s; node scripts/check-changed.mjs passed
in 717.19s; git diff --check passed in 0.13s. The initial changed-check
source scanner exited with SIGILL before selecting checks; the
unchanged replay passed without a tooling or gate change.
- Coordinator Codex autoreview at P2 confirms the working-tree correction
resolves the INDEX-only host-registration finding, with no further
P0-P2 findings.
Runtime preserves authored cron definitions and consumes canonical ownership, delivery, and schedules. Doctor performs supported repairs with verified backups, while update rehearsals retain live legacy sources for the live import. Preserve durable receipts, explicit scope boundaries, and deletion fences.
Published 2026.9.7 updater acceptance passed on Testbox. The separately matched native published-driver CI timeout also fails on clean main; the PR records that inherited failure and its limits.
## What Problem This Solves
Reply orchestration and channel adapters still repeat normalization, routing preflight and send sequencing already owned by shared helpers.
## User Impact
No user-visible behavior change. Reply text, send ordering, receipt reporting, reply targets, private-webchat routing fences and account policies retain their existing behavior.
## Why This Change Was Made
- Use the existing routing decision owner for preflight while preserving lazy runtime loading and the original pre/post-await reads.
- Carry prepared chunk limits into coalescing, use the conversation-label owner directly, and consume LINE's already-normalized media URLs.
- Route SDK text sends through the shared sequence helper, preserving empty chunks, successful undefined results and observer receiver/read timing.
- Remove Signal's single-use attachment formatting wrapper and Zalouser's duplicate sender object; reuse Feishu's local backoff-code owner and the shared receipt normalizers.
- Remove a redundant SDK side-effect import whose value re-export already evaluates the same module.
## Evidence
Independent review completed with no actionable P0–P2 findings after correcting observer forwarding in the shared send loop. Existing assertions are retained; the A2UI fixture readiness fix is described below.
Net **30 production lines removed** across 10 files; one exact shrink-only assertion allowance changes from 3 to 2. One existing UI test gains an event-driven readiness wait; no assertions or cases are removed. No public SDK, configuration, or generated-asset changes.
Blacksmith Testbox proof:
- Candidate `ca6e15f7b39e99e0e4719f4cf2d20c1c58dbe7d7` before the identifier-only lint repair: both cycle checks report **0**; SDK API comparison reports **no changes**; 11 focused files / 320 tests pass; 48 plugin-contract files / 1,132 tests pass; both extension-import inventories report no violations.
- Full sibling coverage on the runtime-identical candidate before reverting private type factoring: 300 files / 4,290 tests pass in 267.24s, with three existing Signal skips. Includes full LINE, Signal, Feishu and Zalouser suites and all dispatch-from-config siblings. The private type factoring was dropped after API comparison detected declaration changes.
- Repaired head `2d1222e5c02406eadc874494e089e4c50a7852bc`: targeted core lint reports zero warnings/errors, 71 SDK reply-payload tests pass, 1,132 plugin-contract tests pass, and both cycle checks report zero. Fresh independent review is clean. This only renames two callback parameters and leaves SDK declarations and runtime behavior unchanged.
- Hosted CI run [36881459169](https://github.com/openclaw/openclaw/actions/runs/36881459169) found two task-caused `eslint(no-shadow)` errors in those callbacks; the repaired head fixes both. The broad changed check independently reached the same error after all type graphs and unused-export scans passed. The repaired-head full `check-changed` replay passed, including all type graphs, all three unused-export scans, lint, and boundary guards. An earlier task-caused assertion ratchet failure was resolved by shrinking the exact Feishu allowance.
The temporary proof checkout was reconstructed from the reviewed Git bundle and verified by exact revision and changed-file hashes after source packaging stalled. No result from a mismatched transport HEAD is counted as proof.
### Fixes found along the way
Hosted CI exposed a browser-fixture race at `board-a2ui.e2e.test.ts:154`: the inline module was inserted but its custom element was not necessarily registered before the first action. Awaiting `customElements.whenDefined` fixes that setup boundary. Production Canvas code and all count/payload assertions are unchanged.
Repair head `fab4fae45be5fd501b7aa21d93ca0ecb90575988` passed all four browser cases and 21 fresh-context executions of the reconnect case on Linux (`--repeats 20 --retry 0`). The full file cost 36.51s wall with one worker; the 21 reconnect executions took 2.115s of test time (29.35s total runner duration). UI E2E types, focused lint, 48 plugin-contract files / 1,132 tests and both zero-cycle checks passed. A negative control restored the original anonymous-listener bug from #161706 in the isolated proof checkout: the unchanged reconnect assertion failed with expected length 1, received 2. The original source was restored and bundles regenerated afterward. The earlier duplicate-config launcher error occurred before tests and was corrected by letting the repository wrapper select the project.
The previous head's remaining stream-reconciliation failure is independently present on hourly main run [36892916454](https://github.com/openclaw/openclaw/actions/runs/36892916454), job `110473226209`: `chat-flow.stream-reconciliation.e2e.test.ts:242`, “reconciles persistence before hydration (tool: false, steer: true)”, omits “The follow-up check is complete.” The mocked fixture authors its own Gateway events and does not execute this PR's changed transport/reply functions. New-head hosted CI will determine the current gate state; no passing replay is claimed as a fix for that separate failure.