--- date: '2026-05-06' source: .meeting-room/.archive/2026-05-06-factory-bootstrap.md status: promoted type: concept title: factory-bootstrap ingested_at: '2026-05-06T18:22:46.484Z' ingested_by: OpeItcLoc03@DESKTOP-NSEF0UK source_project: .meeting-room --- # Software factory bootstrap — design ## Контекст и диагноз В `~/projects/` есть точечные папки-модули будущей "ERP": | Модуль | Артефакт | Состояние (на 2026-05-06) | |---|---|---| | HR / org chart | `.organization/` (roster.md + personas) | частично | | Inventory / templates | `.templates/` | частично (`project/` есть, `agent/`/`report/` пусты) | | Knowledge (local) | `.wiki/` (Karpathy) | живой | | Knowledge (shared) | `projects-wiki` через `mcp__projects-meta__*` | живой, 13–14 страниц | | Tasks / PMO | `.tasks/` (local) + `mcp__projects-meta__tasks_*` (shared) | живой, 14 проектов в кэше | | Shared services | `.common/` (lib/scripts/secrets/prompts/config) | живой | | Tooling registry | `claude-skills/` (~21 скил) | живой | | Bus services | `.common/lib/projects-meta-mcp/`, `.common/lib/interns-mcp/` (subdirs of .common, not standalone repos — см. `[migrate-to-common-lib]`) | живые | | Communications | `.meeting-room/` | живой | **Что не работало для онбординга нового сотрудника:** 1. Нет orchestrator'а — `setup-*` дёргаются по одному вручную. 2. Нет L0 entry point — пока Claude не запущен, ни один setup-* не доступен. 3. Нет manifest'а компонентов — версий, зависимостей, health-check'ов нет нигде. 4. Hardcoded `~/projects/` в путях — переезд = ручная починка. 5. Multi-vendor (Cursor / Copilot CLI / Gemini CLI): `setup-*` пишут только в `~/.claude.json`. 6. Фабрика сама не убрана. ## Решение **Создать новую дот-папку `~/projects/.factory/` — отдельный репо, сиблинг `.common/`. Manifest-driven cross-platform installer/updater всей мета-инфраструктуры.** Размещение выбрано потому, что factory должна работать **до того**, как `.common` склонирован. L0 кладёт `.factory/` первым, дальше уже он ставит всё остальное. Альтернативы (`.common/lib/factory/`, `claude-skills/skills/factory/`) отвергнуты — обе создают cycle dependency. ### Архитектура — три слоя **L0 — Pre-Claude bootstrap** (нативный shell, без runtime-зависимостей). Два под-слоя: **L0a — System bootstrap (toolchain).** Голая машина → готовая к работе. - Системный пакет-менеджер: `winget` (Win11), `brew` (Mac), `apt`/`dnf`/`pacman` (Linux). - `git` + `curl` через системный pkg-mgr. - `mise` (бывш. rtx) — single-binary unified runtime manager. Через него `node@lts`, `python@latest`, `go@latest`, `rust` по требованию. Закрывает nvm/pyenv/goenv одной зависимостью. - Опционально (yes/no/skip): Claude Code, Cursor, VS Code, Ollama, Docker Desktop. **L0b — Factory bootstrap.** После L0a. - `bootstrap.ps1` + `bootstrap.sh` — запуск через `iwr … | iex` / `curl … | bash`. - Делает только: detect platform → спросить `$PROJECTS_DIR` → clone `.factory/` (gitea) → передать управление в L1. - Единое состояние машины: `~/.config/factory/home.toml`: ```toml projects_dir = "C:/Users/vitya/projects" client = "claude-code" # | "copilot-cli" | "gemini-cli" installed_at = "2026-05-06T..." ``` - Если несколько `projects_dir` — `home.toml` хранит активный, флаг `--projects-dir ` переключает. **L1 — `factory.yaml` + `factory` CLI** (один скомпилированный Go-бинарь — см. Q1). - Manifest-схема (черновик): ```yaml components: - name: dot-common source: { type: git, url: "{gitea}/.common" } target: "{projects_dir}/.common" health_check: "test -f {target}/scripts/claude-switch.ps1" - name: projects-meta-mcp # Subdirectory of .common repo — no standalone git source target: "{projects_dir}/.common/lib/projects-meta-mcp" deps: [dot-common] setup_skill: setup-projects-meta health_check: "mcp_tool_available mcp__projects-meta__meta_status" - name: dot-wiki target: "{projects_dir}/.wiki" deps: [projects-meta-mcp] setup_skill: setup-wiki health_check: "test -f {target}/CLAUDE.md && test -f {target}/index.md" - name: claude-skills source: { type: git, url: "{gitea}/claude-skills" } target: "{projects_dir}/claude-skills" post_install: "bash {target}/scripts/install.sh" health_check: "test -d ~/.claude/skills/project-bootstrap" ``` - Команды: `factory install`, `factory update`, `factory status`, `factory diagnose `. - Идемпотентность по health-check'у — re-run = no-op. **L2 — `setup-*` skills остаются.** Factory триггерит через печать инструкции пользователю до тех пор, пока не появится программный slash-command-bridge (см. Q3). ### Что закрывает каждое требование | Требование | Решение | |---|---| | `~/projects` в произвольном месте | `{projects_dir}` substitution в manifest, источник — `home.toml` | | Произвольное имя папки | То же; manifest нигде не хардкодит "projects" | | Несколько папок на одной машине | Профили в `home.toml`; флаг `--profile` | | Cross-platform | L0 — два нативных скрипта; L1 — single-file бинарь под Win/Mac/Linux; L2 — уже работает | | Частичные / устаревшие установки | health_check на каждый компонент; `factory status` показывает дельту; `factory update` чинит | | Multi-vendor | `client` в `home.toml`; manifest-компоненты помечены `clients: [claude-code, copilot-cli]` | ### Anti-recommendations - Один монолитный bash/ps скрипт без manifest'а — за год превратится в legacy. - Python-orchestrator с pip-зависимостями — сам требует bootstrap'а python (chicken-and-egg). - Повторять логику установки в L0 и L2. health_check — single source of truth. - Складывать `factory` в `claude-skills/` — фабрика > tooling; claude-skills должен быть **компонентом** manifest'а. ## Решённые вопросы ### Q1 — runtime для L1: Go Главные аргументы: размер бинаря (5–10 МБ vs 80–100 МБ Deno), старт ~5 мс, тривиальная кросс-компиляция (включая Win/macOS ARM), `go-git/go-git` снимает зависимость от системного git, cobra даёт shell-completion из коробки. Стек: Go 1.23+, `go-git/go-git`, `goccy/go-yaml`, `spf13/cobra`, опционально `charmbracelet/lipgloss`. Поставка — pre-built бинари в `.factory/dist/`, скачиваются L0b. Контраргументы (Deno/Rust/Bun) отброшены: Deno слишком жирный для curl-pipe, Rust learning curve не оправдывает экономию 5 МБ, Bun на Windows ARM ещё не дозрел. ### Q6 — toolchain layer в L0: `mise` поверх системного pkg-mgr L0a: системный pkg-mgr (winget/brew/apt) → ставит git + curl + mise → mise ставит node/python/go/rust. Опциональный интерактивный слой: Claude Code, Cursor, VS Code, Ollama, Docker Desktop. Не отдельные nvm + pyenv + goenv — у каждого свой bootstrap, mise унифицирует. Не свой пакет-менеджер — winget/brew/apt уже стоят на любой целевой ОС. ### Q8 — fresh-enterprise bootstrap (greenfield-mode) `factory` должен уметь поднимать **пустое предприятие** — не только клонировать существующие гитеа-репы (clone-mode), но и **скаффолдить шесть мета-репо из встроенных templates**. Use case: новый юзер хочет повторить эту ERP-архитектуру в своём gitea/github namespace. Что уже покрыто greenfield'ом (повторно использовать): `setup-wiki`, `setup-tasks`, `project-bootstrap`. Что не покрыто (нужны новые скаффолды): 1. **Корневые мета-репо** — `.common/`, `.meeting-room/`, `.organization/`, `.templates/`, claude-skills-fork. 2. `setup-projects-meta` и `setup-interns` ждут готовый source-tree в gitea — нужен **`init`-mode**. Архитектурное предложение — добавить в `factory.yaml` per-component `clone-or-init`-источник: ```yaml components: - name: dot-common source: type: clone-or-init url: "{gitea}/common" # сперва пробуем clone template: "{factory}/templates/enterprise/common" # fallback — scaffold + init+push ``` И команда `factory init --git-host `. **Anti-recommendation:** не пытаться bootstrap'ить сам git-host (gitea/forgejo/github). Factory ожидает, что писательский git-сервер уже существует. Цена: +1 папка `templates/enterprise//` (6 скелетов) + per-component поле `source.template:`. ~неделя на скелеты + ~3 дня на `clone-or-init` логику. Не блокирует v0; реализуется параллельно после v0 production-deploy. ## Открытые вопросы (deferred за v0) - **Q2 — где живёт каноничный manifest в production:** в `.factory/factory.yaml` (один на машину) или в shared Gitea с per-machine override? Склонение: "default в `.factory/` репе, override через `~/.config/factory/home.toml#overrides`". - **Q3 — slash-command-bridge для L2:** ждать программное API от вендоров, или поднять свой через MCP-сервер `factory-mcp` (`factory_install`, `factory_status`)? - **Q4 — версионирование manifest'а:** semver на `factory.yaml` сам? Или per-component pinned `name@v1.2.3` (npm/cargo style)? - **Q5 — secrets:** где хранятся токены в multi-machine setup? Сейчас ad-hoc в `.common/secrets/` и `~/.config/projects-mcp/auth.toml`. - **Q7 — access control / discoverability** (deferred to v2): иерархия видимости компонентов (`public` / `internal` / `secret`). В v0 — все `public`, secrets живут только в `.common/secrets/` с `.gitignore`. - **Q9 — переименование `claude-skills` → `.skills`** (deferred): репо стилистически выпадает из dotfile-конвенции флота. Scope: только git-имя репо в gitea + cloned dir name. Local runtime path `~/.claude/skills/` остаётся (hardcoded в Claude Code). Когда поднимать: при дизайне `factory.yaml` v0.2 / `templates/enterprise//`. ## v0 plan — live bootstrap нового ноута как field-test (выполнено 2026-05-06) Stop-condition: не сидеть в чистом дизайне до победного — у пользователя есть свежая машина, на ней проверяем дизайн руками. Алгоритм: 1. Узнать ОС нового ноута. 2. Бутстрапить пошагово, я директирую каждую команду. 3. Каждая команда логгируется в `.factory/L0/install-log.md` — сырьё для будущих `bootstrap.ps1` / `bootstrap.sh`. 4. После end-to-end успеха — формализуем log в скрипты. 5. Только после field-test'а — за L1 (Go-бинарь и `factory.yaml`). ## Field-test results — 2026-05-06 End-to-end на новом Win11 ноуте отработал. Полный лог — `.factory/L0/install-log.md` (этот же репо). | Шаг | Что | Статус | |---|---|---| | L0a | mise + системный pkg-mgr (winget) | ✅ first-pass | | L0b | clone `factory`, инициализация `~/.config/factory/home.toml` | ✅ | | 11a | clone `claude-skills`, 21 скил доступен через `Skill` тул | ✅ | | 11b | `claude login` + плагины `superpowers@claude-plugins-official` + `context7@claude-plugins-official` | ✅ (manual, browser-redirect) | | 11c.1 | `setup-interns` — MCP-сервер, секреты в `.common/secrets/interns.env` | ✅ | | 11c.2 | `setup-projects-meta` — repo + wiki clone + MCP entry + initial sync | ✅ idempotent re-run (всё уже было — скил детектил наличие и пошёл по path «update + verify») | | 11c.3 | context7 sanity (`resolve-library-id "react"` → 5 matches) | ✅ | | Фикстуры | `.organization/`, `.templates/`, `.meeting-room/` склонированы | ✅ | **Что подтвердилось:** 1. **Q1 (Go для L1) валидно** — текущий L0 на mise+winget хорошо ложится в манифест, runtime-зависимостей сверху не нужно. 2. **Q6 (mise поверх системного pkg-mgr)** — отработало без сюрпризов. Mise успешно поставил node/python. 3. **`setup-projects-meta` idempotency-логика** — корректно различил «fresh install» vs «update+verify». В bootstrap.ps1 повторить. 4. **Plugin-route для context7** — правильное решение (legacy `setup-context7` скил не запускался, конфликта нет). **Gap'ы / кандидаты для bootstrap-скрипта** (полный список — в `install-log.md`): - 11b принципиально не автоматизируется (требует браузер-redirect / device-code) — bootstrap.ps1 должен **парковаться** здесь и ждать пользователя. - Порядок 11c-скилов критичен: `interns` → `projects-meta` → плагины (или их sanity) → фикстуры. Это контракт скрипта, не выбор пользователя. - Каждый setup-* скил уже идемпотентен — bootstrap может звать их без проверки «надо ли». Но pre-check блок самого скрипта (есть ли git, mise, claude, плагины) обязан быть. ## Источник Промоушн брейнсторма из `.meeting-room/.archive/2026-05-06-factory-bootstrap.md`. Участники: user, claude-cso. Старт: 2026-05-06.