Telegraph style. Root rules only. Read scoped AGENTS.md before subtree work.
- Repo:
https://github.com/openclaw/openclaw - Replies: repo-root refs only:
extensions/telegram/src/index.ts:80. No absolute paths, no~/. - Run docs list first:
pnpm docs:listif available; read relevant docs only. - High-confidence answers only when fixing/triaging: verify source, tests, shipped/current behavior, and dependency contracts before deciding.
- Dependency-backed behavior: read upstream dependency docs/source/types first. Do not assume APIs, defaults, errors, timing, or runtime behavior.
- Live-verify when feasible. Check env/
~/.profilefor keys before assuming live tests are blocked; keep secret output redacted. - Missing deps:
pnpm install, retry once, then report first actionable error. - CODEOWNERS: maint/refactor/tests ok. Larger behavior/product/security/ownership: owner ask/review.
- Wording: product/docs/UI/changelog say "plugin/plugins";
extensions/is internal. - New channel/plugin/app/doc surface: update
.github/labeler.yml+ GH labels. - New
AGENTS.md: add siblingCLAUDE.mdsymlink.
- Core TS:
src/,ui/,packages/; plugins:extensions/; SDK:src/plugin-sdk/*; channels:src/channels/*; loader:src/plugins/*; protocol:src/gateway/protocol/*; docs/apps:docs/,apps/,Swabble/. - Installers: sibling
../openclaw.ai. - Scoped guides exist in:
extensions/,src/{plugin-sdk,channels,plugins,gateway,gateway/protocol,agents}/,test/helpers*/,docs/,ui/,scripts/.
- SparseKernel is a local multi-agent kernel, not just a database wrapper. In OpenClaw docs/code, call this OpenClaw SparseKernel when discussing the implementation direction.
- SparseKernel keeps many logical agents as compact durable state and materializes only the active slice through leased tools, files, browsers, and sandboxes.
- Do not add ad hoc JSON files for runtime coordination. Use SQLite migrations for structured runtime state.
- SQLite is the SparseKernel ledger, not a dumping ground. Store agents, sessions, transcript events, tasks, leases, tool calls, capabilities, trust zones, broker state, artifact metadata, audit logs, and usage records there.
- Large blobs must go through the content-addressed artifact store. Do not store screenshots, videos, browser traces, downloads, PDFs, repo snapshots, or exported transcript blobs directly in SQLite by default.
- Agents/plugins never get raw SparseKernel DB access. Route state changes through kernel APIs, SDKs, and brokers that enforce capabilities and emit audit records.
- Expensive resources are leased by trust zone. Sandboxes are allocated by trust boundary, not by logical agent.
- Browser processes are pooled by trust zone; browser contexts are leased by task/session/agent.
- BrowserContext isolation is session isolation, not host isolation. Playwright route blocking is not a hard security boundary.
- Do not disable browser sandboxing or weaken sandbox network/filesystem policy casually. If a backend provides only local/no isolation, name that explicitly in docs, status, and tests.
- Tool calls must go through the tool broker where feasible. Sensitive tool, browser, sandbox, and artifact operations need capability checks plus audit events.
- SparseKernel must stay usable on small VMs: bounded active workers, scarce browser/sandbox leases, compact logs, and retention policies.
- Add tests for migrations, task leases, artifact dedupe, capability allow/deny, audit events, and broker lifecycle behavior.
- Preserve backwards compatibility when introducing SparseKernel into existing OpenClaw runtime paths.
- Core stays extension-agnostic. No bundled ids in core when manifest/registry/capability contracts work.
- Runtime coordination belongs in the local kernel ledger. Prefer typed runtime DB migrations over ad hoc JSON state files for sessions, tasks, leases, tool calls, broker state, resource accounting, permissions, and audit trails.
- SQLite is structured state only. Large screenshots, downloads, PDFs, traces, videos, HTML dumps, workspace snapshots, and exported transcripts are artifacts in a content-addressed store with DB metadata/access records.
- Agents/plugins never get raw kernel DB access. Route state changes through runtime APIs/brokers that enforce capabilities and emit audit records.
- Pool expensive isolation by trust zone, not logical agent. Browser process pools and sandbox pools are keyed by trust zone; browser contexts and leases are per task/session/agent.
- BrowserContext isolation is session isolation, not host isolation. Route blocking and Playwright request guards are convenience controls, not hard security boundaries.
- Do not disable browser sandboxing or weaken sandbox network/filesystem policy casually. If a backend provides only local/no isolation, name that explicitly in docs, status, and tests.
- Brokered tools are the target shape. Sensitive tool invocation, browser allocation, sandbox allocation, and artifact reads/writes need capability checks plus audit events.
- Add migration and broker operation tests with every runtime-ledger schema or broker API change.
- Extensions cross into core only via
openclaw/plugin-sdk/*, manifest metadata, injected runtime helpers, documented barrels (api.ts,runtime-api.ts). - Extension prod code: no core
src/**,src/plugin-sdk-internal/**, other extensionsrc/**, or relative outside package. - Core/tests: no deep plugin internals (
extensions/*/src/**,onboard.js). Useapi.ts, SDK facade, generic contracts. - Extension-owned behavior stays extension-owned: repair, detection, onboarding, auth/provider defaults, provider tools/settings.
- Owner boundary: fix owner-specific behavior in the owner module. Shared/core gets generic seams only; no owner ids, dependency strings, defaults, migrations, or recovery policy. If a bug names an extension or its dependency, start in that extension and add a generic core seam only when multiple owners need it.
- Legacy config repair: doctor/fix paths, not startup/load-time core migrations.
- Core test asserting extension-specific behavior: move to owner extension or generic contract test.
- New seams: backwards-compatible, documented, versioned. Third-party plugins exist.
- Channels:
src/channels/**is implementation; plugin authors get SDK seams. - Providers: core owns generic loop; provider plugins own auth/catalog/runtime hooks.
- Gateway protocol changes: additive first; incompatible needs versioning/docs/client follow-through.
- Config contract: exported types, schema/help, metadata, baselines, docs aligned. Retired public keys stay retired; compat in raw migration/doctor.
- Direction: manifest-first control plane; targeted runtime loaders; no hidden contract bypasses; broad mutable registries transitional.
- Prompt cache: deterministic ordering for maps/sets/registries/plugin lists/files/network results before model/tool payloads. Preserve old transcript bytes when possible.
- Runtime: Node 22+. Keep Node + Bun paths working.
- Install:
pnpm install(keep Bun lock/patches aligned if touched). - CLI:
pnpm openclaw ...orpnpm dev; build:pnpm build. - Smart gate:
pnpm check:changed; explainpnpm changed:lanes --json; staged previewpnpm check:changed --staged. - Sparse worktrees:
pnpm check:changedis sparse-safe and may skip sparse-missing typecheck projects; do not expand sparse checkout just to satisfy changed-gate tsgo. Directpnpm tsgo*remains strict; use a fuller worktree when you need direct typecheck proof. - Prod sweep:
pnpm check; tests:pnpm test,pnpm test:changed,pnpm test:serial,pnpm test:coverage. - Extension tests:
pnpm test:extensions,pnpm test extensions,pnpm test extensions/<id>. - Targeted tests:
pnpm test <path-or-filter> [vitest args...]; never rawvitest. - Vitest flags only; no Jest flags like
--runInBand. For serial runs usepnpm test:serialorOPENCLAW_VITEST_MAX_WORKERS=1 pnpm test .... - Typecheck:
tsgolanes only (pnpm tsgo*,pnpm check:test-types); do not addtsc --noEmit,typecheck,check:types. - Formatting: use
oxfmt, not Prettier. Preferpnpm format:check/pnpm format; for targeted files usepnpm exec oxfmt --check --threads=1 <files...>orpnpm exec oxfmt --write --threads=1 <files...>. - Linting: use repo wrappers (
pnpm lint:*,scripts/run-oxlint.mjs); do not invoke generic JS formatters/lints unless a repo script uses them. - Heavy checks:
OPENCLAW_LOCAL_CHECK=1, modeOPENCLAW_LOCAL_CHECK_MODE=throttled|full; CI/shared useOPENCLAW_LOCAL_CHECK=0. - Blacksmith/Testbox is maintainer opt-in, not a repo-wide default. If Blacksmith access is available and
OPENCLAW_TESTBOX=1is set, or a maintainer's personal AGENTS rules ask for it, use Testbox for broad, slow, Docker, live, E2E, full-suite, or CI-parity validation instead of running those heavy lanes locally. UseOPENCLAW_LOCAL_CHECK_MODE=throttled|fullas the explicit local escape hatch. - Testbox use: run from repo root, pre-warm early with
blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90, reuse the returnedtbx_...id for allrun/downloadcommands, and stop boxes you created before handoff. Timeout bins:90minutes default,240multi-hour,720all-day,1440overnight; anything above1440needs explicit approval and cleanup. - Testbox full-suite profile:
blacksmith testbox run --id <ID> "env NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test". For installable package proof, prefer the GitHubPackage Acceptanceworkflow over ad hoc Testbox commands.
- Triage: list first, hydrate few. Use bounded
gh --json --jq; avoid repeated full comment scans. - Automatic PR/issue discovery: skip maintainer-owned items unless directly relevant. Do not comment, close, label, retitle, rebase, fix up, or land them without Peter asking.
- PR scan/triage: no unsolicited PR comments/reviews. Report in chat only unless explicitly asked, or a close/duplicate action needs a reason comment.
- Search/dedupe: prefer
gh search issues 'repo:openclaw/openclaw is:open <terms>' --json number,title,state,updatedAt --limit 20. - GitHub search boolean text is fussy. If
ORqueries return empty, split exact terms and search title/body/comments separately before concluding no hits. - PR shortlist:
gh pr list ...; thengh pr view <n> --json number,title,body,closingIssuesReferences,files,statusCheckRollup,reviewDecision. - After landing PR: search duplicate open issues/PRs. Before closing: comment why + canonical link.
- GH comments with markdown backticks,
$, or shell snippets: avoid inline double-quoted--body; use single quotes or--body-file. - PR execution artifacts/screenshots: attach them to the PR, comment, or an external artifact store. Do not add
.github/pr-assetsor other PR-only assets to the repo. - PR review answer must explicitly cover: what bug/behavior we are trying to fix; PR/issue URL(s) and affected endpoint/surface; whether this is the best possible fix, with high-certainty evidence from code, tests, CI, and shipped/current behavior.
- CI polling: exact SHA, needed fields only. Example:
gh api repos/<owner>/<repo>/actions/runs/<id> --jq '{status,conclusion,head_sha,updated_at,name,path}'. - Post-land wait: minimal. Exact landed SHA only. If superseded on
main, same-branchcancel-in-progresscancellations are expected; stop once local touched-surface proof exists. Never wait for newer unrelatedmainunless asked. - Wait matrix:
- never:
Auto response,Labeler,Docs Sync Publish Repo,Docs Agent,Test Performance Agent,Stale. - conditional:
CIexact SHA only;Docsonly docs task/no local docs proof;Workflow Sanityonly workflow/composite/CI-policy edits;Plugin NPM Releaseonly plugin package/release metadata. - release/manual only:
Docker Release,OpenClaw NPM Release,macOS Release,OpenClaw Release Checks,Cross-OS Release Checks,NPM Telegram Beta E2E. - explicit/surface only:
QA-Lab - All Lanes,Scheduled Live And E2E,Install Smoke,CodeQL,Sandbox Common Smoke,Parity gate,Blacksmith Testbox,Control UI Locale Refresh.
- never:
/landpr: do not idle onauto-responseorcheck-docs. Treat docs as local proof unlesscheck-docsalready failed with actionable relevant error.- Poll 30-60s. Fetch jobs/logs/artifacts only after failure/completion or concrete need.
- Pre-commit hook: staged formatting only. Validation explicit.
- Changed lanes:
- core prod: core prod typecheck + core tests
- core tests: core test typecheck/tests
- extension prod: extension prod typecheck + extension tests
- extension tests: extension test typecheck/tests
- public SDK/plugin contract: extension prod/test too
- unknown root/config: all lanes
- Before handoff/push for code/test/runtime/config changes:
pnpm check:changed. Tests-only:pnpm test:changed. Full prod sweep:pnpm check. - Docs/changelog-only and CI/workflow metadata-only changes are not changed-gate work by default. Use
git diff --checkplus the relevant formatter/docs/workflow sanity check; escalate topnpm check:changedonly when scripts, test config, generated docs/API, package metadata, or runtime/build behavior changed. - Rebase sanity: after a green
pnpm check:changed, a clean rebase onto currentorigin/maindoes not require rerunning the full changed gate when the rebase has no conflicts and the branch diff is materially unchanged. Do a quickgit status,git diff --check, and diff/stat sanity check; rerun targeted or full checks only if conflict resolution, upstream overlap, generated drift, dependency/config changes, or touched-file content changes make the prior result stale. - Landing on
main: verify touched surface near landing. Default feasible bar:pnpm check+pnpm test. - Hard build gate:
pnpm buildbefore push if build output, packaging, lazy/module boundaries, or published surfaces can change. - Do not land related failing format/lint/type/build/tests. If unrelated on latest
origin/main, say so with scoped proof. - Generated/API drift:
pnpm check:architecture,pnpm config:docs:gen/check,pnpm plugin-sdk:api:gen/check. Trackdocs/.generated/*.sha256; full JSON ignored.
- 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, etc. - 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.
- Comments: brief, only non-obvious logic.
- Split files around ~700 LOC when clarity/testability improves.
- Naming: OpenClaw product/docs;
openclawCLI/package/path/config. - English: American spelling.
- Vitest. Colocated
*.test.ts; e2e*.e2e.test.ts; example modelssonnet-4.6,gpt-5.4. - Avoid brittle tests that grep workflow/docs strings for operator policy. Prefer executable behavior, parsed config/schema checks, or live run proof; put release/CI policy reminders in AGENTS/docs instead.
- Clean timers/env/globals/mocks/sockets/temp dirs/module state;
--isolate=falsesafe. - Hot tests: avoid per-test
vi.resetModules()+ heavy imports. Measure withpnpm test:perf:imports <file>/pnpm test:perf:hotspots --limit N. - Seam depth: pure helper/contract unit tests; one integration smoke per boundary.
- Mock expensive seams directly: scanners, manifests, registries, fs crawls, provider SDKs, network/process launch.
- Prefer injection; if module mocking, mock narrow local
*.runtime.ts, not broad barrels oropenclaw/plugin-sdk/*. - Share fixtures/builders; delete duplicate assertions; assert behavior that can regress here.
- Do not edit baseline/inventory/ignore/snapshot/expected-failure files to silence checks without explicit approval.
- Do not run multiple independent
pnpm test/Vitest commands concurrently in the same worktree. They can race onnode_modules/.experimental-vitest-cacheand fail withENOTEMPTY. Use one groupedpnpm test ...invocation, run targeted lanes sequentially, or set distinctOPENCLAW_VITEST_FS_MODULE_CACHE_PATHvalues when true parallel Vitest processes are needed. - Test workers max 16. Memory pressure:
OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test. - Live:
OPENCLAW_LIVE_TEST=1 pnpm test:live; verboseOPENCLAW_LIVE_TEST_QUIET=0. - Guide:
docs/help/testing.md.
- Docs change with behavior/API. Use docs list/read_when hints; docs links per
docs/AGENTS.md. - Changelog user-facing only; pure test/internal usually no entry.
- Changelog placement: active version
### Changes/### Fixes; every added entry must include at least oneThanks @authorattribution, using credited GitHub username(s). Never addThanks @codex,Thanks @openclaw, orThanks @steipete. - Changelog bullets are always single-line. No wrapping/continuation across multiple lines. Long entries stay on one long line so dedupe, PR-ref, and credit-audit tooling work and so the visual style stays uniform.
- Commit via
scripts/committer "<msg>" <file...>; stage intended files only. It formats staged files; still run gates. - Commits: conventional-ish, concise, grouped.
- No manual stash/autostash unless explicit. No branch/worktree changes unless requested.
main: no merge commits; rebase on latestorigin/mainbefore push. Do not keep chasingmainwith repeated full gates after one green run plus a clean rebase sanity pass.- User says
commit: your changes only.commit all: all changes in grouped chunks.push: maygit pull --rebasefirst. - Do not delete/rename unexpected files; ask if blocking, else ignore.
- Bulk PR close/reopen >5: ask with count/scope.
- PR/issue workflows:
$openclaw-pr-maintainer./landpr:~/.codex/prompts/landpr.md.
- 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. - Env keys: check
~/.profile. - Dependency patches/overrides/vendor changes need explicit approval.
pnpm.patchedDependenciesexact versions only. - Carbon pins owner-only: do not change
@buape/carbonunless Shadow (@thewilloftheshadow, verified bygh) asks. - Releases/publish/version bumps need explicit approval. Release docs:
docs/reference/RELEASING.md; use$openclaw-release-maintainer. - GHSA/advisories:
$openclaw-ghsa-maintainer. - Beta tag/version match:
vYYYY.M.D-beta.N-> npmYYYY.M.D-beta.N --tag beta.
- 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: use app or
openclaw gateway restart/status --deep; no ad-hoc tmux gateway. Logs:./scripts/clawlog.sh. - Version bump touches:
package.json,apps/android/app/build.gradle.kts,apps/ios/version.json+pnpm ios:version:sync, macOSInfo.plist,docs/install/updating.md. Appcast only for Sparkle release. - Mobile LAN pairing: plaintext
ws://loopback-only. Private-networkws://needsOPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1; Tailscale/public usewss://or tunnel. - A2UI hash
src/canvas-host/a2ui/.bundle.hash: generated; ignore unless runningpnpm canvas:a2ui:bundle; commit separately.
- Remote install docs:
docs/install/{exe-dev,fly,hetzner}.md. Parallels smoke:$openclaw-parallels-smoke; Discord roundtrip:parallels-discord-roundtrip. - Rebrand/migration/config warnings: run
openclaw doctor. - Never edit
node_modules. - Local-only
.agentsignores:.git/info/exclude, not repo.gitignore. - CLI progress:
src/cli/progress.ts; status tables:src/terminal/table.ts. - Connection/provider additions: update all UI surfaces + docs + status/config forms.
- Provider tool schemas: prefer flat string enum helpers over
Type.Union([Type.Literal(...)]); some providers rejectanyOf. Not a repo-wide protocol/schema ban. - External messaging: no token-delta channel messages. Follow
docs/concepts/streaming.md; preview/block streaming uses edits/chunks and preserves final/fallback delivery.