AGENTS.MD
Telegraph style. Root rules only. Read scoped AGENTS.md before subtree work.
Skills own workflows; root owns hard policy and routing.
Start
- Repo:
https://github.com/openclaw/openclaw - Replies: repo-root refs only:
extensions/telegram/src/index.ts:80. No absolute paths, no~/. - Docs/user-visible work:
pnpm docs:list, then read relevant docs only. - Existing-solutions preflight: before proposing or building a custom system, feature, workflow, tool, integration, or automation, do a lightweight check for open-source projects, maintained libraries, existing OpenClaw plugins, or free platforms that already solve it well enough. Prefer those when adequate. Build custom only when existing options are unsuitable, too expensive, unmaintained, unsafe, non-compliant, or the user explicitly asks for custom. Avoid paid-service recommendations unless the user explicitly approves spend. Keep this to a brief preflight gate, not a broad research assignment.
- Fix/triage answers need source, tests, current/shipped behavior, and dependency contract proof.
- Reviews/answers: high confidence required. Default to exhaustive relevant codebase search/read, including owners, callers, siblings, tests, docs, and upstream/dependency contracts before verdict. Diff-only review is insufficient.
- Review default: read the whole changed function/module plus callers, callees, sibling implementations, adjacent tests, scoped docs, and dependency/Codex contracts before saying
good,bad,best fix,proof sufficient, or posting a comment. If challenged, keep reading first; do not defend the earlier verdict until the missing path is checked. - Dependency-touching work: direct dependency inspection is mandatory when feasible; do not rely on assumptions, wrappers, or memory. Most dependencies are OSS, so read their source/docs/types. Codex-related work has a hard gate: the acting agent must personally inspect sibling
../codexsource for the exact protocol/runtime behavior before any verdict, comment, approval, merge recommendation, code change, orproof sufficientclaim. If missing, clonehttps://github.com/openai/codex.gitthere first. Subagent reports, PR text, OpenClaw wrappers, generated schemas, memory, and prior bot reviews do not satisfy this gate. No direct../codexcheck means no Codex verdict. Cite Codex files/lines checked in final/review/comment. - Dependency-backed behavior: read upstream docs/source/types first. No API/default/error/timing guesses.
- External API work: live test required. Google/search for additional proof. Prefer official docs/source/types; cite current proof. No memory-only API claims.
- Live-verify when feasible. Never print secrets.
- Missing deps:
pnpm install, retry once, then report first actionable error. - CODEOWNERS: maint/refactor/tests ok. Larger behavior/product/security/ownership: owner ask/review.
- Product/docs/UI/changelog wording: "plugin/plugins";
extensions/is internal. - New channel/plugin/app/doc surface: update
.github/labeler.yml+ GH labels. - New
AGENTS.md: add siblingCLAUDE.mdsymlink; editAGENTS.mdonly.
ClawSweeper Review Policy
- OpenClaw-specific review rules live here; generic ClawSweeper prompts stay repo-agnostic.
- ClawSweeper-owned schema, labels, close reasons, protected-label gates, maintainer-item gates, and mutation rules live in
openclaw/clawsweeper. - Review workers read this full root
AGENTS.mdbefore judging; no reliance on search snippets,head, partial ranges, local excerpts, or truncated copies. Then read every scopedAGENTS.mdthat owns touched paths. - Optional integrations, providers, channels, skill bundles, MCP surfaces, and service workflows route to plugins, ClawHub, or owner repos when current seams suffice. Keep core items for missing core/plugin APIs, bundled regressions, security/core hardening, or maintainer product decisions.
- Plugin APIs, provider routing, auth/session state, persisted preferences, config loading, config/default additions, migrations, setup, startup checks, and fallback behavior are compatibility/upgrade-sensitive. Treat config breaks, new config/default surfaces, removed fallbacks, fail-closed changes, stricter validation, or new operator action as merge risk even with green CI when they can affect existing users, upgrades, provider/plugin behavior, or maintainer operations.
- For PRs that add, remove, or change config/default surfaces with possible compatibility, upgrade, provider/plugin, operator, setup, startup, or fallback impact, ClawSweeper review should emit a
reviewMetricsentry when practical. The metric should name the count and direction of the changes, such as added, changed, or removed config/default surfaces, and explain why the metric matters before merge. When the metric indicates concrete merge risk, also surface the concern inrisks, usemergeRiskLabelswhen the risk matches the label rubric, makebestSolutionname the desired pre-merge state, and ensurelabelJustificationsexplain the specific reason rather than restating the label. - Review whole decision surfaces, not only the touched runtime, provider, channel, harness, plugin seam, or context path. Check sibling Codex/Pi-style runtimes, provider/model routing, channel delivery, gateway/protocol, plugin SDK, and context-management paths when relevant.
- Every PR review must explicitly ask whether the PR is the best fix, not merely a plausible fix. Verdicts need a best-fix judgment backed by enough code reading to compare owner boundaries, callers, siblings, tests, docs, current
main, shipped behavior when relevant, and dependency/Codex contracts when involved. - Before a PR verdict, build a small evidence map: changed surface, entry point, owner boundary, at least one caller and callee, sibling surfaces that share the invariant, existing tests, and current
mainbehavior. If any cell is missing, say the gap instead of concluding. - One-sided fixes need sibling-surface proof, an explanation for why siblings are unaffected, or explicit follow-up work.
- Changelog findings: see Docs / Changelog.
- Public ClawSweeper comments prefer
https://docs.openclaw.ai/...when a public docs page exists; structured evidence still cites repo files, lines, SHAs. - Findings need current source, shipped/current behavior, tests/CI evidence, and dependency contract proof when dependency-backed behavior is involved. Validation is judged against touched and sibling surfaces plus this file's commands; clear evidence matters for user-visible changes, with Telegram/Desktop proof for Telegram-visible behavior when feasible.
- Prefer findings for concrete behavior regressions, missing changed-surface proof, owner-boundary violations, security/API contract issues, or docs/config mismatches.
- Do not file findings for repo policy preference when changed code follows the relevant scoped guide and no user-visible, runtime, security, or maintainer-risk impact is shown.
Map
- Core TS:
src/,ui/,packages/; plugins:extensions/; SDK:src/plugin-sdk/*; channels:src/channels/*; loader:src/plugins/*; protocol:packages/gateway-protocol/*; docs/apps:docs/,apps/. - Installers: sibling
../openclaw.ai. - Scoped guides:
extensions/,src/{plugin-sdk,channels,plugins,gateway,agents}/,packages/,test/helpers*/,docs/,ui/,scripts/.
Docs
- Source docs:
docs/**; publish repo:openclaw/docs; host:https://docs.openclaw.ai. - Flow: source ->
docs-sync-publish.yml-> mirror build -> R2 -> Worker router. - Docs AI:
openclaw/ask-molty; see itsAGENTS.md.
Architecture
- Core stays plugin-agnostic. No bundled ids/defaults/policy in core when manifest/registry/capability contracts work.
- Plugins cross into core only via
openclaw/plugin-sdk/*, manifest metadata, injected runtime helpers, documented barrels (api.ts,runtime-api.ts). - Plugin prod code: no core
src/**,src/plugin-sdk-internal/**, other pluginsrc/**, or relative outside package. - Core/tests: no deep plugin internals (
extensions/*/src/**,onboard.js). Use public barrels, SDK facade, generic contracts. - Owner boundary: owner-specific repair/detection/onboarding/auth/defaults/provider behavior lives in owner plugin. Shared/core gets generic seams only.
- Dependency ownership follows runtime ownership: plugin-only deps stay plugin-local; root deps only for core imports or intentionally internalized bundled plugin runtime.
- Internal bundled plugins ship in core dist; bundled-only facade loader ok only for them.
- External official plugins own package/deps and are excluded from core dist; core uses registry-aware
facade-runtimeor generic contracts. - Externalizing a bundled plugin: update package excludes, official catalogs, docs, tests, and prove core runtime paths resolve installed plugin roots before root-dep removal.
- Runtime reads canonical config only. No silent compat for old/malformed config keys. If a config change invalidates existing files, add a matching
openclaw doctor --fixmigration. Core/auth config repairs live in core doctor; plugin-owned config repairs live in that plugin's doctor contract (legacyConfigRules/normalizeCompatibilityConfig). - OpenAI Codex is folded into
openai. No new/liveopenai-codexprovider/plugin/auth/model routes; treat them as legacy input only. Runtime/setup/auth/catalog useopenai+openai/*; doctor/migrations repair staleopenai-codex/*profiles/metadata. - Config/env surface bar is high;
openclaw.jsonand environment variables are already large. Before adding a config option or env var, first prove existing product behavior, provider selection, defaults, or doctor migration cannot solve it. Prefer removing or consolidating config/env options when touching these surfaces. Core supports only the latest config shape;openclaw doctor --fixmigrates older shipped shapes into the current one. - CLI setup flows are public API when external docs, installers, or integrations can copy them. Changes to
openclaw onboard,openclaw configure, their documented flags, non-interactive behavior, or generated config shape are compatibility-sensitive API contract changes; prefer additive flags/aliases, deprecation windows, and backward-preserving migrations over breaking existing snippets. - Fix shape: default to clean bounded refactor, not smallest patch. Move ownership to right boundary; delete stale abstractions, duplicate policy, dead branches, wrappers, fallback stacks.
- New binary fallible-operation results use
Resultfrom@openclaw/normalization-core/result; domain-rich outcomes keep named discriminated unions. - Fix observed local failures with generic product rules; do not hardcode names, ids, log phrases, or user examples in prod code unless they are an explicit contract.
- Tests may use observed examples, but prod literals need a short contract reason.
- Compatibility is opt-in. "Shipped" means reachable from a release Git tag; main/GitHub/PR/unreleased code is not shipped.
- Refactor default: one canonical path. Delete the old path unless user explicitly wants compat or the shipped public contract is obvious and cited.
- Core runtime consumes only current canonical shapes/config/data. Legacy or retired shapes normalize only in doctor/migration code before runtime; no runtime shims, aliases, or fallback readers.
- State/storage migrations are database-first. Runtime reads/writes the canonical store only. Old file stores, sidecars, aliases, and fallback readers belong in
openclaw doctor --fixmigration code only, never steady-state runtime. - Storage default: SQLite only. Do not add JSON/JSONL/TXT/sidecar files for OpenClaw-owned runtime state, caches, queues, registries, indexes, cursors, checkpoints, or plugin scratch data.
- Any SQLite change requiring a schema-version bump needs explicit user discussion and acceptance before implementation. Agents must not advance SQLite schema versions autonomously.
- Purely additive SQLite surface (new tables; downgraded builds keep working, just without the new feature): do not bump the schema version. Declare in the canonical schema file plus a one-time idempotent lazy ensure on first feature use; fold into the migration path at the next natural bump. Bumps are reserved for changes older readers cannot tolerate.
- SQLite runtime access uses Kysely helpers, not raw SQL statement strings, except schema DDL, migrations, low-level DB bootstrap, or narrowly justified SQLite primitives.
- SQLite write transactions are synchronous commit sections only. Finish async planning, filesystem access, plugin hooks, and predicates before
BEGIN; then reread and validate authoritative rows before writing. Never return a Promise or executeawaitfrom a transaction callback. - Use the shared state DB (
state/openclaw.sqlite) for global runtime state and plugin KV data. Use the per-agent DB (agents/<agentId>/agent/openclaw-agent.sqlite) for agent-scoped state/cache. Use a dedicated SQLite DB only when schema, volume, or lifecycle clearly does not fit those stores. - Legacy state/cache files are migration debt. When touching code that reads/writes them, prefer moving the data into SQLite or calling out the refactor follow-up; do not add parallel file paths.
- File storage must be a named product artifact: import/export, user attachment, log, backup, or external tool contract. If it is app state or cache, it belongs in SQLite.
- Before adding any path under state dirs, choose one: shared state DB, plugin KV, agent DB, or dedicated SQLite schema. If none fits, design the SQLite owner/schema first.
- Cache/transient state gets no compat migration unless a shipped user contract is cited. Prefer delete/drop/rebuild over import. If old state can be lost without user-visible data loss, remove the old path entirely.
- Persistent user state gets one migration owner. Doctor migrates, verifies, and then runtime assumes the new shape. No dual-write, read-through fallback, lazy import, or "if SQLite fails use JSON" branches.
- Fallback is a product decision, not an implementation convenience. Before adding one, name the shipped contract, failure mode, removal plan, and why doctor cannot solve it. Otherwise delete it.
- Keep old behavior only for an explicit public API/config/plugin SDK/data contract, tagged upgrade path, security/migration boundary, dependency contract, or observed prod state.
- If unsure, ask before preserving compat. Do not keep aliases, shims, fallback stacks, stale names, or obsolete tests just in case.
- Tests alone do not make internals contracts. If compat stays, name the contract and migration/removal plan in code, test, or PR.
- Lean code is a goal. No internal shims, aliases, legacy names, broad fallbacks, or defensive branches just to reduce diff or handle unrealistic edge cases.
- Handle real production states, tagged upgrade paths, security boundaries, and dependency contracts. Public/hostile/observed malformed input gets care; hypothetical malformed input does not.
- Deprecate shipped public contracts only.
- Plugin SDK exception: shipped external API gets new API first plus named compat/deprecation, small tests/docs if useful, removal plan.
- Migrate internal/bundled callers to modern API in the same change. Do not let internal compat become permanent architecture.
- Channels are implementation under
src/channels/**; plugin authors get SDK seams. Providers own auth/catalog/runtime hooks; core owns generic loop. - Message/channel plugins stay transport-only. They render portable presentation/actions, enforce transport limits, and map native callback envelopes. They do not own product command trees, plugin/provider policy, or feature-specific menus.
- Portable command UI must use typed presentation actions, not raw string inference. Do not make channels guess that
valuestarting with/means a native command; core/owner plugins declare command actions, channels map them when supported. - Raw callback data is transport/private. Approval, command, URL, web-app, and select actions must stay distinguishable before channel encoding so transport adapters do not special-case product strings.
- Agent run terminal state: normalize/merge via
src/agents/agent-run-terminal-outcome.ts; do not rederive timeout/cancel precedence in projections. - Hot paths should carry prepared facts forward: provider id, model ref, channel id, target, capability family, attachment class. Do not rediscover with broad plugin/provider/channel/capability loaders.
- Do not fix repeated request-time discovery with scattered caches. Move the canonical fact earlier; reuse prepared runtime objects; delete duplicate lookup branches.
- Gateway/plugin metadata is process-stable: installs, manifests, catalogs, generated paths, bundled metadata. Changes require restart or explicit owner reload/install/doctor flow.
- Runtime hot paths: no freshness polling (
stat/realpath/JSON reread/hash). Reuse current snapshots, install records, discovery, lookup tables, root scopes, resolved paths. - Process-local metadata caches ok when lifecycle-owned and bounded/single-slot. Freshness exceptions need named owner + tests.
- Inline comments: preserve reviewer context at the code site. Required for non-obvious cross-path/state invariants, lifecycle ordering, ownership boundaries, queue/dedupe symmetry, TTL/cache expiry, cleanup/release coupling, session/id adoption, fallback behavior, platform/dependency caps, deterministic ordering, compact encoded state, or intentional caller differences.
- Comment shape: 1-3 short lines; state why the branch/helper exists, what contract it protects, and the bad outcome if removed. Cite nearby constants/helpers when useful. No syntax narration, PR/user-specific lore, or obvious mechanics.
- Gateway protocol changes: additive first; incompatible needs versioning/docs/client follow-through.
- Protocol version bumps: explicit owner confirmation only; never automatic/generated.
- Config contract: exported types, schema/help, metadata, baselines, docs aligned. Retired public keys stay retired; compat in raw migration/doctor only.
- Prompt cache: deterministic ordering for maps/sets/registries/plugin lists/files/network results before model/tool payloads. Preserve old transcript bytes when possible.
- Agent tool schema cleanup: remove stale args cleanly; no hidden compat for model-facing params just to avoid churn.
Commands
- Runtime: Node 22.22.3+, 24.15+, or 25.9+; Node 24 recommended. Keep Node + Bun paths working.
- Package manager/runtime: repo defaults only. No swaps without approval.
- Install:
pnpm install(keep Bun lock/patches aligned if touched). Agent dependency installation for tests/builds defaults to the selected remote box; do not reconcile a local Codex worktree just to run validation. - CLI:
pnpm openclaw ...orpnpm dev; build:pnpm build. - Packaged
pnpm buildomits QA Lab + qa-channel by design (source-checkout only). To exerciseopenclaw qa/qa-channel from a built dist, build withOPENCLAW_BUILD_PRIVATE_QA=1 pnpm build(emitsdist/plugin-sdk/qa-lab.js,qa-runtime.js,dist/extensions/{qa-lab,qa-channel}) or run viapnpm dev. - Agent proof routing: source trust first, proof size second. Only trusted source may run locally; then run one/few focused tests and cheap static checks locally when the existing checkout dependencies are ready. Use Crabbox only for larger suites, changed gates with typecheck/lint fan-out, builds, Docker, packaging, E2E, live proof, and cross-platform work. Trusted maintainer heavy proof defaults to Blacksmith Testbox. Contributor/fork code remains untrusted unless a maintainer explicitly approves credentialed execution after review; an explicit owner/maintainer instruction to land named, reviewed PRs is that approval, so do not ask twice. Otherwise use secretless fork CI or sanitized direct AWS Crabbox, never a credential-hydrated Testbox. Never run an untrusted checkout's scripts, tests, checks, wrappers, config, or package hooks locally, regardless of proof size. Sanitized AWS must launch an installed trusted Crabbox binary from a clean trusted
maincheckout and fetch only the remote PR via--fresh-pr; never execute a wrapper, config, or command from the untrusted local checkout. Before warmup, unsetCRABBOX_AWS_INSTANCE_PROFILEand allCRABBOX_TAILSCALE*overrides; fail closed unless resolvedaws.instanceProfileis empty. Force--network public --tailscale=false, clear exit-node/LAN flags, and requirecrabbox inspectto report public networking with no Tailscale state before any script. Upload trustedscripts/crabbox-untrusted-bootstrap.shfrom cleanmainalongside--fresh-pr; it proves the remote IMDSv2 IAM credentials endpoint returns 404, verifies the reviewed head SHA, unsetsNODE_OPTIONS, installs pinned Node/pnpm, verifies the package-manager pin, isolatesHOME, installs dependencies, then runs the requested test. Use a newly warmed lease bound to one reviewed head SHA, setCRABBOX_ENV_ALLOW=CI, and use--no-hydrate. Never reuse a trusted/previously hydrated lease or carry an untrusted lease across head revisions; stop and rewarm when the SHA changes. No repoOPENCLAW_*allowlist, existing auth profile, instance role, tailnet/LAN access, moving PR head, or ambient Node preload may reach untrusted execution. Do not pre-warm at task start. Acquire the safe backend lazily for the first heavy proof, reuse that one lease, then stop it before handoff. Remote boxes are preferred, not mandatory: when the remote backend is unavailable (broker/DNS/network/lease failure), trusted-source work falls back to local execution โ including heavier suites and gates โ instead of blocking; note the fallback and reason in the proof summary. Untrusted source never falls back to local. - Test commands: trusted-source focused local proof uses
node scripts/run-vitest.mjs <path-or-filter>; remote or normal-checkout proof may usepnpm test <path-or-filter> [vitest args...],pnpm test:changed,pnpm test:serial, orpnpm test:coverage. Never rawvitest. - If raw Vitest is unavoidable, use
vitest run ...; barevitest ...starts local watch mode and will not exit on its own. - Vitest repetition: no
--repeat; use a bounded shell loop around the focused repo test command. - Local agent test execution is allowed only for trusted source and one/few focused files when the existing dependency install is ready. In a Codex worktree or linked/sparse checkout, use
node scripts/run-vitest.mjs <path-or-filter>; never direct localpnpm test*, and never reconcile dependencies merely to keep proof local. - Checks/lint in a trusted normal source checkout:
pnpm check:changedclassifies first; docs-only, no-change, and small metadata plans stay local when dependencies are ready, while typecheck/lint fan-out delegates to Crabbox/Testbox. Never run this repository-controlled classifier locally for untrusted source. Inspect lanes withpnpm changed:lanes --json; staged/path-scoped forms arepnpm check:changed --stagedandpnpm check:changed -- <files...>. - Checks in a trusted Codex worktree or linked/sparse checkout: avoid direct local
pnpm check*; usenode scripts/check-changed.mjs [--staged|-- <files...>]. It can classify without installed dependencies and delegates heavy or dependency-missing proof before loading package-backed helpers. For untrusted source, do not execute this repository-controlled wrapper locally. - Extension tests:
pnpm test:extensions,pnpm test extensions,pnpm test extensions/<id>. - Typecheck:
tsgolanes only (pnpm tsgo*,pnpm check:test-types); never addtsc --noEmit,typecheck,check:types. - Formatting:
oxfmt, not Prettier. Write paths withpnpm format <paths>; noformat:writescript. Checks use repo wrappers (pnpm format:*,scripts/run-oxlint.mjs; fullpnpm lint:*only when scope requires). - SDK surface gate:
pnpm plugin-sdk:surface:check; noplugin-sdk:surface-reportscript. - Build before push when build output, packaging, lazy/module boundaries, dynamic imports, or published surfaces can change; agent builds default to the selected remote box unless platform-specific proof requires another remote host.
Validation
- Use
$openclaw-testingfor test/CI choice and$crabboxfor remote/full/E2E proof. - Classify source trust before proof size. Do not pre-warm for anticipated work. Run focused proof locally only for trusted source; untrusted source uses secretless fork CI or sanitized direct AWS regardless of size. Lazily acquire the appropriate backend at the first remote command. Trusted maintainer heavy proof prefers Blacksmith Testbox; if the remote backend is unavailable, run the proof locally for trusted source and say so in the proof summary rather than blocking. Reuse one acquired lease for later heavy commands, sync the current checkout for every run, then stop it before handoff.
- Warm Testbox from the task checkout; ownership is checkout-path scoped;
--reclaimonly for intentional transfer. - One Testbox lease, one active command; never sync/reclaim during a run.
- Testbox
--reclaimdoes not retarget the remote checkout; never cross repos. - Base/head changed: stop and rewarm Testbox; never override stale lease checks.
- Compound Testbox commands:
bash -lc, neversh -lc; job env uses Bashdeclare. - Testbox cleanup:
blacksmith testbox stop --id <tbx_id>; id is not positional. - Delegated Testbox rejects
--fresh-prand--stop-after; sync current checkout, workflow owns lifecycle. - PR review artifacts: keep template enum values; put evidence detail in summaries.
- Crabbox request means real scenario proof: install/update/call/repro user path; not just copy tests and run them remotely.
- Blacksmith Testbox delegated runs: omit
--stop-after; unsupported, cleanup is delegated. - Visual proof: use Crabbox, set up like a user, then screenshot-verify. No harness/bypass/shortcut unless explicitly asked.
- Trusted-source local agent work includes one/few focused tests,
git diff --check, targeted formatting, and cheap static probes when dependencies already exist. Untrusted source executes none of its repository-controlled tooling locally. Computationally intensive work uses the selected remote box. - In Codex or linked worktrees, direct local
pnpm test*,pnpm check*,pnpm crabbox:run, andscripts/committercan trigger pnpm dependency reconciliation or install prompts. Prefernodewrappers locally and Crabbox/Testbox for pnpm-gated proof. - Direct Blacksmith lease: use
blacksmith testbox run; Crabbox wrapper reuse needs a wrapper-created lease. - Wrapper Testbox reuse requires its local SSH key; missing after restart/handoff means warm fresh.
- Dirty-sync generator proof: compare hashes before/after;
git diffincludes the synced patch. - Crabbox wrapper
stophas no--timing-json; usenode scripts/crabbox-wrapper.mjs stop --provider <provider> --id <id>. - Repo-native PR worktree may omit
node_modules; prove remotely, then usegit commit --no-verify, notscripts/committer. - Release-branch formatting: Testbox or existing binary; never local
pnpm execreconciliation. - Targeted local format/lint: existing
./node_modules/.bin/*; neverpnpm execreconciliation. - Parallel agents share the checkout; never switch its branch while sibling work runs.
- Testbox status:
blacksmith testbox status --id <tbx_id>; no--jsonflag. - QA CLI
--output-dirmust be repo-relative. - Full suites, changed gates, builds, typechecks, lint fan-out, Docker/package/E2E/live/cross-OS proof, or anything computationally intensive: Crabbox/Testbox.
- Testbox owns Chromium; never pass Crabbox
--browsertoprovider=blacksmith-testbox. - Testbox warmup must print a lease id; silent success is unusable. Verify before reuse; fall back to one-shot
run. - If local proof fans out or becomes expensive, stop it and lazily acquire the remote box.
- Before handoff/push: prove touched surface. Before landing to
main: proof matches actual risk. Bounded behavior-neutral refactor: focused tests/checks enough; no issue proof or full/broad suite by default. - Release-branch full validation: freeze the product-complete Code SHA, then use
node scripts/full-release-validation-at-sha.mjs --sha <code-sha> --target-ref release/YYYY.M.PATCH; no raw dispatch withouttarget_context_ref. - Pre-land/pre-commit code changes: mandatory fresh
$autoreviewuntil no accepted/actionable findings remain. Do not land code on CI, ClawSweeper, prior review comments, or your own manual review alone unless user explicitly opts out or scope is truly trivial/docs-only. If findings want refactor, refactor; no ugly fixes. - Autoreview uncommitted changes:
--mode uncommitted; nodirtymode. - Autoreview staged/uncommitted diff: use
--mode uncommitted; nostagedmode. - If proof is blocked, say exactly what is missing and why.
- Do not land related failing format/lint/type/build/tests. If unrelated on latest
origin/main, say so with scoped proof. - Docs/changelog-only and CI/workflow metadata-only:
git diff --checkplus relevant docs/workflow sanity; escalate only if scripts/config/generated/package/runtime behavior changed. - Prompt snapshots: CI truth is Linux Node 24. If macOS local passes but CI drifts, reproduce/generate in Linux before rerun.
GitHub / PRs
- Fresh GitHub items: read
CONTRIBUTING.md, the issue chooser/form, PR template, and.github/CODEOWNERS; blank issues are disabled; preserve templates and evidence requirements. - Issue first for bugs, user-facing features, architecture/product decisions, or work needing durable discussion. Bounded maintainer-requested refactor may go direct; agent decides whether an issue adds value. PRs use the template, link context, and keep durable problem/impact/evidence sections.
- Route support to Discord and security through
SECURITY.md. Use listed maintainer areas/CODEOWNERS; never guess mentions. - Use
$openclaw-pr-maintainerimmediately for maintainer-side OpenClaw issue/PR review, triage, duplicates, labels, comments, close, land, or evidence. Contributor PR creation/refresh follows the requested contributor workflow; linked refs alone do not require maintainer archive tooling. - Issue/PR start:
git status -sb; if clean,git pull --ff-only; if dirty, yell before pull/rebase. - PR refs:
gh pr view/difforgh api, not web search. Prefergitcrawlfor maintainer discovery; missing/stalegitcrawlfalls through to livegh, not contributor setup. Verify live withghbefore mutation. gh pr viewtakes the branch positionally; no--headflag.gh pr diffhas no--stat; usegh pr view --json changedFiles,additions,deletionsorgit diff --stat.- zsh: quote
gh apiendpoints containing?or brackets; otherwise glob expansion corrupts the invocation. gh pr checks --json: uselink, not nonexistentdetailsUrl.- Blacksmith Testbox status/stop:
--id <tbx_id>; no status JSON flag. - Crabbox final timing JSON = proof complete; if portal sync hangs after it, interrupt wrapper only.
- Sparse-sync temp checkout may claim kept Testbox; repo-path reuse needs
--reclaim. - GitHub Actions: resolve workflow files from
.github/workflowsor API; never infer filenames from display names. - Yielded exec: retain the returned session id before polling; never blind-retry.
- zsh: quote command globs; unmatched patterns abort before the tool runs.
- Nested remote shell: avoid local
$()expansion; use remote-safe validation. - zsh: don't use
pathas a variable; it rewrites$PATH. scripts/prartifacts: preserve template enum values; validate before prepare.scripts/prsubcommands require a PR number; no subcommand--helpplaceholder.scripts/prreview: checkout main baseline, then PR, before artifact validation.- Review artifacts: validate from PR-head mode; moving main invalidates main-baseline guard.
scripts/prprepare/merge:mainPRs only; non-main uses reviewed release-branch flow.- After every PR push, rerun
scripts/pr review-init; checkout alone leaves stale guard SHA. rg: options/globs before--;--immediately before a leading-dash pattern only.gh --jqis not standalonejq; pipe JSON tojqfor variables or--arg.- Shared checkout: serialize
git fetch; on ref-lock failure, re-read the ref before retry. - Git fetch/pull yielding without completion: inspect/stop only the owned process before retry; never overlap retries.
gh api --paginate '<endpoint>' | jq -s ...; gh--slurpmay emit nothing and forbids--jq/--template.- Main-bound workflow dispatch: resolve server
mainSHA immediately before dispatch; retry if identity fails aftermainadvances. gh run view --jsonusesattempt, notattemptNumber.- Crabbox stop: no
--timing-json; usenode scripts/crabbox-wrapper.mjs stop --provider <provider> --id <id>. - macOS
findhas no-printf; use-print0plusstat. - Actions checkout refs: use full 40-char SHAs; short SHAs resolve as branches/tags.
- zsh Git object paths: use
${sha}:path;$sha:pathinvokes parameter modifiers. - Bare issue/PR URL/number: inspect live and take the efficient maintainer path; switch branches/refs when useful.
- No unsolicited PR labels/retitles/rebases/fixups/landing. Comments/reviews ok only for reviewable findings, pre-merge proof, or close/duplicate reason after explicit close/sweep/landing request.
- Maintainer decision closes the cluster: if deciding reported behavior/proposed fix is not planned, comment+close all directly associated open issues/PRs unless explicitly told to keep one open. Associated means linked PRs/issues, duplicates, companion workaround PRs, and the canonical issue for the rejected behavior.
- Do not leave associated issues open for hypothetical future repros. Close with rationale; ask for a new issue or reopen only if concrete new evidence appears. Close comment states: decision, why, supported alternative, and what evidence would change the decision.
- Issue/PR work: search strong related issues/PRs before final; close proven dupes/fixed siblings. If none close, suggest one next related follow-up.
- PR superseded by
main: if code proof showsmainalready has same-or-better behavior, comment canonical commit/PR + focused proof, then close. Bar high: inspect PR diff, current code/tests, linked issue, caller/sibling path. If unsure, leave open. - Issue/PR numbers need a short summary every time; assume the reader has not opened or read them.
- Before presenting a batch of issues/PRs, use smart subagents to verify live state and current
main; omit closed/fixed items, and comment+close items already fixed onmainwhen maintainer action is authorized. - Generic triage and landing shortlists: exclude PRs authored by maintainers with broad repository access until 14 days after creation. An ordinary request for landing candidates does not override this gate; only a named PR or explicit request for maintainer-owned work does.
- PR review answer: bug/behavior, URL(s), affected surface, provenance for regressions when traceable, best-fix judgment, evidence from code/tests/CI/current or shipped behavior.
- PR reviewable findings: post them on the PR, not chat-only, so author sees actionable feedback.
- Issue/PR final answer: last line is the full GitHub URL.
- PR verification: before merge, post land-ready work done, exact local commands, CI/Testbox run IDs, before/after proof when used, and known proof gaps.
- Issue fixed on
mainwith proof: comment proof + commit/PR, then close. - After landing or requested close/sweep: search duplicates; comment proof + canonical commit/PR/release before closing.
- After PR merge/ship: concise prose recap, not a bullet pile; cover behavior, key surface, proof, and issue/PR state. Check for worthwhile refactor or simplification follow-ups; suggest any warranted.
shipthat fixes an issue: after push, comment proof + commit link, then close the issue.- Public GH comments: show draft in chat first unless user explicitly asked to post/comment/reply/close/merge/land. After work starts and changes/proof exist, post the review/proof/commit comment.
- Representing user: if user already has a comment/thread for the point, update/reply there when possible; avoid duplicate PR/issue comments.
- No surprise GH writes: chat must mention every posted/updated public comment with URL.
- GH comments with backticks,
$, or shell snippets: use heredoc/body file, not inline double-quoted--body. - PR create: real body required. Use the current template:
What Problem This Solves,Why This Change Was Made,User Impact, andEvidence; include visible refs, behavior, and validation. - PR create races GitHub's merge-ref computation: the pull_request-open CI run can drop entirely or die as
startup_failure/BuildFailed((Unknown event), not rerunnable). Prevention:gh pr create --draft, pollmergeablenon-null, thengh pr ready. After opening, verify the CI workflow attached to the head SHA; if missing, the hourlypr-ci-sweeperre-fires it, or close/reopen manually. - PR create/refresh: keep PR branches takeover-ready. Use a branch maintainers can push to, or for fork PRs ensure
maintainer_can_modify/ GitHub'sAllow edits by maintainersis enabled unless explicitly told otherwise or GitHub's Actions/secrets warning makes that unsafe. - GitHub issue/PR create: read
$agent-transcript; ask about sanitized transcript logs when available. - Contributor PRs: parsed context requires authored
What Problem This SolvesandEvidencesections. Do not require field-level proof forms; reviewers inspect code, tests, and CI for correctness. - PR artifacts/screenshots: attach to PR/comment/external artifact store. Never push screenshots, videos, proof images, or proof assets to OpenClaw or any product repo branch, including temp artifact branches. Use Crabbox artifact publishing plus the manifest URL. Do not commit
.github/pr-assets. - CI polling: exact SHA, relevant checks only, minimal fields. Skip routine noise (
Auto response,Labeler, docs agents, performance/stale). Logs only after failure/completion or concrete need. Nevergh run watch; its 3s polling exhausts API quota. Use sparse GraphQL rollups. Filtergh run listby workflow/branch/commit; broad JSON lists can exceed relay caps.gh --jqhas no jq--arg; use--commitfor SHA filtering. Reruns needgh run view <run> --attempt <n>; default output may show the prior attempt. - Trusted-workflow release-branch CI: pass
target_ref+release_candidate_ref; neverrelease_gate(requires workflow head == target). - Agent PR landing to
main: use only the repo-nativescripts/prwrapper: runscripts/pr review-init <PR>, follow its emitted checkout/guard guidance, initialize and complete review artifacts withscripts/pr review-artifacts-init <PR>, validate them withscripts/pr review-validate-artifacts <PR>, then runOPENCLAW_TESTBOX=1 scripts/pr prepare-run <PR>andscripts/pr merge-run <PR>. The Testbox flag is mandatory for agents so prepare verifies hosted CI/Testbox on the current head or reuses a patch-identical pre-rebase run green within 24 hours instead of running full gates locally.prepare-runfails fast; invoke only after exact-head CI is complete and green. For owner-approved reviewed fork code without hosted Testbox, useOPENCLAW_PR_GATES_REMOTE=testboxinstead. Do not rebase only becausemainadvanced; merge drift is advisory unless strict drift is explicitly enabled, while GitHub still blocks conflicts. Do not idle onauto-responseorcheck-docs. - After GitHub throttling, check core quota before
scripts/pr prepare-runormerge-run. A failed operation can retain its lock; verify no child remains, then recover only with its emitted token. - Local
scripts/pr: unsetGITHUB_TOKEN,GH_TOKEN,HOMEBREW_GITHUB_API_TOKEN; ambient tokens can select an exhausted or wrong identity. - Non-main PRs: do not run
scripts/pr prepare-runormerge-run; they diff againstmain. Use review artifacts, exact base-head CI, revalidateheadRefOid, thengh pr merge --match-head-commit <verified-sha>. - Merge guard shells: start
set -euo pipefail; a failed[[ ... ]]alone does not stop a later merge command. - After
scripts/pr merge-runremoves its worktree,cdto a persistent repo before follow-up commands. scripts/prreview JSON: land-ready recommendationREADY FOR /prepare-pr,issueValidation.status=valid; neverAPPROVE.
Code
- TS ESM, strict. Avoid
any; prefer real types,unknown, narrow adapters. - No
@ts-nocheck. Lint suppressions only intentional + explained. - External boundaries: prefer
zodor existing schema helpers. - Runtime branching: discriminated unions/closed codes over freeform strings. Avoid semantic sentinels (
?? 0, empty object/string). - Cross-function state: when valid combos matter, return a closed mode/result shape. Avoid parallel nullable fields or derived booleans that callers must keep in sync; make impossible states unrepresentable.
- Formatter-friendly shape: when oxfmt explodes an expression vertically, extract named booleans, payloads, or small helpers. Do not change width or use format-ignore for local compactness.
- Calls should be boring: complex decisions happen above; call args/object fields are names, literals, or simple property reads.
- Prefer early returns over nested condition pyramids. Split code into gather -> normalize -> decide -> act.
- Use named intermediates only for domain meaning or readability; avoid temp-variable soup.
- Correct but not over-engineered. Correctness on real inputs/states is mandatory; extra layers, guards, and generality for imagined ones are defects, not rigor.
- Codebase is already large; pragmatism wins. Extremely unlikely edge cases are tradable for real simplification โ name the accepted tradeoff (comment or PR) so it is a decision, not an oversight.
- Code size matters. Prefer small clear code; maintainability includes not growing LOC without payoff.
- Refactors should delete about as much local complexity as they add. If LOC grows, the new ownership/API needs to clearly pay for it.
- Refactors should reduce non-test LOC unless they remove a larger architectural cost. Treat positive prod LOC as a smell. Before closeout, run
git diff --numstat; if non-test LOC grew, trim or explicitly justify why fewer paths now exist. - Prefer deleting branches, modes, adapters, and tests over preserving them. A refactor that adds a second path has probably failed unless the old path is a cited shipped contract.
- New helpers/files must pay rent immediately: fewer call paths, fewer concepts, or less repeated logic. No helpers for one-off compat, naming translation, or speculative resilience.
- Before adding helpers/files, check whether existing code can absorb the behavior with less new surface.
- Keep APIs narrow: export only current caller needs; keep types/helpers local by default.
- Return the smallest useful shape. Avoid broad result objects, flags, metadata unless callers use them.
- Avoid adapter layers that only rename fields. Move real responsibility or leave code local.
- Inline simple one-use objects/spreads when clearer. Extract only when it removes duplication or hard logic.
- Tests prove behavior/regressions, not every internal branch.
- Tests are welcome, but review them before landing for duplication and value. Delete useless tests, such as assertions for behavior or paths just removed.
- Tests protect canonical behavior and migration boundaries, not obsolete internals. Delete tests for removed fallback paths instead of updating them.
- For non-trivial refactors, check
git diff --numstatbefore closeout. If LOC grew, trim or explain why. - Prefer existing narrow helpers over repeated casts/guards. Add local helpers when 2+ nearby call sites share real boundary logic.
- Prefer ctor parameter properties for injected deps/config. Do not ban them for erasable-syntax purity.
- Prefer
satisfiesfor registries/config maps; derive types from schemas when a runtime schema already exists. - Table-drive repetitive tests when it reduces code and keeps failure names clear.
- Dynamic import: no static+dynamic import for same prod module. Use
*.runtime.tslazy boundary. After edits:pnpm build; check[INEFFECTIVE_DYNAMIC_IMPORT]. - Cycles: keep
pnpm check:import-cycles+ architecture/madge green. - Classes: no prototype mixins/mutations. Prefer inheritance/composition. Tests prefer per-instance stubs.
- Split files around ~700 LOC when clarity/testability improves.
- Never add a
max-linessuppression. Existing suppressions are grandfathered TODOs; split the file and remove its suppression plus baseline entry. - Naming: OpenClaw product/docs;
openclawCLI/package/path/config. - English: American spelling.
Tests
- Vitest. Colocated
*.test.ts; e2e*.e2e.test.ts; example modelssonnet-4.6,gpt-5.6-luna; test GPT with Luna preferred; use Sol when capability matters; no GPT-4.x agent-smoke defaults. - Prefer behavior tests over workflow/docs string greps. Put operator policy reminders in AGENTS/docs.
- QA scenario sources are YAML only:
qa/scenarios/index.yamlandqa/scenarios/<theme>/*.yaml. Do not add fencedqa-scenario/qa-flowMarkdown files underqa/scenarios/. - Clean timers/env/globals/mocks/sockets/temp dirs/module state;
--isolate=falsesafe. - Tests asserting resolver/root-containment paths:
fs.realpathmkdtemp/tmp roots first. macOSos.tmpdir()is a/var->/private/varsymlink; prod resolvers return canonical paths, so raw mkdtemp assertions pass on Linux CI but fail on Mac. - Explicit
vi.mockfactories must export every binding prod touches, including error classes used ininstanceofchecks;vi.importActualthe defining module for those instead of stub classes. - Prefer injection and narrow
*.runtime.tsmocks over broad barrels oropenclaw/plugin-sdk/*. - Do not edit baseline/inventory/ignore/snapshot/expected-failure files to silence checks without explicit approval.
- Do not run independent
pnpm test/Vitest commands concurrently in one worktree; Vitest cache races withENOTEMPTY. Group one command or use distinctOPENCLAW_VITEST_FS_MODULE_CACHE_PATH. - Vitest rejects Jest
--runInBand; useOPENCLAW_VITEST_MAX_WORKERS=1 pnpm testfor serial proof. Test workers max 16. - Live:
OPENCLAW_LIVE_TEST=1 pnpm test:live; verboseOPENCLAW_LIVE_TEST_QUIET=0. - Live gateway tests: session-owned dev gateway only โ isolated
OPENCLAW_STATE_DIR+ free port. Never bind the operator's real gateway port (default 18789) while their gateway runs. - Never stop/restart/kickstart a gateway service you did not start (launchd/systemd/tmux) or edit its live
~/.openclawstate/config; that is the operator's running instance โ explicit per-task operator approval required. - Realistic data: copy the state/DB into your dev state dir and test the copy. In-place migration of a live gateway's state needs explicit operator approval.
- Guide:
docs/reference/test.md.
Docs / Changelog
- Use
$technical-documentationfor docs writing/review. Docs change with behavior/API. - Codex harness upgrade (
extensions/codex/package.json@openai/codex): refreshdocs/plugins/codex-harness.mdmodel snapshot from the new harnessmodel/list. - Docs final answers: include relevant full
https://docs.openclaw.ai/...URL(s). If issue/PR work too, GitHub URL last. CHANGELOG.md: release-only. Do not edit for normal PRs, directmainfixes, orship it; release generation owns it. Do not ask contributors/agents for changelog edits.- User-facing
fix/feat/perf: put release-note context in PR body, squash message, or direct commit: behavior, surface, issue/PR refs, credited human author/reporter. - Release generation: derive
CHANGELOG.mdfrom merged PRs + all directmaincommits. Entries: active### Changes/### Fixes, single-line, thank credited humans; never thank bots/forbidden handles:@openclaw,@clawsweeper,@codex,@steipete.
Git
- Commit via
scripts/committer "<msg>" <file...>; stage intended files only. - Commits: conventional-ish, concise, grouped.
- No manual stash/autostash unless explicit. Branch switches ok when useful; no new worktrees unless requested.
main: no merge commits; rebase on latestorigin/mainbefore push. After one green run plus clean rebase sanity, do not chase movingmainwith repeated full gates.- User says
commit: your changes only.commit all: all changes in grouped chunks.push: maygit pull --rebasefirst. - User says
ship it: commit intended changes, pull --rebase, push. - Do not delete/rename unexpected files; ask if blocking, else ignore.
- Bulk PR close/reopen >50: ask with count/scope.
Security / Release
- Never commit real phone numbers, videos, credentials, live config.
- Secrets: channel/provider creds in
~/.openclaw/credentials/; model auth profiles in~/.openclaw/agents/<agentId>/agent/auth-profiles.json. - SecretRef failures isolate to the smallest known owning surface. Proven-inactive surfaces skip; unknown ownership fails closed. Gateway refuses startup only when its own ingress protection cannot be established, config is structurally invalid, or the owning surface is unknown. Otherwise start, mark the exact capability/account/route configured-unavailable, emit a typed redacted diagnostic, and forbid implicit credential fallback. On reload, retain last-known-good only for an unchanged ref+provider; a changed unresolved ref makes that owner cold. Doctor and status must list every degraded owner.
- Dependency patches/overrides/vendor changes need explicit approval.
pnpm-workspace.yamlpatched dependencies use exact versions only. - Release/package guards: no hard-coded retired-package denylists; use generic artifact/dependency checks or fix build source.
- Lockfiles/shrinkwrap are security surface: review
pnpm-lock.yaml,npm-shrinkwrap.json,package-lock.json; root/plugin npm packages ship shrinkwrap, not package-lock. - Carbon pins owner-only: do not change
@buape/carbonunless Shadow (@thewilloftheshadow, verified bygh) asks. - Releases/publish/version bumps need explicit approval. Use
$release-openclaw-maintainer. - Active release scope lock: freeze the operator-selected cut SHA and release
identity through publish and verification. Moving
main, unrelated CI, optional backports, refactors, cleanup, and normal forward-ports are not part of the release work queue. - Touch
mainduring a release only when the operator requests it or the smallest critical main-owned blocker prevents that release. Return to the release branch immediately; defer broader main work until closeout. - Release versions use
YYYY.M.PATCH, wherePATCHis a sequential monthly release-train number, never the calendar day. Stable and beta tags determine the current train; alpha-only tags do not consume or advance the beta/stable patch number. After2026.6.5, the next beta train is2026.6.6-beta.1even if higher alpha-only tags exist. - Alpha/nightly versions use the next unreleased train plus an incrementing prerelease number. Repeated nightlies for the same train increment only
alpha.N; they must not mint a new patch number from the date. - Backports are optional. Apply only the operator-selected set; when requested without a target, use the newest open
release/branch. - Regular beta/stable flow has two immutable identities:
- Code SHA: version prep plus any optional backports/release fixes, with no release changelog mutation. Full product validation belongs here.
- Release SHA: a descendant of the green Code SHA whose complete diff is exactly
CHANGELOG.md. Tag, npm preflight, package/install acceptance, and publish belong here.
- Never generate the release changelog before the Code SHA has green Full Release Validation. A product/code failure changes the Code SHA and restarts product validation. A workflow/harness/infrastructure failure is fixed in trusted tooling and rerun against the same Code SHA; do not mutate the candidate to satisfy newer tooling.
- After green Code SHA validation, generate and review
CHANGELOG.mdonce. Dispatch Full Release Validation for the Release SHA with evidence reuse enabled;changelog-only-release-v1may reuse the Code SHA product evidence only when GitHub independently proves the entire descendant delta isCHANGELOG.md. Any other path change requires a new Code SHA and fresh full validation. - Release-SHA proof is intentionally narrow: release-note/provenance checks, npm preflight/package bytes, install/update acceptance, and publish readiness. Do not rerun the full product matrix merely because the changelog changed.
- Pass the successful Release-SHA validation run and npm preflight run into
release:candidate; do not let the candidate helper dispatch duplicate copies of evidence that already passed. - Keep one release operator and one watcher per release identity. Resume partial publish from successful immutable child artifacts/runs; never rebuild or republish an already-published package version.
- GHSA/advisories:
$openclaw-ghsa-maintainer/$security-triage. Secret scanning:$openclaw-secret-scanning-maintainer. - Beta tag/version match:
vYYYY.M.PATCH-beta.N-> npmYYYY.M.PATCH-beta.N --tag beta.
Platform / Ops
- Before simulator/emulator testing, check real iOS/Android devices.
- "restart iOS/Android apps" = rebuild/reinstall/relaunch, not kill/launch.
- SwiftUI: Observation (
@Observable,@Bindable) over newObservableObject. - Mac gateway: dev watch =
pnpm gateway:watch; managed installs =openclaw gateway restart/status --deep; logs =./scripts/clawlog.sh. No launchd/ad-hoc tmux. - Mac app permission testing: stable app path + real signing identity required. No
--no-sign,SIGN_IDENTITY=-, or raw debug binary; TCC prompts/listing won't stick. - Version bump surfaces live in
$release-openclaw-maintainer. - Parallels:
$openclaw-parallels-smoke; Discord roundtrip:$parallels-discord-roundtrip. - Crabbox/WebVNC human demos: keep remote desktop visible/windowed; no fullscreen remote browser unless video/capture-style output.
- Before sharing WebVNC links, use Crabbox screenshot first; verify real app/path works and target UI is not broken.
- ClawSweeper ops:
$clawsweeper. Deployed hook sessions may post one concise#clawsweepernote only when surprising/actionable/risky; if using message tool, reply exactlyNO_REPLY. - Generated-media completions wake the requester agent first. Requester visible-reply config decides final text vs message tool; direct media send is fallback/recovery only.
message_tool_only: normal agent final visible reply = current-sourcemessage(action=send)only. NoNO_REPLYprompt/contract; no message call = no source reply. Plugin-owned bound-thread reply = plugin return value; no message tool needed. Never auto-publish private final.- Memory wiki prompt digest stays tiny; prefer
wiki_search/wiki_get; verify contact data before use; source-class provenance for generated people facts. - Rebrand/migration/config warnings: run
openclaw doctor. - Never edit
node_modules. - Local-only
.agentsignores:.git/info/exclude, not repo.gitignore. - Provider tool schemas: prefer flat string enum helpers over
Type.Union([Type.Literal(...)]); some providers rejectanyOf. - External messaging: no token-delta channel messages. Follow
docs/concepts/streaming.md.
Source: AGENTS.md