Goto: Latest Published | Latest Beta | Main Development
Compatibility trap for OpenClaw plugin contracts. crabpot keeps a curated set of real community plugins pinned under plugins/ and runs seam-focused compatibility checks against OpenClaw plugin APIs. The goal is to catch contract drift before external plugin authors do. Built on top of plugin-inspector, the testing harness for OpenClaw.
main follows a promoted green OpenClaw source pin plus npm latest plugin artifacts, with bundled fixtures source-packed from that pinned checkout. crab-beta follows beta npm dist-tags for externalized packages and source-packs bundled fixtures. crab-development checks openclaw/openclaw main against source-packed official plugin artifacts from that same OpenClaw checkout.
- Last dashboard update: Sep 23, 2026, 17:09 UTC
- Source:
github-default-pin - OpenClaw version:
2026.9.3 - OpenClaw SHA:
d9b899649800 - Dashboard target:
openclaw/openclaw@d9b899649800 + npm latest plugin artifacts - Plugin artifacts:
npm latest fixture set plus bundled source-packed fixtures - GitHub report run: 35890594794
| Metric | Result |
|---|---|
| Fixtures | 59 |
| Hard breakages | 0 |
| Warnings | 104 |
| Suggestions | 165 |
| Issues | 269 |
| P0 issues | π΄ P0 7 |
| P1 issues | π P1 34 |
| Live issues | 7 total / 7 P0 |
| Compat gaps | 28 |
| Deprecation warnings | 22 |
| Inspector gaps | 148 |
| Upstream metadata | 64 |
| Contract probes | 237 |
| Policy failures | 0 |
| Policy warnings | 1 |
| Ref diff failures | 0 |
| Profile failures | 0 |
| Execution probes | 0 pass / 0 fail / 0 blocked |
| Synthetic probes | 506 ready / 10 blocked / 516 total |
| Cold import | 5 ready / 114 blocked / 119 entrypoints |
| Workspace plan | 119 entrypoints / 79 installs / 15 builds |
| Platform risks | 17 Windows / 17 container |
| Jiti loader candidates | 20 |
| Import loop | p50 2935ms / p95 2952ms / plugin delta RSS 0MB / plugin delta CPU 21ms / OpenClaw import 58ms / activate 0.5ms |
| Runtime profile | p50 0ms / command p95 0ms / max RSS n/a / 1 sample/command |
| Phase | p50 | p95 |
|---|---|---|
Import (full) |
58ms | 62.6ms |
Activate (full:register) |
0.5ms | 0.5ms |
| Severity | Class | Fixture | Code | Decision | Title |
|---|---|---|---|---|---|
| π΄ P0 | live-issue | aiwerk-mcp-bridge | unknown-hook-name | core-compat-adapter | aiwerk-mcp-bridge: fixture uses a hook missing from target OpenClaw |
| π΄ P0 | live-issue | connectclaw | unknown-hook-name | core-compat-adapter | connectclaw: fixture uses a hook missing from target OpenClaw |
| π΄ P0 | live-issue | honcho | unknown-hook-name | core-compat-adapter | honcho: fixture uses a hook missing from target OpenClaw |
| π΄ P0 | live-issue | honcho | unknown-registration-name | core-compat-adapter | honcho: fixture calls a registrar missing from target OpenClaw |
| π΄ P0 | live-issue | memos-cloud | unknown-hook-name | core-compat-adapter | memos-cloud: fixture uses a hook missing from target OpenClaw |
| π΄ P0 | live-issue | openclaw-telemetry | unknown-hook-name | core-compat-adapter | openclaw-telemetry: fixture uses a hook missing from target OpenClaw |
| π΄ P0 | live-issue | opik-openclaw | unknown-hook-name | core-compat-adapter | opik-openclaw: fixture uses a hook missing from target OpenClaw |
| π P1 | compat-gap | agentchat | missing-compat-record | core-compat-adapter | agentchat: compat-dependent behavior lacks registry coverage |
| π P1 | compat-gap | bluebubbles | sdk-export-missing | core-compat-adapter | bluebubbles: plugin SDK import aliases are missing from target package exports |
| π P1 | compat-gap | connectclaw | missing-compat-record | core-compat-adapter | connectclaw: compat-dependent behavior lacks registry coverage |
- plugin manifests and install metadata
- native tool registration and dynamic tool schemas
- channel registration and message delivery seams
- lifecycle hooks such as
gateway_start,gateway_stop, andbefore_install - agent hooks such as
before_tool_call,before_prompt_build,llm_input,llm_output, andagent_end - provider capability registration such as speech/TTS
- plugin-owned services, routes, subprocesses, and async job patterns
crabpot/
crabpot.config.json fixture manifest and seam tags
plugins/ external plugin repositories as git submodules
reports/ generated compatibility report artifacts
scripts/ manifest and fixture helpers
test/ repo-level checks
docs/ operating notes and seam matrix
npm test
node scripts/list-fixtures.mjs
node scripts/sync-fixtures.mjs --check
npm run report
npm run contract:capture
npm run contract:synthetic
npm run cold-import
npm run workspace:plan
npm run platform:probes
npm run import:profile
npm run execution:report
npm run profile
npm run contract:coverage
npm run readme:summaryTo materialize the fixture repos as submodules:
node scripts/sync-fixtures.mjs --materialize
git submodule update --init --recursiveThat command mutates .gitmodules and plugins/*. Commit those changes when
you intentionally pin or update fixture revisions.
Fixture execution.gatewayMethodPrerequisites entries name the exact Gateway
methods that require host state or live credentials. Synthetic runs record those
methods as blocked before invoking them; they do not turn rejected responses
into passes. Status methods without those prerequisites still execute. Each
allowed blocker has a named follow-up in crabpot.ci-policy.json. Matrix recovery,
bootstrap, and verification status require a configured account and crypto runtime;
other Matrix methods keep their normal response checks. Voice Call declares its
nine methods individually: runtime/provider setup, active call input, or a pending
continuation operation must exist before the corresponding probe can run.
Programmatic callers supplying isolated inputs and runtime can pass an explicit
gatewayMethodPrerequisites map to the synthetic runner. Omit satisfied methods
from that map; an empty map exercises every method with normal response checks.
The source pin includes Inspector's process resource collector. On a trusted
checkout, resolve that source and run the existing smoke with --resources:
inspector_root="$(node --input-type=module -e 'import path from "node:path"; import { resolvePluginInspectorCliPath } from "./scripts/plugin-inspector-source.mjs"; process.stdout.write(path.dirname(path.dirname(resolvePluginInspectorCliPath())));')"
CRABPOT_PLUGIN_INSPECTOR_CLI=source CRABPOT_PLUGIN_INSPECTOR_DIR="$inspector_root" npm run plugin-inspector:smoke -- --resourcesAn explicit CRABPOT_PLUGIN_INSPECTOR_DIR selects the source or installed package
under test; without it, the resolver can use a sibling checkout before the pin.
The source and packed CI lanes exercise the public resource-profile export and
write plugin-inspector-resources.json alongside the existing smoke reports.
The published npm pin stays unchanged until a release includes this API; omit
--resources when testing older packages.
This checks collector observations in a fresh child: JSON-transported snapshots, a retained 1 MiB Buffer, and a timer added then removed. CPU/RSS cover the child process; other memory and active resources cover its main thread. It does not measure a plugin workload, peak memory, memory release, or a leak. The child uses an empty environment; that is not a network sandbox for external plugin code.
Inspector smoke and generated-surface commands default to 10 minutes. Inspector
checkout Git and npm commands and fixture-security npm audits default to 2 minutes. Set
CRABPOT_PLUGIN_INSPECTOR_TIMEOUT_MS, CRABPOT_GIT_TIMEOUT_MS, or
CRABPOT_NPM_TIMEOUT_MS to a decimal integer from 1 through 2147483647;
zero, fractions, trailing text, and infinite timeouts are rejected.
Static-suite steps default to 10 minutes when CRABPOT_STATIC_STEP_TIMEOUT_MS
is unset or empty. Nonempty settings use the same decimal-integer range and
reject malformed values before starting a step.
Commands remain synchronous to callers, with a separate bounded supervisor for startup, execution, output, and descendant cleanup. Captured checkout and generated-surface output retains the 1 MiB combined stdout/stderr limit. Smoke output streams through inherited native descriptors without a total-output cap. Fixture-security audits retain a 16 MiB combined capture limit.
The first command error is preserved when cleanup also fails. Unconfirmed
cleanup adds cleanupError to the result and ; command cleanup was not confirmed
to the error message; it is never reported as success. On POSIX, an unexpected
supervisor Worker loss can leave descendants alive. The caller does not signal
a cached process-group number after losing its owner. Permission errors are
not proof of process-group extinction.
Windows requires 64-bit Windows 10/Server 2016 or newer and Windows PowerShell:
commands enter their private Job at creation, and setup failures never fall back
to uncontained execution. A separate helper Job contains bootstrap compiler
children too. Native Git runs directly; only batch commands use cmd.exe.
Windows Jobs retain owner-loss cleanup independently of the Worker.
This process ownership is not a sandbox for hostile plugin code.
Start with the dashboard at the top of this README. It is the condensed view of the generated reports: fixture count, breakages, warnings, issue backlog, probe coverage, cold-import blockers, workspace execution shape, and runtime profile.
For deeper review, open the reports in this order:
| Need | Command | Primary report |
|---|---|---|
| Main compatibility triage, decision matrix, issue backlog | npm run report |
reports/crabpot-report.md |
| Stable issue list for compat-layer planning | npm run report |
reports/crabpot-issues.md |
| Hooks, registrars, SDK imports, and entrypoints that need capture | npm run contract:capture |
reports/crabpot-capture.md |
| Executable synthetic hook/registration probe plan | npm run contract:synthetic |
reports/crabpot-synthetic-probes.md |
| Why plugin entrypoints cannot be safely cold-imported yet | npm run cold-import |
reports/crabpot-cold-import.md |
| Isolated install/build/capture commands Crabpot would run | npm run workspace:plan |
reports/crabpot-workspace-plan.md |
| Results from opt-in isolated fixture execution | npm run execution:report |
reports/crabpot-execution-results.md |
| Behavioral eval planning for plugin categories | npm run eval:behavior |
stdout / .crabpot/results/behavior/ when executed |
| Boot time and RSS against the target OpenClaw registry surface | npm run profile |
reports/crabpot-runtime-profile.md |
| China and adjacent external plugin monitor candidates | manual live discovery pass | reports/crabpot-external-plugin-monitor.md |
| README dashboard refresh from all generated JSON reports | npm run readme:summary |
README.md, reports/crabpot-dashboard-data.json |
Each Markdown report has a matching JSON file beside it for CI, dashboards, and
future inspector tooling. The JSON is the contract; the Markdown is the review
surface. reports/crabpot-dashboard-data.json is the compact machine-readable
dashboard card used to compare crab-beta and crab-development against
main.
scripts/run-resource-campaign.mjs runs every configured resourceWorkloads
scenario sequentially, with 1β10 repetitions (default 3). Run it from the frozen
built OpenClaw root inside an already isolated runner:
node /crabpot/scripts/run-resource-campaign.mjs \
--plugin-inventory /fixtures/inventory.json --inputs /fixtures/inputs.json \
--out /out/campaign-1 --repetitions 3 --executeThe output directory must not exist; its parent must exist. Omit --execute
to enumerate the full inventory without importing adapters or starting hosts.
Optionally pass --distribution core, external or source to run only configured
scenarios in that inventory distribution. The full inventory remains in every
repetition: excluded configured rows are blocked with
not-selected-for-this-campaign; plugins without configured scenarios remain unsupported.
Dependency activation does not earn workload coverage. selection records the
chosen distribution, selected/excluded configured counts and its status/reason.
The CLI labels this partial scope and exits zero only when at least one selected
scenario exists, every selected repetition is exercised and no configured scenario
is absent from the inventory. Selection planning therefore exits nonzero.
Successful selected work leaves the overall campaign blocked while excluded work remains;
omitting --distribution preserves the existing all-distributions behavior.
This command does not build, download, provision isolation, configure credentials
or install campaign prerequisites. The workload adapter still owns native local
plugin installation. Prepare its archives and offline dependency cache first.
The outer runner must enforce a minimal environment, network/resource limits and
a deadline, then stop and join the whole sandbox on interruption or failure.
Generate pins inside the final Linux image, using its Node runtime with
process.threadCpuUsage, after the frozen host, consumer and archives are present:
node /crabpot/scripts/prepare-resource-inputs.mjs \
--plugin-inventory /fixtures/inventory.json --host-root /app \
--out /fixtures/inputs.json --archive /fixtures/plugin.tgzRepeat --archive for each local archive; omit it for bundled-only workloads.
The output must not exist and its parent must exist. The writer validates the
inventory digest and matching build commit, reads this consumer's manifest,
and hashes the required files, available configured adapters and archives.
It does not build, download, install or run workloads.
The generated input-pins JSON has this shape (replace placeholders with real identities; do not copy the sample hashes):
{
"schemaVersion": 1,
"hostCommit": "<full inventory/build commit>",
"runtime": { "node": "<exact process.version>", "platform": "linux", "arch": "x64" },
"files": {
"host": { "openclaw.mjs": "<sha256>", "dist/build-info.json": "<sha256>" },
"crabpot": { "crabpot.config.json": "<sha256>" }
},
"artifacts": [{ "path": "/fixtures/plugin.tgz", "sha256": "<sha256>" }]
}The abbreviated maps must also pin all four host instrumentation files:
scripts/e2e/kitchen-sink-rpc-walk.mts,
scripts/e2e/lib/kitchen-sink-resources.mts,
scripts/lib/gateway-bench-profile.ts, and
scripts/lib/gateway-bench-profile-preload.ts. The Crabpot map must include
scripts/run-resource-campaign.mjs, scripts/run-resource-workload.mjs,
scripts/resource-workload-contract.mjs, scripts/resource-coverage.mjs,
scripts/manifest-lib.mjs and every available configured adapter. Paths in these
maps are relative to their respective roots. Pin the actual built entry
(openclaw.mjs, dist/index.mjs or dist/index.js). Additional files may be
pinned. Use an empty artifacts array for bundled-only scenarios. This verifies
declared local bytes, not the entire build or dependency closure; freeze those
inputs in the outer runner. Runtime, files and archives are checked before and
after each invocation, and producer receipt hashes must agree.
Postverification also runs after runner, receipt persistence or receipt validation
failures; input drift prevents credit and stops subsequent invocations.
Each receipt must match the requested scenario and the pinned Gateway runtime.
The Node version comparison accounts only for process.version's leading v;
Gateway snapshots use process.versions.node without that prefix.
campaign.json checkpoints one full-inventory outcome table per repetition;
repetition-N/<scenario>.json retains each returned raw receipt before validation.
Configured-but-unrun scenarios are blocked. Absent adapters, missing dependencies
and configured IDs outside the inventory remain explicit gaps; unrelated plugins
are unsupported, never healthy. Multiple scenarios for one plugin are rejected.
Pass one repetition's validated receipts to the existing coverage report; never
combine duplicate plugin receipts across repetitions as additional coverage.
A blocked, failed, invalid or thrown execution stops admission of subsequent work. Later
rows remain blocked and earlier receipts survive. An interrupted run retains its
last checkpoint, including execution-in-progress; that is not completion or
cleanup proof. An executed campaign exits nonzero for failures or configured
blocked work. Unsupported rows do not fail the command. complete means all
configured work completed, not that every inventory plugin was measured.
Failures retain their stage (preverify, run, receipt-write,
receipt-validation or postverify), a recognized error type/code and bounded,
path-redacted validation context. Arbitrary runner exception text, stacks and
assertion payloads are omitted; raw workload receipts remain separate evidence.
If postverification also fails, postverifyDiagnostic retains that sanitized
failure alongside the original diagnostic and reason.
CPU/memory observations remain report-only: no resource thresholds, leak verdicts,
automatic retries, cross-run aggregation, calibration execution or CI scheduling
are added. Supply separately qualified calibration with matching frozen inputs.
Add a committed OpenClaw plugin inventory to the existing report:
# In the OpenClaw source checkout, using the commit of the measured Gateway:
pnpm --silent plugins:inventory:json --commit <full-commit-sha> > plugin-inventory.json
# In Crabpot, with explicit paths to the exported inventory and optional pilot:
npm run report -- --plugin-inventory <plugin-inventory.json> \
--kitchen-sink-resource-report <kitchen-sink-resource.json>The inventory command requires an OpenClaw revision that provides
plugins:inventory:json. The optional pilot is the existing OpenClaw
--resource-profile Kitchen Sink report, not a collector or import report.
Resource inputs stay separate from --execution-results.
JSON and Markdown show the full source inventory independently of Crabpot's
configured and selected compatibility fixtures. Plugins without workload
adapters are explicitly unsupported. Configured workloads without a supplied
receipt are blocked with reason workload-report-not-supplied; this does not
infer whether they ran or why a receipt is missing. Supplied receipts retain
their validated outcomes. Imports, registration captures, and collector checks
receive no workload credit. Kitchen Sink calibration appears
outside that denominator. Failed receipts retain partial counts and errors;
a different Gateway source commit blocks calibration for the selected inventory.
The reader validates inventory integrity and producer-reported identities. It does not independently attest artifact bytes, infer leaks from RSS, or upgrade unsupported disposal observations. These options leave default compatibility reports unchanged and add no performance budget gate.
Workload adapters live in scripts/resource-workloads/; their scenario IDs and
required completion counts live in crabpot.config.json under resourceWorkloads.
They exercise the real built Gateway through OpenClaw's source-checkout
runResourceGatewayCase helper. An OpenClaw revision containing that helper and
Linux Node with process.threadCpuUsage() are required. Missing prerequisites
produce a blocked receipt, never plugin coverage.
Prepare a container with one frozen built OpenClaw checkout, this Crabpot checkout, and the matching inventory. Run from the OpenClaw checkout root:
node /crabpot/scripts/run-resource-workload.mjs \
--scenario workboard-card-crud-v1 --plugin-inventory /fixtures/inventory.json \
--out /out/workboard.json --executeOmit --execute for a plan without starting a Gateway. The runner must enforce
network isolation, CPU/memory limits and an outer deadline, then stop and join
the whole container on failure. Killing only this script can leave the
Gateway's separate process group alive. A temporary HOME alone is not isolation.
Workboard uses its real SQLite worker: one first CRUD cycle and 20 warm cycles, each asserting create/update/list/delete/list results and an empty final store. The first cycle follows an initial empty-store read; it does not measure first database activation. No cards can dispatch agent work. Each case also measures startup, idle and 20 neutral RPCs against an empty-host baseline. Main-isolate heap/thread CPU excludes the SQLite worker; process CPU/RSS includes it. Short windows do not establish periodic-service cost or disposal retention.
Beam's beam-receiver-cycle-v1 uses one HTTP upload and four catalog RPCs per
cycle. It verifies the stored two-message transcript, archives the snapshot and
checks that the catalog is empty. One first cycle and 20 warm cycles stay below
the receiver's normal rate limit. Mirroring is unconfigured; this does not cover
remote publishing, continuation, expiry or concurrent uploads. Select this
scenario with the same command and a separate output receipt.
TypeSafe's typesafe-systemone-decisions-v1 requires a genuine full OpenClaw
source checkout with matching built dist, source inventory and qualified
dependencies. Keep src/, extensions/ and pnpm-workspace.yaml from that
same revision: native discovery admits TypeSafe's bundledDist: false source
plugin as bundled. Copying a plugin next to an installed package is not this
route. The plugin's installed-host and plugin API requirements remain unchanged;
this workload does not establish installed-package compatibility.
The scenario selects typesafe/kev-latest for the decision role and invokes the
real decision_evaluate core tool against a strict loopback /v1/systemone
peer. Each of one first and 20 warm calls checks boolean, choice and score
answers, host provenance and exactly one completed HTTP request. No hosted key
or conversational model is configured. The empty-host case compares startup
and neutral RPCs only; decision work is an absolute enabled-host observation,
not an overhead delta. Model inference, hosted Jev behavior and model quality
are unmeasured. Peer resources are outside the Gateway measurements; the peer
closes after Gateway shutdown, including on preparation or workload failure.
Session-share's session-share-receiver-v1 connects a synthetic node through the
public Gateway client and real device/node pairing. Each cycle lists and reads a
fixed remote session, asserting both returned data and one node invocation per
RPC. One first cycle and 20 warm cycles run after pairing completes. The mock
node closes and joins before the final observation. Remote storage, source-side
privacy/redaction, pairing cost and cross-host network behavior are unmeasured;
the mock client's resources are outside the Gateway process measurements.
Add the receipt to the existing report from the Crabpot checkout:
npm run report -- --plugin-inventory /fixtures/inventory.json \
--resource-workload-report /out/workboard.jsonRepeat the option for distinct plugins. Same-plugin repetitions belong in
separate reports. Credit requires complete raw snapshots, matching derived
measurements, configured completion counts, identical host artifacts, the
expected active plugin and joined shutdown without forced termination. Failed
receipts preserve partial work. Plugins without configured adapters remain
unsupported; configured workloads without receipts remain blocked.
Adapters that need a dependency plugin and a matched workload control declare
pairedWorkload alongside requiredOperations:
"pairedWorkload": {
"dependencies": ["provider-plugin"],
"targetActivation": "workload"
}Both isolated cases execute the same declared phases, in order, with identical
completion counts. The baseline has only the dependencies active; the enabled
case must activate the target during work (workload) or before work (startup).
Use scoped for caller-owned registry handles that execute without publishing
to the Gateway registry, such as lazy tool-result middleware. Both catalog
observations must then equal the dependency set; receipts name this observation
scope explicitly. Adapters must still assert the real target behavior and every
declared operation. Catalog absence alone proves neither execution nor failure.
This does not claim a root-registry transition or in-process handle disposal.
Dependencies must be present in the source inventory. They do not earn separate
coverage from another plugin's workload. Without this declaration, existing
adapters retain their empty-host baseline and startup activation requirement.
prepare(context, { enabled, onCleanup }) runs in both paired cases before the
Gateway starts. It may return case-local state. Install the same pinned fixture
archive and configure the same synthetic workload inputs in both cases, changing
only target enablement. Register each adapter-owned server or peer with
onCleanup(async () => { ... }) immediately after acquisition. The consumer runs
these callbacks in reverse order after the host joins its Gateway, including
preparation, startup and workload failures. A cleanup failure fails the receipt;
callbacks must finish or reject within the outer runner's deadline.
Registration is open during preparation, work and measurement drain. Before
disposal it becomes closing, then closed after every registered callback
settles. Registration during drain is accepted and disposed in reverse order.
Registration while closing or closed throws synchronously and fails the case
and in-memory receipt even if the caller catches the error. The rejected callback
is not accepted or invoked: its caller retains cleanup ownership. Adapters must
join their own background work before completion; a saved callback is not a
resource lease after the run or permission to amend an already written receipt.
run(context, requirements, { enabled, state, onCleanup }) receives that state
and the same requirements in both cases. Await each
context.measure(name, count, operation) in Object.entries(requirements) order.
Host phases always come first, including when workload phase names are numeric. Assert the
operation's semantic outcome before resolving it. Every started measurement has
an immediate rejection handler and is drained before the host can stop the
Gateway. Returning with a pending measurement fails the contract; adapter and
drained measurement failures are both retained. The baseline must prove the
unmodified result, and the enabled case the target's effect. The consumer owns
phase/count and before/after active-plugin checks; adapters own these semantic
assertions. This contract adds no provider, process runner or plugin mocks.
New receipts use plugin-resource-workload schema v2. Each case retains whole
Gateway measurements and records expected/observed activation plus adapter
cleanup. comparison.hostPhases compares startup and neutral observations;
comparison.workloadPhases contains only matched workload deltas, calculated as
enabled minus baseline, including CPU per completed operation. Signed deltas
include run noise; they are not CPU allocations to plugin functions. Empty-host
scenarios have no matched workload deltas. The reader still accepts published
v1 empty-host receipts under their original contract, but v1 cannot satisfy a
paired scenario. Kitchen Sink v1 calibration is unchanged. No SQLite changes or
real-plugin workload coverage result from this orchestration contract alone.
Paired v2 receipts require the host-recorded fixtures array in each case and
matching archive SHA-256 multisets. Archive labels are validated; byte hashes,
including multiplicity, bind installed inputs regardless of installation order.
Both arrays may be empty for host-bundled plugins. Plugin IDs alone do not prove
that the baseline and enabled case installed the same package bytes.
Behavior evals are profile-driven, default to a dry plan, and stay
credential-free unless execution is explicitly enabled. The default profile is
the forward LCM tracking gate against latest OpenClaw and latest
@martian-engineering/lossless-claw. It verifies recall inside one stable
session-key family after /lossless rotate, with the seed pushed behind the
fresh tail so raw transcript replay is not enough to pass:
npm run eval:behaviorRun the recent historical repro target from Discord chatter with:
npm run eval:behavior -- --profile recent-lcm-2026-5-22 --jsonThe companion quarantine gate installs a synthetic malformed context-engine
plugin, selects it in the active slot, requires the agent turn to continue
through downgrade/default behavior, and tracks the gateway health quarantine
signal as an expected failure until that OpenClaw health contract ships in
latest:
npm run eval:behavior -- --profile forward-context-engine-quarantine-gateExecution is opt-in and isolated. The current POC stages a QA-lab-style mock
model provider, starts an isolated gateway, drives chat.send -> agent.wait
-> chat.history, requires the /lossless rotate command response, and checks
the latest assistant response on the recall turn using the same stable session
key:
CRABPOT_EXECUTE_BEHAVIOR=1 npm run eval:behavior -- --execute --profile recent-lcm-2026-5-22 --runner local --timeout-ms 120000The historical LCM target is intentionally red: openclaw@2026.5.22 with
@martian-engineering/lossless-claw@0.11.2 may fail before final recall as a
behavior-turn-mismatch, or at final recall with memory-recall-mismatch. The
forward LCM target is stricter and only expects the final summary-backed recall
failure until latest OpenClaw plus LCM can recall after rotate from summarized
context rather than raw transcript history. To test a candidate fix, pin a
newer OpenClaw
package while keeping the same LCM plugin and override the expectation:
CRABPOT_EXECUTE_BEHAVIOR=1 npm run eval:behavior -- --execute --profile forward-lcm-release-gate --openclaw-version 2026.5.26 --plugin npm:@martian-engineering/lossless-claw@0.11.2 --expect must-pass --runner local --timeout-ms 240000Behavior eval execution writes an empty npm user config inside the temp
workspace so local npm policy, such as a before cutoff, does not silently move
latest back to an older release.
Use the main compatibility report like this:
| Section | What to do with it |
|---|---|
| Hard Breakages | Treat as release-blocking contract drift. |
| Warnings | Review for target OpenClaw compatibility gaps or plugin metadata drift. |
| Suggestions To OpenClaw Compat Layer | Convert into compat-layer work, inspector follow-ups, or upstream plugin requests. |
| Issue Findings | Use stable CRABPOT-* ids for tracking and comparison across runs. |
| Contract Probe Backlog | Turn into tests before changing a plugin-facing seam. |
| Decision Matrix | Decide whether the fix belongs in core compat, the future inspector, or the plugin upstream. |
By default, reports target the OpenClaw checkout configured in
crabpot.config.json. Point a run at a branch, tag, SHA checkout, or local fork
with --openclaw:
node scripts/generate-report.mjs --openclaw ../openclaw
node scripts/generate-report.mjs --check --openclaw ../openclawCrabpot does not execute third-party plugin code during default checks. The workspace plan is dry planning unless you explicitly opt into isolated execution. Preview a fixture lane first:
npm run workspace:execute -- --fixture wecom --dry-runThen run isolated execution only when you want install/build/import side effects inside Crabpot's generated workspace:
CRABPOT_EXECUTE_ISOLATED=1 npm run workspace:execute -- --fixture wecom
npm run execution:reportThe OpenClaw Ref Compatibility workflow can be run from GitHub Actions with
an OpenClaw branch, tag, or SHA. Set openclaw_repository when testing a fork,
and openclaw_ref to the exact ref under review.
The default job runs the static contract suite against that checkout and uploads
the generated reports. The optional isolated job runs one fixture lane when
run_isolated_fixture is enabled and fixture is set, then uploads
.crabpot/results/ plus the execution summary report.
Required CI runs the Default Track suite against the immutable OpenClaw SHA in
.github/openclaw-default-track.json. The separately labeled OpenClaw HEAD Canary (Advisory) workflow runs the same suite against OpenClaw main, uploads
reports for every platform, and never blocks merges. A missing GitHub tag for
the npm latest version is a canary warning, not a required-lane failure. The
required dashboard records metadata from the pinned checkout and never resolves
OpenClaw HEAD or requires a matching upstream release tag.
Promote a green canary SHA with one command, then open the resulting focused PR:
npm run openclaw:promote -- <40-character-openclaw-sha>The command validates the OpenClaw commit and updates both the SHA and promotion
date. The daily OpenClaw Default Track Pin Age workflow fails once that date is
more than 14 days old, prompting another deliberate canary-to-pin promotion.
Fixtures should earn their spot by covering a distinct seam. Popularity is a useful signal, but a small plugin that exercises a rare hook is more valuable than the fourth web-search wrapper.
The first fixture set intentionally covers channels, dynamic tools, LLM observation, diagnostics, gateway-owned services, async jobs, provider capabilities, and security/policy hooks.
.github/workflows/resource-campaign.yml runs a report-only campaign daily at
03:43 UTC and on manual dispatch from this repository's main. GitHub can delay
scheduled runs. It never runs pull-request code. A reviewed full OpenClaw commit
must be set in RESOURCE_HOST_SHA; an unset pin fails preparation instead of
selecting a moving branch. The commit must contain the committed inventory
export and shared resource Gateway host.
Preparation installs the frozen source dependencies, uses core's Docker package builder and functional image, and records the host/archive/image/consumer identities. The native source harness is mounted into that installed image. Input pins are generated inside the actual Linux Node runtime. Measurement uses no network, two CPUs, 4 GiB memory with no extra swap, 512 PIDs, init, a minimal environment and a 20-minute command deadline. Core owns the Gateway lifecycle; the outer wrapper verifies that its exact named container is absent after a successful Docker daemon readback. Unknown closure fails the run, preserving both the original command exit and cleanup outcome.
The campaign executes --distribution core with three sequential repetitions.
The complete source inventory stays in every report: configured external or
source-only scenarios are blocked by selection, and plugins without configured scenarios
remain unsupported. A successful selected sweep is not full-inventory coverage.
No CPU/memory threshold or leak verdict is enforced. Calibration is qualified
separately; this campaign does not rerun the calibration controls. External
archives are not prepared or installed by this workflow.
Artifacts are uploaded on failure as well as success: preparation and execution
logs, inventory, image/package provenance, runtime input pins, partial campaign
receipts and closure.json. A missing closure receipt is unconfirmed termination,
not a clean result. Hard runner loss can prevent finalization; retained evidence
must not be interpreted as a successful campaign. Ordinary CI remains unchanged.