.agents/skills/openclaw-test-performance/SKILL.md
Use evidence first. The goal is real pnpm test, plugin-suite, and
plugin-inspector speed/RSS improvement with coverage intact, not runner tuning by
guesswork.
AGENTS.md files before editing:
src/agents/AGENTS.md for agent/import hotspots.src/channels/AGENTS.md and src/plugins/AGENTS.md for plugin/channel
laziness.src/gateway/AGENTS.md for server lifecycle tests.test/helpers/AGENTS.md and test/helpers/channels/AGENTS.md for shared
contract helpers.src/infra/outbound/AGENTS.md for outbound/media/action tests.pnpm test:perf:groups --full-suite --allow-failures --output <file>
for full-suite ranking.pnpm test:extensions:batch <plugin[,plugin...]> or plugin-inspector command
before jumping to the full extension sweep./usr/bin/time -l pnpm test <file-or-files> --maxWorkers=1 --reporter=verboseOPENCLAW_VITEST_IMPORT_DURATIONS=1 OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1.scripts/committer "<message>" <paths...> and push when the
user asked for commits/pushes. Stage only files touched for this attack.Use this section when perf work involves bundled plugins, plugin-inspector, SDK barrels, package-boundary tests, or extension suites.
pnpm test extensions/<id> or pnpm test:extensions:batch <id>pnpm run test:extensions:package-boundary:canary and
pnpm run test:extensions:package-boundary:compilepnpm test:extensionspnpm test:extensions:memory -- --json .artifacts/test-perf/extensions-memory.jsonplugin-inspector;
keep wrappers thin and collect peak RSS when the command supports it.test:extensions:batch groups before
pnpm test:extensions.blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90blacksmith testbox run --id <ID> "OPENCLAW_TESTBOX=1 pnpm test:extensions:batch <ids>"openclaw-pre-release-plugin-testing and Package Acceptance rather than
trusting source-only timing.Collect at least one stable metric before and after. Prefer the same machine and
same command. For Testbox comparisons, use the same tbx_... id when possible.
| Metric | Use for | Preferred source |
|---|---|---|
| wall time | user-visible suite cost | /usr/bin/time -l, test wrapper duration, Testbox run time |
| Vitest duration | test body/import cost | Vitest output per file/shard |
| import duration | broad barrel/runtime loads | OPENCLAW_VITEST_IMPORT_DURATIONS=1 |
| max RSS | memory pressure and OOM risk | /usr/bin/time -l, pnpm test:extensions:memory, wrapper memory summaries |
| CPU/user/sys | CPU-bound vs wait-bound split | /usr/bin/time -l locally, Testbox job timing when local CPU is noisy |
| heap snapshots | real leak vs retained module graph | openclaw-test-heap-leaks workflow |
Local scoped command with CPU/RSS:
timeout 240 /usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose
Plugin import memory profile:
pnpm build
pnpm test:extensions:memory -- --top 20 --json .artifacts/test-perf/extensions-memory.json
Targeted plugin import memory:
pnpm test:extensions:memory -- --extension discord --extension telegram --skip-combined
Heap/RSS escalation:
OPENCLAW_TEST_MEMORY_TRACE=1 \
OPENCLAW_TEST_HEAPSNAPSHOT_INTERVAL_MS=60000 \
OPENCLAW_TEST_HEAPSNAPSHOT_DIR=.tmp/heapsnap \
OPENCLAW_TEST_WORKERS=2 \
OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144 \
pnpm test
Use openclaw-test-heap-leaks when RSS keeps growing across intervals, workers
OOM, or the suspect command has app-object retention. Do not call RSS growth a
leak until snapshots or retainers support it.
getChannelPlugin() fallback used when an already-loaded fixture or pure
parser would suffice.api.ts, runtime-api.ts, test-api.ts, or plugin-sdk barrels pulled
into hot tests.importActual() around broad modules.vi.resetModules() plus fresh imports in per-test loops.beforeAll while runtime state resets in
afterEach.test/fixtures/test-timings.unit.json, causing hotspot
files to stay in shared workers.node_modules/.experimental-vitest-cache without
distinct OPENCLAW_VITEST_FS_MODULE_CACHE_PATH values.Scoped file:
timeout 240 /usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose
Scoped file with import breakdown:
timeout 240 /usr/bin/time -l env \
OPENCLAW_VITEST_IMPORT_DURATIONS=1 \
OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1 \
pnpm test <file> --maxWorkers=1 --reporter=verbose
Grouped suite:
pnpm test:perf:groups --full-suite --allow-failures \
--output .artifacts/test-perf/<name>.json
Extension batch:
pnpm test:extensions:batch <plugin[,plugin...]> -- --reporter=verbose
All extension tests:
pnpm test:extensions
Package-boundary plugin checks:
pnpm run test:extensions:package-boundary:canary
pnpm run test:extensions:package-boundary:compile
Reuse an existing Vitest JSON report:
pnpm test:perf:groups --report <vitest-json> \
--output .artifacts/test-perf/<name>.json
pnpm check:changed before push; in maintainer
Testbox mode run it in the warmed Testbox.pnpm test:changed or the exact edited tests.pnpm build when touching lazy-loading, bundled artifacts, package
boundaries, dynamic imports, build output, or public surfaces.pnpm plugin-sdk:api:check or
pnpm plugin-sdk:api:gen when the API surface may drift.pnpm install and retry the exact failed
command once.| Metric | Before | After | Gain |
| -------------- | -----: | -----: | ------------: |
| File wall time | `Xs` | `Ys` | `-Zs` (`P%`) |
| Max RSS | `XMB` | `YMB` | `-ZMB` (`P%`) |
| CPU user/sys | `X/Ys` | `A/Bs` | explain |
Keep the final concise: