Files
upstream-zulip/AGENTS.md
upstream-mirror 64898b2c79
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
2026-09-15 15:05:30 +05:00

15 KiB

AGENTS.md - Guidelines for AI Contributions to Zulip

This file provides guidance to AI coding agents for contributing to the Zulip codebase. These guidelines are designed to produce contributions that meet the same high standards we expect from human contributors.

When doing any work with AI assistants, it is crucial to follow our AI use policy and guidelines (the "AI use policy and guidelines" section in CONTRIBUTING.md). In particular:

Do not submit an AI-generated PR you haven't personally understood and tested, as this wastes maintainers' time. PRs that appear to violate this guideline will be closed without review.

Do not post AI-generated messages in the Zulip development community -- we want to read your own genuine expression of your thoughts.

Claude Code automatically uses .claude/rules/ and .claude/skills/. If you are a different agent, read the files in these folders to see the rest of the agent instructions and when they're relevant. Rules are relevant for files listed in paths: at the top of the rule file. Skills explain when they're relevant in their description.

Philosophy

Zulip is a team chat application used by thousands of organizations, built to last for many years. It is developed by a vibrant open-source community, with maintainers who have consistently emphasized high standards for codebase readability, code review, commit discipline, debuggability, automated testing, tooling, documentation, and all the other subtle details that together determine whether software is easy to understand, operate, and modify.

Zulip's engineering strategy is to "move quickly without breaking things". This is possible because the project has invested years in testing, tooling, code structure, documentation, and development practices that catch bugs systematically rather than relying on individual vigilance. Maintainers spend most of their review time on product decisions and code structure/readability, not on chasing correctness issues — because the process is designed to prevent them.

This means Zulip's coding philosophy is to focus relentlessly on making the codebase easy to understand and difficult to make dangerous mistakes. This applies equally to AI-generated contributions. Every change should make the codebase more maintainable and easier to read.

No detail is too small

There is no category of "minor issue" that is acceptable to ship — if something is broken in any state, size, theme, or language where a user would encounter it, it must be fixed before merging. If a fix would require a design decision, raise it as a question rather than shipping the broken state. See .claude/rules/ui-testing.md for what to test for UI changes (the file loads automatically when you work on frontend files).

The right attitude is: "What could go wrong, and how do I verify that it doesn't?" not "It looks fine to me." What isn't tested probably doesn't work — this applies to visual changes just as much as to backend logic.

Understand before coding

Before writing any code, you must understand:

  1. What the existing code does and why, including the relevant help center or developer-facing documentation.
  2. What problem you're solving, in its full scope.
  3. Why your approach is the right solution, and available alternatives.
  4. How you will verify that your work is correct, and avoid regressions that are plausible for the type of work you're doing.

The answer to "Why is X an improvement?" should never be "I'm not sure."

Workflow

Follow this workflow for every task: understand → propose → implement → verify.

1. Understand Before Coding

Before making any changes:

# Read relevant documentation
cat docs/*/<relevant-area>.md
cat starlight_help/src/content/docs/<topic>.md
cat api_docs/<topic>.md and read the relevant part of zerver/openapi/zulip.yaml

# Look at existing code patterns
git grep "similar_function_name"
git log --oneline -20 -- path/to/file.py

# Check for related issues on GitHub

2. Propose an Approach

Before writing code, explain the plan:

  • Explain your understanding of the problem and all relevant design decisions
  • What changes are needed and why
  • How the changes fit with existing patterns
  • What could break and how to prevent regressions

3. Implement in Minimal, Coherent Commits

Structure changes as clean commits:

  • Backend and API changes, with tests and API doc changes documented fully using our double-entry changelog system. Instructions for documentation can be found in .claude/rules/api-changelog.md (loaded automatically when you edit zerver/openapi/zulip.yaml).
  • Frontend UI changes (with tests and user-facing documentation updates). Remember to plan to use your visual test skill (.claude/skills/visual-test/SKILL.md) to check your work whenever you change web app code (HTML, CSS, JS).

Each commit should be self-contained, highly readable and reviewable using git show --color-moved, and pass lint/tests independently. If extracting new files or moving code, always do that in a separate commit from other changes.

4. Verify Before Finalizing

Run the linter and the relevant tests before making each commit; see "Testing Requirements" below for instructions.

Run through the /self-review skill's checklist before suggesting opening a PR (.claude/skills/self-review/SKILL.md).

Before You Start

Read the Relevant Documentation

Zulip has over 185,000 words of developer documentation. Before working on any area:

  • Read documentation from docs/, starlight_help/src/content/docs/, and api_docs/. docs/subsystems/directory-structure.md explains where code lives.
  • Read existing code in the area you're modifying.
  • Use git grep to find similar patterns in the codebase and read those.

Understand the Code Style

  • Be consistent with existing code. Look at surrounding code and follow the same patterns, as this is a thoughtfully crafted codebase.
  • Use clear, greppable names for functions, arguments, variables, and tests. Future developers will git grep for relevant terms when researching a problem, so names should communicate purpose clearly.
  • Keep everything well factored for maintainability. Avoid duplicating code, especially where access control or subtle correctness is involved.
  • Run ./tools/lint to catch style issues before committing, including mypy issues.
  • Prefer writing code that is readable without explanation over heavily commented code using clever tricks.
  • Don't reference line numbers (e.g., filter.ts:493) in comments, commit messages, or PR descriptions; they get stale when files are edited. Reference symbol names instead.

Comments

  • Comments should explain "why" when the reason isn't obvious, not narrate "what" the code does.
  • Comments should have a line to themself except for CSS px math.
  • Comments should never describe the change being made or a bug being fixed. They should read as if they were there all along. Descriptions of the diff belong in the commit message instead.
  • Match comment density of nearby code and similar functions. If similar code doesn't comment on something, don't comment on it in new code.
  • Avoid metaphors in favor of clear descriptive language. Match existing idioms verbatim for comments describing specific situations (e.g., /* Override bootstrap defaults */) rather than writing a new or longer explanation.

Further rules

Frontend rules can be found in .claude/rules/frontend.md (loaded automatically when you work on frontend JS/TS and template files).

CSS rules can be found in .claude/rules/css.md (loaded automatically when you work on CSS files).

Python rules can be found in .claude/rules/python.md (loaded automatically when you work on .py files).

See: docs/contributing/code-style.md

Commit Discipline

Zulip follows the Git project's practice of "Each commit is a minimal coherent idea." This is non-negotiable.

Each Commit Must:

  1. Be coherent: Implement one logical change completely and atomically.
  2. Pass tests: Include test updates in the same commit as code changes.
  3. Not make Zulip worse: Work is ordered so no commit has regressions.
  4. Be safe to deploy individually: Or explain in detail why not.
  5. Be minimal and reviewable: Don't combine moving code with changing it in the same commit; make liberal use of small prep commits for no-op refactoring that are easy to verify.

Never:

  • Mix multiple separable changes in a single commit.
  • Create a commit that "fixes" a mistake from an earlier commit in the same PR; always edit Git to fix the original commit.
  • Add content in one commit only to remove or move it in the next; plan upfront what belongs where and do it right the first time.
  • Include debugging code, commented-out code, or temporary TODOs.
  • Leave commits that break if a later commit in the PR is dropped. When a commit is flagged as potentially droppable, verify all earlier commits work correctly without it.

Open Independently Useful Fixes as Separate PRs

If, while building a feature, you find and fix a pre-existing bug or make a refactor that would be worth merging even if the feature never lands, split this into its own PR. A small isolated PR gets more scrutiny than the same change as part of a large PR. Prep commits that mainly make sense in the context of the feature PR should stay in the feature's PR.

Commit Message Format

Use the /commit-message skill whenever you write or reword a commit message (.claude/skills/commit-message/SKILL.md).

Testing Requirements

Zulip server takes pride in its ~98% test coverage. All server changes must include nice tests that follow our testing philosophy.

When writing tests, follow our testing philosophy in .claude/rules/testing.md (loaded automatically when you work on test files).

Manage your time by running specific backend test collections, not the entire suite; the node suite is fast enough to run in full.

# Includes mypy and typescript checkers
./tools/lint path/to/changed/files.py
./tools/test-backend zerver.tests.test_relevant_module
./tools/test-js-with-node

Manual Testing for UI Changes

If a PR makes frontend changes, manually verify the affected UI using the checklist in .claude/rules/ui-testing.md (loaded automatically when you work on frontend files).

What Not To Do

  • Always check if you're working on top of the latest upstream/main, and fetch + rebase when starting a project so you're not using a stale branch. If you're continuing a project, start by rebasing, resolving merge conflicts carefully.
  • Don't make design or UX decisions silently. When a technical constraint forces a tradeoff, present the constraint and options to the user rather than picking one. Never remove features, hide UI elements, or change interaction patterns without asking.
  • Don't submit code you haven't tested
  • Don't skip becoming familiar with the code you're modifying
  • Don't make claims about code behavior without verification, and cite your sources.
  • Don't generate PR descriptions that just describe what files changed
  • Always do a pre-mortem: Think about how to avoid a bug recurring, how it might break something that already works, or imagine under what circumstances your changes might need to be reverted.

Pull Request Guidelines

When opening a pull request, prefix the PR title with [ai] (e.g., [ai] compose: Fix cursor position after emoji insertion.). Use upstream/main as the base branch. Use the /pr-description skill to write the PR description (.claude/skills/pr-description/SKILL.md).

When to Pause and Discuss

Recommend pausing for discussion when:

  • The approach involves security-sensitive code
  • Database migrations are needed (See docs/subsystems/schema-migrations.md).
  • The change alters behavior in several subsystems at once (renames and type annotation changes that touch many files do not count)
  • Performance implications are unclear
  • The feature design isn't fully specified
  • The API or data model design isn't fully specified
  • An API change may not be compatible (See docs/processes/api-design.md).
  • Existing tests are failing for unclear reasons

Task-Specific Approaches

For Bug Fixes

  1. Look at the relevant code and brainstorm theories for how the bug might be possible
  2. Provide a clear explanation for the bug, and ideally provide steps for reproducing the bug on main in the dev environment. Verify the cause of the bug before suggesting a fix, unless a bug is very difficult to verify, in which case say so and explain a hypothesis instead.
  3. Analyze and propose a fix with a clear explanation
  4. Write tests that would have caught this bug if possible
  5. Audit for whether the bug may exist elsewhere or might be re-introduced and propose appropriate changes to address if so.

For New Features

  1. Read the relevant documentation in docs/
  2. Show similar existing features in the codebase
  3. Propose an implementation approach before coding
  4. Implement, following "Commit Discipline" above

For Refactoring

  1. Show the current implementation
  2. Explain what makes it problematic
  3. Propose the refactoring approach
  4. Implement in commits that each leave the codebase working
  5. No behavior changes unless explicitly discussed
  6. Verify completeness: use git grep to find all occurrences and confirm nothing was missed
  • Contributing guide: docs/contributing/contributing.md
  • Code style: docs/contributing/code-style.md
  • Commit discipline: docs/contributing/commit-discipline.md
  • Testing overview: docs/testing/testing.md
  • Backend tests: docs/testing/testing-with-django.md
  • Code review: docs/contributing/code-reviewing.md
  • mypy guide: docs/testing/mypy.md

Help Center Documentation

When making any user-facing change, read docs/documentation/helpcenter.md in full and review the relevant help center articles under starlight_help/src/content/docs/ for any updates that should be made. The writing guide there is the source of truth for help center conventions, components, and structure.

When you encounter a Zulip narrow URL (e.g., from chat.zulip.org in a GitHub issue, PR, or user message), use the /fetch-zulip-messages skill (.claude/skills/fetch-zulip-messages/SKILL.md) to read the conversation. Do not use WebFetch — it cannot access Zulip message content.

Common Commands

./tools/provision           # Set up development environment
./tools/run-dev             # Start development server
./tools/lint                # Run all linters
./tools/test-backend        # Run Python tests
./tools/test-js-with-node   # Run JavaScript tests
./tools/run-mypy            # Run type checker
git grep "pattern"          # Search codebase (use extensively!)

Most of these commands take minutes, so run them in the background.

If a tool complains that provision is outdated, run ./tools/provision to fix it. Do not use --skip-provision-check to work around the error; the check exists because tests and linters depend on provisioned dependencies being current.

If the development environment runs in a container (Vagrant/Docker), run ./tools/ commands inside it. Files and new screenshots will be accessible on both sides.

vagrant ssh -c 'cd ~/zulip && ./tools/lint path/to/changed/files.py'

If vagrant ssh fails with a Docker daemon error, Docker Desktop isn't running; ask the user to start it.