diff --git a/AGENTS.md b/AGENTS.md index 7716c571362..290528191c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,335 +1,190 @@ -# Repository Guidelines +# AGENTS.MD -- Repo: https://github.com/openclaw/openclaw -- In chat replies, file references must be repo-root relative only (example: `extensions/telegram/src/index.ts:80`); never absolute paths or `~/...`. -- Do not edit files covered by security-focused `CODEOWNERS` rules unless a listed owner explicitly asked for the change or is already reviewing it with you. Treat those paths as restricted surfaces, not drive-by cleanup. +Telegraph style. Root rules only. Read scoped `AGENTS.md` before touching a subtree. -## Project Structure & Module Organization +## Start -- Source code: `src/` (CLI wiring in `src/cli`, commands in `src/commands`, infra in `src/infra`, media pipeline in `src/media`, web provider helpers in `src/web` and `src/plugins/web-*provider*.ts`). -- Tests: colocated `*.test.ts`. -- Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`. -- Nomenclature: use "plugin" / "plugins" in docs, UI, changelogs, and contributor guidance. The bundled workspace plugin tree remains the internal package layout to avoid repo-wide churn from a rename. -- Bundled plugin naming: for repo-owned workspace plugins, keep the canonical plugin id aligned across `openclaw.plugin.json:id`, the default workspace folder name, and package names anchored to the same id (`@openclaw/` or approved suffix forms like `-provider`, `-plugin`, `-speech`, `-sandbox`, `-media-understanding`). Keep `openclaw.install.npmSpec` equal to the package name and `openclaw.channel.id` equal to the plugin id when present. Exceptions must be explicit and covered by the repo invariant test. -- Plugins: live in the bundled workspace plugin tree (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them. -- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `openclaw` in `devDependencies` or `peerDependencies` instead (runtime resolves `openclaw/plugin-sdk` via jiti alias). -- Import boundaries: extension production code should treat `openclaw/plugin-sdk/*` plus local `api.ts` / `runtime-api.ts` barrels as the public surface. Do not import core `src/**`, `src/plugin-sdk-internal/**`, or another extension's `src/**` directly. -- Installers served from `https://openclaw.ai/*`: live in the sibling repo `../openclaw.ai` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`). -- Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs). - - Core channel docs: `docs/channels/` - - Core channel code: `src/channels`, `src/routing`, `src/web` - - Bundled plugin channels: `extensions//` (for example Discord, Telegram, Slack, Matrix, Zalo, ZaloUser, Voice Call) -- When adding channels/plugins/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/plugin label colors). +- Repo: `https://github.com/openclaw/openclaw` +- Replies: repo-root file refs only, e.g. `extensions/telegram/src/index.ts:80`. No absolute paths, no `~/`. +- CODEOWNERS: maintenance/refactors/tests are ok. For larger behavior, product, security, or ownership-sensitive changes, get a listed owner request/review first. +- First pass: run docs list (`bin/docs-list` or `pnpm docs:list`; ignore if unavailable), then read only relevant docs/guides. +- Missing deps: run `pnpm install`, rerun once, then report first actionable error. +- Use "plugin/plugins" in docs/UI/changelog. `extensions/` remains internal workspace layout. +- Add channel/plugin/app/doc surface: update `.github/labeler.yml` and matching GitHub labels. +- New `AGENTS.md`: add sibling `CLAUDE.md` symlink to it. -## Architecture Boundaries +## Repo Map -- Start here for the repo map: - - bundled workspace plugin tree = bundled plugins and the closest example surface for third-party plugins - - `src/plugin-sdk/*` = the public plugin contract that extensions are allowed to import - - `src/channels/*` = core channel implementation details behind the plugin/channel boundary - - `src/plugins/*` = plugin discovery, manifest validation, loader, registry, and contract enforcement - - `src/gateway/protocol/*` = typed Gateway control-plane and node wire protocol -- Progressive disclosure lives in local boundary guides: - - repo root `AGENTS.md` - - bundled-plugin-tree `extensions/AGENTS.md` - - `src/plugin-sdk/AGENTS.md` - - `src/channels/AGENTS.md` - - `src/plugins/AGENTS.md` - - `src/gateway/protocol/AGENTS.md` -- Workflow hygiene: - - Do not grep or existence-check every `docs/*.md`, `AGENTS.md`, or guide path mentioned in this file before starting work. - - Read only the guides and docs that are directly relevant to the files or boundary you are touching. - - Only do full broken-link or missing-guide sweeps when the task is explicitly about docs or repo-instruction maintenance. -- Plugin and extension boundary: - - Public docs: `docs/plugins/building-plugins.md`, `docs/plugins/architecture.md`, `docs/plugins/sdk-overview.md`, `docs/plugins/sdk-entrypoints.md`, `docs/plugins/sdk-runtime.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/sdk-provider-plugins.md` - - Definition files: `src/plugin-sdk/plugin-entry.ts`, `src/plugin-sdk/core.ts`, `src/plugin-sdk/provider-entry.ts`, `src/plugin-sdk/channel-contract.ts`, `scripts/lib/plugin-sdk-entrypoints.json`, `package.json` - - Invariant: core must stay extension-agnostic. Adding a bundled or third-party extension should not require unrelated core edits just to teach core that the extension exists. - - Rule: extensions must cross into core only through `openclaw/plugin-sdk/*`, manifest metadata, and documented runtime helpers. Do not import `src/**` from extension production code. - - Rule: core code and tests must not deep-import bundled plugin internals such as a plugin's `src/**` files or `onboard.js`. If core needs a bundled plugin helper, expose it through that plugin's `api.ts` and, when it is a real cross-package contract, through `src/plugin-sdk/.ts`. - - Rule: do not add hardcoded bundled extension/provider/channel/capability id lists, maps, or named special cases in core when a manifest, capability, registry, or plugin-owned contract can express the same behavior. - - Rule: extension-owned compatibility behavior belongs to the owning extension. Core may orchestrate generic doctor/config flows, but extension-specific legacy repairs, detection rules, onboarding, auth detection, and provider defaults should live in plugin-owned contracts. - - Rule: for legacy config specifically, prefer doctor-owned repair paths over startup/load-time core migrations. Do not add new plugin-specific legacy migration logic to shared core/runtime surfaces when `openclaw doctor --fix` can own it. - - Rule: when a test is asserting extension-specific behavior, keep that coverage in the owning extension when feasible. Core tests should assert generic contracts and registry/capability behavior, not extension internals. - - Refactor trigger: if you encounter core code or tests that name a specific extension/provider/channel for extension-owned behavior, refactor toward a generic registry/capability/plugin-owned seam instead of adding another special case. - - Compatibility: new plugin seams are allowed, but they must be added as documented, backwards-compatible, versioned contracts. We have third-party plugins in the wild and do not break them casually. -- Channel boundary: - - Public docs: `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/architecture.md` - - Definition files: `src/channels/plugins/types.plugin.ts`, `src/channels/plugins/types.core.ts`, `src/channels/plugins/types.adapters.ts`, `src/plugin-sdk/core.ts`, `src/plugin-sdk/channel-contract.ts` - - Rule: `src/channels/**` is core implementation. If plugin authors need a new seam, add it to the Plugin SDK instead of telling them to import channel internals. -- Provider/model boundary: - - Public docs: `docs/plugins/sdk-provider-plugins.md`, `docs/concepts/model-providers.md`, `docs/plugins/architecture.md` - - Definition files: `src/plugins/types.ts`, `src/plugin-sdk/provider-entry.ts`, `src/plugin-sdk/provider-auth.ts`, `src/plugin-sdk/provider-catalog-shared.ts`, `src/plugin-sdk/provider-model-shared.ts` - - Rule: core owns the generic inference loop; provider plugins own provider-specific behavior through registration and typed hooks. Do not solve provider needs by reaching into unrelated core internals. - - Rule: avoid ad hoc reads of `plugins.entries..config` from unrelated core code. If core needs plugin-owned auth/config behavior, add or use a generic seam (`resolveSyntheticAuth`, public SDK/helper facades, manifest metadata, plugin auto-enable hooks) and honor plugin disablement plus SecretRef semantics. - - Rule: vendor-owned tools and settings belong in the owning plugin. Do not add provider-specific tool config, secret collection, or runtime enablement to core `tools.*` surfaces unless the tool is intentionally core-owned. -- Gateway protocol boundary: - - Public docs: `docs/gateway/protocol.md`, `docs/gateway/bridge-protocol.md`, `docs/concepts/architecture.md` - - Definition files: `src/gateway/protocol/schema.ts`, `src/gateway/protocol/schema/*.ts`, `src/gateway/protocol/index.ts` - - Rule: protocol changes are contract changes. Prefer additive evolution; incompatible changes require explicit versioning, docs, and client/codegen follow-through. -- Config contract boundary: - - Canonical public config lives in exported config types, zod/schema surfaces, schema help/labels, generated config metadata, config baselines, and any user-facing gateway/config payloads. Keep those surfaces aligned. - - When a legacy config key is retired from the public contract, remove it from every public config surface above. Keep backward compatibility only through raw-config migration/doctor seams unless explicit product policy says otherwise. - - Do not reintroduce removed legacy aliases into public types/schema/help/baselines “for convenience”. If old configs still need to load, handle that in `legacy.migrations.*`, config ingest, or `openclaw doctor --fix`. - - `hooks.internal.entries` is the canonical public hook config model. `hooks.internal.handlers` is compatibility-only input and must not be re-exposed in public schema/help/baseline surfaces. -- Bundled plugin contract boundary: - - Public docs: `docs/plugins/architecture.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-overview.md` - - Definition files: `src/plugins/contracts/registry.ts`, `src/plugins/types.ts`, `src/plugins/public-surface-loader.ts`, `src/plugins/public-surface-runtime.ts`, `src/plugins/provider-public-artifacts.ts`, `src/plugins/web-provider-public-artifacts.ts` - - Rule: keep manifest metadata, runtime registration, public SDK exports, and contract tests aligned. Do not create a hidden path around the declared plugin interfaces. -- Extension test boundary: - - Keep extension-owned onboarding/config/provider coverage under the owning bundled plugin package when feasible. - - If core tests need bundled plugin behavior, consume it through public `src/plugin-sdk/.ts` facades or the plugin's `api.ts`, not private extension modules. - - Shared helpers under `test/helpers/**` are part of that same boundary. Do not hardcode repo-relative `extensions/**` imports there, and do not keep plugin-local deep mocks in shared helpers just because multiple tests use them. - - When core tests or shared helpers need bundled plugin public surfaces, use `src/test-utils/bundled-plugin-public-surface.ts` for `api.ts`, `runtime-api.ts`, `contract-api.ts`, `test-api.ts`, plugin entrypoint `index.js`, and resolved module ids for dynamic import or mocking. - - If a core test is asserting extension-specific behavior instead of a generic contract, move it to the owning extension package. -- Scoped guides still matter: - - `extensions/AGENTS.md` expands extension/plugin boundary rules. - - `src/channels/AGENTS.md` expands core channel boundary and hot-path rules. - - `src/plugin-sdk/AGENTS.md` expands public SDK contract rules. - - `src/plugins/AGENTS.md` expands plugin loading, registry, and manifest rules. - - `src/gateway/protocol/AGENTS.md` expands typed Gateway protocol rules. - - `src/gateway/AGENTS.md` expands Gateway server hot-path and plugin artifact rules. - - `src/agents/AGENTS.md` expands agent test/import performance rules. - - `test/helpers/AGENTS.md` and `test/helpers/channels/AGENTS.md` expand shared test helper boundary rules. -- Plugin architecture direction: - - Keep a manifest-first control plane: discovery, validation, enablement, setup hints, and activation planning should stay metadata-driven by default. - - Keep runtime execution separate: actual provider/channel/tool execution should resolve through narrow targeted loaders, not broad registry materialization. - - Host loads plugins; plugins do not load host internals. Prefer a small versioned host/kernel seam plus documented SDK entrypoints over ambient reachability. - - Treat broad runtime registries and mutable global plugin state as transitional compatibility surfaces, not the target architecture. - - If a setup or config flow truly needs plugin runtime, make that explicit instead of silently importing runtime code on the cold path. +- Core TS: `src/`, `ui/`, `packages/` +- Bundled plugins: `extensions/` +- Plugin SDK/public contract: `src/plugin-sdk/*` +- Core channel internals: `src/channels/*` +- Plugin loader/registry/contracts: `src/plugins/*` +- Gateway protocol: `src/gateway/protocol/*` +- Docs: `docs/` +- Apps: `apps/`, `Swabble/` +- Installers served from `openclaw.ai`: sibling `../openclaw.ai` -## Scoped Workflow Guides +Scoped guides: -- `docs/AGENTS.md` owns Mintlify docs, docs links, and docs i18n rules. -- `ui/AGENTS.md` owns Control UI i18n and generated locale rules. -- `scripts/AGENTS.md` owns script-runner, local-check lock, and test/lint wrapper rules. +- `extensions/AGENTS.md`: bundled plugin rules +- `src/plugin-sdk/AGENTS.md`: public SDK rules +- `src/channels/AGENTS.md`: channel core rules +- `src/plugins/AGENTS.md`: plugin loader/registry rules +- `src/gateway/AGENTS.md`, `src/gateway/protocol/AGENTS.md`: gateway/protocol rules +- `src/agents/AGENTS.md`: agent import/test perf rules +- `test/helpers/AGENTS.md`, `test/helpers/channels/AGENTS.md`: shared test helpers +- `docs/AGENTS.md`, `ui/AGENTS.md`, `scripts/AGENTS.md`: docs/UI/scripts -## exe.dev VM ops (general) +## Architecture -- Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set). -- SSH flaky: use exe.dev web terminal or Shelley (web agent); keep a tmux session for long ops. -- Update: `sudo npm i -g openclaw@latest` (global install needs root on `/usr/lib/node_modules`). -- Config: use `openclaw config set ...`; ensure `gateway.mode=local` is set. -- Discord: store raw token only (no `DISCORD_BOT_TOKEN=` prefix). -- Restart: stop old gateway and run: - `pkill -9 -f openclaw-gateway || true; nohup openclaw gateway run --bind loopback --port 18789 --force > /tmp/openclaw-gateway.log 2>&1 &` -- Verify: `openclaw channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/openclaw-gateway.log`. +- Core must stay extension-agnostic. No core special cases for bundled plugin/provider/channel ids when manifest/registry/capability contracts can express it. +- Extensions cross into core only via `openclaw/plugin-sdk/*`, manifest metadata, injected runtime helpers, and documented local barrels (`api.ts`, `runtime-api.ts`). +- Extension production code must not import core `src/**`, `src/plugin-sdk-internal/**`, another extension's `src/**`, or relative paths outside its package. +- Core code/tests must not deep-import plugin internals (`extensions/*/src/**`, `onboard.js`). Use plugin `api.ts` / public SDK facade / generic contract. +- Extension-owned behavior stays in the extension: legacy repair, detection, onboarding, auth/provider defaults, provider tools/settings. +- Legacy config repair: prefer doctor/fix paths over startup/load-time core migrations. +- If a core test asserts extension-specific behavior, move it to the owning extension or a generic contract test. +- New seams: backwards-compatible, documented, versioned. Third-party plugins exist. +- Channels: `src/channels/**` is implementation. Plugin authors get SDK seams, not channel internals. +- Providers: core owns generic inference loop; provider plugins own provider-specific auth/catalog/runtime hooks. +- Gateway protocol changes are contract changes: additive first; incompatible needs versioning/docs/client follow-through. +- Config contract: keep exported types, schema/help, generated metadata, baselines, docs aligned. Retired public keys stay retired; compatibility belongs in raw migration/doctor paths. +- Plugin architecture direction: manifest-first control plane; targeted runtime loaders; no hidden paths around declared contracts; broad mutable registries are transitional. +- Prompt-cache rule: deterministic ordering for maps/sets/registries/plugin lists/files/network results before model/tool payloads. Preserve old transcript bytes when possible. -## Build, Test, and Development Commands +## Commands -- Runtime baseline: Node **22+** (keep Node + Bun paths working). -- Install deps: `pnpm install` -- If deps are missing (for example `node_modules` missing, `vitest not found`, or `command not found`), run the repo’s package-manager install command (prefer lockfile/README-defined PM), then rerun the exact requested command once. Apply this to test/build/lint/typecheck/dev commands; if retry still fails, report the command and first actionable error. -- Pre-commit hooks are installed by the package `prepare` script (`git config core.hooksPath git-hooks`). The hook formats/lints staged source files and runs `pnpm check` unless the staged change is docs-only or `FAST_COMMIT=1` is set. -- `FAST_COMMIT=1` skips the repo-wide `pnpm check` inside the pre-commit hook only. The hook still runs targeted formatting/linting for staged files and restages formatter changes. Use it when you intentionally want a faster commit path and are running equivalent targeted verification manually. It does not change CI and does not change what `pnpm check` itself does. -- Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches). -- Prefer Bun for TypeScript execution (scripts, dev, tests): `bun ` / `bunx `. -- Run CLI in dev: `pnpm openclaw ...` (bun) or `pnpm dev`. -- Node remains supported for running built output (`dist/*`) and production installs. -- Mac packaging (dev): `scripts/package-mac-app.sh` defaults to current arch. -- Type-check/build: `pnpm build` -- TypeScript checks are split by architecture boundary. Normal entrypoints: - - `pnpm tsgo`: fastest core production check (`src/`, `ui/`, `packages/`; no `extensions/` include roots). - - `pnpm tsgo:prod`: core + bundled extension production graphs; this is what `pnpm check` runs. - - `pnpm check:test-types` / `pnpm tsgo:test`: core + bundled extension test graphs. - - `pnpm tsgo:all`: every production + test graph through project references; use for full typecheck sweeps. - - Boundary/debug slices exist (`tsgo:core:test`, `tsgo:extensions:test`, `tsgo:core:all`, `tsgo:extensions:all`, `tsgo:test:src`, `tsgo:test:ui`, `tsgo:test:packages`), but do not present them as the normal flow. - - `pnpm tsgo:profile [core-test|extensions-test|--all]`: profile fresh graph cost into `.artifacts/tsgo-profile/`. Diagnostic-only profile slices (`core-test-agents`, `core-test-non-agents`) exist for investigating agent graph cost; do not treat them as normal user-facing checks. -- Do not add `tsc --noEmit`, `typecheck`, or `check:types` lanes for repo type checking. Use `tsgo` graphs. `tsc` is allowed only when emitting declaration/package-boundary compatibility artifacts that `tsgo` does not replace. -- Boundary rule: core must not know extension implementation details. Extensions hook into core through manifests, registries, capabilities, and public `openclaw/plugin-sdk/*` contracts. If you find core production code naming a specific extension, or a core test that is really testing extension-owned behavior, call it out and prefer moving coverage/logic to the owning extension or a generic contract test. -- Lint/format: `pnpm check` - - `pnpm lint`: type-aware lint shards for core, extensions, and scripts, run in parallel after shared boundary artifacts are prepared once. - - `pnpm lint:core`, `pnpm lint:extensions`, `pnpm lint:scripts`: focused lint shards. - - `pnpm lint:all`: legacy single-pass repo-wide oxlint, useful when comparing shard behavior. - - `pnpm lint:apps`: app lint surface such as Swift; keep app lint separate from repo TypeScript lint. -- Local agent/dev shells default to host-aware `OPENCLAW_LOCAL_CHECK=1` behavior for `pnpm tsgo` and `pnpm lint`; set `OPENCLAW_LOCAL_CHECK_MODE=throttled` to force the lower-memory profile, `OPENCLAW_LOCAL_CHECK_MODE=full` to keep lock-only behavior, or `OPENCLAW_LOCAL_CHECK=0` in CI/shared runs. -- Format check: `pnpm format:check` (oxfmt --check) -- Format fix: `pnpm format` or `pnpm format:fix` (oxfmt --write) -- Terminology: - - "gate" means a verification command or command set that must be green for the decision you are making. - - A local dev gate is the fast default loop, usually `pnpm check` plus any scoped test you actually need. - - A landing gate is the broader bar before pushing `main`, usually `pnpm check`, `pnpm test`, and `pnpm build` when the touched surface can affect build output, packaging, lazy-loading/module boundaries, or published surfaces. - - A CI gate is whatever the relevant workflow enforces for that lane (for example `check`, `check-additional`, `build-smoke`, or release validation). -- Local dev gate: prefer `pnpm check` for the normal edit loop. It runs production typecheck, lint, and independent policy guards. It keeps test typecheck and repo-architecture policy guards out of the default local loop. -- Timed local gate: use `pnpm check:timed` to see per-stage cost. Use `pnpm check:timed:all-types` when investigating test typecheck cost. Add `:architecture` only when investigating the CI architecture gate locally. -- CI architecture gate: `check-additional` enforces architecture and boundary policy guards that are intentionally kept out of the default local loop. -- Formatting gate: the pre-commit hook runs targeted formatting on staged source files before `pnpm check`. If you want a repo-wide formatting-only preflight locally, run `pnpm format:check` explicitly. -- If you need a fast commit loop, `FAST_COMMIT=1 git commit ...` skips the hook’s repo-wide `pnpm check`; targeted formatting/linting still runs, so use that only when you are deliberately covering the touched surface some other way. -- Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage` -- Generated baseline drift detection uses SHA-256 hash files under `docs/.generated/` (`.sha256` files tracked in git; full JSON baselines are gitignored, generated locally for inspection). -- Config schema drift uses `pnpm config:docs:gen` / `pnpm config:docs:check`. -- Plugin SDK API drift uses `pnpm plugin-sdk:api:gen` / `pnpm plugin-sdk:api:check`. -- If you change config schema/help or the public Plugin SDK surface, run the matching gen command and commit the updated `.sha256` hash file. Keep the two drift-check flows adjacent in scripts/workflows/docs guidance rather than inventing a third pattern. -- When a `tsgo` graph fails, triage by coherent surface instead of by raw error count: rerun the failing graph, group failures by package/module/type contract, open the source-of-truth type or export file first, fix the root mismatch, then rerun the failing graph before widening into downstream consumers. Check `origin/main` before doing broad cleanup because some apparent type debt is already fixed upstream. -- For narrowly scoped changes, prefer narrowly scoped tests that directly validate the touched behavior. If no meaningful scoped test exists, say so explicitly and use the next most direct validation available. -- Verification modes for work on `main`: - - Default mode: `main` is relatively stable. Count pre-commit hook coverage when it already verified the current tree, avoid rerunning the exact same checks just for ceremony, and prefer keeping CI/main green before landing. - - Fast-commit mode: `main` is moving fast and you intentionally optimize for shorter commit loops. Prefer explicit local verification close to the final landing point, and it is acceptable to use `--no-verify` for intermediate or catch-up commits after equivalent checks have already run locally. -- Preferred landing bar for pushes to `main`: in Default mode, favor `pnpm check` and `pnpm test` near the final rebase/push point when feasible. In fast-commit mode, verify the touched surface locally near landing without insisting every intermediate commit replay the full hook. -- Scoped tests prove the change itself. `pnpm test` remains the default `main` landing bar; scoped tests do not replace full-suite gates by default. -- Hard gate: if the change can affect build output, packaging, lazy-loading/module boundaries, or published surfaces, `pnpm build` MUST be run and MUST pass before pushing `main`. -- Default rule: do not land changes with failing format, lint, type, build, or required test checks when those failures are caused by the change or plausibly related to the touched surface. Fast-commit mode changes how verification is sequenced; it does not lower the requirement to validate and clean up the touched surface before final landing. -- For narrowly scoped changes, if unrelated failures already exist on latest `origin/main`, state that clearly, report the scoped tests you ran, and ask before broadening scope into unrelated fixes or landing despite those failures. -- Do not use scoped tests as permission to ignore plausibly related failures. +- Runtime: Node 22+. Keep Node and Bun paths working. +- Install: `pnpm install` (Bun supported; keep lockfiles/patches aligned if touched). +- Dev CLI: `pnpm openclaw ...` or `pnpm dev`. +- Build: `pnpm build` +- Smart local gate: `pnpm check:changed` +- Explain smart gate: `pnpm changed:lanes --json` +- Pre-commit view: `pnpm check:changed --staged` +- Normal full prod sweep: `pnpm check` +- Full tests: `pnpm test` +- Targeted tests: `pnpm test [vitest args...]`; do not call raw `vitest`. +- Coverage: `pnpm test:coverage` +- Format check/fix: `pnpm format:check` / `pnpm format` +- Typecheck: + - `pnpm tsgo`: fastest core prod graph + - `pnpm tsgo:prod`: core + extensions prod graphs; used by `pnpm check` + - `pnpm check:test-types` / `pnpm tsgo:test`: all test graphs + - `pnpm tsgo:all`: all prod + test project refs + - Debug slices exist; do not present as normal user flow. + - Profile: `pnpm tsgo:profile [core-test|extensions-test|--all]` +- Type policy: use `tsgo`; do not add `tsc --noEmit`, `typecheck`, or `check:types` lanes. `tsc` only for declaration/package-boundary emit gaps. +- Lint: + - `pnpm lint`: core/extensions/scripts shards + - `pnpm lint:core`, `pnpm lint:extensions`, `pnpm lint:scripts` + - `pnpm lint:apps`: Swift/app surface, separate from TS lint + - `pnpm lint:all`: legacy comparison lane +- Local heavy-check behavior: `OPENCLAW_LOCAL_CHECK=1` default; `OPENCLAW_LOCAL_CHECK_MODE=throttled|full`; `OPENCLAW_LOCAL_CHECK=0` for CI/shared runs. -## Prompt Cache Stability +## Gates -- Treat prompt-cache stability as correctness/perf-critical, not cosmetic. -- Any code that assembles model or tool payloads from maps, sets, registries, plugin lists, MCP catalogs, filesystem reads, or network results must make ordering deterministic before building the request. -- Do not rewrite older transcript/history bytes on every turn unless you intentionally want to invalidate the cached prefix. Legacy cleanup, pruning, normalization, and migration logic should preserve recent prompt bytes when possible. -- If truncation or compaction is required, prefer mutating newest or tail content first so the cached prefix stays byte-identical for as long as possible. -- For cache-sensitive changes, require a regression test that proves turn-to-turn prefix stability or deterministic request assembly; helper-local tests alone are not enough. +- Pre-commit hook: staged format/lint, then `pnpm check:changed --staged`; docs/markdown-only skips changed-scope check; `FAST_COMMIT=1` skips changed-scope check only. +- Changed lanes: + - core prod => core prod typecheck + core tests + - core tests => core test typecheck/tests only + - extension prod => extension prod typecheck + extension tests + - extension tests => extension test typecheck/tests only + - public SDK/plugin contract => extension prod/test validation too + - unknown root/config => all lanes +- Local loop: prefer `pnpm check:changed`; use `pnpm check` for full prod TS/lint sweep. +- Landing on `main`: verify touched surface near landing; default bar is `pnpm check` + `pnpm test` when feasible. +- Hard build gate: run/pass `pnpm build` before push if build output, packaging, lazy/module boundaries, or published surfaces can change. +- Do not land related failing format/lint/type/build/tests. If failures are unrelated on latest `origin/main`, say so and give scoped proof. +- CI architecture gate: `check-additional`; local equivalent `pnpm check:architecture`. +- Config docs drift: `pnpm config:docs:gen/check` +- Plugin SDK API drift: `pnpm plugin-sdk:api:gen/check` +- Generated docs baselines: tracked `docs/.generated/*.sha256`; full JSON ignored. -## Coding Style & Naming Conventions +## Code Style -- Language: TypeScript (ESM). Prefer strict typing; avoid `any`. -- Formatting/linting via Oxlint and Oxfmt. -- Never add `@ts-nocheck` and do not add inline lint suppressions by default. Fix root causes first; only keep a suppression when the code is intentionally correct, the rule cannot express that safely, and the comment explains why. -- Do not disable `no-explicit-any`; prefer real types, `unknown`, or a narrow adapter/helper instead. Update Oxlint/Oxfmt config only when required. -- Prefer `zod` or existing schema helpers at external boundaries such as config, webhook payloads, CLI/JSON output, persisted JSON, and third-party API responses. -- Prefer discriminated unions when parameter shape changes runtime behavior. -- Prefer `Result`-style outcomes and closed error-code unions for recoverable runtime decisions. -- Keep human-readable strings for logs, CLI output, and UI; do not use freeform strings as the source of truth for internal branching. -- Avoid `?? 0`, empty-string, empty-object, or magic-string sentinels when they can change runtime meaning silently. -- If introducing a new optional field or nullable semantic in core logic, prefer an explicit union or dedicated type when the value changes behavior. -- New runtime control-flow code should not branch on `error: string` or `reason: string` when a closed code union would be reasonable. -- Dynamic import guardrail: do not mix `await import("x")` and static `import ... from "x"` for the same module in production code paths. If you need lazy loading, create a dedicated `*.runtime.ts` boundary (that re-exports from `x`) and dynamically import that boundary from lazy callers only. -- Dynamic import verification: after refactors that touch lazy-loading/module boundaries, run `pnpm build` and check for `[INEFFECTIVE_DYNAMIC_IMPORT]` warnings before submitting. -- Circular dependencies: `pnpm check` runs the fast runtime import-cycle guard. `pnpm check:architecture` (and CI `check-additional`) also runs the broader madge import-cycle guard; keep both green before landing architecture-sensitive changes. -- Extension SDK self-import guardrail: inside an extension package, do not import that same extension via `openclaw/plugin-sdk/` from production files. Route internal imports through a local barrel such as `./api.ts` or `./runtime-api.ts`, and keep the `plugin-sdk/` path as the external contract only. -- Extension package boundary guardrail: inside a bundled plugin package, do not use relative imports/exports that resolve outside that same package root. If shared code belongs in the plugin SDK, import `openclaw/plugin-sdk/` instead of reaching into `src/plugin-sdk/**` or other repo paths via `../`. -- Extension API surface rule: `openclaw/plugin-sdk/` is the only public cross-package contract for extension-facing SDK code. If an extension needs a new seam, add a public subpath first; do not reach into `src/plugin-sdk/**` by relative path. -- Never share class behavior via prototype mutation (`applyPrototypeMixins`, `Object.defineProperty` on `.prototype`, or exporting `Class.prototype` for merges). Use explicit inheritance/composition (`A extends B extends C`) or helper composition so TypeScript can typecheck. -- If this pattern is needed, stop and get explicit approval before shipping; default behavior is to split/refactor into an explicit class hierarchy and keep members strongly typed. -- In tests, prefer per-instance stubs over prototype mutation (`SomeClass.prototype.method = ...`) unless a test explicitly documents why prototype-level patching is required. -- Add brief code comments for tricky or non-obvious logic. -- Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via `createDefaultDeps`. -- Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability. -- Naming: use **OpenClaw** for product/app/docs headings; use `openclaw` for CLI command, package/binary, paths, and config keys. -- Written English: use American spelling and grammar in code, comments, docs, and UI strings (e.g. "color" not "colour", "behavior" not "behaviour", "analyze" not "analyse"). +- TypeScript ESM. Strict types. Avoid `any`; prefer real types/`unknown`/narrow adapters. +- No `@ts-nocheck`. No lint suppressions unless intentional and explained. +- External boundaries: prefer `zod` or existing schema helpers. +- Runtime branching: prefer discriminated unions / closed codes over freeform strings. +- Avoid magic sentinels like `?? 0`, empty object/string when semantics change. +- Dynamic import: do not mix static and dynamic import for same module in prod path. Use dedicated `*.runtime.ts` lazy boundary. After lazy-boundary edits, run `pnpm build` and check `[INEFFECTIVE_DYNAMIC_IMPORT]`. +- Cycles: keep `pnpm check:import-cycles` and architecture/madge cycle checks green. +- Classes: no prototype mixins/mutations. Use explicit inheritance/composition. Tests prefer per-instance stubs. +- Comments: brief only for non-obvious logic. +- File size: split around ~700 LOC when it improves clarity/testability. +- Product naming: **OpenClaw** product/docs; `openclaw` CLI/package/path/config. +- Written English: American spelling. -## Release / Advisory Workflows +## Tests -- Use `$openclaw-release-maintainer` at `.agents/skills/openclaw-release-maintainer/SKILL.md` for release naming, version coordination, release auth, and changelog-backed release-note workflows. -- Use `$openclaw-ghsa-maintainer` at `.agents/skills/openclaw-ghsa-maintainer/SKILL.md` for GHSA advisory inspection, patch/publish flow, private-fork checks, and GHSA API validation. -- Release and publish remain explicit-approval actions even when using the skill. +- Vitest. Tests colocated `*.test.ts`; e2e `*.e2e.test.ts`. +- Example models in tests: `sonnet-4.6`, `gpt-5.4`. +- Clean up timers/env/globals/mocks/sockets/temp dirs/module state; `--isolate=false` must stay safe. +- Hot tests: avoid per-test `vi.resetModules()` + fresh heavy imports; prefer static or `beforeAll` imports and reset state directly. +- Avoid broad `importOriginal()` / broad `openclaw/plugin-sdk/*` partial mocks in hot tests. Add narrow local `*.runtime.ts` seam and mock it. +- Use existing deps/callback/runtime injection seams before module mocks. +- Import-dominated test time is a boundary smell; shrink import surface before adding cases. +- Replacing slow integration coverage: extract production composition into a named helper and test that helper. +- Do not modify baseline/inventory/ignore/snapshot/expected-failure files to silence checks without explicit approval. +- Do not set test workers above 16. For memory pressure: `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`. +- Live: `OPENCLAW_LIVE_TEST=1 pnpm test:live`; full logs `OPENCLAW_LIVE_TEST_QUIET=0`. +- Full testing guide: `docs/help/testing.md`. -## Testing Guidelines +## Docs / Changelog -- Framework: Vitest with V8 coverage thresholds (70% lines/branches/functions/statements). -- Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`. -- When tests need example Anthropic/OpenAI model constants, prefer `sonnet-4.6` and `gpt-5.4`; update older Anthropic/GPT examples when you touch those tests. -- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic. -- Write tests to clean up timers, env, globals, mocks, sockets, temp dirs, and module state so `--isolate=false` stays green. -- Test performance guardrail: do not put `vi.resetModules()` plus `await import(...)` in `beforeEach`/per-test loops for heavy modules unless module state truly requires it. Prefer static imports or one-time `beforeAll` imports, then reset mocks/runtime state directly. -- Test performance guardrail: if a test file uses stable `vi.mock(...)` hoists or other static module mocks, do not pair them with `vi.resetModules()` and a fresh `await import(...)` in every `beforeEach`. Import the heavy module once in `beforeAll`, then reset/prime mocks in `beforeEach` so Browser/Matrix-style hotspot tests do not pay the module graph cost per case. -- Test performance guardrail: inside an extension package, prefer a thin local seam (`./api.ts`, `./runtime-api.ts`, or a narrower local `*.runtime-api.ts`) over direct `openclaw/plugin-sdk/*` imports for internal production code. Keep local seams curated and lightweight; only reach for direct `plugin-sdk/*` imports when you are crossing a real package boundary or when no suitable local seam exists yet. -- Test performance guardrail: keep expensive runtime fallback work such as snapshotting, migration, installs, or bootstrap behind dedicated `*.runtime.ts` boundaries so tests can mock the seam instead of accidentally invoking real work. -- Test performance guardrail: for import-only/runtime-wrapper tests, keep the wrapper lazy. Do not eagerly load heavy verification/bootstrap/runtime modules at module top level if the exported function can import them on demand. -- Test performance guardrail: prefer explicit mock factories over `importOriginal()` for broad modules. Reserve `importOriginal()` for narrow modules where partial-real behavior is genuinely needed. -- Test performance guardrail: do not partial-mock broad `openclaw/plugin-sdk/*` barrels in hot tests. Add a plugin-local `*.runtime.ts` seam and mock that seam instead. -- Test performance guardrail: when production code already accepts `deps`, callbacks, or runtime injection, use that seam in tests before adding module-level mocks. -- Test performance guardrail: prefer narrow public SDK subpaths such as `models-provider-runtime`, `skill-commands-runtime`, and `reply-dispatch-runtime` over older broad helper barrels when both expose the needed helper. -- Test performance guardrail: treat import-dominated test time as a boundary bug. Refactor the import surface before adding more cases to the slow file. -- Test performance guardrail: when replacing a slow integration test with helper-level coverage, extract the exact production composition into a named helper and test that helper. Do not trade coverage shape for speed without preserving the behavior proof somewhere cheaper. -- Test performance guardrail: for plugin-owned static descriptors used by core tests or cold paths, prefer lightweight public artifacts with full-runtime fallback over loading broad bundled plugin barrels. -- Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat. -- For targeted/local debugging, use the native root-project entrypoint: `pnpm test [vitest args...]` (for example `pnpm test src/commands/onboard-search.test.ts -t "shows registered plugin providers"`); do not default to raw `pnpm vitest run ...` because it bypasses the repo's default config/profile/pool routing. -- Do not set test workers above 16; tried already. -- Vitest now defaults to native root-project `threads`, with hard `forks` exceptions for `gateway`, `agents`, and `commands`. Keep new pool changes explicit and justified; use `OPENCLAW_VITEST_POOL=forks` for full local fork debugging. -- If local Vitest runs cause memory pressure, the default worker budget now derives from host capabilities (CPU, memory band, current load). For a conservative explicit override during land/gate runs, use `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`. -- Live tests (real keys): `OPENCLAW_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`. -- `pnpm test:live` defaults quiet now. Keep `[live]` progress; suppress profile/gateway chatter. Full logs: `OPENCLAW_LIVE_TEST_QUIET=0 pnpm test:live`. -- Full kit + what’s covered: `docs/help/testing.md`. -- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process). -- Changelog placement: in the active version block, append new entries to the end of the target section (`### Changes` or `### Fixes`); do not insert new entries at the top of a section. -- Changelog attribution: use at most one contributor mention per line; prefer `Thanks @author` and do not also add `by @author` on the same entry. -- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one. -- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available. +- Update docs when behavior/API changes. Use docs list/read_when hints. +- Docs links: see `docs/AGENTS.md`. +- Changelog: user-facing only. Pure test/internal changes usually no entry. +- Changelog placement: append to active version `### Changes`/`### Fixes`; at most one contributor mention, prefer `Thanks @user`. -## Commit & Pull Request Guidelines +## Git -- Use `$openclaw-pr-maintainer` at `.agents/skills/openclaw-pr-maintainer/SKILL.md` for maintainer PR triage, review, close, search, and landing workflows. -- This includes auto-close labels, bug-fix evidence gates, GitHub comment/search footguns, and maintainer PR decision flow. -- For the repo's end-to-end maintainer PR workflow, use `$openclaw-pr-maintainer` at `.agents/skills/openclaw-pr-maintainer/SKILL.md`. +- Use `scripts/committer "" `; stage only intended files. +- Commits: conventional-ish, concise/action-oriented. Group related changes. +- No manual stash/autostash unless explicitly requested. No branch/worktree changes unless requested. +- No merge commits on `main`; rebase on latest `origin/main` before push. +- User says "commit": commit your changes only. "commit all": commit everything in grouped chunks. "push": may `git pull --rebase` first. +- Do not delete/rename unexpected files; ask if it blocks. Otherwise ignore unrelated WIP. +- If bulk PR close/reopen affects >5 PRs, ask with exact count/scope. +- PR/issue workflows: use `$openclaw-pr-maintainer`. +- `/landpr`: use `~/.codex/prompts/landpr.md`. -- `/landpr` lives in the global Codex prompts (`~/.codex/prompts/landpr.md`); when landing or merging any PR, always follow that `/landpr` process. -- Create commits with `scripts/committer "" `; avoid manual `git add`/`git commit` so staging stays scoped. -- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`). -- Group related changes; avoid bundling unrelated refactors. -- PR submission template (canonical): `.github/pull_request_template.md` -- Issue submission templates (canonical): `.github/ISSUE_TEMPLATE/` +## Security / Release -## Git Notes +- Never commit real phone numbers, videos, credentials, live config. +- Secrets: channel/provider credentials under `~/.openclaw/credentials/`; model auth profiles under `~/.openclaw/agents//agent/auth-profiles.json`. +- Env keys: check `~/.profile`. +- Dependency patches/overrides/vendor changes require explicit approval. `pnpm.patchedDependencies` must use exact versions. +- Carbon pins owner-only: do not change `@buape/carbon` versions unless Shadow (`@thewilloftheshadow`, verified by `gh`) asks. +- Releases/publish/version bumps require explicit approval. +- Release docs: `docs/reference/RELEASING.md`; use `$openclaw-release-maintainer`. +- GHSA/advisories: use `$openclaw-ghsa-maintainer`. +- Beta tag/version must match, e.g. `vYYYY.M.D-beta.N` => npm `YYYY.M.D-beta.N --tag beta`. -- If `git branch -d/-D ` is policy-blocked, delete the local ref directly: `git update-ref -d refs/heads/`. -- Agents MUST NOT create or push merge commits on `main`. If `main` has advanced, rebase local commits onto the latest `origin/main` before pushing. -- Bulk PR close/reopen safety: if a close action would affect more than 5 PRs, first ask for explicit user confirmation with the exact PR count and target scope/query. +## Apps / Platform -## Security & Configuration Tips +- Before simulator/emulator testing, check connected real iOS/Android devices first. +- "restart iOS/Android apps" = rebuild/reinstall/relaunch, not kill/launch. +- SwiftUI: prefer Observation (`@Observable`, `@Bindable`) over new `ObservableObject`. +- mac gateway: use app or `openclaw gateway restart/status --deep`; avoid ad-hoc tmux gateway sessions. Rebuild mac app locally, not over SSH. +- mac logs: `./scripts/clawlog.sh`. +- Version bump touches: `package.json`, `apps/android/app/build.gradle.kts`, `apps/ios/version.json` then `pnpm ios:version:sync`, `apps/macos/.../Info.plist`, `docs/install/updating.md`. Appcast only for Sparkle release. +- iOS Team ID: `security find-identity -p codesigning -v`; fallback `defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers`. +- Mobile LAN pairing: plaintext `ws://` is loopback-only by default. Trusted private-network `ws://` needs `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1`; Tailscale/public use `wss://` or a tunnel. +- A2UI hash `src/canvas-host/a2ui/.bundle.hash`: generated; ignore unless running `pnpm canvas:a2ui:bundle`; commit separately. -- Channel/provider state lives under `~/.openclaw/credentials/`; rerun `openclaw channels login` if logged out. Model auth profiles live under `~/.openclaw/agents//agent/auth-profiles.json`; legacy OAuth import still reads `~/.openclaw/credentials/oauth.json`. -- Pi sessions live under `~/.openclaw/agents//sessions/` by default; `session.store` can override the session store path. -- Environment variables: see `~/.profile`. -- Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples. -- Release flow: use the private [maintainer release docs](https://github.com/openclaw/maintainers/blob/main/release/README.md) for the actual runbook, `docs/reference/RELEASING.md` for the public release policy, and `$openclaw-release-maintainer` for the maintainership workflow. +## External Ops -## Local Runtime / Platform Notes +- Remote install docs: `docs/install/exe-dev.md`, `docs/install/fly.md`, `docs/install/hetzner.md`. +- Parallels smoke: `$openclaw-parallels-smoke`; Discord roundtrip: `parallels-discord-roundtrip`. -- Vocabulary: "makeup" = "mac app". -- Rebrand/migration issues or legacy config/service warnings: run `openclaw doctor` (see `docs/gateway/doctor.md`). -- Use `$openclaw-parallels-smoke` at `.agents/skills/openclaw-parallels-smoke/SKILL.md` for Parallels smoke, rerun, upgrade, debug, and result-interpretation workflows across macOS, Windows, and Linux guests. -- For the macOS Discord roundtrip deep dive, use the narrower `.agents/skills/parallels-discord-roundtrip/SKILL.md` companion skill. -- Never edit `node_modules` (global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go in `tools.md` or `AGENTS.md`. -- If you need local-only `.agents` ignores, use `.git/info/exclude` instead of repo `.gitignore`. -- When adding a new `AGENTS.md` anywhere in the repo, also add a `CLAUDE.md` symlink pointing to it (example: `ln -s AGENTS.md CLAUDE.md`). -- Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/openclaw && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`. -- CLI progress: use `src/cli/progress.ts` (`osc-progress` + `@clack/prompts` spinner); don’t hand-roll spinners/bars. -- Status output: keep tables + ANSI-safe wrapping (`src/terminal/table.ts`); `status --all` = read-only/pasteable, `status --deep` = probes. -- Gateway may run as an app-managed launchd job. Restart the gateway via the app or `openclaw gateway restart`; inspect with `openclaw gateway status --deep` or, for the default profile, `launchctl print gui/$UID/ai.openclaw.gateway`. Use `scripts/restart-mac.sh` when you need to rebuild/relaunch the local macOS app itself. The app LaunchAgent uses `ai.openclaw.mac`. **When debugging on macOS, start/stop the gateway via the app or gateway CLI, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.** -- macOS logs: use `./scripts/clawlog.sh` to query unified logs for the OpenClaw subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`. -- If shared guardrails are available locally, review them; otherwise follow this repo's guidance. -- SwiftUI state management (iOS/macOS): prefer the `Observation` framework (`@Observable`, `@Bindable`) over `ObservableObject`/`@StateObject`; don’t introduce new `ObservableObject` unless required for compatibility, and migrate existing usages when touching related code. -- Connection providers: when adding a new connection, update every UI surface and docs (macOS app, web UI, mobile if applicable, onboarding/overview docs) and add matching status + configuration forms so provider lists and settings stay in sync. -- Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/version.json` (source for generated iOS config and Fastlane metadata), `apps/macos/Sources/OpenClaw/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), and `docs/install/updating.md` (pinned npm version). -- "Bump version everywhere" means all version locations above, then run `pnpm ios:version:sync` for iOS generated outputs. Only touch appcast metadata when cutting a new macOS Sparkle release. -- **Restart apps:** “restart iOS/Android apps” means rebuild (recompile/install) and relaunch, not just kill/launch. -- **Device checks:** before testing, verify connected real devices (iOS/Android) before reaching for simulators/emulators. -- Mobile pairing: `ws://` (cleartext) is allowed for private LAN addresses (RFC 1918, link-local, mDNS `.local`) and loopback. Private LAN hosts typically lack PKI-backed identity, so requiring TLS there adds complexity without meaningful security gain. `wss://` is required for Tailscale and public endpoints. -- Security report scope: reports that treat cleartext `ws://` mobile pairing over private LAN as a vulnerability are out of scope unless they demonstrate a trust-boundary bypass beyond passive network observation on the same LAN. -- iOS Team ID lookup: `security find-identity -p codesigning -v` → use Apple Development (…) TEAMID. Fallback: `defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers`. -- A2UI bundle hash: `src/canvas-host/a2ui/.bundle.hash` is auto-generated; ignore unexpected changes, and only regenerate via `pnpm canvas:a2ui:bundle` (or `scripts/bundle-a2ui.sh`) when needed. Commit the hash as a separate commit. -- Release signing/notary credentials are managed outside the repo; maintainers keep that setup in the private [maintainer release docs](https://github.com/openclaw/maintainers/tree/main/release). -- Lobster palette: use the shared CLI palette in `src/terminal/palette.ts` (no hardcoded colors); apply palette to onboarding/config prompts and other TTY UI output as needed. -- When asked to open a “session” file, open the Pi session logs under `~/.openclaw/agents//sessions/*.jsonl` (use the `agent=` value in the Runtime line of the system prompt; newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there. -- Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac. -- Voice wake forwarding tips: - - Command template should stay `openclaw-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Don’t add extra quotes. - - launchd PATH is minimal; ensure the app’s launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`openclaw` binaries resolve when invoked via `openclaw-mac`. +## Misc Footguns -## Collaboration / Safety Notes - -- When working on a GitHub Issue or PR, print the full URL at the end of the task. -- When answering questions, respond with high-confidence answers only: verify in code; do not guess. -- Carbon version edits are owner-only: do not change `@buape/carbon` version pins unless you are Shadow (@thewilloftheshadow) as verified by gh. -- Any dependency with `pnpm.patchedDependencies` must use an exact version (no `^`/`~`). -- Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default. -- **Multi-agent safety:** do **not** create/apply/drop `git stash` entries unless explicitly requested (this includes `git pull --rebase --autostash`). Assume other agents may be working; keep unrelated WIP untouched and avoid cross-cutting state changes. -- **Multi-agent safety:** when the user says "push", you may `git pull --rebase` to integrate latest changes (never discard other agents' work). When the user says "commit", scope to your changes only. When the user says "commit all", commit everything in grouped chunks. -- **Multi-agent safety:** prefer grouped `commit` / `pull --rebase` / `push` cycles for related work instead of many tiny syncs. -- **Multi-agent safety:** do **not** create/remove/modify `git worktree` checkouts (or edit `.worktrees/*`) unless explicitly requested. -- **Multi-agent safety:** do **not** switch branches / check out a different branch unless explicitly requested. -- **Multi-agent safety:** running multiple agents is OK as long as each agent has its own session. -- **Multi-agent safety:** when you see unrecognized files, keep going; focus on your changes and commit only those. -- Lint/format churn: - - If staged+unstaged diffs are formatting-only, auto-resolve without asking. - - If commit/push already requested, auto-stage and include formatting-only follow-ups in the same commit (or a tiny follow-up commit if needed), no extra confirmation. - - Only ask when changes are semantic (logic/data/behavior). -- **Multi-agent safety:** focus reports on your edits; avoid guard-rail disclaimers unless truly blocked; when multiple agents touch the same file, continue if safe; end with a brief “other files present” note only if relevant. -- Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause. -- Code style: add brief comments for tricky logic; keep files under ~700 LOC when feasible (split/refactor as needed). -- Tool schema guardrails (google-antigravity): avoid `Type.Union` in tool input schemas; no `anyOf`/`oneOf`/`allOf`. Use `stringEnum`/`optionalStringEnum` (Type.Unsafe enum) for string lists, and `Type.Optional(...)` instead of `... | null`. Keep top-level tool schema as `type: "object"` with `properties`. -- Tool schema guardrails: avoid raw `format` property names in tool schemas; some validators treat `format` as a reserved keyword and reject the schema. -- Never send streaming/partial replies to external messaging surfaces (WhatsApp, Telegram); only final replies should be delivered there. Streaming/tool events may still go to internal UIs/control channel. -- For manual `openclaw message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tool’s escaping. -- Release guardrails: do not change version numbers without operator’s explicit consent; always ask permission before running any npm publish/release step. -- Beta release guardrail: when using a beta Git tag (for example `vYYYY.M.D-beta.N`), publish npm with a matching beta version suffix (for example `YYYY.M.D-beta.N`) rather than a plain version on `--tag beta`; otherwise the plain version name gets consumed/blocked. +- Rebrand/migration/config warnings: run `openclaw doctor`. +- Never edit `node_modules`. +- Local-only `.agents` ignores: use `.git/info/exclude`, not repo `.gitignore`. +- CLI progress: use `src/cli/progress.ts`; status tables: `src/terminal/table.ts`. +- Connection/provider additions: update all UI surfaces + docs + status/config forms. +- Provider-facing tool schemas: prefer flat string enum helpers over `Type.Union([Type.Literal(...)])`; some providers reject generated `anyOf`. Do not treat this as a repo-wide protocol/schema ban. +- External messaging surfaces: no token-delta channel messages. Follow `docs/concepts/streaming.md`; preview/block streaming uses message edits/chunks and must preserve final/fallback delivery.