Openclaw Upstream Pr
Guides submitting pull requests to the upstream OpenClaw repository.
OpenClaw Upstream Pull Request
Workflow for creating and submitting pull requests to the openclaw/openclaw GitHub repository from the kip-claw/openclaw fork.
Prerequisites
- GitHub CLI (
gh) installed and authenticated askip-claw(verify withgh auth status) - SSH key at
~/.ssh/id_ed25519configured for GitHub - Crabbox CLI (
crabbox) installed with Cloudflare provider configured - Env vars
CRABBOX_CLOUDFLARE_RUNNER_URLandCRABBOX_CLOUDFLARE_RUNNER_TOKENin~/.openclaw/.env
# gh is already authenticated system-wide — no need to export GH_TOKEN.
# Source the env only for Crabbox credentials:
source ~/.openclaw/.env && export CRABBOX_CLOUDFLARE_RUNNER_URL CRABBOX_CLOUDFLARE_RUNNER_TOKEN
CRITICAL env note: values in ~/.openclaw/.env are not auto-loaded into random interactive shells. Before any Crabbox command in a fresh terminal/session, run the source ~/.openclaw/.env line above (or explicitly pass CRABBOX_CLOUDFLARE_RUNNER_URL/CRABBOX_CLOUDFLARE_RUNNER_TOKEN). If you skip this, Crabbox may appear "not configured" even though the values exist in the file.
Prefer gh over naked git/curl wherever an equivalent exists. gh handles auth, fork-awareness, and API plumbing for you. Only fall back to raw git for operations gh doesn't cover (commit, push, rebase, local branching).
Fork Setup
The fork lives at kip-claw/openclaw. Use gh repo clone — it automatically sets the upstream remote to the parent repo for forks:
gh repo clone kip-claw/openclaw /tmp/openclaw-patch
cd /tmp/openclaw-patch
# `upstream` remote is already configured by `gh repo clone` for forks. Verify:
git remote -v # should show origin=kip-claw/openclaw, upstream=openclaw/openclaw
# Sync the fork's default branch with upstream before branching:
gh repo sync kip-claw/openclaw --source openclaw/openclaw
git fetch upstream
git checkout -b fix/descriptive-branch-name upstream/main
The repo includes a .crabbox.yaml that configures Cloudflare remote builds (provider, instance class, setup steps, named jobs). No local pnpm install is needed — dependencies install on the remote container.
Source Layout
Key directories in the OpenClaw source:
| Path | Purpose |
|---|---|
src/cron/ |
Cron scheduler, timers, watchdogs |
src/config/ |
Configuration types and loaders |
src/agent/ |
Agent runtime, embedded sessions |
src/gateway/ |
Gateway server, routing |
src/delivery/ |
Message delivery (telegram, etc.) |
src/logging/ |
Diagnostics, liveness, phases |
TypeScript source compiles to dist/ as bundled JS files (e.g., dist/server-cron-*.js).
Extension / Plugin PRs
Not every PR is a core src/ fix. Bundled plugins live under extensions/<name>/ (e.g. memory-wiki, memory-core, channel plugins). The maintainers' stated default is "most features should be third-party plugins, not core changes." A change to a bundled extension is more defensible than a core change, but still expect pushback — if the PR adds a capability, preempt it in the body with a short "Why in core / why this extension owns it" paragraph (e.g. it's a thin read-only accessor over data the plugin already derives internally; a third-party plugin couldn't reach it without the unsafe-local escape hatch or duplicating logic).
Plugin manifest contract (bites silently). Every agent tool a plugin registers via api.registerTool must also be declared in that plugin's extensions/<name>/openclaw.plugin.json under contracts.tools (sorted array of tool names). If you add a tool in index.ts/src/tool.ts but forget the manifest, the plugin registry rejects the registration at runtime (plugin must declare contracts.tools for: <name>) and the test:contracts:plugins lane fails — but test:extension <name> does not catch it. This is exactly the kind of thing codex review flags and the per-extension test misses.
Validation commands for extension PRs (run remotely on Crabbox — see git caveat in Step 5):
pnpm build
pnpm tsgo:extensions # typecheck extension source
pnpm tsgo:extensions:test # typecheck extension tests
pnpm lint:extensions # oxlint (0 warnings / 0 errors expected)
oxfmt --check extensions/<name> # formatter (NOT prettier — repo uses oxfmt)
pnpm test:extension <name> # that plugin's vitest suite
pnpm test:contracts:plugins # cross-plugin contract lane (enforces the manifest above)
pnpm format:docs:check # if the PR touches docs/
Repo tooling is oxfmt (.oxfmtrc.jsonc) + oxlint, tsgo for typecheck, vitest for tests, format-docs.mjs + markdownlint-cli2 for docs. There is no prettier — pnpm exec prettier will "not found". oxfmt <file> auto-fixes; oxfmt --check verifies.
Workflow
1. Identify the Problem in Production
Reproduce the issue on the local Pi 5 instance. Gather evidence:
# Journal logs with timestamps
sudo journalctl -u openclaw --since "1 hour ago" --no-pager | grep -i "error\|timeout\|abort"
# Cron job state
openclaw cron list 2>&1
openclaw cron get <job-id> 2>&1
2. Find the Relevant Source
Locate the compiled runtime to understand the code path:
# Find compiled modules
ls /usr/lib/node_modules/openclaw/dist/
# Search for constants/functions
grep -n "RELEVANT_CONSTANT" /usr/lib/node_modules/openclaw/dist/*.js
Then find the corresponding TypeScript source in the fork:
cd /tmp/openclaw-patch
grep -rn "RELEVANT_CONSTANT" src/
3. Validate the Fix Locally First
Patch the compiled JS in production to confirm the fix works before writing the PR:
# Edit the compiled file
sudo nano /usr/lib/node_modules/openclaw/dist/server-cron-*.js
# Clear Node compile cache (CRITICAL — stale bytecode will mask your patch)
sudo rm -rf /var/tmp/openclaw-compile-cache
# Restart the gateway
sudo systemctl restart openclaw
# Test
openclaw cron run <job-id>
sudo journalctl -u openclaw -f
4. Write the TypeScript Fix
Apply the equivalent change in the fork's TypeScript source:
cd /tmp/openclaw-patch
# Edit the relevant .ts files
# Follow existing code patterns (naming, exports, config structure)
Conventions observed:
- Constants use
SCREAMING_SNAKE_CASE - Config resolution functions follow
resolve<PropertyName>()pattern - Optional config fields use
property?: typein interface definitions - Watchdog/timeout values should be configurable via
CronConfigwhen possible
5. Build & Test via Crabbox (Cloudflare)
Crabbox runs build and typecheck remotely on Cloudflare containers (standard-4: 4 vCPU, 12 GiB RAM). This satisfies the CONTRIBUTING.md requirement to "Run tests: pnpm build && pnpm check" before submitting. The .crabbox.yaml in the repo handles provider selection, dependency installation, and named jobs.
Remote-first test policy (default): run every test/validation that can run remotely on Crabbox, not on the Pi. Use local Pi execution only when the check requires local runtime state, credentials, or live channel integrations that cannot exist in an ephemeral Crabbox container.
Important: Crabbox can handle both build validation AND behavior proof generation. The quick-check job proves compilation + typecheck. The proof job builds the patched binary and runs targeted commands to produce real output for the PR body. For fixes that require runtime state (gateway running, cron jobs active), use local Pi production evidence instead.
Quick one-shot (build + typecheck)
cd /tmp/openclaw-patch
crabbox job run quick-check
This syncs your working tree to a fresh container, runs git init + pnpm install + pnpm build + typecheck. Completes in ~3-4 minutes.
Named jobs (from .crabbox.yaml)
| Job | What it does | Use when |
|---|---|---|
crabbox job run quick-check |
build + typecheck | Before committing (fast, reliable) |
crabbox job run build |
build only | Verify compilation |
crabbox run --provider cloudflare --shell 'pnpm docs:list' |
docs index validation | Docs-only or doc-touching PRs |
crabbox job run proof |
build + run proof commands | Generate behavior proof for PR body |
crabbox job run gateway-smoke |
build + start gateway + health check | Proving gateway boots with patches applied |
crabbox job run check |
build + full check (incl. lint) | Final validation (may hit timeout on large repos) |
crabbox job run test-changed |
targeted tests | Validating specific changes |
Iterative development (warm box)
For rapid feedback while developing a fix. The huge win is not re-paying pnpm install + pnpm build (~8 min combined) on every run — pay it once, then iterate in seconds.
[!CRITICAL] Warm boxes have NO
.git— git-dependent tests fail unless you init it. Ephemeralcrabbox run(no--id) doesgit init/commitfor you. The warm-box path (crabbox warmup+crabbox run --id) syncs via rsync and does NOT include.git— the workspace is not a git repo. Many contract tests enumerate files viagit ls-files(their names read"lists … from git without walking …"). With no git they fall back tofs.readdirSyncand fail en masse withexpected "readdirSync" to not be called at all, but actually been called N times. In this session 8 of thetest:contracts:pluginsfiles failed this way — a pure environment artifact, nothing to do with the code. Alwaysgit init && git add -A && git commitin the warm-box workspace before runningtest:contracts:plugins(or any git-listing test). With git present the suite passed 986/986; without it, 8 spurious failures.
# Lease a persistent container — leases in ~1s ("warmup complete total=854ms")
crabbox warmup --provider cloudflare
# Returns an id (cbx_...) + slug (e.g. "brisk-crab"). Reuse the cbx_ id for all runs.
# --reclaim is REQUIRED if you run from a different cwd than where you warmed up
# (else: "lease cbx_... is claimed by repo <path>; use --reclaim").
# First run: set up git (MANDATORY, see callout) + deps
crabbox run --id cbx_... --reclaim --provider cloudflare -- bash -lc \
'git init -q && git config user.email t@t.co && git config user.name t && git add -A && git commit -q -m snapshot && pnpm install --frozen-lockfile'
# Subsequent runs: build/test (git + deps persist on the box)
crabbox run --id cbx_... --reclaim --provider cloudflare -- bash -lc 'pnpm build && pnpm tsgo:extensions'
crabbox run --id cbx_... --reclaim --provider cloudflare -- bash -lc 'pnpm test:contracts:plugins 2>&1 | tail -40'
# Done — release. NOTE: `stop` takes the id POSITIONALLY, provider flag FIRST.
# `crabbox stop --id <x>` is WRONG (`flag provided but not defined: -id`).
crabbox stop --provider cloudflare cbx_...
# If you forget, it auto-releases after the 30m idle timeout — no 24/7 box needed.
Sizing / limits (asked-and-answered): default -type standard-4, -class beast are already the top tier. The ~15 min cutoff is a Cloudflare Worker wall-clock/stream limit, not CPU starvation — a bigger -type finishes faster (buying headroom) but does not raise the ceiling. -idle-timeout (default 30m) governs auto-release; the box is never idle mid-run so that's not what cuts long commands. For cross-session speed without a live box, see crabbox checkpoint (snapshot post-install/build) and crabbox cache warm.
set -e masks test failures. In a crabbox script, set -e + pnpm test ...; echo EXIT=$? aborts the whole script the instant the test returns non-zero — before your echo/log-dump runs, so you never see why it failed. Either drop set -e around the test, or write output to a file and always cat/tail it regardless of exit: pnpm test:contracts:plugins > /tmp/c.log 2>&1; echo "EXIT=$?"; tail -60 /tmp/c.log.
Flaky streaming — retry, don't panic. Transient cloudflare stream ended before completion and HTTP2 PROTOCOL_ERROR on upload happen; they are not real failures. Retry. To minimize exposure, split work into short commands (install / build / each test suite / proof) rather than one long chained run that risks the wall-clock and a mid-stream drop.
Troubleshooting
# Verify provider is healthy
crabbox doctor --provider cloudflare
# If a container hits stream timeout, use quick-check instead of full check
# Cloudflare containers have ~15 min stream limit — lint can exceed this on large repos
Limitations:
- ~15 min stream timeout — full lint (
pnpm check) may exceed this; usequick-checkfor reliable runs - 20 GB disk — fits the monorepo comfortably
- No SSH into the container — output streaming only
- Containers are ephemeral; warm boxes persist only while leased
- Full test suite (
pnpm test) exceeds the timeout; usetest-changedor run tests in upstream CI
6. Generate Behavior Proof via Crabbox
After quick-check passes, generate real behavior proof for the PR body. The proof job in .crabbox.yaml builds the patched binary and runs commands that demonstrate the fix works.
Customize the proof job
Edit .crabbox.yaml's proof job — replace the placeholder proof commands with fix-specific ones:
proof:
command: >-
git config --global user.email x@x &&
git config --global user.name x &&
git init -q && git add -A && git commit -m x -q &&
corepack enable &&
pnpm install --frozen-lockfile &&
pnpm build &&
echo "=== PROOF START ===" &&
node openclaw.mjs --version &&
echo "--- fix-specific proof below ---" &&
node openclaw.mjs <your-proof-command> &&
echo "=== PROOF END ==="
What to put in proof commands (depends on fix type):
| Fix type | Proof command examples |
|---|---|
| CLI behavior | node openclaw.mjs <subcommand> --flag |
| Config resolution | node -e "import('./dist/entry.js').then(...)" |
| Cron/watchdog | node openclaw.mjs cron list 2>&1 | head -20 |
| Build artifact | ls -la dist/<expected-file> |
| Error handling | node openclaw.mjs <trigger-condition> 2>&1 (show graceful handling) |
Run proof
cd /tmp/openclaw-patch
crabbox job run proof
Completes in ~3 min. Copy the output between === PROOF START === and === PROOF END === into the PR body's Real behavior proof section.
Warm box variant (for iterating on proof commands)
crabbox warmup --provider cloudflare
# First: setup + build
crabbox run --id <slug> -- 'git config --global user.email x@x && git config --global user.name x && git init -q && git add -A && git commit -m x -q && corepack enable && pnpm install --frozen-lockfile && pnpm build'
# Then iterate on proof commands quickly (no rebuild needed):
crabbox run --id <slug> -- 'node openclaw.mjs <proof-command>'
crabbox run --id <slug> -- 'node openclaw.mjs <another-proof-command>'
# Done
crabbox stop <slug>
Proof via a throwaway vitest spec (best for plugin/tool PRs — no build needed)
Importing a tool from dist/ for proof forces a full pnpm build (~slow, and the step most likely to hit the stream limit / a mid-stream drop). Avoid it: write a throwaway *.test.ts that invokes the real tool/function against a seeded fixture and console.logs the output, then run it with vitest — vitest transforms TS on the fly, so no build is required and it runs in seconds on a warm box.
// extensions/<name>/src/_proof.test.ts — TEMP, do NOT commit
import { it } from "vitest";
import { createWikiOpenItemsTool } from "./tool.js";
import { resolveMemoryWikiConfig } from "./config.js";
// ...seed a temp vault on disk...
it("PROOF", async () => {
const tool = createWikiOpenItemsTool(config);
const out = await tool.execute("proof", {});
console.log(out.content[0].text);
console.log("details =", JSON.stringify(out.details));
});
crabbox run --id cbx_... --reclaim --provider cloudflare -- bash -lc \
'pnpm exec vitest run extensions/<name>/src/_proof.test.ts 2>&1 | tail -60'
Create the spec locally (so it syncs to the box), capture the printed output for the PR, then delete it before committing (rm extensions/<name>/src/_proof.test.ts) — it must not land in the PR. vitest prints console.log under a stdout | … header.
When to use Pi instead of Crabbox
Use local Pi production evidence when the proof requires:
- A running gateway with real plugins/channels connected
- Active cron jobs with historical state
- Real message delivery (Telegram, Matrix, etc.)
- Network-dependent features (health checks against live services)
- Auth/credential resolution — stored OAuth tokens, API keys in the profile store, or cross-provider credential lookups (Crabbox containers have no stored credentials)
For these cases, follow Step 3's local validation approach and paste journal logs.
Lesson learned: Crabbox is excellent as a remote CI (build + typecheck confirmation without local toolchain), but for fixes involving credential resolution (e.g. openai/openai-codex cross-provider auth), the proof MUST come from the Pi where real credentials exist. A fresh container cannot exercise auth paths that depend on stored OAuth tokens. Plan accordingly — use quick-check for build validation, then patch the local Pi runtime (/usr/lib/node_modules/openclaw/dist/) for behavior proof. Remember the dist files use tabs for indentation; Python sed/replace scripts must account for this.
7. Commit and Push
git is still the right tool for local commits and pushing branches (no gh equivalent for these):
cd /tmp/openclaw-patch
git add -A
git commit -m "fix(cron): descriptive summary of the change
Longer explanation of the problem, root cause, and fix approach.
Include production evidence (durations, error messages) that proves the fix."
git push -u origin fix/descriptive-branch-name
8. Create the Pull Request
The PR body must include a Real behavior proof section. This is a hard requirement from upstream — unit tests, mocks, CI, lint, and typechecks alone do NOT satisfy it.
[!WARNING] The external-PR gate is a LITERAL heading match, not a content check. The
Real behavior proofCI job runsscripts/github/real-behavior-proof-check.mjs→real-behavior-proof-policy.mjs, which requires the body to contain headings matching the exact regexes/^#{2,6}\s+What Problem This Solves\b/imand/^#{2,6}\s+Evidence\b/im. A semantically-identical but reworded heading (e.g.## What problem does this solve?) fails the job even with perfect content. Use the verbatim headings## What Problem This Solvesand## Evidence. The section must also be non-empty / not a placeholder (n/a,tbd,-, etc. count as missing). Maintainer/collaborator/bot PRs skip this gate; external PRs (like ours) do not.
[!NOTE] Editing the PR body/title: use
gh api, notgh pr edit.gh pr editresolves the author's org membership and fails on this token:GraphQL: … 'login' field requires … 'read:org' … token has only … [repo, workflow, …]. Update the body via the REST API instead (no org scope needed):gh api -X PATCH repos/openclaw/openclaw/pulls/<N> -f body="$(cat body.md)"Fetch the current body first (
gh api repos/openclaw/openclaw/pulls/<N> --jq .body) if you're appending rather than replacing.
# gh is system-authenticated — no env export needed.
gh pr create \
--repo openclaw/openclaw \
--head kip-claw:fix/descriptive-branch-name \
--title "fix(scope): short title" \
--body "$(cat <<'EOF'
## Problem
Description of the issue with production evidence.
## Root Cause
What causes it in the source code (reference specific files/lines).
## Fix
What the PR changes and why.
## Real behavior proof
> Required by CONTRIBUTING.md. Must show real post-patch behavior from your own setup.
**Setup:** Cloudflare Container (standard-4: 4 vCPU, 12 GiB RAM, Node 24.x) via Crabbox
*(or: Raspberry Pi 5 (4GB, ARM64, Debian bookworm) if proof requires running gateway)*
**BEFORE (unpatched):**
<paste real production output — journal logs, health output, error messages>
**AFTER (patched):**
What was NOT tested: <list edge cases or platforms you didn't cover>
Acceptable proof formats: terminal output, journal logs, openclaw health output,
screenshots, recordings, redacted runtime logs, linked artifacts.
NOT acceptable alone: unit tests, mocks, snapshots, lint, typechecks, CI green.
Environment
- OpenClaw version: (from
openclaw --version) - Platform: Raspberry Pi 5 (4GB, ARM64, Debian bookworm)
- Node: (from
node --version)
AI Disclosure
- AI-assisted (Claude Code / Claude Opus 4.8)
- Human-run real behavior proof from own setup
- Understand what the code does EOF )"
### 9. Search for Related Issues/PRs
Before or after submitting, search for existing work on the same problem:
```bash
gh search prs --repo openclaw/openclaw "relevant keywords" --state open
gh search issues --repo openclaw/openclaw "relevant keywords"
If a related PR exists:
- Cross-reference it from your PR
- Comment on the existing PR with your production evidence
- If it has labels like
needs-real-behavior-proof, provide the proof
gh pr comment <number> --repo openclaw/openclaw --body "Production evidence from my environment: ..."
gh issue comment <number> --repo openclaw/openclaw --body "Cross-reference: #your-pr-number"
10. After Submission
Use gh to monitor the PR after creation — no need to leave the terminal:
gh pr view --repo openclaw/openclaw <number> # full PR detail + comments
gh pr checks --repo openclaw/openclaw <number> # CI status
gh pr status --repo openclaw/openclaw # all your PRs at a glance
gh pr diff --repo openclaw/openclaw <number> # confirm what reviewers see
- Resolve or reply to bot review conversations (ClawSweeper, Codex) yourself — do not leave them for maintainers (
gh pr comment/gh pr reviewfor replies) - If Codex review doesn't trigger, run
codex review --base origin/mainlocally and address findings - Monitor for maintainer feedback
ClawSweeper — the automated reviewer (take it seriously)
clawsweeper[bot] is the primary automated review on every PR. It posts one durable, marker-backed comment per PR and edits that same comment on each re-review (no duplicate comments). Key behaviors observed:
- Its findings are high quality. In this session it correctly caught a runtime-breaking manifest omission (P1) that all local checks missed, then on the next round found 3 more real defects (an opaque-id-instead-of-text bug, an
anyOf-vs-flat-enum provider-compat issue, and filtered-vs-unfiltered count inconsistency) plus called out that a hand-written "proof" didn't match actual code behavior. Verify each finding against the code, but assume it's right until proven otherwise — don't argue, fix. - It gates on real proof. Status
📣 needs proof/ rating🦪 silver shellfishetc.; it explicitly blocks when the posted proof "does not demonstrate the registered tool through OpenClaw." A fabricated/idealized proof block will be caught and called out. Generate proof from an actual invocation (see the vitest-spec technique above). - Re-trigger with
@clawsweeper re-review(PR author or write-access can request review-only). Post it as a normal issue comment:gh api repos/openclaw/openclaw/issues/<N>/comments -f body="@clawsweeper re-review"re-review/re-rundo not start autofix/automerge — those are maintainer-only (@clawsweeper autofix|automerge|address review). - Respond point-by-point in a new comment, don't silently push. Reference each finding, say what changed, and paste the real proof. It (and maintainers) read that thread.
Codex CLI review (pre-flight, before you even push)
Run codex review --base origin/main locally to catch what CI's ClawSweeper will catch — cheaper to fix before submitting. Model gotchas on the ChatGPT-account auth: gpt-5.5 works; gpt-5.3-codex (not on the account) and gpt-5.6-terra (needs a newer CLI) are rejected. Override with -c model="gpt-5.5". If auth expires (refresh token already used / token_expired), re-login from the Pi terminal. Note this also doubles as an early-warning signal for the terra OAuth expiry the deployment tracks.
- Be prepared to rebase if
upstream/mainmoves ahead. Usegh repo syncto update the fork's default branch first, then rebase locally (rebase has noghequivalent):
cd /tmp/openclaw-patch
gh repo sync kip-claw/openclaw --source openclaw/openclaw
git fetch upstream
git rebase upstream/main
git push origin fix/branch-name --force-with-lease
PR Rules (from CONTRIBUTING.md)
- 20 PR cap per author — exceeding this triggers auto-close with
r: too-many-prs - No refactor-only PRs unless explicitly requested by a maintainer
- No test/CI-only PRs for known
mainfailures — maintainers track those already - One logical change per PR — don't bundle unrelated fixes
- Do not edit
CHANGELOG.md— maintainers add changelog entries when landing - Do not edit
CODEOWNERS-protected paths unless an owner asked for the change - American English in code, comments, docs, and UI strings
- Features should generally be plugins (use plugin SDK), not core changes — ask in Discord first
Lessons Learned
- Always validate locally first. Patching compiled JS and testing in production provides irrefutable evidence for the PR.
- Clear
/var/tmp/openclaw-compile-cacheafter any patch to compiled JS — Node caches bytecode and will ignore your edits otherwise. - Real behavior proof is mandatory. Paste actual before/after output from production. Reviewers will block PRs without it.
- Search for existing PRs before submitting. Your evidence may unblock someone else's stalled PR.
- Make config values overridable rather than just bumping hardcoded constants. This is more likely to be accepted upstream.
- Resolve bot conversations yourself. ClawSweeper and Codex review comments are your responsibility to address or resolve.
- Mark AI assistance. If using LLM tools, disclose it and confirm you understand the code.
- Use warm boxes for iteration. Leasing a warm container avoids repeated pnpm installs (~23s each). Stop it when done to free resources.
- Don't install deps locally. The Pi has limited disk; let Crabbox handle pnpm install on the remote container.
- Crabbox handles both build proof AND behavior proof. The
proofjob builds the binary and runs targeted commands to generate real output. Only fall back to local Pi when proof requires a running gateway, real channels, or active cron state. - Use
quick-checkovercheck. Full lint can exceed Cloudflare's ~15 min stream timeout.quick-check(build + typecheck) is reliable and catches most issues. - Base64-encode complex shell scripts in
.crabbox.yaml. Crabbox wraps commands in temp script files, breaking embedded quotes. For scripts with loops, conditionals, or single/double quotes, base64-encode the script and decode on the container:echo <base64> | base64 -d > /tmp/script.sh && chmod +x /tmp/script.sh && /tmp/script.sh. Thegateway-smokejob uses this pattern. - Prefer
ghover nakedgit/curl.ghis system-authenticated and fork-aware:gh repo clonesetsupstreamautomatically,gh repo syncupdates the fork without manual fetch/merge,gh pr {create,view,checks,diff,comment,status}cover the PR lifecycle. Only fall back to rawgitfor local-only operations: commit, push, rebase, branch. Exception: editing a PR body/title needsgh api -X PATCH …/pulls/<N> -f body=…becausegh pr editdemands aread:orgscope this token lacks. - Warm boxes have no
.git. The single biggest time-sink this session:test:contracts:pluginsfailed 8 files on a warm box purely because rsync doesn't sync.git, so git-listing tests fell back toreaddirSync. Alwaysgit init && git add -A && git commitin the warm-box workspace before git-dependent tests. Ephemeralcrabbox run(no--id) doesn't hit this. Don't waste time blaming your diff — checkgit rev-parse --is-inside-work-treein the box first. - The external-PR CI gate matches heading text literally. Body must contain verbatim
## What Problem This Solvesand## Evidence. Reworded headings fail even with correct content (real-behavior-proof-policy.mjs). - ClawSweeper is right more than you'd think — fix, don't argue. It caught a manifest omission and 3 correctness defects local checks missed. Verify against code, then fix. Re-trigger with an
@clawsweeper re-reviewissue comment; respond point-by-point in a new comment with real proof. - Never fabricate proof. An idealized/hand-written "real behavior proof" that the code can't actually produce will be caught (by ClawSweeper, and it's just wrong). Generate proof from a real invocation — for tools/plugins, a throwaway
*.test.tsrun viapnpm exec vitest run(no build needed) is the fast, reliable path. - Plugin tools must be in
openclaw.plugin.jsoncontracts.tools. Register a tool in code but omit the manifest → runtime rejection +test:contracts:pluginsfailure, invisible totest:extension <name>. set -ein crabbox scripts hides the failing test log. It aborts before your echo/catruns. Dump the log unconditionally and inspect it.- Beware stray NUL bytes turning a file "binary." A literal
\x00(e.g. an intended separator that came through as NUL) makes git treat the source as binary —git diff --numstatshows- -,git diffsays "Binary files differ",filereportsdata, and the PR diff won't render. Detect withpython3 -c "d=open(p,'rb').read(); print([i for i,b in enumerate(d) if b==0])"; fix by replacing the byte. Sanity-checkgit diff --statdoesn't showBinfor a text file before committing. - Split crabbox work into short commands. Install / build / each test suite / proof as separate runs beats one long chain — dodges the ~15 min Worker wall-clock and transient stream drops (
stream ended before completion, HTTP2PROTOCOL_ERROR), which are just retryable flakes. crabbox stopsyntax:crabbox stop --provider cloudflare <cbx_id>(provider flag first, id positional).--idis not a validstopflag. Warm boxes also auto-release after the 30m idle timeout, so 24/7 leasing is never required.
Notes
- The fork at
kip-claw/openclawshould stay in sync with upstream between PRs - Clean up working directories after PRs are merged:
rm -rf /tmp/openclaw-patch - The local patched runtime at
/usr/lib/node_modules/openclaw/dist/will be overwritten on the nextnpm update -g openclaw
