> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fim.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Contributing

> Development conventions, type safety, testing, and code quality standards for FIM One contributors.

<Tip>
  This page covers development conventions and code quality standards. For the full contributing guide — including the Pioneer Program, field testing, PR process, and community links — see [CONTRIBUTING.md on GitHub](https://github.com/fim-ai/fim-one/blob/master/CONTRIBUTING.md).
</Tip>

## Setup

Prerequisites: Python 3.11+, [uv](https://docs.astral.sh/uv/), Node.js 18+, pnpm.

```bash theme={null}
git clone https://github.com/fim-ai/fim-one.git && cd fim-one
cp example.env .env                        # LLM_API_KEY is optional for contributors
uv sync --all-extras                       # Python dependencies
cd frontend && pnpm install && cd ..       # frontend dependencies
bash scripts/setup-hooks.sh                # pre-commit hooks (required)
```

<Note>
  `LLM_API_KEY` is only required to run the backend locally or to preview auto-translated locale output on commit. Contributors without a key can still commit EN changes — a GitHub Actions workflow translates on `master` after PR merge. See [i18n](#i18n) below.
</Note>

Verify everything works:

```bash theme={null}
uv run pytest tests/ -x -q                # tests
uv run mypy src/fim_one/                   # type check
uv run ruff check src/                     # lint
cd frontend && pnpm build && cd ..         # frontend build
```

***

## Type safety

mypy strict mode is enforced. The entire codebase passes with zero errors, and the pre-commit hook runs mypy on every staged `.py` file with full import chain checking.

**Rules:**

* Type hints required on all public functions.
* No `# type: ignore` except for `import-untyped` (third-party libs without stubs).
* Use `from __future__ import annotations` for forward references.
* Fix the actual types -- never suppress errors to make CI pass.

***

## Testing

Every new module **must** have a corresponding test file. Every feature commit **must** include tests.

| Convention | Pattern                      |
| ---------- | ---------------------------- |
| File       | `tests/test_{module}.py`     |
| Class      | `Test{Feature}`              |
| Method     | `test_{behavior_under_test}` |

**Rules:**

* All existing tests must pass before committing: `uv run pytest tests/ -x -q`.
* Tests must not depend on external services -- mock databases, MCP servers, HTTP endpoints, and LLM calls using `unittest.mock` / `AsyncMock`.
* Run tests with coverage: `uv run pytest --cov=fim_one`.

***

## Code style

* **Async-first.** Use `async def` for I/O-bound operations. Wrap synchronous calls with `asyncio.to_thread()` rather than blocking the event loop.
* **Ruff for linting.** `uv run ruff check src/` -- line length is 100 characters.
* **Minimal `__init__.py` exports.** Only re-export the public API; keep internal symbols private.
* **Package managers.** `uv` for Python, `pnpm` for frontend. Never use `pip` or `npm`.

***

## Git conventions

**Atomic commits.** One logical change per commit. Split unrelated changes even if they were developed together.

**Commit message format:** `type: description`

| Type       | When to use                                |
| ---------- | ------------------------------------------ |
| `feat`     | New user-facing feature                    |
| `fix`      | Bug fix                                    |
| `refactor` | Code restructuring with no behavior change |
| `docs`     | Documentation only                         |
| `test`     | Adding or updating tests                   |
| `chore`    | Build, CI, dependency updates              |

**Never skip hooks** with `--no-verify`. The pre-commit pipeline exists to catch errors before they reach the repository.

***

## Pre-commit hook pipeline

The hook runs automatically on every commit. It only processes staged files relevant to each step -- if you only changed Python files, the i18n step is skipped, and vice versa.

| Order | Step               | Trigger                                                                                                                | What it does                                                                                     |
| ----- | ------------------ | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| 0     | Locale-edit guard  | Any staged file under `messages/{locale}/`, `docs/{locale}/`, or `README.{locale}.md`                                  | Aborts the commit — no override. Fix translations via `scripts/translation-glossary.md` instead. |
| 1     | OpenAPI spec regen | `src/fim_one/web/**/*.py` changed                                                                                      | Re-exports `docs/openapi.json` from FastAPI routes                                               |
| 2     | i18n translation   | `messages/en/*.json`, `docs/*.mdx`, or `README.md` changed (needs local `LLM_API_KEY`, else CI handles it after merge) | Translates new/changed keys into all target locales                                              |
| 3     | mypy type checking | Any `src/fim_one/**/*.py` changed                                                                                      | Runs mypy with full import chain on staged files                                                 |
| 4     | MDX validation     | Any `.mdx` file staged                                                                                                 | Validates JSX/MDX syntax before it reaches Mintlify                                              |

If any step fails, the commit is aborted. Fix the reported errors and retry.

***

## i18n

FIM One supports 6 locales (EN, ZH, JA, KO, DE, FR). Translations are fully automated.

**Only edit English source files:**

* `frontend/messages/en/{namespace}.json` -- UI strings
* `docs/*.mdx` (root level, not locale subdirs) -- documentation
* `README.md` -- project readme

Other locale files (`messages/zh/`, `docs/zh/`, `README.zh.md`, etc.) are **auto-generated** and the pre-commit hook unconditionally refuses commits that manually edit them. To fix a mistranslation, edit [`scripts/translation-glossary.md`](https://github.com/fim-ai/fim-one/blob/master/scripts/translation-glossary.md) — the prompt-injected single source of truth for every translation rule — then regenerate affected files with `uv run scripts/translate.py --files <en-sources> --force`. Every glossary fix applies to all five locales permanently.

**Two paths to generating locale files:**

1. **Local pre-commit hook** — runs if `LLM_API_KEY` is set in `.env`. Translation happens before push, so you can preview the output.
2. **CI fallback** — if the local hook had no key and skipped, [`.github/workflows/i18n-sync.yml`](https://github.com/fim-ai/fim-one/blob/master/.github/workflows/i18n-sync.yml) translates on `master` after merge and auto-commits the result.

Contributors without an API key can commit EN changes freely — the CI path ensures locales land correctly.

**Adding a new i18n namespace:** Create `messages/en/{ns}.json` -- other locale files are generated on the next commit or on CI after merge.

**Force full retranslation:** `uv run scripts/translate.py --all` (requires local `LLM_API_KEY`).

***

## Database migrations

Dev uses SQLite, production uses PostgreSQL. One set of Alembic migration files must work on both.

**Key rules:**

* Every new ORM model or column **must** have a migration -- never rely on `metadata.create_all()`.
* All migrations **must** be idempotent -- use helpers from `fim_one.migrations.helpers` (`table_exists`, `table_has_column`, `index_exists`).
* Boolean defaults: `server_default=sa.text("FALSE")` / `sa.text("TRUE")`. Never `"0"` / `"1"` -- PostgreSQL rejects integer literals for Boolean columns.
* SQLite cannot `ALTER COLUMN` -- use `op.batch_alter_table()` for column constraint changes.
* After writing a migration, immediately run `uv run alembic upgrade head` to apply it.

***

## Quick reference

```bash theme={null}
# Development
./start.sh dev                            # hot reload (Python + Next.js HMR)
./start.sh dev:api                        # API only, dev mode (hot reload)
./start.sh dev:ui                         # Frontend only, dev mode (HMR)
./start.sh api                            # FastAPI only (headless)

# Quality checks
uv run pytest tests/ -x -q               # tests (stop on first failure)
uv run pytest --cov=fim_one              # tests with coverage
uv run mypy src/fim_one/                  # type check
uv run ruff check src/                    # lint

# i18n
uv run scripts/translate.py --all         # force full retranslation

# Database
uv run alembic upgrade head               # apply migrations
uv run alembic revision -m "description"  # create new migration
```
