# Стенд разработки — поднять проект на чистой машине > Отвечает на ОДИН вопрос: что поставить и в каком порядке, чтобы все четыре зоны собрались и их > батареи прошли. **Версий и рецептов зон здесь НЕТ** — каждая строка называет свой носитель и > команду, которой номер читается: скопированный сюда номер стал бы вторым носителем и разошёлся > бы с первым (D39.112). Что уже построено и что в очереди — CURRENT-STATE в [PROGRESS.md](PROGRESS.md), > здесь только «как поднять». > > ⚠ **Проверено исполнением 24.08.2026** на чистой Ubuntu 26.04 LTS (WSL2), пользователь `ubuntu-26`, > репозиторий `~/projects/textmachine`. Результаты прогонов — в теле; всё, что не прогнано, названо > вслух в разделе «Чего этот стенд не проверяет». ## 1. Что ставится БЕЗ root, и где оно живёт Схема одна для всех: дистрибутив разворачивается в `~/.local/opt/<инструмент>`, а исполняемое симлинкуется в `~/.local/bin` — он уже в `PATH` из `~/.profile` Ubuntu, поэтому rc-файлы править не надо. Каждый архив ставится ТОЛЬКО после сверки sha256 с подписью вендора. | Инструмент | Кто требует и какой версии (носитель) | Как ставится | |---|---|---| | **Go** | `backend/Makefile` `GO_MIN_VERSION` и `platform/Makefile` `GO_MIN_VERSION` (у платформы выше — она сетевая, разбор — комментарий `toolchain` в `platform/go.mod`). Прочесть: `grep -h GO_MIN_VERSION backend/Makefile platform/Makefile` | тарбол с `go.dev/dl` → `~/.local/opt/go`, симлинки `go`/`gofmt`. ⚠ Брать ПОСЛЕДНИЙ ПАТЧ ПИНОВАННОГО МИНОРА, а не следующий минор: линтер пинован и его находки версионно-зависимы | | **golangci-lint** | `GOLANGCI_VERSION` в обоих Makefile, точным равенством (`tools-check` сверяет строку версии). Прочесть: `grep -h GOLANGCI_VERSION backend/Makefile platform/Makefile` | release-бинарь с GitHub → `~/.local/bin/golangci-lint`; чексуммы — `golangci-lint--checksums.txt` того же релиза | | **Node + npm** | `frontend/package.json` `engines.node`; менеджер — npm из поставки Node (`frontend/docs/STACK_DECISIONS.md` §1). Прочесть: `python3 -c "import json;print(json.load(open('frontend/package.json'))['engines'])"` | тарбол с `nodejs.org/dist` → `~/.local/opt/node`; чексуммы — `SHASUMS256.txt` того же каталога. ⚠ `frontend/.npmrc` ставит `engine-strict=true` — Node ниже пола это ОШИБКА установки, а не предупреждение | | **PostgreSQL** | `platform/README.md` (мажор) — рецепт целиком, включая порт и DSN, живёт в `platform/docs/STACK_DECISIONS.md` §«Postgres на стенде без root» | ровно по тому рецепту: micromamba + conda-forge в `~/.local/pgsql`, кластер в `~/.local/share/tmstand/pgdata`, сокет в `/tmp`. Рецепт ратифицирован зоной и sudo не требует | | **micromamba** | нужен только предыдущей строке | один статический бинарь; ⚠ на чистой Ubuntu 26 НЕТ `bzip2`, поэтому `tar -xjf` падает — архив распаковывается модулем `tarfile` питона (`bz2` в stdlib есть) | ## 2. Что БЕЗ root не ставится — и почему обход недопустим | Пакет | Кто без него не работает | Почему нельзя обойти | |---|---|---| | **`build-essential`** (`gcc` + `make`) | `make test`/`make battery`/`make check` ОБЕИХ Go-зон | Обе цели гоняют `go test -race`, а `-race` требует cgo, то есть C-компилятора. Комментарий над целью в `backend/Makefile` говорит прямо: снять `-race` значит превратить отсутствие тулчейна в зелёный прогон, доказывающий меньше, чем он заявляет. `make` нужен и сам по себе: `platform` `TestTheToolchainGateComparesVersionsRatherThanMatchingThem` зовёт `make version-check` и без `make` СКИПАЕТСЯ | | **`python3-venv`** | вся зона `eval/` | В системном питоне Ubuntu 26 нет `ensurepip`, поэтому `python3 -m venv .venv` создаёт окружение БЕЗ pip. Ратифицированная зоной установка — `python3 -m venv .venv && .venv/bin/pip install -r requirements.txt` (`eval/README.md` п.5) — на этом и останавливается | Ставится одной командой; она же — единственное, что в этом файле просит пароль: ```sh sudo apt-get update && sudo apt-get install -y build-essential python3-venv ``` ## 3. Порядок подъёма 1. **Go + golangci-lint** → `cd backend && make battery` (после шага 2 — `make battery-stand`). 2. **Postgres** по рецепту зоны → `cd platform && TM_PLATFORM_TEST_DSN=<из рецепта> make check`. 3. **Node** → `cd frontend && npm ci && npm run check`. ⚠ `npm ci` через `prepare` ставит pre-commit-диспетчер в `.git/hooks/` — **это единственный путь, которым включается гейт якорей доков** (`docs/scripts/githooks/pre-commit`). На клоне, где во фронте не ставили зависимости, гейт МОЛЧА выключен. Поставить его отдельно, без установки зависимостей: `node frontend/scripts/githooks/install.mjs`. 4. **Python-окружение полигона** — по `eval/README.md` п.5. 5. **Данные книг** — раздел 4 ниже. 6. **Дев-стенд платформы целиком** (демон + сид через живой интейк + дев-прокси фронта) — рецепт в `platform/deploy/README.md` §«Дев-стенд». Он же — самая дешёвая сквозная проверка: сид гоняет движковый бинарь настоящим вызовом. ## 4. Каталог книг Книги и производные лежат в **`<репозиторий>/books`** и версионируются ОТДЕЛЬНЫМ git-репозиторием (свой `origin`, свой лог); внешний репозиторий держит `books/` в `.gitignore` и о нём ничего не знает. Инвариант канона «книга и производные вне git» этим не нарушен — вне ЭТОГО git, — но следствия новые: - `git clean -xdf` во внешнем репозитории снёс бы весь каталог вместе с его историей. `clean` и так запрещён каноном (`CLAUDE.md` §Гардрейлы); здесь у запрета появилась вторая цена. - `git status` внешнего репозитория НЕ показывает правки книг. Состояние книг читается только `git -C books status`. - ⚠ Прежний адрес каталога был `~/books`, и он захардкожен в трёх местах движка и в скриптах полигона. Диспозиция — строки бэклога; до их закрытия корпус-гейченные тесты движка запускаются с переопределениями (`TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{RECORDS,SEED}`), которые в коде уже предусмотрены. ## 5. Чего этот стенд не проверяет Гейт обязан печатать, чего он не смотрит, — иначе его молчание читается как покрытие. - **`-race` не гонялся ни в одной зоне** (нет C-тулчейна, раздел 2). Все зелёные прогоны ниже — БЕЗ детектора гонок, и это не «батарея прошла». - **`make` не гонялся** — цели исполнялись командами из самих Makefile. Расхождение цели и того, что запускали, этим не поймано. - **Ни одного платного вызова.** Всё зелёное — $0-пути: сборки, юнит-тесты, `tmctl manifest/status`, интейк платформы. `tmctl translate` не запускался. - **`TestMinerFullBookParity` СКИПАЕТСЯ на любом клоне**: ему нужен `eval/exp16/data/jieba_dict_general_zh.txt`, который в git не попадает (`.gitignore`) и пересобирается — носитель диспозиции — строка бэклога 123. - **Браузеры Playwright не скачаны** ⇒ `npm run check:full` (цели `shot`/`scenes`, оси axe) не гонялся; `npm run check` их не включает. - **Локальной модели нет:** ни `ollama`, ни GPU (`nvidia-smi` отсутствует). Замеры полигона, которым нужна локальная модель, на этой машине не воспроизводимы. - **OIDC-провайдера нет** — вход на стенде идёт дев-логином; боевой путь входа не проверялся. ## 6. Ловушки, подтверждённые на этой машине - **`no_proxy=` в окружении WSL — это Windows-синтаксис, и curl его не понимает.** `curl http://127.0.0.1:8080/healthz` отдаёт **403** от прокси, и это читается как дефект кода, которого нет. Лечится по месту: `curl --noproxy '*'` (так и записано в `platform/docs/STACK_DECISIONS.md` §«Грабли стенда») или `NO_PROXY=127.0.0.1,localhost,::1`. ⚠ **Go-бинари проекта эта грабля НЕ задевает, и не потому, что понимают ``:** пробой `http.ProxyFromEnvironment` показано, что loopback не проксируется и при `no_proxy=""` вовсе — это встроенное правило, а не заслуга переменной. Значит симптом асимметричен: ручная проверка curl'ом краснеет там, где сам сервис зелёный. - **`tmctl` берёт глагол ПЕРВЫМ аргументом, флаги после**: `tmctl manifest --config book.yaml`. Обратный порядок печатает `--config book.yaml is required` — сообщение выглядит как «флаг не передан», хотя флаг передан. - **`tmplatformctl seed` требует и `TM_PLATFORM_DSN`, и `TM_PLATFORM_DEV_LOGIN`** в СВОЁМ окружении, а не только в окружении демона: он ходит и в базу, и в HTTP. - **Шаблон книги для платформы (`TM_PLATFORM_BOOK_TEMPLATE`) обязан нести АБСОЛЮТНЫЕ пути** к `pipeline:`/`models:`. `backend/example/book.yaml` годится как заготовка, но его относительные `../configs/...` из каталога книги платформы не разрешаются.