```md # ArchDoc (V1) — Проектный документ для разработки **Формат:** PRD + Tech Spec (Python-only, CLI-only) **Стек реализации:** Rust (CLI), анализ Python через AST, генерация Markdown (diff-friendly) **Дата:** 2026-01-25 --- ## 1. Контекст и проблема ### 1.1. Боль - Документация архитектуры и связей в кодовой базе устаревает практически сразу. - В новых чатах LLM не имеет контекста проекта и не понимает “рельсы”: где что лежит, какие модули, какие зависимости критичны. - В MR/PR сложно быстро оценить архитектурный impact: что поменялось в зависимостях, какие точки “пробило” изменения. ### 1.2. Цель Сделать CLI-инструмент, который по существующему Python-проекту генерирует и поддерживает **человеко- и LLM-читаемую** документацию: - от верхнего уровня (папки, модули, “рельсы”) - до **уровня функций/методов** (что делают и с чем связаны) при этом обновление должно быть **детерминированным** и **diff-friendly**. --- ## 2. Видение продукта **ArchDoc** — CLI на Rust, который: 1) сканирует репозиторий Python-проекта, 2) строит модель модулей/файлов/символов и связей (imports + best-effort calls), 3) генерирует/обновляет набор Markdown-файлов так, чтобы `git diff` показывал **смысловые** изменения, 4) создаёт “Obsidian-style” навигацию по ссылкам: индекс → модуль → файл → символ (function/class/method). --- ## 3. Область охвата (V1) ### 3.1. In-scope (обязательно) - Только **CLI** (без MCP/GUI в V1). - Только **Python** (в дальнейшем расширяемость под другие языки). - Документация: - `ARCHITECTURE.md` как входная точка, - детальные страницы по модулям и файлам, - детализация по символам (functions/classes/methods) с связями. - Связи: - dependency graph по импортам модулей, - best-effort call graph на уровне файла/символа, - inbound/outbound зависимости (кто зависит / от кого зависит). - Diff-friendly обновление: - маркерные секции, - перезапись только генерируемых блоков, - стабильные ID и сортировки. ### 3.2. Out-of-scope (V1) - MCP, IDE-интеграции. - Полный семантический резолв вызовов (уровень LSP/type inference) — только best-effort. - Визуальная “сеточка графа” — в roadmap (V2+). - LLM-суммаризация кода — V1 не должен “придумывать”; описание берём из docstring + эвристика. --- ## 4. Основные термины ### 4.1. Symbol (символ) Именованная сущность, которой можно адресно дать документацию и связи: - `function` / `async function` (def/async def), - `class`, - `method` (внутри class), - (опционально) module/package как верхнеуровневые сущности. **Symbol ≠ вызов.** Symbol — это **определение**, call/reference — **использование**. --- ## 5. Пользовательские сценарии ### S1. init Пользователь выполняет `archdoc init`: - создаётся `ARCHITECTURE.md` (в корне проекта), - создаётся `archdoc.toml` (рекомендуемо) и директория `docs/architecture/*` (если нет). ### S2. generate/update Пользователь выполняет `archdoc generate` (или `archdoc update`): - анализирует репозиторий, - создаёт/обновляет Markdown-артефакты, - в MR/PR дифф отражает только смысловые изменения. ### S3. check (CI) `archdoc check`: - завершает процесс с non-zero кодом, если текущие docs не соответствуют тому, что будет сгенерировано. --- ## 6. Продуктовые принципы (не обсуждаются) 1) **Детерминизм:** один и тот же вход → один и тот же выход. 2) **Diff-friendly:** минимальный шум в `git diff`. 3) **Ручной контент не затираем:** всё вне маркеров — зона ответственности человека. 4) **Без “галлюцинаций”:** связи выводим только из анализа (AST + индекс), иначе помечаем как unresolved/external. 5) **Масштабируемость:** кеширование, инкрементальные обновления, параллельная обработка. --- ## 7. Артефакты вывода ### 7.1. Структура файлов (рекомендуемая) ``` ARCHITECTURE.md docs/ architecture/ _index.md rails.md layout.md modules/ .md files/ .md ```` ### 7.2. Обязательные требования к контенту - `ARCHITECTURE.md` содержит: - название, описание (manual), - Created/Updated (Updated меняется **только если** изменилась любая генерируемая секция), - rails/tooling, - layout, - индекс модулей, - критичные dependency points (fan-in/fan-out/cycles). - `modules/.md` содержит: - intent (manual), - boundaries (генерируемое), - deps inbound/outbound (генерируемое), - symbols overview (генерируемое). - `files/.md` содержит: - intent (manual), - file imports + deps (генерируемое), - индекс symbols в файле, - **один блок на каждый symbol** с назначением и связями. --- ## 8. Diff-friendly обновление (ключевое) ### 8.1. Маркерные секции Любая генерируемая часть окружена маркерами: - `` - `` Для символов: - `` - `` Инструмент **обновляет только содержимое внутри** этих маркеров. ### 8.2. Ручные секции Рекомендуемый паттерн: - `` - `` Инструмент не трогает текст в этих блоках и вообще не трогает всё, что вне `ARCHDOC` маркеров. ### 8.3. Детерминированные сортировки - списки модулей/файлов/символов сортируются лексикографически по стабильному ключу, - таблицы имеют фиксированный набор колонок и формат, - запрещены “плавающие” элементы (кроме Updated, который обновляется только при изменениях). ### 8.4. Updated-таймстамп без шума Правило V1: - пересчитать контент-хеш генерируемых секций, - **если** он изменился → обновить `Updated`, - **иначе** не менять дату. --- ## 9. Stable IDs и якоря ### 9.1. Symbol ID Формат: - `py::::` Примеры: - `py::app.billing::apply_promo_code` - `py::app.services.user::UserService.create_user` Коллизии: - добавить `#` (например, от сигнатуры/позиции). ### 9.2. File doc имя `` конвертируется в: - `files/.md` - где `path_sanitized` = заменить `/` на `__` Пример: - `src/app/billing.py` → `docs/architecture/files/src__app__billing.py.md` ### 9.3. Якоря Внутри file docs якорь для symbol: - `#` где `` = безопасная форма от symbol_id - дополнительно можно вставить ``. --- ## 10. Python анализ (V1) ### 10.1. Что считаем модулем - Python package: директория с `__init__.py` - module: `.py` файл, который принадлежит package/root Поддержка src-layout: - конфиг `src_roots = ["src", "."]` ### 10.2. Извлекаем из AST (обязательно) - `import` / `from ... import ...` + алиасы - определения: `def`, `async def`, `class`, методы в классах - docstring (первая строка как “краткое назначение”) - сигнатура: аргументы, defaults, аннотации типов, return annotation (если есть) ### 10.3. Call graph (best-effort, без type inference) Резолв вызовов: - `Name()` вызов `foo()`: - если `foo` определён в этом файле → связываем на локальный symbol, - если `foo` импортирован через `from x import foo` (или алиас) → связываем на `x.foo`, - иначе → `external_call::foo`. - `Attribute()` вызов `mod.foo()`: - если `mod` — импортированный модуль/алиас → резолвим к `mod.foo`, - иначе → `unresolved_method_call::mod.foo`. Важно: лучше пометить как unresolved, чем “натянуть” неверную связь. ### 10.4. Inbound связи (кто зависит) - на уровне модулей/файлов: строим обратный граф импортов - на уровне symbols: строим обратный граф calls там, где вызовы резолвятся --- ## 11. “Что делает функция” (без LLM) ### 11.1. Источник истины: docstring - `purpose.short` = первая строка docstring - `purpose.long` (опционально) = первые N строк docstring ### 11.2. Эвристика (если docstring нет) - по имени: `get_*`, `create_*`, `update_*`, `delete_*`, `sync_*`, `validate_*` - по признакам в AST: - наличие HTTP клиентов (`requests/httpx/aiohttp`), - DB libs (`sqlalchemy/peewee/psycopg/asyncpg`), - tasks/queue (`celery`, `kafka`, `pika`), - чтение/запись файлов (`open`, `pathlib`), - raising exceptions, early returns. Формат результата: одна строка с меткой `[heuristic]`. ### 11.3. Manual override - секция “Manual notes” для каждого symbol — зона ручного уточнения. --- ## 12. CLI спецификация ### 12.1. Команды - `archdoc init` - создаёт `ARCHITECTURE.md`, `docs/architecture/*`, `archdoc.toml` (если нет) - `archdoc generate` / `archdoc update` - анализ + запись/обновление файлов - `archdoc check` - проверка: docs совпадают с тем, что будет сгенерировано ### 12.2. Флаги (V1) - `--root ` (default: `.`) - `--out ` (default: `docs/architecture`) - `--config ` (default: `archdoc.toml`) - `--verbose` - `--include-tests/--exclude-tests` (можно через конфиг) --- ## 13. Конфигурация (`archdoc.toml`) Минимальный конфиг V1: ```toml [project] root = "." out_dir = "docs/architecture" entry_file = "ARCHITECTURE.md" language = "python" [scan] include = ["src", "app", "tests"] exclude = [".venv", "venv", "__pycache__", ".git", "dist", "build", ".mypy_cache", ".ruff_cache"] follow_symlinks = false [python] src_roots = ["src", "."] include_tests = true [output] single_file = false per_file_docs = true [diff] update_timestamp_on_change_only = true [thresholds] critical_fan_in = 20 critical_fan_out = 20 ```` --- ## 14. Шаблоны Markdown (V1) ### 14.1. `ARCHITECTURE.md` (skeleton) (Важное: ручные блоки + маркерные генерируемые секции.) ```md # ARCHITECTURE — ## Project summary **Name:** **Description:** ## Key decisions (manual) - ## Non-goals (manual) - --- ## Document metadata - **Created:** - **Updated:** - **Generated by:** archdoc (cli) v0.1 --- ## Rails / Tooling > Generated. Do not edit inside this block. --- ## Repository layout (top-level) > Generated. Do not edit inside this block. --- ## Modules index > Generated. Do not edit inside this block. --- ## Critical dependency points > Generated. Do not edit inside this block. --- ## Change notes (manual) - ``` ### 14.2. `docs/architecture/layout.md` ```md # Repository layout ## Manual overrides - `src/app/` — --- ## Detected structure > Generated. Do not edit inside this block. ``` ### 14.3. `docs/architecture/modules/.md` ```md # Module: - **Path:** - **Type:** python package/module - **Doc:** ## Module intent (manual) --- ## Dependencies > Generated. Do not edit inside this block. --- ## Symbols overview > Generated. Do not edit inside this block. ``` ### 14.4. `docs/architecture/files/.md` ```md # File: - **Module:** - **Defined symbols:** - **Imports:** ## File intent (manual) --- ## Imports & file-level dependencies > Generated. Do not edit inside this block. --- ## Symbols index > Generated. Do not edit inside this block. --- ## Symbol details ### `py::::` - **Kind:** function | class | method - **Signature:** `` - **Docstring:** `` - **Defined at:** `` (optional) #### What it does #### Relations **Outbound calls (best-effort):** - - external_call:: - unresolved_method_call:: **Inbound (used by) (best-effort):** - #### Integrations (heuristic) - HTTP: yes/no - DB: yes/no - Queue/Tasks: yes/no #### Risk / impact - fan-in: - fan-out: - cycle participant: - critical: #### Manual notes ``` --- ## 15. Техническая архитектура реализации (Rust) ### 15.1. Модули приложения (рекомендуемое разбиение crates/modules) * `cli` — парсинг аргументов, команды init/generate/check * `scanner` — обход файлов, ignore, include/exclude * `python_analyzer` — AST парсер/индексатор (Python) * `model` — IR структуры данных (ProjectModel) * `renderer` — генерация Markdown (шаблоны) * `writer` — diff-aware writer: обновление по маркерам * `cache` — кеш по хешам файлов (опционально в V1, но желательно) ### 15.2. IR (Intermediate Representation) — схема данных Минимальные сущности: **ProjectModel** * modules: Map * files: Map * symbols: Map * edges: * module_import_edges: Vec (module → module) * file_import_edges: Vec (file → module/file) * symbol_call_edges: Vec (symbol → symbol/external/unresolved) **Module** * id, path, files[], doc_summary * outbound_modules[], inbound_modules[] * symbols[] **FileDoc** * id, path, module_id * imports[] (normalized) * outbound_modules[], inbound_files[] * symbols[] **Symbol** * id, kind, module_id, file_id, qualname * signature (string), annotations (optional structured) * docstring_first_line * purpose (docstring/heuristic) * outbound_calls[], inbound_calls[] * integrations flags * metrics: fan_in, fan_out, is_critical, cycle_participant **Edge** * from_id, to_id, edge_type, meta (optional) --- ## 16. Алгоритмы (ключевые) ### 16.1. Scanner * применить exclude/include и игноры * собрать список `.py` файлов * определить src_root и module paths ### 16.2. Python Analyzer Шаги: 1. Пройти по каждому `.py` файлу 2. Распарсить AST 3. Извлечь: * imports + алиасы * defs/classes/methods + сигнатуры + docstrings * calls (best-effort) 4. Построить Symbol Index: `name → symbol_id` в рамках файла и модуля 5. Резолвить calls через: * локальные defs * from-import алиасы * import module алиасы 6. Построить edges, затем обратные edges (inbound) ### 16.3. Writer (diff-aware) * загрузить существующий md (если есть) * найти маркеры секций * заменить содержимое секции детерминированным рендером * сохранить всё вне маркеров неизменным * если файл отсутствует → создать по шаблону * пересчитать общий “генерируемый хеш”: * если изменился → обновить `Updated`, иначе оставить --- ## 17. Критичные точки (impact analysis) Метрики: * **fan-in(symbol)** = число inbound вызовов (resolved) * **fan-out(symbol)** = число outbound вызовов (resolved + unresolved по отдельному счётчику) * **critical**: * `fan-in >= thresholds.critical_fan_in` OR * `fan-out >= thresholds.critical_fan_out` OR * участие в цикле модулей Выводить top-N списки в `ARCHITECTURE.md`. --- ## 18. Нефункциональные требования * Время генерации: приемлемо на средних репо (ориентир — минуты, с перспективой кеширования). * Память: не грузить весь исходный текст в память надолго; хранить только необходимое. * Безопасность: по умолчанию не включать секреты/бинарники; уважать exclude. * Надёжность: если AST не парсится (битый файл) — лог + продолжить анализ остальных, пометив файл как failed. --- ## 19. Acceptance Criteria (V1) 1. `archdoc init` создаёт: * `ARCHITECTURE.md` с manual блоками и маркерами секций * `docs/architecture/*` с базовыми файлами (или создаёт при generate) 2. Повторный `archdoc generate` на неизменном репо даёт: * нулевой diff (включая `Updated`, который не меняется без контентных изменений) 3. Изменение одной функции/файла приводит: * к локальному diff только соответствующего symbol блока и агрегатов (indexes/critical points) 4. `archdoc check` корректно детектит рассинхронизацию и возвращает non-zero. --- ## 20. План релизов (Roadmap) ### V1 (текущий документ) * Python-only CLI * modules/files/symbols docs * import graph + best-effort call graph * diff-friendly writer * init/generate/check ### V2 (следующий шаг) * Экспорт графа в JSON/Mermaid * Простая локальная HTML/MD визуализация “как в Obsidian” (сетка зависимостей) * Улучшение резолва calls (больше случаев через алиасы/простые типы) ### V3+ * Подключение других языков (через tree-sitter провайдеры) * Опционально LSP режим для точного call graph * MCP/IDE интеграции --- ## 21. Backlog (V1 — минимально достаточный) ### Эпик A — CLI и конфиг * A1: `init` создаёт skeleton + config * A2: `generate/update` парсит конфиг и пишет docs * A3: `check` сравнивает с виртуально сгенерированным выводом ### Эпик B — Python анализ * B1: scanner и определение module paths * B2: AST import extraction + алиасы * B3: defs/classes/methods extraction + signatures/docstrings * B4: call extraction + best-effort resolution * B5: inbound/outbound построение графов ### Эпик C — Markdown генерация и writer * C1: renderer шаблонов * C2: marker-based replace секций * C3: stable sorting и формат таблиц * C4: update timestamp on change only ### Эпик D — Critical points * D1: fan-in/fan-out метрики * D2: top lists в ARCHITECTURE.md * D3: module cycles detection (простая графовая проверка) --- ## 22. Примечания по качеству (сразу закладываем тестируемость) * Golden-tests: на маленьком fixture repo хранить ожидаемые md и проверять детерминизм. * Unit-tests на writer: заменить секцию без изменения остального файла. * Unit-tests на import/call resolution: алиасы `import x as y`, `from x import a as b`. --- ## 23. Итог V1 фиксирует базовый продукт: **полная архитектурная документация до уровня функций** с зависимостями и impact, обновляемая безопасно и читаемо через `git diff`. Инструмент закрывает задачу: дать LLM и человеку стабильную “карту проекта” и контролировать критичные точки при изменениях. --- ``` ```