Setup
Prerequisites: Python 3.11+, uv, Node.js 18+, pnpm.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 below.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: ignoreexcept forimport-untyped(third-party libs without stubs). - Use
from __future__ import annotationsfor 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.
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 deffor I/O-bound operations. Wrap synchronous calls withasyncio.to_thread()rather than blocking the event loop. - Ruff for linting.
uv run ruff check src/— line length is 100 characters. - Minimal
__init__.pyexports. Only re-export the public API; keep internal symbols private. - Package managers.
uvfor Python,pnpmfor frontend. Never usepipornpm.
Git conventions
Atomic commits. One logical change per commit. Split unrelated changes even if they were developed together. Commit message format:type: description
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.
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 stringsdocs/*.mdx(root level, not locale subdirs) — documentationREADME.md— project readme
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 — 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:
- Local pre-commit hook — runs if
LLM_API_KEYis set in.env. Translation happens before push, so you can preview the output. - CI fallback — if the local hook had no key and skipped,
.github/workflows/i18n-sync.ymltranslates onmasterafter merge and auto-commits the result.
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— useop.batch_alter_table()for column constraint changes. - After writing a migration, immediately run
uv run alembic upgrade headto apply it.