Openclaw · EN · Part 4

Ch 14 Contributing To OpenClaw

14-ch-14-Contributing-to-OpenClaw.md

第14章 Contributing to OpenClaw

The OpenClaw project didn't start as a polished product with thousands of tests and twenty messaging channels. It began as a personal playground—a learning experiment that evolved through names like Warelay, Clawdbot, and Moltbot before becoming OpenClaw. Today, it's maintained by a community that values thoughtful contributions over raw PR volume. If you're reading this, you're already part of that community.

OpenClaw runs on your devices, in your channels, with your rules. That local-first philosophy extends to how we build software: clear boundaries, explicit security decisions, and code that anyone can read, modify, and extend. This chapter guides you through contributing effectively—whether you're fixing a typo, adding a channel, or diving into the agent runtime.

Contribution Workflow

The path from "I want to help" to "merged" follows a familiar GitHub flow, but OpenClaw has specific expectations that make reviews smoother for everyone. Let's walk through it.

Rendering diagram...

Forking and Branching

Start by forking https://github.com/openclaw/openclaw to your GitHub account. Clone your fork locally:

git clone https://github.com/YOUR_USERNAME/openclaw.git
cd openclaw
git remote add upstream https://github.com/openclaw/openclaw.git

Create a branch for your work. Branch names should describe what you're doing:

# Good
git checkout -b fix/telegram-reply-race
git checkout -b add/matrix-channel-provider
git checkout -b docs/update-contributing-guide

# Less helpful
git checkout -b stuff
git checkout -b fix2

Keep your branch synced with upstream while you work:

git fetch upstream
git rebase upstream/main

Committing Changes

OpenClaw follows a specific commit message convention. The project uses a committer script to enforce consistent formatting:

# Use the project's committer script
scripts/committer "CLI: add verbose flag to send" src/cli/commands/send.ts

If you commit manually, follow this pattern:

<area>: <summary>

Optional body explaining why.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

Area prefixes include: CLI, Gateway, Agents, Channels, Plugins, Config, Docs, Tests, Infra.

🤿 深入底层:The committer script runs git status, git diff, and git log in parallel to analyze changes before staging files. It ensures you don't accidentally commit credentials, large binaries, or unrelated changes—a safety net that's caught dozens of would-be mistakes.

PR Creation

When your changes are ready and tests pass, push to your fork:

git push origin fix/telegram-reply-race

Create a pull request on GitHub. OpenClaw provides a PR template that guides you through providing the right context. A well-structured PR description looks like this:

## Summary

- Problem: Telegram replies were arriving out of order when multiple messages
  were sent within the same second due to race condition in reply dispatch
- Why it matters: Users received confusing, out-of-context responses that
  broke conversation flow
- What changed: Added sequence ID tracking and FIFO queue for pending replies
- What did NOT change: No changes to inbound message handling or other channels

## Change Type

- [x] Bug fix
- [ ] Feature

## Root Cause

- Root cause: The Telegram send handler didn't account for network latency
  variation when dispatching multiple replies rapidly
- Missing detection: No integration test covered rapid sequential sends
- Contributing context: Issue #47231 reported by user in production

## Regression Test Plan

- Coverage level that should have caught this:
  - [x] Seam / integration test
- Target test: src/channels/telegram/send.test.ts
- Scenario: Send three replies within 100ms, verify order preserved
- Why smallest reliable: Unit test would mock away the actual race condition

Code Review Process

Every PR goes through review before merging. OpenClaw uses a CODEOWNERS file that automatically requests review from maintainers based on what files you changed:

# Security-sensitive code requires secops review
/src/security/ @openclaw/secops
/src/gateway/*auth*.ts @openclaw/secops

# Release workflow requires release manager approval
/.github/workflows/openclaw-npm-release.yml @openclaw/openclaw-release-managers

This means if your PR touches authentication code, the security team must approve. If it changes the release workflow, release managers sign off.

⚠️ 避坑指南:CODEOWNERS uses last-match-wins semantics. If you add overlapping rules below the secops block, include @openclaw/secops on those entries too or you'll silently remove required security review. Several contributors have accidentally done this—the file even has a warning comment about it.

Coding Standards

OpenClaw's codebase follows specific patterns that evolved from supporting twenty channels, five thousand source files, and thirty-five hundred tests. These aren't arbitrary rules—they're lessons learned from production debugging at scale.

TypeScript Patterns

The project uses strict TypeScript. any is avoided; prefer real types, unknown, or narrow adapter helpers:

// Avoid
function parseConfig(data: any): Config {
  return data.config;
}

// Prefer
function parseConfig(data: unknown): Result<Config, ParseError> {
  if (!isRecord(data)) return err(new ParseError("expected object"));
  // ...
}

At external boundaries (config, webhooks, APIs), use Zod or existing schema helpers:

import { z } from 'zod';

const WebhookPayloadSchema = z.object({
  eventId: z.string(),
  timestamp: z.number(),
  payload: z.unknown(),
});

function handleWebhook(raw: unknown) {
  const result = WebhookPayloadSchema.safeParse(raw);
  if (!result.success) {
    throw new WebhookError(`invalid payload: ${result.error}`);
  }
  // TypeScript knows result.data is correctly typed
}

Prefer discriminated unions when parameter shape changes runtime behavior:

// Good: TypeScript forces exhaustive handling
type AgentCommand = 
  | { type: 'wake', agentId: string }
  | { type: 'sleep', agentId: string, duration: number };

function execute(cmd: AgentCommand) {
  switch (cmd.type) {
    case 'wake': return wakeAgent(cmd.agentId);
    case 'sleep': return sleepAgent(cmd.agentId, cmd.duration);
    // TypeScript error if you miss a case
  }
}

Naming Conventions

Product/docs headings use OpenClaw; CLI commands, packages, and paths use openclaw. Written English follows American spelling: "color" not "colour", "behavior" not "behaviour".

Plugin naming keeps IDs aligned across the manifest, workspace folder, and package names. For repo-owned workspace plugins:

  • openclaw.plugin.json:id = plugin ID
  • Default workspace folder name = plugin ID
  • Package name = @openclaw/<id> or approved suffix like -provider, -plugin

☕ 茶水间时间:This alignment isn't pedantry—it's what lets the plugin system discover, validate, and install plugins without runtime loading. When IDs drift across these three locations, you get "plugin not found" errors that are frustrating to debug. The repo even has an invariant test that enforces this rule for bundled plugins.

Guardrails and Rules

Some patterns are explicitly forbidden because they've caused real bugs:

Never use ?? 0, empty-string, or magic-string sentinels when they can change runtime meaning silently:

// Dangerous: 0 might be a valid count
const count = response.count ?? 0;
if (count > 0) { /* ... */ }  // Is 0 meaningful or missing?

// Better: explicit union
type CountResult = { available: true, count: number } | { available: false };

Never mix static and dynamic imports for the same module:

// Forbidden: creates split code paths
import { helper } from './utils.js';  // somewhere in the file...
const late = await import('./utils.js');  // ...and here

// Correct: create a runtime boundary
// utils.runtime.ts
export * from './utils.js';

// caller.ts
const utils = await import('./utils.runtime.js');

Never share class behavior via prototype mutation:

// Forbidden: TypeScript can't track this
function applyPrototypeMixins(target: any, ...sources: any[]) {
  sources.forEach(source => {
    Object.getOwnPropertyNames(source.prototype).forEach(name => {
      target.prototype[name] = source.prototype[name];
    });
  });
}

// Correct: explicit inheritance or composition
class BaseMessenger { /* ... */ }
class TelegramMessenger extends BaseMessenger { /* ... */ }

Code Quality Expectations

Files should stay under ~700 LOC when feasible. Splitting isn't a hard rule, but longer files become harder to test and review. When you add brief comments, focus on tricky or non-obvious logic—don't document what the code already says.

The project uses Oxlint for linting and Oxfmt for formatting. These replace ESLint/Prettier but enforce similar consistency. Pre-commit hooks run pnpm check (typecheck + lint + format). For faster commit loops during development:

FAST_COMMIT=1 git commit ...

This skips the repo-wide pnpm format and pnpm check in the hook only—use it when you're covering the touched surface through other verification.

Code Review Process

Code review in OpenClaw isn't about gatekeeping—it's about maintaining quality across a distributed codebase with many contributors. Understanding how review works helps you navigate it smoothly.

Review Criteria

Maintainers evaluate PRs against several dimensions:

Correctness: Does the code do what it claims? Bug fixes must explain the root cause, not just the symptom. Features must have clear use cases.

Boundaries: Does the change respect architecture boundaries? Extensions must cross into core only through openclaw/plugin-sdk/*. Core code must not deep-import plugin internals.

Testing: Does the change include appropriate tests? For bug fixes, there should be a regression test. For features, unit or integration tests prove the behavior.

Docs: Do user-facing changes get documented? Config changes need schema updates. New features need docs. Pure test additions usually don't need changelog entries.

Security: Does the change touch security-sensitive surfaces? Auth, secrets, sandboxing, and command execution get extra scrutiny.

Common Feedback

You'll see certain patterns in review comments:

"Break this into smaller PRs" - OpenClaw prefers focused changes. PRs over 5,000 lines are reviewed only in exceptional circumstances. One PR = one issue/topic.

"Add a regression test" - Bug fixes should lock in the fix with a test. The PR template even has a section for this.

"Update the public contract" - If you change config schema or the Plugin SDK, regenerate the baselines. The project has drift detection for exactly this.

"Use the SDK boundary" - If your core code reaches into plugin internals, the reviewer will ask you to expose a helper through the public SDK instead.

Responding to Reviews

When you receive feedback:

  1. Address each point - Don't leave comments hanging
  2. Explain if you disagree - Good maintainers are open to being wrong
  3. Push updates - New commits trigger re-review automatically
  4. Resolve bot conversations - If a bot review is fixed, resolve that conversation yourself. Don't leave cleanup for maintainers.

The PR workflow emphasizes conversation over rigid process. If something is unclear, ask. The Discord community and GitHub Issues are both good places for questions.

🤿 深入底层:The project includes a $openclaw-pr-maintainer skill that maintainers use for PR triage, review, and landing workflows. This includes auto-close labels, bug-fix evidence gates, and GitHub comment/search footnotes that have caught many would-be mistakes. The skill essentially encodes maintainer judgment as a checklist—useful to understand what maintainers are looking for.

Maintainer Workflow

When maintainers review your PR, they're checking it against a mental (and sometimes literal) checklist:

  • Does the PR template have proper context?
  • Is there evidence the fix works?
  • Are tests affected appropriately?
  • Did the contributor run pnpm check locally?
  • Does security impact need special review?
  • Are CODEOWNERS requirements met?

For bug fixes, maintainers look for evidence—logs, screenshots, or before/after test output. "Trust but verify" means maintainers trust you're reporting a real bug, but they need to see proof the fix actually addresses it.

Getting Help and Staying Updated

OpenClaw has a community that welcomes questions and helps contributors get unstuck. Knowing where to ask saves time for everyone.

Discord Community

The Discord server is the best place for real-time conversation:

  • Questions about how something works: Ask in #help or #dev
  • Design discussions: Use #dev before writing major code
  • Show your work: #show-and-tell is for sharing what you built

The community includes maintainers, plugin authors, and users. Someone has probably hit the same problem you're facing.

GitHub Issues

Before creating an issue, search existing ones. OpenClaw has thousands of issues covering:

  • Bug reports: Include reproduction steps, environment, and logs
  • Feature requests: Explain the use case, not just the proposed solution
  • Documentation gaps: Point to what's unclear or missing

Well-written issues get faster responses. Include:

## Environment
- OS: macOS 15.2
- Node version: v22.14.0
- OpenClaw version: 2026.4.4

## Steps to Reproduce
1. Run `openclaw gateway run`
2. Send message via Telegram
3. Gateway crashes

## Actual Behavior
Error: Cannot read property 'userId' of undefined

## Expected Behavior
Message should be processed normally

VISION Document

The VISION.md file at the repo root explains project direction and priorities:

  • Current focus: security, bug fixes, setup reliability
  • Next priorities: model providers, channel support, performance
  • What won't be merged: skills that can live on ClawHub, heavy orchestration layers

Reading this before proposing features saves everyone time. If your idea aligns with stated priorities, it's more likely to be accepted. If it conflicts with "what we will not merge," consider whether there's a different approach.

Asking Questions

Good questions get good answers. Include:

  • What you're trying to do: "I want to add a new channel..."
  • What you've tried: "I looked at the Telegram plugin and..."
  • Where you're stuck: "I don't understand how the pairing flow..."

Bad questions are vague: "It doesn't work" or "How do I contribute?" give responders nothing to work with.

Staying Informed

To follow OpenClaw development:

  • Watch the repo on GitHub - See commits, PRs, and releases
  • Join Discord - Real-time discussion and announcements
  • Read CHANGELOG.md - User-facing changes in each version
  • Check docs.openclaw.ai - Updated documentation

The project moves fast—iteration is quick by design. What's true today might change next month. Staying plugged in helps you contribute effectively.


You've now seen the full contribution lifecycle from finding an issue to landing code in main. The workflow might seem involved, but each step exists for a reason: someone once shipped a bug that step would have caught.

OpenClaw runs on your devices, in your channels, with your rules. That philosophy extends to how we build it—transparently, with clear boundaries, and as a community. Your contributions, whether documentation fixes, bug reports, or major features, make OpenClaw better for everyone.

Chapter Exercises

  1. Create a fork and submit a documentation PR - Find a typo or unclear section in the docs, fix it, and open your first PR following the template.

  2. Review an existing PR using the criteria - Browse open PRs at github.com/openclaw/openclaw/pulls and evaluate one against the review criteria in this chapter. What feedback would you give?

  3. Write a commit following the conventions - Take a hypothetical change (e.g., "fix memory leak in agent runtime") and write a proper commit message with the scripts/committer format.