The entry stylesheet aggregated approval, config, config-quick, and
lobster-pet styles even though every consumer is a lazy route chunk or
lazily defined element, and two small UI landings tipped startup CSS to
exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run
29643357786). Move each stylesheet to its owning lazy module — pages
already own their CSS this way (chat, agents, cron) — and keep only the
shell's pre-hydration approval booting subset in the entry via the new
approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the
budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS
precedent from #110528.
* feat(ui): manage MCP servers directly on the Settings MCP page
* refactor(ui): mcp config section owned by the MCP settings page only
* chore(ui): drop test-only McpServersCard export for knip
* perf(ui): drop zod and lazy-load json5 out of Control UI startup
zod's only UI consumer was the custom-theme shape layer, whose deep CSS
validators already re-check every token; replace the two shallow schemas
with a plain record reader and delete the zod jitless CSP shim + test.
Startup keeps one schema library total (typebox, protocol-owned, already
lazy via the approval page).
json5 now loads through a lazy runtime boundary: strict JSON.parse is the
fast path, the parser warms with the config editor and whenever a config
snapshot or raw draft actually needs JSON5 (comments, trailing commas).
The redaction sanitize path parses the authoritative raw once at snapshot
ingestion (async-safe) and submits against the carried parsed fact, so
secret-placeholder handling never races the lazy parser.
Startup JS: 321.8 -> 295.8 KiB gzip, 13 -> 12 requests; budgets ratchet
to 310 KiB / 18 requests.
* fix(ui): gate config submits on pending JSON5 original parse
A JSON5 config racing the first parser load could reach the sanitize step
with a null parsed original and pass redaction placeholders through.
setConfigRawOriginal now parses synchronously whenever the parser is warm
and tracks a pending promise otherwise; submit, auto-save, and teardown
flush paths defer to that promise (teardown keeps its synchronous prefix
when no parse is pending).
* fix(ui): keep teardown config flush synchronous and JSON5 diff cache non-sticky
Teardown flush must dispatch before unload destroys the context; the
gateway's restore-or-reject sentinel contract backs the rare unsanitized
window. Raw-diff parse failures no longer cache while the lazy JSON5
parser is still loading, so a transient cold-parser miss retries on the
next render instead of pinning an empty diff.
* fix(ui): harden lazy JSON5 boundary against double-submit and failed loads
Claim the config busy flag before awaiting a pending JSON5 original parse
so a second click cannot slip past the busy state (autosave overlap is
already serialized by the in-flight registry and drain discipline). A
rejected json5 chunk import now resets the loader for retry, the per-state
pending promise is never-rejecting and self-clearing, and fire-and-forget
warms swallow rejections.
* docs(ui): note autosave entry serialization at the JSON5 parse await
* fix(ui): fill raw pending-changes diff once the lazy JSON5 parser lands
First diff open could race the parser chunk and render an empty list with
nothing scheduling a retry; renderConfig now re-renders when the warm
completes, and the browser test warms the parser in setup to assert the
steady state the view module guarantees in prod.
* perf(ui): drop typebox from Control UI startup by lazy-loading the approval page
The approval document page was statically registered in app-host, pulling
the protocol validators and the full typebox runtime (schemas, value
checks, formats, locales) into every Control UI boot even though the page
only renders for /approve document URLs.
- register openclaw-approval-page through the existing OptionalCustomElement
seam, preloaded only when bootstrap resolves an approval document mode;
the approval document's booting splash covers the load gap
- ratchet startup budgets to the new baseline (340 KiB gzip, 20 requests)
so the win cannot silently regress
Startup JS: 369.1 -> 321.8 KiB gzip (-13%), 22 -> 13 requests.
* chore(ui): record approval-page label in i18n raw-copy baseline
* perf(ci): drop compact bin cap to 190s to split straggler pairings
Fresh census from green run 29633628994: every fan-out job queues 65s,
the pack sits at ~160s, but compact-large-3 ran 195s because the 235s
cap let core-runtime-media-ui (hint 124) and core-unit-src-security
(hint 95) share one bin. A 190s cap forbids such pairings; the plan goes
from 21 to 25 compact bins and the hinted max drops accordingly, so the
run wall tracks the pack instead of one straggler. Costs ~4 extra
runners per run; wall is the optimization target.
* perf(test): run DOM-free Control UI tests in the node environment
35 ui/src test files whose transitive import closure never touches DOM
APIs or the lit runtime carried the ui config's jsdom default anyway; on
CI the per-file jsdom construction dominates the shard (environment
95.2s vs tests 59.4s in run 29633628994). Annotate them with the
repo-standard @vitest-environment node docblock (29 files in ui/ already
use it). Verified: full ui config run passes with the annotations; the
only failing files are three storage tests that fail identically on a
clean checkout under local Node 26 and are untouched here.
* chore: kick dropped push event for PR head ingest
* feat(system-agent): hatch into the agent after a fresh setup applies
The hatch is a ceremony: a fresh-install setup apply now announces the hatch
and hands straight off (open-tui in the terminal, open-agent for gateway
clients); the web custodian page lands in agent chat with the birth-sequence
opener prefilled as a draft so one Send wakes the seeded BOOTSTRAP. Non-setup
persistent operations keep their in-place replies.
* fix(system-agent): hatch only on clean post-write verification
* fix(ui): scope hatch draft to setup handoffs
* fix(onboarding): seed hatch handoffs across clients
* fix(i18n): refresh native hatch inventory
* fix(onboarding): seed remote gateway hatch handoff
* fix(macos): localize hatch draft
* fix(onboarding): gate hatch on pending bootstrap
* fix(i18n): sync hatch source to ios catalog
* fix(onboarding): gate hatch on canonical pending state
Rewriting apps/.i18n/native-source.json without regenerating the derived
Android/Apple artifacts lands a stale iOS catalog and turns the repo-wide
apple/android i18n checks red for every PR (this is how main broke after
recent app-string additions: the inventory and translations were
committed while Localizable.xcstrings was not regenerated).
native-app-i18n.ts sync --write now chains syncAndroidAppI18n and a new
exported syncAppleAppI18n (catalog + InfoPlist strings) so one command
leaves the whole i18n tree consistent. The shared locale list moves to
scripts/native-i18n-locales.ts to break the import cycle between the
inventory scanner's top-level CLI await and the derived generators.
* fix(ci): bound CLI installer smoke curl with connect and max-time
The non-root CLI installer smoke pipes curl directly into bash under a
single docker-run timeout. curl has no default connect or total timeout,
so a stalled CDN connect or hung response body could pin the container
until the outer docker-run budget kills it. Bound the curl with the same
--connect-timeout 30 --max-time 300 shape already used by install-sh-smoke
and install-sh-e2e, and enable pipefail so curl failures are not masked
by the bash consumer.
* test(ci): cover bounded CLI installer pipeline
* fix(ci): bound preceding non-root installer download
* fix(ci): harden bounded installer URL handoff
Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(sqlite): gate schema version before integrity scans and quarantine terminal open failures
* feat(gateway): refuse incompatible database schemas at boot and verify integrity in the background
* feat(update): refuse installing builds that cannot open the current databases
* docs: add database schema reference with version history and downgrade guidance
* test(update): tolerate optional runner options in git-mutation mocks
* refactor(update): split npm package-target metadata out of update-check
* chore: model verifier internals for knip and regenerate docs map
* chore(i18n): resync stale iOS localization catalog
* chore: allowlist read-only preflight and verifier raw SQLite access
* feat(ui): five-zone sidebar with agent identity card, threads/groups/coding zones, and footer bar
* fix(ui): promote main-session children via the gateway row's literal key
* fix(ui): filter and sort promoted main-session threads; hide empty Coding count
* fix(ui): collapsed zones no longer consume the visible session page budget
* fix(ui): a11y — offline card label and catalog lists outside the Coding list container
* fix(ui): zone headers use pre-pagination counts so paged-out zones stay visible
* chore(ui): document catalog-adoption dedup invariant on promoted threads
* chore(ui): seed five-zone sidebar fixtures in the mock-dev harness
* fix(ui): resolve the canonical global main key for the identity card under global scope
* fix(ui): global scope resolves the global sentinel as canonical main even pre-hello
* fix(ui): drop dead channelDisplayLabel, avoid map-spread, type zone test helper
Second calibration pass after the cli-runner reliability whale fix
(#109772) and the stripe-wall correction (#109899): the compact group
hints drifted below loaded-fleet reality, so FFD packed the heaviest
groups into ~300s bins (checks-node-compact-large-2 at 242-314s shard
wall, up to 389s job wall) while tail bins idled.
Method: parsed [shard:*] begin/end timestamps from all 147 compact job
logs across seven green CI runs whose head SHAs carry the whale-fix
hints (runs 29605136624, 29605203485, 29605983019, 29606701461,
29611308972, 29611457693, 29611500865; 7 samples per group). Each hint
is the per-group mean after dropping cache-warm/contention outliers
outside [median/1.5, median*1.5].
Packing cap: with honest loaded walls a 220s cap no longer fits either
pool (adds one job to each); 235s keeps the same job count (6 large +
15 small + dist) and flattens the ceiling. Median job setup overhead
measured 60s (p90 87s), so a 235s bin stays near the 5-minute PR
budget.
Predicted bin shard walls (sum of measured group means):
before: large 220/301/200/219/257/112, small heavies 251/235/218/217/215, max 301
after: large 235/232/229/228/228/156, small heavies 236/234/234/234/233, max 236
large-pool max/mean 1.38 -> 1.08
Biggest hint deltas (old -> new): startup-core 98->156, core-tooling-4
71->125, core-unit-fast-isolated 50->90, core-tooling-3 82->108,
runtime-server 14->29, storage-state 55->70, media-ui 113->124,
tui-pty 103->116, runner-cli-1 18->8, unit-src-security 108->95.
Exclusive bins keep their 150s cap and 5-bin count: tui-pty (116) no
longer shares a bin with tooling-isolated (166s combined measured);
FFD now pairs tooling-2 with tooling-isolated at 144s.
STRIPE_FILE_SECONDS_HINTS left unchanged: measured stripe walls sit in
exclusive bins far below the packing ceiling (tooling stripes
94-125s), and refitting per-file hints would reshuffle stripe
membership and invalidate the measured group means above.