An autonomous coding agent for Brave projects. Built on Claude Code, it reads Product Requirements Documents (PRDs), implements user stories, runs tests, creates PRs, handles review feedback, and manages CI — all autonomously in iterative loops.
Each clone of this repo targets a single project (configured via config.json). To use with multiple repositories, clone once per project with its own config.
The bot operates in an iterative loop with a state machine workflow:
- Reads PRD from
data/prd.json - Picks the next story using smart priority (reviewers first!):
- URGENT: Pushed PRs where reviewer responded (respond immediately)
- HIGH: Pushed PRs where bot responded (check for new reviews/merge)
- MEDIUM: Committed PRs (create PR)
- NORMAL: Start new development work
- Executes based on current status:
- pending: Implement, test, and commit →
committed - committed: Push branch and create PR →
pushed(lastActivityBy: "bot") - pushed: Check merge readiness FIRST, then:
- If mergeable: Merge →
merged - If reviewer commented: Implement feedback, push → (lastActivityBy: "bot")
- If waiting: Skip to next story (will check again next iteration)
- If mergeable: Merge →
- merged: Post-merge monitoring (CI breakage, flakes)
- pending: Implement, test, and commit →
- Updates progress in
data/progress.txt - Repeats until all stories have terminal status (
merged,skipped, orinvalid)
Story Lifecycle:
pending
↓
(implement + test)
↓
committed
↓
(push + create PR)
↓
pushed ◄─────────┐
╱ │ ╲ │
╱ │ ╲ │
merge wait review │
│ │ ↓ │
│ │ (implement + test)
│ │ ↓ │
│ │ (commit + push)
│ │ └───────┘
↓ │
merged └─► (check again next iteration)
↓
(post-merge monitoring)
Key Points:
- Initial development: pending → (implement+test) → committed
- Review response: pushed → (implement+test) → (commit+push) → pushed
- Same quality gates apply — ALL tests must pass whether initial development or responding to reviews
Anti-Stuck Guarantee: Every pushed PR is checked for merge readiness on EVERY iteration, even when lastActivityBy: "bot". Approved PRs never get stuck.
Smart Waiting: The bot tracks lastActivityBy to avoid spamming PRs while ensuring progress. See docs/workflow-state-machine.md for detailed flow.
- Claude Code CLI installed and configured
- GitHub CLI (
gh) authenticated with your account - jq for JSON parsing:
brew install jq(macOS) orsudo apt install jq(Debian/Ubuntu) - flock for run locking:
brew install flock(macOS — included by default on Linux) - Git configured with your credentials
- Python 3 (for utility scripts)
- signal-cli (optional) — for Signal message notifications. See Signal Notifications below
- Access to the target GitHub repositories
Clone alongside (or near) your target project's repository:
git clone git@github.com:brave-experiments/brave-dev-bot.git my-project-bot
cd my-project-botmake setupThe setup wizard handles everything interactively and is fully idempotent — safe to re-run at any time. It will:
- Create
config.json(if missing) — prompts for project name, GitHub org, PR/issue repositories, bot username/email, and issue labels. Ifconfig.jsonalready exists, shows current values and skips unless you explicitly choose to reconfigure. - Initialize submodules — pulls the best-practices submodule.
- Create data files (
prd.json,run-state.json,progress.txt) from templates if missing. Never overwrites existing files. - Generate org members cache (
.ignore/org-members.txt) via GitHub API if missing. - Configure git identity in the target repo if not already set (using the bot username/email from config).
- Install pre-commit hooks for both the target repo and bot repo.
For existing brave-core deployments: You can skip the wizard entirely by copying the reference config: cp config.brave-core.json config.json, then run make setup to complete the remaining steps.
Option A: Use the /prd skill (recommended)
claude
> /prd [describe your feature]Follow the prompts, then use /prd-json to convert to data/prd.json.
Option B: Manually edit data/prd.json
{
"stories": [
{
"id": "STORY-1",
"title": "Fix authentication test",
"priority": 1,
"status": "pending",
"branchName": null,
"prNumber": null,
"prUrl": null,
"lastActivityBy": null,
"acceptanceCriteria": [
"npm run test -- auth_tests"
]
}
]
}Status values: "pending" | "committed" | "pushed" | "merged" | "skipped" | "invalid"
lastActivityBy values: null (fresh) | "bot" (waiting for reviewer) | "reviewer" (bot should act)
run.sh can be run from any directory — it resolves all paths from its own location:
./run.sh # Default: 10 iterations
./run.sh 20 # Run 20 iterations
./run.sh 10 tui # TUI mode (interactive terminal UI)Skills (like /review-prs) are run interactively from the bot directory:
claude
> /review-prs 1d opentail -f data/progress.txtPress Ctrl+C. The bot will attempt to switch back to the master branch before exiting.
./scripts/reset-run-state.shResets data/run-state.json for a fresh run while preserving configuration flags.
Bot-only skills are available as slash commands in Claude Code. 8 bot-specific skills are included:
| Skill | Description |
|---|---|
/prd |
Generate structured PRDs with clarifying questions |
/prd-json |
Convert PRD markdown to prd.json format |
/prd-clean |
Archive merged/invalid stories to prd.archived.json |
/add-backlog-to-prd |
Fetch open issues from configured issue repo by label and add to PRD |
| Skill | Description |
|---|---|
/review-prs |
Batch review PRs for best practices violations. Supports auto mode for cron. Deduplicates against existing bot comments. Tracks prior comment context |
/learnable-pattern-search |
Analyze PR review comments to discover learnable patterns. Supports self-review mode to identify overly strict rules |
/update-best-practices |
Fetch and merge upstream Chromium documentation guidelines |
| Skill | Description |
|---|---|
/check-signal |
Check incoming Signal messages and execute commands |
14 additional developer-facing skills (commit, pr, review, preflight, etc.) are maintained in brave-core-tools. See that repo's README for the full list. These are included here as a git submodule for best-practices access but are not used by the bot directly.
data/run-state.json controls per-run behavior:
| Field | Description |
|---|---|
runId |
Timestamp when this run started (auto-set) |
storiesCheckedThisRun |
Story IDs already processed in this run |
skipPushedTasks |
Skip all "pushed" PRs, only work on new development |
enableMergeBackoff |
Enable post-merge monitoring (default: true) |
mergeBackoffStoryIds |
Array of specific merged story IDs to check, or null for all |
lastIterationHadStateChange |
Tracks if work was done last iteration |
Project-specific configuration (gitignored, created by make setup). Keys:
project.name: Project name (e.g.brave-core)project.org: GitHub organization (e.g.brave)project.prRepository: PR repository asowner/repo(e.g.brave/brave-core)project.issueRepository: Issue repository asowner/repo(e.g.brave/brave-browser)project.defaultBranch: Default branch for the PR repo (e.g.master)project.targetRepoPath: Path to the target git repo (relative to parent dir or absolute)bot.username: Bot's GitHub usernamebot.email: Bot's email for git commitsbot.claudeModel: Claude model to use (opus,sonnet, etc.)labels.prLabels: Labels applied to bot-created PRslabels.issueLabels: Labels used for backlog issue fetchingbestPractices.submodule: Name of the best-practices submodule
A config.example.json template and config.brave-core.json reference config are included.
Product Requirements Document defining user stories and acceptance criteria.
stories[].id: Unique story identifierstories[].priority: Execution order (1 = highest)stories[].status: Story statestories[].branchName: Git branch name (set when work starts)stories[].prNumber: PR number (set when PR created)stories[].prUrl: PR URL (set when PR created)stories[].lastActivityBy: Who acted last —"bot"|"reviewer"|nullstories[].acceptanceCriteria: Test commands that must pass
Log of completed iterations:
- Codebase Patterns section (reusable patterns discovered during development)
- Per-iteration entries with status transitions, files changed, test results
- Learnings for future iterations
Agent instructions defining workflow, testing requirements, git operations, security guidelines, and quality standards.
The bot enforces a comprehensive set of best practices organized by category:
- Architecture — Layering, dependency injection, factory patterns
- Coding Standards — Naming, CHECK vs DCHECK, IWYU, ownership, WeakPtr, base utilities
- Testing — Async patterns, JavaScript tests, navigation, test isolation
- Frontend — TypeScript/React, XSS prevention, component patterns
- Build System — BUILD.gn, buildflags, DEPS, GRD resources
- Chromium src/ Overrides — Override patterns, minimizing duplication
- Documentation — Inline comments, method docs
See BEST-PRACTICES.md for the full index and quick checklist (available via the brave-core-tools submodule).
Each user story gets its own branch, created automatically by the bot from data/prd.json.
Two hooks are installed by make setup:
Target repo hook (hooks/pre-commit):
Blocks the configured bot account from modifying dependency files (package.json, DEPS, Cargo.toml, go.mod, etc.). Prevents bots from introducing external dependencies without review.
Bot repo hook (hooks/pre-commit-bot-repo):
Blocks committing data/prd.json, data/progress.txt, and data/run-state.json to ensure user-specific files don't get committed.
feat: [Story ID] - [Story Title]
Commits must NOT include Co-Authored-By lines.
All acceptance criteria tests MUST run and pass before marking a story as complete. The bot will:
- Run ALL tests specified in acceptance criteria
- Use background execution for long-running tests
- Never skip tests regardless of duration
- Only mark a story complete when tests actually pass
Run the automated test suite to verify setup:
./tests/test-suite.shValidates GitHub API integration, file structure, filtering scripts, pre-commit hooks, and configuration files.
The bot filters all GitHub data (issues, PR reviews) through security scripts that check authors against Brave org membership:
./scripts/filter-issue-json.sh 12345 markdown # Filtered issue content
./scripts/filter-pr-reviews.sh 12345 # Filtered PR reviewsExternal user content is marked as [filtered] to prevent prompt injection attacks.
Org membership is cached in .ignore/org-members.txt (survives reboots, unlike /tmp). The cache is required for run.sh to start — regenerate with make setup if missing.
When running with an external GitHub account that can't see private org membership:
cd scripts
cp trusted-reviewers.txt.example trusted-reviewers.txt
# Add one trusted username per line- Dependency restrictions: Pre-commit hook prevents bot from updating dependencies
- Bot permissions: Uses
--dangerously-skip-permissionsfor autonomous operation - Review differentiation: Review skill rejects fixes that aren't materially different from previous failed attempts
See SECURITY.md for complete guidelines.
The bot can send real-time notifications via Signal when key events happen (PR created, review comments posted, CI retriggered, etc.). This is entirely optional — everything works without it.
brew install signal-cliOr download manually from the signal-cli releases page.
Requires Java 21+ (Homebrew handles this automatically).
You can register any phone number you own. Numbers must be in E.164 format (e.g., +14155551234).
Signal requires an SMS attempt before allowing voice verification. For a landline, the SMS won't arrive — that's expected.
# Step 1: Solve the captcha
# Go to https://signalcaptchas.org/registration/generate.html
# Solve the captcha, then RIGHT-CLICK "Open Signal" and copy the link.
# The link starts with signalcaptcha://
# Step 2: Request SMS verification with the captcha token (SMS won't arrive on landline — that's OK)
signal-cli -u +1YOURPHONENUMBER register --captcha "signalcaptcha://YOUR-CAPTCHA-TOKEN"
# Step 3: Wait at least 60 seconds for the SMS attempt to expire
# Step 4: Request voice verification (this will call your landline)
signal-cli -u +1YOURPHONENUMBER register --voice
# Step 5: Answer the call, note the 6-digit code, then verify
signal-cli -u +1YOURPHONENUMBER verify 123456Troubleshooting landline registration:
- Steps 2 and 4 must be separate commands. Do NOT pass
--voiceand--captchatogether — it will fail withInvalidTransportModeException. - If step 4 fails with "Before requesting voice verification you need to request SMS verification and wait a minute", the SMS attempt (step 2) didn't register properly. Get a fresh captcha token and redo step 2 — tokens expire quickly.
- The voice call may have a 3-5 second silence before the code is spoken. Use speakerphone and listen carefully. If no code is read, hang up and re-run step 4 to get a new call.
- If the voice call consistently has no audio, try a mobile number instead (see below).
# Step 1: Solve captcha (same URL as above), then register
signal-cli -u +1YOURMOBILENUMBER register --captcha "signalcaptcha://YOUR-CAPTCHA-TOKEN"
# Step 2: Wait for SMS with code, then verify
signal-cli -u +1YOURMOBILENUMBER verify 123456After registration, set a display name so the recipient knows who's messaging:
signal-cli -u +1YOURPHONENUMBER updateProfile --given-name "Brave Core" --family-name "Bot"Add these to your .envrc:
export SIGNAL_SENDER="+14155551234" # Your registered signal-cli number
export SIGNAL_RECIPIENT="+14155559876" # Number to receive notificationsThen reload: direnv allow
./scripts/signal-notify.sh "Hello from brave-dev-bot!"You should receive the message on the recipient's Signal account.
| Event | Example Message |
|---|---|
| PR created | PR created: #12345 - Fix auth test https://... |
| PR merged | PR merged: #12345 - Fix auth test https://... |
| Review feedback addressed | Review addressed: PR #12345 - removed unnecessary thread hop |
| PR review comments posted | Review complete: 5 PRs reviewed, 2 with violations, 4 comments posted |
| CI jobs retriggered | CI retriggered: PR #12345 - 3 jobs restarted (2 normal, 1 WIPE_WORKSPACE) |
| Learnable patterns found | Learnable patterns: analyzed 10 PRs, found 3 patterns, updated coding-standards.md |
| Best practice PR created | Best practice PR created: https://... - Adjust RunUntilIdle rule |
The notification script (scripts/signal-notify.sh) silently does nothing if:
SIGNAL_SENDERorSIGNAL_RECIPIENTenv vars are not setsignal-cliis not installed- The message send fails for any reason
No skill or workflow will error due to missing Signal configuration.
When the branch changes between runs, previous state is automatically archived:
archive/
└── 2026-01-30-old-branch/
├── data/prd.json
└── data/progress.txt
brave-dev-bot/ # (or your clone name)
├── README.md # This file
├── config.json # Project config (gitignored, created by setup)
├── config.example.json # Config template
├── brave-core-tools/ # Git submodule: best practices, dev skills, shared scripts
│ ├── BEST-PRACTICES.md # Index of all best practices
│ ├── SECURITY.md # Security guidelines
│ ├── .claude/skills/ # 14 developer-facing skills
│ ├── docs/best-practices/ # Best practices sub-documents
│ └── scripts/ # Shared utility scripts
├── LICENSE # MPL-2.0 license
├── Makefile # Dev commands: make test, lint, format, setup, schedules
├── run.sh # Main entry point (iterations, TUI mode)
├── data/
│ ├── prd.json # Product requirements (gitignored)
│ ├── prd.example.json # Example PRD template
│ ├── run-state.json # Run state tracking (gitignored)
│ ├── run-state.example.json # Example run state template
│ ├── progress.txt # Progress log (gitignored)
│ └── progress.example.txt # Example progress template
├── .ignore/
│ └── org-members.txt # Cached org members (security, gitignored)
├── .claude/
│ ├── CLAUDE.md # Claude agent instructions
│ └── skills/ # Bot-only Claude Code skills (8 skills)
│ ├── prd/ # PRD generation
│ ├── prd-json/ # PRD to JSON converter
│ ├── prd-clean/ # Archive merged/invalid stories
│ ├── add-backlog-to-prd/ # Fetch issues from GitHub
│ ├── review-prs/ # Batch PR review with caching
│ ├── check-signal/ # Check incoming Signal messages
│ ├── update-best-practices/ # Merge upstream Chromium guidelines
│ └── learnable-pattern-search/ # Analyze PR reviews for patterns
├── docs/ # Detailed workflow and reference docs
│ ├── workflow-state-machine.md
│ ├── workflow-pending.md
│ ├── workflow-committed.md
│ ├── workflow-pushed.md
│ ├── workflow-merged.md
│ ├── workflow-skipped-invalid.md
│ ├── testing-requirements.md
│ ├── git-repository.md
│ ├── progress-reporting.md
│ ├── run-state-management.md
│ └── learnable-patterns.md
├── hooks/
│ ├── pre-commit # Target repo hook (blocks dependency updates)
│ └── pre-commit-bot-repo # Bot repo hook (blocks config file commits)
├── scripts/
│ ├── fetch-issue.sh # Fetch filtered GitHub issues
│ ├── filter-issue-json.sh # Filter issues to org members
│ ├── filter-pr-reviews.sh # Filter PR reviews to org members
│ ├── signal-notify.sh # Signal notification helper (optional)
│ └── trusted-reviewers.txt.example # Allowlist template
├── logs/ # Bot run logs
└── tests/
├── test-suite.sh # Automated test suite
└── README.md # Test documentation
Run make setup again or manually configure git in your target repo with the username/email from config.json.
If the bot account is trying to modify dependencies — this is intentional. Use existing libraries or a different git account.
Run make setup to regenerate .ignore/org-members.txt, or manually:
gh api /orgs/<your-org>/members --paginate --jq '.[].login' > .ignore/org-members.txtExpected. The bot uses run_in_background: true and high timeouts (1-2 hours) for long operations.
The bot will automatically rebase and re-sync:
git fetch
git rebase origin/master
npm run sync -- --no-historyWhen adding new patterns or learnings, see docs/learnable-patterns.md for guidance on where each type of pattern belongs.
This project is licensed under the Mozilla Public License 2.0 (MPL-2.0). See the LICENSE file for details.
For issues or questions:
- Check
data/progress.txtfor detailed logs - Review
.claude/CLAUDE.mdfor agent behavior - Verify configuration with
make setup - Run
./tests/test-suite.shto validate setup