# Git - единственный источник истины

## Принцип (2026-05-14, user explicit)

Каждый проект, который **можно** держать в git - держим в git. Git - это single source of truth: не локальная папка на диске, не "закоммичу когда будет готово", не состояние в контексте сессии. Если работа не в git - для следующей сессии, для другой машины, для тебя-после-перезагрузки её не существует.

Пользователь сформулировал прямо: "сделай гит единственным источником истины... всё что можно нужно слить в гит мы сливаем и гит источник истины".

## Деплой -> коммит (+ push) ВСЕГДА вместе (2026-06-03, user explicit)

Если изменение **задеплоено** (на сервер / в прод / в контейнер) - оно ОБЯЗАНО быть тут же
**закоммичено и запушено**. Не оставлять deployed-but-uncommitted: прод уходит вперёд git, git
перестаёт быть источником истины, следующая сессия не знает, что реально в проде и из какого кода
оно собрано.

Инвариант (user сформулировал прямо 2026-06-03): **«если мы работаем в репо - коммитим и деплоим
всегда»**. После КАЖДОЙ выкладки - сразу `git add <явные пути> && git commit && git push`; не копить
«задеплою сейчас, закоммичу потом» («потом» = после перезагрузки/смены сессии, когда контекст утерян).

Real-world кейс (этого пункта): 2026-06-03 за сессию задеплоено ~6 изменений (Термо-прокси,
классификация, фронт-фиксы), но закоммичено лишь в конце по явной просьбе user. Правильно -
коммит+push сразу после каждой выкладки.

## Почему это особенно важно для агентских сессий

Обычный аргумент за git - бэкап и история. Для нашего workflow есть ещё один, более острый:

**Сессия Claude обрывается и перезагружается.** Контекст конечен, бывают reload'ы, бывает context refresh / compaction. Всё, что жило только в conversation context или в незакоммиченных локальных файлах - после перезагрузки в зоне риска: новая сессия не знает, что существует, в каком оно состоянии, что уже сделано, почему приняты решения.

Git закрывает эту дыру механически:
- `git log` - что сделано и когда
- `git status` / `git diff` - что в работе прямо сейчас
- сам код и доки на диске под версионным контролем - текущая реальность, а не пересказ из памяти

Это та же логика, что и session-handoff (project-level rule), но на уровень ниже: handoff отвечает "что делать дальше", git отвечает "что вообще существует и в каком виде". Handoff может устареть, потеряться, быть неточным - git-коммит durable и проверяемый.

## Что коммитим - почти всё

Всё, что можно слить в git - сливаем:
- код (очевидно)
- docs, спеки, архитектурные документы, layer breakdowns
- дизайн-пакеты от дизайнера (markdown, HTML-прототипы, скриншоты разумного размера)
- конфиги (`package.json`, `tsconfig.json`, `wrangler.toml`, CI-yaml, сам `.gitignore`)
- research-артефакты, относящиеся к проекту
- handoffs, chronicles, PROBLEMS.md, feature_list.json, init.sh
- project-level `.claude/rules/` и CLAUDE.md проекта
- `.env.example` - шаблон переменных без значений

## Что НЕ коммитим - ровно 4 класса

`.gitignore` - это формальное определение того, что **не** является источником истины. Только 4 класса:

| Класс | Примеры | Почему наружу |
|---|---|---|
| **Регенерируемое** | `node_modules/`, `dist/`, `build/`, `.vite/`, `*.tsbuildinfo`, кэши | Воспроизводится из закоммиченного кода одной командой |
| **Секреты** | `.env`, `.env.*`, `.dev.vars`, `*.key`, `*.pem`, токены | Утечка в git-историю необратима, вычищать дорого |
| **Машинный мусор** | `.DS_Store`, `Thumbs.db`, `*.swp` | Локально для машины/ОС, для проекта - шум |
| **Тяжёлые бинарники** | датасеты, веса моделей, видео, большие архивы | Раздувают репо безвозвратно; место - S3/R2/Volume + ссылка в репо |

Если файл не попадает ни в один из 4 классов - он коммитится. Сомневаешься - коммить (вернуть файл из истории проще, чем восстановить никогда не закоммиченное).

## `.gitignore` шаблон (web-проект: Vite + Cloudflare)

Стартовый шаблон, адаптировать под стек проекта:

```gitignore
# Регенерируемое
node_modules/
dist/
build/
.vite/
*.tsbuildinfo

# Cloudflare / Wrangler
.wrangler/
.dev.vars

# Секреты
.env
.env.*
!.env.example
*.key
*.pem

# Логи
*.log
npm-debug.log*

# Машинный мусор
.DS_Store
Thumbs.db
*.swp
```

Для ML-проекта добавить: `*.ckpt`, `*.safetensors`, `*.pth`, `data/`, `outputs/`, `checkpoints/` - веса и датасеты живут в Volume/S3, не в git.

## Workflow

### Новый проект
1. `.gitignore` **первым** - до `git init`, чтобы регенерируемое и секреты не попали даже в первый коммит (вычищать из истории дорого и требует force-push).
2. `git init -b main`
3. `git add` явных путей (не слепой `git add -A` - см. CLAUDE.md commit protocol), затем `git status --short` - посмотреть что застейджено перед коммитом.
4. Первый коммит (без Claude-атрибуции - см. no-claude-attribution.md).
5. `gh repo create <name> --private --source=. --remote=origin --push` - **приватный**, сразу, на старте работы. Public - только по явной просьбе пользователя.
6. Verify: `gh repo view <owner>/<name> --json visibility` (PRIVATE?), `git status -sb` (синхронизирован с origin?).

### Существующий проект
- Перед работой в чужом/старом репо - billing-safety hermes.md pre-check (см. safety-billing.md).
- Коммитим **регулярно** - после каждого осмысленного шага. Маленькие коммиты с понятными сообщениями вместо одного большого в конце.
- Push не реже конца сессии. Локальный коммит без push - половина страховки (диск может умереть, машина смениться).

## Anti-patterns

- ❌ "Сделаю репо когда проект будет готов" - к тому моменту уже потеряны промежуточные состояния и причины решений
- ❌ "Работаю в локальной папке, закоммичу потом" - "потом" нередко = после перезагрузки, когда контекст уже потерян
- ❌ Важное состояние вне git: ключевые заметки только в conversation, конфиг только на диске, "помню что решили X"
- ❌ Слепой `git add -A` / `git add .` - может затащить секреты или мусор; стейджить явные пути
- ❌ Public-репозиторий без явной просьбы пользователя
- ❌ Тяжёлые бинарники в git "чтобы не потерять" - раздувают репо навсегда; правильно - в S3/R2/Volume + ссылка
- ❌ Накопить 20 изменений и один коммит "init" в конце - теряется гранулярность и возможность откатить точечно

## Связь с другими правилами

- **no-claude-attribution.md** - коммиты без `Co-Authored-By: Claude` и `Generated with Claude Code`
- **safety-billing.md** - hermes.md pre-check перед работой в существующем репо
- **secrets-as-data.md** + `pre_push_public_repo_scan.py` - защита на выходной границе; приватные репо проходят push без скана, публичные - с 2-агентным сканом
- **session-handoff.md** (project-level) - handoff = "что делать дальше", git = "что существует"; дополняют друг друга
- **CLAUDE.md** "After making any change" пункт 3 (push to GitHub) - частный случай этого правила
- **CLAUDE.md** "Public Skills Repository" - claude-code-config repo обновляется по этому же принципу

## Real-world кейс (этого правила)

**2026-05-14, diffusion.love.** В ходе сессии: создана папка проекта, проведён анализ дизайна, написаны `docs/layers.md`, user-journey, файлы анализа - всё в локальной папке **без git**. Затем пользователь написал "пришлось перегрузить, продолжаем" - сессию реально перезагрузили. После перезагрузки пришлось верифицировать состояние с диска заново через PowerShell, потому что git ещё не был источником истины и не было `git log` чтобы быстро понять "что сделано". Сразу после - `git init` + приватный репо `AnastasiyaW/diffusion-love` + это правило. Перезагрузка-в-середине-работы - ровно тот сценарий, ради которого правило существует.
