* perf(control-ui): load built-in theme palettes on demand
Every built-in theme's tokens shipped in the startup stylesheet, so the default
path downloaded six palettes it never painted and each new theme taxed everyone.
That is what pushed the startup CSS ceiling from 45 to 47 KiB when Tide, Beacon,
and Phosphor landed.
Moves the twelve non-default palettes into public/themes/<family>.css, one file
per family covering both modes. Claw stays inline because its tokens are the
:root defaults, so the default path loses nothing and gains the bytes back.
The first-paint story this needed: index.html's boot script now links the active
family's palette during head parsing, which makes it render-blocking exactly
like the app stylesheet, so a persisted theme paints its own colours on the
first frame instead of flashing the default. The href is built from the mount
prefix the gateway already stamps on <html>, so it follows a configured Control
UI base path without the script having to know one. theme.ts keeps the link
correct when the theme changes at runtime, reusing the helper the webfont
stylesheets already use.
The nested resolve-theme ternary became a family table in the same script, since
it now picks an asset as well as a data-theme value.
startup CSS 45.8 -> 42.2 KiB gzip, below the 44.3 KiB it measured before the
three themes landed; ceiling restored 47 -> 45 KiB
base.css 64.0 -> 35.8 KiB raw
Adds a regression test that blocks every bundle script and asserts the palette
still applies, so moving this back into the app bundle fails instead of silently
reintroducing the flash. Verified it catches that: with the boot-script link
removed the assertion reports `expected null to be '/themes/tide.css'`.
* fix(control-ui): publish themes after their palettes load
* fix(control-ui): clean up palette completion listeners
* refactor(control-ui): consolidate theme name resolution
* refactor(plugin-sdk): replace API baselines with diffs
* perf(plugin-sdk): bound API diff resources
* fix(plugin-sdk): isolate API diff dependencies
* fix(release): forward Plugin SDK acknowledgement
* fix(release): enforce SDK acknowledgement on publish
* chore: preserve generated-doc ignore policy
* fix(release): freeze SDK API evidence before publish
* fix(ci): satisfy SDK evidence guards
* fix(release): bind complete SDK evidence
* fix(release): authenticate plugin SDK evidence
* fix(plugin-sdk): abort interrupted API diffs
* test(ui): freeze page clock in background-tasks rail e2e
The rail transcript is compared byte-for-byte across the detail-panel
round-trip while it renders live relative ages; on slow CI runners the
second boundary ticks between the two reads (11s -> 12s) and fails the
equality assertion. Fix the page Date with Playwright setFixedTime while
keeping timers running so the tasks.list polling assertions still hold.
Repro: a 1.5s stall between the reads fails pre-fix with the exact CI
diff and passes post-fix.
* fix(scripts): drop unused export on dependency-evidence CLI main
Knip's workflow scan re-roots script references after an actions/checkout
step that sets path:, so the new trusted-tooling checkout in
openclaw-npm-release.yml stops marking this CLI as a workflow entry and
its exported main() surfaces as an unused export in check-dependencies.
Nothing imports main; the module invokes it through its own entry guard,
so the export keyword was dead surface either way.
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(plugin-sdk): back every workspace facade export with a real src facade
packages/plugin-sdk declared 59 export subpaths but only 24 had src facade
files; the other 35 defaults pointed at nonexistent ./src/<name>.ts targets
that typecheck (types resolve via boundary dist) but fail runtime resolution.
Create the 35 missing facades (each mirrors the sibling 3-line re-export of
the core src/plugin-sdk entrypoint; all verified live, none retired), and
extend scripts/sync-plugin-sdk-exports.mts to own the facade package too:
exports are regenerated from the src directory, every facade must name a
canonical SDK entrypoint, and --check fails on dangling keys or orphan
facades. The check-changed Plugin SDK surface lane now also triggers on
packages/plugin-sdk/** so the guard actually runs when the map changes.
* fix(plugin-sdk): validate facades before any exports write
ClawSweeper P2: write mode reported a stale facade but still exited 0,
after syncRootPackageExports had already rewritten the root manifest.
Hoist facade validation ahead of all writes and exit nonzero in both
modes; an invalid facade now fails closed with no partial manifest
update (proven: hash-identical manifests plus exit 1 in write mode).
Recognize Crabbox workload-routing provider exhaustion as a pre-dispatch backend outage so check:changed runs its documented local fallback.
The observed full provider-chain outage previously exited 2 without running local lanes.
The existing command-exit veto still runs first, so a remote run that actually executed and failed remains fail-closed.