Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ Key patterns:
- Editing backend Python? See `.claude/rules/backend/code-conventions.md` first — filename, typing, naming conventions, SQLAlchemy/Pydantic patterns, module re-export convention.
- Editing the frontend chat UI, `useChat`, message rendering, or tool-call panels? See `.claude/rules/frontend/vercel-ai-sdk.md` first — pinned UI docs at `docs/vercel-ai-sdk-ui.txt` cover the UI surface only; the backend SSE stream is the source of truth; `ChatMessage` typing must stay threaded through every `UseChatHelpers` site.
- Editing frontend code in general? See `.claude/rules/frontend/code-conventions.md` first — import-alias scope, shadcn/ui usage, kebab-case filenames, `cn()` from `@/lib/utils`, memoization policy.
- Writing code in any language here? See `docs/agents/conventions.md` — the cross-cutting "why" behind naming, intent-carrying docstrings, and where rationale lives. The language-specific `.claude/rules/` files carry the detail.
30 changes: 30 additions & 0 deletions docs/agents/conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Code Conventions

How to write code in this repo so a coding agent — any of them, not just one tool — can find it by `grep` and read its intent without guessing. These codify existing practice and act as guardrails against drift; expand as patterns recur.

Language-specific detail lives in `.claude/rules/backend/code-conventions.md` and `.claude/rules/frontend/code-conventions.md`. This file is the cross-cutting "why" those rules share.

## Names are how agents find code

Agents `grep` for what they're trying to do before they read. Name for behavior, not layer.

- **Functions and behavioral modules:** verb-first and specific — `detect_content_type`, `build_chat_instructions`, `discover_markdown_files`, `chunk_pdf_pages`. Not `process`, `handle`, `manage`. Frontend hooks follow the same rule: `useThreads`, not `useData`.
- **No grab-bag modules.** Never `utils.py`, `helpers.py`, `misc.py`, `common.py`. Code with no home names a missing concept — find it. (This repo's `utils/` already obeys this: every file is concept-named, e.g. `message_serialization.py`.)
- **Keep framework-convention names.** `app.py`, `config.py`, `schemas.py`, `deps.py`, `hooks.py`, and the files under `routers/` are what an agent expects in a FastAPI / Pydantic AI repo. Don't "behavioralize" these. Frontend filenames stay kebab-case (`rag-sources-dialog.tsx`) per the Vercel template lineage.

## Docstrings carry intent; comments carry local "why"

An agent retrieves a symbol, then reads its docstring — so the docstring is the contract. The recipe (see `agents/hooks.py` for the model: one line of *what*, then the non-obvious *why* — that an uncaught tool exception would crash the stream mid-response):

1. One line: what it is.
2. The non-obvious *why* — the thing an agent would otherwise get wrong.
3. Cross-ref the governing decision when one exists (a future `docs/adr/` entry, a `services/` module that owns the rule).
4. A **"do not"** wherever the code looks fixable but isn't — e.g. the round-trip ordering in `utils/message_serialization.py`, or the recovery routing in `agents/hooks.py` that looks like dead defensiveness but keeps the stream alive. Agents act on negative constraints; state intentional weirdness explicitly or it gets "fixed."

This complements — does not contradict — the existing rule against name-restating docstrings (`backend/code-conventions.md`). A module docstring that states intent earns its place; a function docstring that just re-says the function name does not. Keep docstrings to slow-changing content (contracts, invariants, rationale), not step-by-step logic that drifts out of sync. Inline comments only for non-obvious *why* at a specific line — never to restate *what*.

## Rationale lives in durable docs, not the root agent file

Keep `CLAUDE.md` lean: invariants, entry points, and pointers. Detailed conventions already live in `.claude/rules/`; the *why* behind a non-obvious choice belongs in a decision record, and the docstring that implements it cites that record — so an agent is pointed at the rationale before it "simplifies" the choice away.

ADRs are the planned home for that rationale (`docs/adr/`), to be established in the upcoming `grill-with-docs` pass. Until then, capture the *why* in module docstrings and cross-reference it there. Don't let resolved rationale pile up as prose in the root agent file.
Loading