In the last few weeks, Claude has been doing a good chunk of our engineering work.
Like any new engineer, it needed some handholding to get stuff done on day one—but after we invested in onboarding, it can do much more with less guidance.
Claude’s onboarding doc is CLAUDE.md. Whenever Claude does something dumb, I update CLAUDE.md to help it avoid that mistake going forward. We check this file into source control so we can all share it.
Now I can get Claude started on a task way faster, and it’s less likely to go down dead-ends and more likely to succeed. We wait for it less, we correct it less often, and it costs less. Each line in CLAUDE.md is probably saving us hours.
Here’s our full CLAUDE.md. The rest of this post is a section-by-section breakdown.

1. Overview
We start simple, telling it the basics of what Chorus does, the tech stack we use, and the other parts of the system it might need to know about.
# Claude's Guide to Chorus Development
## What is Chorus?
Chorus is a native Mac app that lets you chat with all the AIs.
It lets you send one prompt and see responses from Claude, o3-pro, Gemini, etc. all at once.
It's built with Tauri, React, TypeScript, TanStack Query, and a local sqlite database.
Key features:
- MCP support
- Ambient chats (start a chat from anywhere)
- Projects
- Bring your own API keys
Most of the functionality lives in this repo. There's also a backend that handles accounts, billing, and proxying the models' requests; that lives at app.chorus.sh and is written in Elixir.
2. Your role
Here I describe how I want Claude to interact with me. This is to mitigate some of its common failure modes:
It makes unwarranted assumptions about how the app will run
It claims to have tested its changes, when it has no way to do so
I give it some documentation and it doesn’t bother to read the documentation (especially puzzling… you’d think this would be a no-brainer)
## Your role
Your role is to write code. You do NOT have access to the running app, so you cannot test the code. You MUST rely on me, the user, to test the code.
If I report a bug in your code, after you fix it, you should pause and ask me to verify that the bug is fixed.
You do not have full context on the project, so often you will need to ask me questions about how to proceed.
Don't be shy to ask questions -- I'm here to help you!
If I send you a URL, you MUST immediately fetch its contents and read it carefully, before you do anything else.
3. Workflow
To keep Claude’s work isolated, I tell it to create a new branch starting with claude/
and to tag any issues or PRs it creates with by-claude
.
Even still, it often commits and pushes to main
, which is incredibly annoying. I think it’s just forgetting which branch it’s on. I haven’t yet figured out a good way to prevent this. For now, the workaround is to manually set it up on a branch before letting it start.
We try to use GitHub as a source of truth for anything important:
I put context into a GitHub issue
I paste a link to the issue into Claude Code
Claude does some work and writes about what it did in a PR description
If I have follow-ups, I ask Claude to keep GitHub up to date
This means I don’t need to worry about losing track of Claude’s sessions. It’s always easy to start up a new one to continue work.
## Workflow
We use GitHub issues to track work we need to do, and PRs to review code. Whenever you create an issue or a PR, tag it with "by-claude". Use the `gh` bash command to interact with GitHub.
To start working on a feature, you should:
1. Setup
- Read the relevant GitHub issue (or create one if needed)
- Checkout main and pull the latest changes
- Create a new branch like `claude/feature-name`. NEVER commit to main. NEVER push to origin/main.
2. Development
- Commit often as you write code, so that we can revert if needed.
- When you have a draft of what you're working on, ask me to test it in the app to confirm that it works as you expect. Do this early and often.
3. Review
- When the work is done, verify that the diff looks good with `git diff main`
- While you should attempt to write code that adheres to our coding style, don't worry about manually linting or formatting your changes. There are Husky pre-commit Git hooks that will do this for you.
- Push the branch to GitHub
- Open a PR.
- The PR title should not include the issue number
- The PR description should start with the issue number and a brief description of the changes.
- Next, you should write a test plan. I (not you) will execute the test plan before merging the PR. If I can't check off any of the items, I will let you know. Make sure the test plan covers both new functionality and any EXISTING functionality that might be impacted by your changes
4. Fixing issues
- To reconcile different branches, always rebase or cherry-pick. Do not merge.
Sometimes, after you've been working on one feature, I will ask you to start work on an unrelated feature. If I do, you should probably repeat this process from the beginning (checkout main, pull changes, create a new branch). When in doubt, just ask.
4. 80/20 context
This section is our Pareto context: it’s the 20% of context about our codebase that’s useful across 80% of changes we make.
An overview of important files and directories:
## Project Structure
- **UI:** React components in `src/ui/components/`
- **Core:** Business logic in `src/core/chorus/`
- **Tauri:** Rust backend in `src-tauri/src/`
Important files to be aware of:
- `src-tauri/src/migrations.rs` - Database migrations
- `src/core/chorus/DB.ts` - Queries against the sqlite database
- `src/core/chorus/API.ts` - TanStack Query queries and mutations
- `src/ui/components/MultiChat.tsx` - Main interface
- `src/ui/components/ChatInput.tsx` - The input box where the user types chat messages
- `src/ui/components/AppSidebar.tsx` - The sidebar on the left
- `src/ui/App.tsx` - The root component
Other terms we commonly refer to in GitHub issues or in messages to Claude:
Other features:
- Model picker, which lets the user select which models are available in the chat -- implemented in`ManageModelsBox.tsx`
- Quick chats (aka Ambient Chats), a lightweight chat window -- implemented, alongside regular chats, in `MultiChat.tsx`
- Projects, which are folders of related chats -- start with `AppSidebar.tsx`
- Tools and "connections" (aka toolsets) -- start with `Toolsets.ts`
Two common processes Claude will need to follow:
## Making data model changes
Changes to the data model will typically require most of the following steps:
- Making a new migration in `src-tauri/src/migrations.rs` (if changes to the sqlite database scheme are needed)
- Modifying fetch and read functions in `src/core/chorus/DB.ts`
- Modifying data types (stored in a variety of places)
- Adding or modifying TanStack Query queries in `src/core/chorus/API.ts`
## Debugging provider calls
- When we run into issues with the requests we're sending to model providers (e.g., the way we format system prompts, attachments, tool calls, or other parts of the conversation history) a helpful debugging step is to add the line `console.log(`createParams: ${JSON.stringify(createParams, null, 2)}`);` to ProviderAnthropic.ts. Then you can ask me to send a message to Claude and show you the log output.
5. Screenshots
I thought it might be good for Claude to get some visual intuition for what it’s working on, so I gave it some screenshots of the app. I haven’t noticed it using them, but I don’t usually read all its tool calls so I might be missing it.
## Screenshots
I've put some screenshots of the app in the `screenshots` directory. If you're working on the UI at all, take a look at them. Keep in mind, though, that they may not be up to date with the latest code changes.
6. Coding style
Most of this section was autogenerated when I ran Claude’s /init
command, but I did go through and edit it myself.
The most important part is the last line: Claude LOVES to cut corners by using setTimeout
, useRef
, useImperativeHandle
, and type assertions. I tell it to ask me for permission first.
As far as I can tell, it’s stopped cutting corners in those ways, so I guess it finds it easier to write good code than to ask for permission.
## Coding style
- **TypeScript:** Strict typing enabled, ES2020 target. Use `as` only in exceptional circumstances, and then only with an explanatory comment. Prefer type hints.
- **Paths:** `@ui/*`, `@core/*`, `@/*` aliases. Use these instead of relative imports.
- **Components:** PascalCase for React components
- **Interfaces:** Prefixed with "I" (e.g., `IProvider`)
- **Hooks:** camelCase with "use" prefix
- **Formatting:** 4-space indentation, Prettier formatting
- **Promise handling:** All promises must be handled (ESLint enforced)
- **Nulls:** Prefer undefined to null. Convert `null` values from the database into undefined, e.g. `parentChatId: row.parent_chat_id ?? undefined`
IMPORTANT: If you want to use any of these features, you must alert me and explicitly ask for my permission first: `setTimeout`, `useImperativeHandle`, `useRef`, or type assertions with `as`.
7. Permissions
Claude’s other onboarding doc, along with CLAUDE.md, is .claude/settings.json
.
We grant Claude a bunch of default permissions so that it can go and cook without waiting for us.
We’d like a way to allow Claude to commit and push anywhere except main
, but Anthropic’s permissions system doesn’t support that, so for now we just let it commit and push anywhere.
All the AI, on your Mac.
©2025 Melty Labs