/
kochenov
/
uni
Обзор
Документация
Войти
/
kochenov
/
uni
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
Makefile
687 строк
37 KB
Dmitry Kochenov
Initial commit: UMP base structure
16 июл 2026, 21:14
16 июл 2026, 21:14
86b4d0e
Код
Авторство
О чём код?
# Universal Modular Platform - Makefile (v31) .PHONY: help setup preflight check-step docs-dev docs-build docs-preview docs-check validate validate-plans validate-md validate-rules agent-log agent-tail agent-status step-state resume loop-reset dev-up dev-down dev-reset dev-logs dev-ps _check-compose-file errors-log errors-clear protect-check protect-list protect-init protect-diff protect-update protect-genkey protect-info step-plan step-init step-run step-verify pr-create pr-merge extract-section step-show step-reset tag-create tag-list tag-push recovery-check manual-analyze manual-branch manual-commit manual-pr manual-merge-check diagnostics stage-analyze step-tasklist step-review step-stray-check branch-check state-validate help: @awk 'BEGIN {FS = ":.*##"; printf "Commands:\n\n"} /^[a-zA-Z_-]+:.*##/ { printf " %-20s %s\n", $$1, $$2 }' $(MAKEFILE_LIST) setup: ## Интерактивная настройка проекта (git, tools, deps) @echo "=== Интерактивная настройка Universal Modular Platform ===" @echo "" @# 1. Git config @if [ -z "$$(git config user.name)" ]; then \ echo "⚠ Git user.name не задан"; \ read -p " Введите ваше имя для git: " GIT_NAME; \ git config user.name "$$GIT_NAME"; \ echo " ✓ Git user.name установлен: $$GIT_NAME"; \ else \ echo " ✓ Git user.name: $$(git config user.name)"; \ fi @if [ -z "$$(git config user.email)" ]; then \ echo "⚠ Git user.email не задан"; \ read -p " Введите ваш email для git: " GIT_EMAIL; \ git config user.email "$$GIT_EMAIL"; \ echo " ✓ Git user.email установлен: $$GIT_EMAIL"; \ else \ echo " ✓ Git user.email: $$(git config user.email)"; \ fi @echo "" @# 2. Git remote @if [ -z "$$(git remote get-url origin 2>/dev/null)" ]; then \ echo "⚠ Git remote 'origin' не настроен"; \ echo " Выберите вариант:"; \ echo " 1) GitVerse (gitverse.ru)"; \ echo " 2) GitHub"; \ echo " 3) Другой (ввести URL вручную)"; \ echo " 4) Пропустить (настрою позже)"; \ read -p " Ваш выбор [1-4]: " REMOTE_CHOICE; \ case $$REMOTE_CHOICE in \ 1) read -p " Введите username на GitVerse: " GV_USER; \ git remote add origin https://gitverse.ru/$$GV_USER/universal-modular-platform.git; \ echo " ✓ Remote добавлен: gitverse.ru/$$GV_USER/universal-modular-platform"; \ ;; \ 2) read -p " Введите username на GitHub: " GH_USER; \ git remote add origin https://github.com/$$GH_USER/universal-modular-platform.git; \ echo " ✓ Remote добавлен: github.com/$$GH_USER/universal-modular-platform"; \ ;; \ 3) read -p " Введите URL remote: " REMOTE_URL; \ git remote add origin $$REMOTE_URL; \ echo " ✓ Remote добавлен: $$REMOTE_URL"; \ ;; \ 4) echo " ⚠ Remote пропущен. Настройте позже: git remote add origin <URL>"; \ ;; \ *) echo " ⚠ Неверный выбор. Remote пропущен."; \ ;; \ esac; \ else \ echo " ✓ Git remote: $$(git remote get-url origin)"; \ fi @echo "" @# 3. gh CLI @if ! command -v gh >/dev/null 2>&1; then \ echo "⚠ GitHub CLI (gh) не установлен"; \ echo " Установите для автоматических PR и tags:"; \ echo " macOS: brew install gh"; \ echo " Linux: sudo apt install gh"; \ echo " Windows: https://cli.github.com/"; \ echo " Или используйте GitVerse web UI для PR."; \ else \ echo " ✓ GitHub CLI: $$(gh --version | head -1)"; \ fi @echo "" @# 4. Python + uv @echo "=== Установка Python зависимостей ===" uv sync --group dev @echo " ✓ Python зависимости установлены (uv sync --group dev)" @echo "" @# 5. Node.js @echo "=== Установка Node.js зависимостей ===" npm install @echo " ✓ Node.js зависимости установлены (npm install)" @echo "" @# 6. Pre-commit @if command -v pre-commit >/dev/null 2>&1; then \ pre-commit install; \ echo " ✓ Pre-commit hooks установлены"; \ fi @echo "" @# 7. Защита файлов @echo "=== Инициализация защиты файлов ===" @if [ ! -f _meta/immutable-snapshot.json ]; then \ uv run python3 scripts/protect_files.py --init --force; \ echo " ✓ Immutable snapshot создан"; \ else \ echo " ✓ Immutable snapshot уже существует"; \ fi @echo "" @# 7.1. Напоминание о UMP_PROTECT_KEY (FIX-V30-3.2) @echo "=== Защита HMAC-ключа (аудит v30, §3.2) ===" @echo " Текущая стратегия (см. scripts/protect_files.py:_get_hmac_key):" @echo " 1) env var UMP_PROTECT_KEY (наивысший приоритет — рекомендуется)" @echo " 2) .ump-protect.key в репозитории (командный ключ, COMMITTED)" @echo " 3) ~/.config/ump/protect.key (host-specific fallback)" @echo "" @echo " ⚠ Файл .ump-protect.key лежит в git — агент с доступом на запись может" @echo " пересчитать подпись после изменения защищённых файлов (self-attack)." @echo " Рекомендация: сгенерируйте личный UMP_PROTECT_KEY и добавьте в shell rc:" @echo " make protect-genkey # выведет команду для ~/.bashrc / ~/.zshrc" @echo "" @echo "=== Настройка завершена ===" @echo "Проверьте готовность: make preflight" preflight: uv run python3 scripts/preflight_check.py check-step: @echo "=== Check previous step ===" @CURRENT_PHASE=$$(grep -E "^CURRENT_PHASE:" STEP_STATE.md 2>/dev/null | head -1 | sed 's/.*: *//' | tr -d ' ') ; \ if [ "$$CURRENT_PHASE" = "awaiting-continue" ]; then echo "OK: previous step done (PR created, awaiting manual merge + continue)"; \ else echo "WARN: step not done (phase=$$CURRENT_PHASE)"; echo "Run: uv run python3 scripts/run_phase.py <step> FINALIZATION"; fi resume: @echo "=== Resume ===" @grep -A5 "CURRENT_STATE" STEP_STATE.md 2>/dev/null @echo "" @uv run python3 scripts/agent_log.py tail --lines 10 2>/dev/null || true loop-reset: @if [ -z "$(STEP)" ]; then \ echo "ERROR: CURRENT_STEP не задан. Укажите: make loop-reset STEP=1.5"; \ exit 1; \ fi uv run python3 scripts/orchestrate_step.py $(STEP) --reset-loop docs-dev: npm run docs:dev docs-build: npm run docs:build docs-preview: npm run docs:preview docs-check: ## Проверить доступность документации на GitVerse Pages @echo "=== Проверка доступности документации ===" @REMOTE_URL=$$(git remote get-url origin 2>/dev/null); \ if [ -z "$$REMOTE_URL" ]; then \ echo "⚠ Remote не настроен — проверка пропущена"; \ exit 0; \ fi; \ OWNER=$$(echo $$REMOTE_URL | sed -n 's|.*[:/]\([^/]*/[^/]*\)\.git|\1|p' | sed 's|/universal-modular-platform||'); \ if [ -z "$$OWNER" ]; then \ echo "⚠ Не удалось определить owner из remote URL"; \ exit 0; \ fi; \ PAGES_URL="https://$$OWNER.gitverse.io/universal-modular-platform/"; \ echo "Проверяю: $$PAGES_URL"; \ HTTP_CODE=$$(curl -s -o /dev/null -w "%{http_code}" -L --max-time 10 "$$PAGES_URL" 2>/dev/null || echo "000"); \ if [ "$$HTTP_CODE" = "200" ]; then \ echo "✓ Документация доступна: $$PAGES_URL"; \ exit 0; \ else \ echo "✗ Документация НЕ доступна (HTTP $$HTTP_CODE)"; \ echo " URL: $$PAGES_URL"; \ echo " Возможные причины:"; \ echo " 1. GitVerse Pages не настроен (Settings → Pages)"; \ echo " 2. Сборка VitePress не прошла (проверьте CI)"; \ echo " 3. Документация ещё не опубликована (подождите 1-2 минуты после push)"; \ echo " Шаг НЕ считается завершённым, пока документация не доступна."; \ exit 1; \ fi validate: validate-plans validate-md validate-rules @echo "All checks passed" validate-plans: uv run python3 scripts/validate_plans.py validate-md: uv run python3 scripts/validate_md.py --all validate-rules: uv run python3 scripts/validate_rules.py agent-log: uv run python3 scripts/agent_log.py tail --lines 50 agent-tail: uv run python3 scripts/agent_log.py tail --lines $(N) agent-status: uv run python3 scripts/orchestrate_step.py $(STEP) --show-progress step-state: @cat STEP_STATE.md # NOTE: docker-compose.yml/Dockerfile не поставляются в этом шаблоне (создаются # в рамках плана инфраструктуры, см. plans/04-infrastructure.yaml). Все dev-* # цели проверяют наличие compose-файла и дают понятную подсказку вместо # «cryptic» ошибки Docker. _check-compose-file: @if [ ! -f docker-compose.yml ] && [ ! -f compose.yml ] && [ ! -f compose.yaml ]; then \ echo "ERROR: docker-compose.yml не найден в корне проекта."; \ echo " Он создаётся шагами из plans/04-infrastructure.yaml — выполните этот этап"; \ echo " ('выполни шаг 4.x') прежде чем запускать make dev-*."; \ exit 1; \ fi dev-up: _check-compose-file docker compose up -d dev-down: _check-compose-file docker compose down dev-reset: _check-compose-file docker compose down -v dev-logs: _check-compose-file docker compose logs -f --tail=100 dev-ps: _check-compose-file docker compose ps dev-module: ## Запустить модуль (выбрать из apps/backend/modules/) @echo "=== Запуск модуля ===" @MODULES=$$(ls -d apps/backend/modules/*/ 2>/dev/null | sed 's|apps/backend/modules/||' | sed 's|/||'); \ if [ -z "$$MODULES" ]; then \ echo "⚠ Модули не найдены в apps/backend/modules/"; \ exit 1; \ fi; \ echo "Доступные модули:"; \ echo "$$MODULES" | nl -w2 -s') '; \ read -p " Выберите модуль (номер): " MODULE_NUM; \ MODULE=$$(echo "$$MODULES" | sed -n "$$MODULE_NUM p"); \ if [ -z "$$MODULE" ]; then \ echo " ✗ Неверный номер"; \ exit 1; \ fi; \ echo " ✓ Выбран модуль: $$MODULE"; \ echo " Запуск..."; \ cd apps/backend/modules/"$$MODULE" && uv run granian --host 0.0.0.0 --port 8000 --app app:app dev-module-stop: ## Остановить все модули @echo "=== Остановка модулей ===" @echo " Завершение процессов granian..."; \ pkill -f granian || echo " (нет активных granian процессов)" errors-log: ## Показать последние ошибки @tail -20 _meta/errors.log 2>/dev/null || echo "Ошибок нет" errors-clear: ## Очистить лог ошибок @rm -f _meta/errors.log && echo "Лог ошибок очищен" # === Аудит v21: защита immutable-файлов === protect-check: ## Проверить immutable-файлы (exit 0=OK, 1=изменён) uv run python3 scripts/protect_files.py --check protect-list: ## Показать список immutable-файлов uv run python3 scripts/protect_files.py --list protect-init: ## Создать snapshot SHA-256 immutable-файлов uv run python3 scripts/protect_files.py --init protect-diff: ## Показать git diff изменённых immutable-файлов uv run python3 scripts/protect_files.py --diff protect-update: ## Обновить snapshot (требует _meta/.allow-protected-edit) uv run python3 scripts/protect_files.py --update protect-genkey: ## Сгенерировать личный UMP_PROTECT_KEY (FIX-V30-3.2) @echo "=== Генерация личного UMP_PROTECT_KEY (аудит v30, §3.2) ===" @echo "" @echo "Зачем: .ump-protect.key лежит в git и не защищает от агента, который" @echo "имеет доступ на запись в репозиторий. Личный UMP_PROTECT_KEY в env var" @echo "переопределяет командный ключ (см. scripts/protect_files.py:_get_hmac_key)." @echo "" @KEY=$$(python3 -c "import secrets; print(secrets.token_hex(32))"); \ echo "Сгенерированный ключ (64 hex символа / 256 бит):"; \ echo " $$KEY"; \ echo ""; \ echo "Добавьте в ~/.bashrc (или ~/.zshrc):"; \ echo " export UMP_PROTECT_KEY=$$KEY"; \ echo ""; \ echo "Затем перезапустите shell и проверьте:"; \ echo " make protect-info"; \ echo ""; \ echo "⚠ После установки UMP_PROTECT_KEY локальный snapshot должен быть" @echo " пересоздан с тем же ключом:" @echo " touch _meta/.allow-protected-edit" @echo " make protect-init" @echo " rm _meta/.allow-protected-edit" @echo " Иначе --check будет падать с 'signature invalid' (snapshot подписан" @echo " командным ключом, а проверка идёт с личным)." @echo "" @echo "⚠ Для CI: добавьте UMP_PROTECT_KEY как repository secret на GitVerse" @echo " (Settings → Secrets and variables → Actions)." protect-info: ## Показать, какой HMAC-ключ используется сейчас @uv run python3 scripts/protect_files.py --info # === UMP Step Management (v28) === step-plan: ## Сгенерировать план шага: make step-plan STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make step-plan STEP=1.5"; exit 1; fi @echo "=== Генерация плана для шага $(STEP) ===" uv run python3 scripts/orchestrate_step.py $(STEP) --gen-plan > docs/step-$(STEP)-PLAN.md @echo "План сохранён: docs/step-$(STEP)-PLAN.md" @echo "Покажите план пользователю. После 'выполняй' запустите: make step-init STEP=$(STEP)" step-init: ## Инициализировать progress-файл: make step-init STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make step-init STEP=1.5"; exit 1; fi @echo "=== Инициализация прогресса для шага $(STEP) ===" @touch _meta/step-$(STEP)-PLAN.ack uv run python3 scripts/orchestrate_step.py $(STEP) --init-progress @echo "Progress создан. Запустите: make step-run STEP=$(STEP) PHASE=PREFLIGHT" step-run: ## Запустить фазу: make step-run STEP=X.Y PHASE=Z @if [ -z "$(STEP)" ] || [ -z "$(PHASE)" ]; then echo "ERROR: STEP и PHASE обязательны. Пример: make step-run STEP=1.5 PHASE=PREFLIGHT"; exit 1; fi @echo "=== Запуск фазы $(PHASE) для шага $(STEP) ===" uv run python3 scripts/run_phase.py $(STEP) $(PHASE) step-verify: ## Верифицировать субагента: make step-verify STEP=X.Y PROMPT=Z @if [ -z "$(STEP)" ] || [ -z "$(PROMPT)" ]; then echo "ERROR: STEP и PROMPT обязательны. Пример: make step-verify STEP=1.5 PROMPT=docs-writer"; exit 1; fi @echo "=== Верификация $(PROMPT) для шага $(STEP) ===" uv run python3 scripts/verify_subagent.py $(PROMPT) $(STEP) pr-create: ## Создать PR на GitVerse: make pr-create STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make pr-create STEP=1.5"; exit 1; fi @echo "=== Создание PR для шага $(STEP) ===" @BRANCH=$$(grep "branch:" _meta/step-progress-$$(echo $(STEP) | tr '.' '-').yaml 2>/dev/null | head -1 | sed 's/.*: *//' | tr -d ' '); \ if [ -z "$$BRANCH" ]; then BRANCH="feature/step-$(STEP)"; fi; \ echo "Ветка: $$BRANCH"; \ if command -v gh >/dev/null 2>&1; then \ gh pr create --title "Шаг $(STEP)" --body "Автоматический PR для шага $(STEP)" --base main --head $$BRANCH; \ else \ REMOTE_URL=$$(git remote get-url origin 2>/dev/null); \ OWNER_REPO=$$(echo $$REMOTE_URL | sed -n 's|.*[:/]\([^/]*/[^/]*\)\.git|\1|p'); \ echo "gh CLI не установлен. Создайте PR вручную на GitVerse:"; \ if [ -n "$$OWNER_REPO" ]; then \ echo " https://gitverse.ru/$$OWNER_REPO/pull/new/$$BRANCH"; \ else \ echo " <URL вашего репозитория на GitVerse>/pull/new/$$BRANCH (remote 'origin' не настроен)"; \ fi; \ fi pr-merge: ## Слить PR в main: make pr-merge STEP=X.Y (аудит v36 — только GitVerse UI) @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make pr-merge STEP=1.5"; exit 1; fi @echo "=== Слияние PR для шага $(STEP) в main ===" @echo "🔒 Аудит v36: локальный git merge --squash КАТЕГОРИЧЕСКИ ЗАПРЕЩЁН." @echo " Слияние выполняется ТОЛЬКО через GitVerse web UI → Squash and merge." @BRANCH=$$(grep "branch:" _meta/step-progress-$$(echo $(STEP) | tr '.' '-').yaml 2>/dev/null | head -1 | sed 's/.*: *//' | tr -d ' '); \ if [ -z "$$BRANCH" ]; then BRANCH="feature/step-$(STEP)"; fi; \ REMOTE_URL=$$(git remote get-url origin 2>/dev/null); \ OWNER_REPO=$$(echo $$REMOTE_URL | sed -n 's|.*[:/]\([^/]*/[^/]*\)\.git|\1|p'); \ echo "Откройте PR в GitVerse web UI и нажмите 'Squash and merge':"; \ if [ -n "$$OWNER_REPO" ]; then \ echo " https://gitverse.ru/$$OWNER_REPO/pulls"; \ else \ echo " <URL вашего репозитория на GitVerse>/pulls (remote 'origin' не настроен)"; \ fi; \ echo ""; \ echo "После merge пользователь сообщает: 'merge готов'."; \ echo "Затем агент запускает: make diagnostics STEP=$(STEP)" tag-create: ## Создать annotated tag для шага: make tag-create STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make tag-create STEP=1.5"; exit 1; fi @echo "=== Создание тега для шага $(STEP) ===" @# Найти title шага из YAML (через scripts/get_step_title.py — PEP 735 / FIX-V29) @TITLE=$$(uv run python3 scripts/get_step_title.py $(STEP) 2>/dev/null); \ if [ -z "$$TITLE" ]; then echo "ERROR: Не удалось получить title для шага $(STEP)"; exit 1; fi; \ TAG="step-$(STEP)"; \ echo "Создаю annotated tag: $$TAG"; \ CURRENT_BRANCH=$$(git branch --show-current); \ echo " Текущая ветка: $$CURRENT_BRANCH"; \ git tag -a "$$TAG" -m "Шаг $(STEP): $$TITLE" 2>/dev/null || { echo "⚠ Тег $$TAG уже существует"; exit 0; }; \ echo "✓ Тег $$TAG создан на ветке $$CURRENT_BRANCH"; \ if git remote get-url origin >/dev/null 2>&1; then \ git push origin "$$TAG" 2>/dev/null && echo "✓ Тег отправлен на remote" || echo "⚠ Не удалось отправить тег (нет прав или нет сети)"; \ else \ echo "⚠ Remote не настроен — тег только локально"; \ fi tag-list: ## Показать все теги шагов @git tag -l "step-*" --sort=-version:refname | head -20 tag-push: ## Отправить все теги на remote @git push origin --tags 2>/dev/null && echo "✓ Теги отправлены" || echo "⚠ Не удалось отправить теги" extract-section: ## Извлечь секцию из .md: make extract-section FILE=... SECTION=... @if [ -z "$(FILE)" ] || [ -z "$(SECTION)" ]; then echo "ERROR: FILE и SECTION обязательны"; exit 1; fi uv run python3 scripts/extract_section.py $(FILE) $(SECTION) step-show: ## Показать прогресс шага: make step-show STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан"; exit 1; fi uv run python3 scripts/orchestrate_step.py $(STEP) --show-progress step-reset: ## Сбросить шаг (force): make step-reset STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан"; exit 1; fi uv run python3 scripts/orchestrate_step.py $(STEP) --init-progress --force # === Аудит v35 (требования пользователя) — новые цели === stage-analyze: ## Аудит v35 (требование №5): анализ Этапа перед стартом шага @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make stage-analyze STEP=1.4"; exit 1; fi @echo "=== STAGE-ANALYSIS (аудит v35, требование №5) для шага $(STEP) ===" @# Определить stage_id по первому символу step_id (например, 1.4 → 01) @STAGE_ID=$$(echo "$(STEP)" | cut -c1); \ STAGE_ID=$$(printf "%02d" $$STAGE_ID 2>/dev/null || echo "0$$STAGE_ID"); \ STAGE_YAML=$$(ls plans/$$STAGE_ID-*.yaml 2>/dev/null | head -1); \ if [ -z "$$STAGE_YAML" ]; then \ echo "ERROR: YAML-план для этапа $$STAGE_ID не найден в plans/"; \ exit 1; \ fi; \ echo "Этап: $$STAGE_ID"; \ echo "YAML: $$STAGE_YAML"; \ echo ""; \ echo "Резюме шагов этапа:"; \ grep -E "^\s+- id:|^\s+title:" $$STAGE_YAML | head -40; \ echo ""; \ echo "Для полного анализа — агент GigaCode вызовет промпт .agent/prompts/stage-analyzer.md"; \ echo "с параметрами: stage_id=$$STAGE_ID, current_step_id=$(STEP), stage_yaml_path=$$STAGE_YAML"; \ echo ""; \ echo "Если в STEP_STATE.md нет 'STAGE_ANALYSIS_DONE: $$STAGE_ID' — гейт сработает автоматически" step-tasklist: ## Аудит v35 (требование №1): сгенерировать task-list шага @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make step-tasklist STEP=1.4"; exit 1; fi @echo "=== TASK-LIST GENERATOR (аудит v35, требование №1) для шага $(STEP) ===" @echo "Промпт: .agent/prompts/task-list-generator.md" @echo "Параметры из YAML (через orchestrate_step.py):" @uv run python3 scripts/orchestrate_step.py $(STEP) --print-step 2>&1 | head -30 @echo "" @echo "После генерации task-list — пользователь подтверждает «выполняй», затем:" @echo " make step-init STEP=$(STEP)" @echo " make step-run STEP=$(STEP) PHASE=PREFLIGHT" step-review: ## Аудит v35 (требование №1): code review шага @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make step-review STEP=1.4"; exit 1; fi @echo "=== CODE-REVIEW (аудит v35, требование №1) для шага $(STEP) ===" @echo "Промпт: .agent/prompts/code-reviewer.md" @echo "" @echo "Применяется только если step_type=implementation (или document со скриптами)." @echo "Запускается ПОСЛЕ TESTING PASS, ДО PHASE2A." @echo "" @echo "Для запуска через стандартный pipeline:" @echo " make step-verify STEP=$(STEP) PROMPT=code-reviewer" @echo "" @echo "Результат: в docs_target добавлена секция '## Code Review (аудит v35)'" @echo "При P0>=1 или P1>=3 — возврат на TESTING (debug-fixer → code-writer)" step-stray-check: ## Аудит v35 (требование №3): обработка незакоммиченных файлов @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make step-stray-check STEP=1.4"; exit 1; fi @echo "=== STRAY-FILES CHECK (аудит v35, требование №3) для шага $(STEP) ===" @if [ -z "$$(git status --porcelain)" ]; then \ echo "✓ Дерево чистое. Незакоммиченных файлов нет."; \ else \ echo "⚠ Обнаружены незакоммиченные файлы:"; \ git status --porcelain; \ echo ""; \ echo "Запускаю классификацию и обработку..."; \ uv run python3 scripts/classify_and_handle_stray.py $(STEP) $(if $(SLUG),--slug $(SLUG),) $(if $(DRY_RUN),--dry-run,); \ fi branch-check: ## Аудит v35 (требование №4): проверить, что текущая ветка соответствует шагу @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make branch-check STEP=1.4"; exit 1; fi @CURRENT=$$(git branch --show-current 2>/dev/null || echo 'detached'); \ X=$$(echo "$(STEP)" | cut -d. -f1); \ Y=$$(echo "$(STEP)" | cut -d. -f2); \ if echo "$$CURRENT" | grep -qE "^feature/step-$$X\.$$Y(-|\.|\$$)"; then \ echo "✓ OK: ветка '$$CURRENT' соответствует шагу $(STEP)"; \ exit 0; \ elif echo "$$CURRENT" | grep -qE "^feature/step-$$X-$$Y(-|\.|\$$)"; then \ echo "✓ OK: ветка '$$CURRENT' соответствует шагу $(STEP) (формат с дефисом)"; \ exit 0; \ elif [ "$$CURRENT" = "main" ]; then \ echo "⚠ Текущая ветка — main. Допускается только после финализации или для шага 1.0."; \ exit 0; \ else \ echo "❌ BRANCH MISMATCH (аудит v35, требование №4)"; \ echo " Ожидалась ветка: feature/step-$$X.$$Y-*"; \ echo " Текущая ветка: $$CURRENT"; \ echo ""; \ echo "Действия:"; \ echo " 1. Вернитесь на правильную ветку: git checkout feature/step-$$X.$$Y-<slug>"; \ echo " 2. Или перезапустите Pre-flight: make step-run STEP=$(STEP) PHASE=PREFLIGHT"; \ exit 5; \ fi state-validate: ## Аудит v36+v37: запустить state validator (pre/post-check для фазы) @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make state-validate STEP=1.4 PHASE=FINAL_REVIEW CHECK=pre"; exit 1; fi @if [ -z "$(PHASE)" ]; then echo "ERROR: PHASE не задан. Пример: make state-validate STEP=1.4 PHASE=FINAL_REVIEW CHECK=pre"; exit 1; fi @if [ -z "$(CHECK)" ]; then echo "INFO: CHECK не задан, по умолчанию 'pre'."; CHECK=pre; fi @echo "=== STATE VALIDATOR (аудит v36+v37) для шага $(STEP), фаза $(PHASE), check=$(CHECK) ===" @echo "См. .agent/runtime/state-validator.md для деталей проверок." @echo "" @echo "Источники состояния:" @echo " 1. STEP_STATE.md: CURRENT_STEP, CURRENT_PHASE" @grep -E "^(CURRENT_STEP|CURRENT_PHASE):" STEP_STATE.md 2>/dev/null | sed 's/^/ /' || echo " ⚠ STEP_STATE.md не найден" @echo " 2. _meta/step-progress.yaml: step_id, current_phase, phases[]" @PROGRESS_FILE="_meta/step-progress-$$(echo $(STEP) | tr '.' '-').yaml"; \ if [ -f "$$PROGRESS_FILE" ]; then \ grep -E "^(step_id|current_phase):" "$$PROGRESS_FILE" | sed 's/^/ /'; \ else \ echo " ⚠ progress-файл не найден: $$PROGRESS_FILE"; \ fi @echo " 3. git status: branch, dirty files" @echo " branch=$$(git branch --show-current 2>/dev/null || echo detached), dirty_files=$$(git status --porcelain 2>/dev/null | wc -l)" @echo "" @echo "Pre-check для фазы $(PHASE):" @CURRENT_BRANCH=$$(git branch --show-current 2>/dev/null || echo detached); \ X=$$(echo "$(STEP)" | cut -d. -f1); \ Y=$$(echo "$(STEP)" | cut -d. -f2); \ if echo "$$CURRENT_BRANCH" | grep -qE "^feature/step-$$X[\\.-]$$Y"; then \ echo " ✓ Branch match: $$CURRENT_BRANCH"; \ else \ echo " ❌ Branch mismatch: ожидалась feature/step-$$X.$$Y-*, текущая $$CURRENT_BRANCH"; \ exit 14; \ fi @if [ "$(CHECK)" = "pre" ]; then \ if [ "$(PHASE)" = "PREFLIGHT" ] || [ "$(PHASE)" = "FINALIZATION" ]; then \ DIRTY_COUNT=$$(git status --porcelain 2>/dev/null | wc -l); \ if [ "$$DIRTY_COUNT" -gt 0 ]; then \ echo " ❌ Clean working tree: $$DIRTY_COUNT файлов не закоммичено (требуется для $(PHASE))"; \ exit 14; \ else \ echo " ✓ Clean working tree OK"; \ fi; \ fi; \ echo " ✓ Permission check: см. .agent/runtime/permission-policy.yaml"; \ elif [ "$(CHECK)" = "post" ]; then \ PROGRESS_FILE="_meta/step-progress-$$(echo $(STEP) | tr '.' '-').yaml"; \ if [ -f "$$PROGRESS_FILE" ]; then \ PHASE_STATUS=$$(grep -A 3 "^ $(PHASE):" "$$PROGRESS_FILE" 2>/dev/null | grep "status:" | head -1 | awk '{print $$2}'); \ echo " ✓ Phase $(PHASE) status: $$PHASE_STATUS"; \ fi; \ fi @echo "" @echo "✓ State validation PASSED для шага $(STEP), фаза $(PHASE), check=$(CHECK)" # === Аудит v31: START-WITH-AGENT.md — новые цели === recovery-check: ## Сценарий 1: проверить состояние проекта (для старта сессии) @echo "=== RECOVERY CHECK (аудит v31, START-WITH-AGENT.md §2) ===" @echo "" @echo "--- Git состояние ---" @if [ -d .git ]; then \ echo " branch: $$(git branch --show-current 2>/dev/null || echo 'detached')"; \ echo " last commit: $$(git log --oneline -1 2>/dev/null || echo 'нет коммитов')"; \ echo " changes: $$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') файлов изменено"; \ echo " stash: $$(git stash list 2>/dev/null | wc -l | tr -d ' ') спрятанных"; \ else \ echo " ⚠ Git НЕ инициализирован (нет .git/)"; \ fi @echo "" @echo "--- STEP_STATE ---" @if [ -f STEP_STATE.md ]; then \ grep -E "CURRENT_STEP|CURRENT_PHASE|COMPLETED_STEPS" STEP_STATE.md 2>/dev/null | head -5; \ else \ echo " ⚠ STEP_STATE.md не существует (проект не инициализирован)"; \ fi @echo "" @echo "--- Progress-файлы ---" @ls _meta/step-progress-*.yaml 2>/dev/null | head -5 || echo " (нет активных шагов)" @ls _meta/step-progress-*.done.yaml 2>/dev/null | head -5 || echo " (нет завершённых шагов)" @echo "" @echo "--- Agent log (последние 5 строк) ---" @tail -5 _meta/agent.log 2>/dev/null || echo " (лог пуст)" @echo "" @echo "--- Preflight ---" @uv run python3 scripts/preflight_check.py 2>&1 | tail -10 || true @echo "" @echo "=== Конец recovery-check. Сообщите пользователю резюме в формате START-WITH-AGENT.md §2.2 ===" # === Сценарий 4: ручные изменения пользователя === manual-analyze: ## Сценарий 4: анализ ручных изменений (git status + diff + checks) @echo "=== АНАЛИЗ РУЧНЫХ ИЗМЕНЕНИЙ (аудит v31, START-WITH-AGENT.md §5) ===" @echo "" @echo "--- Изменённые файлы (git status) ---" @git status --porcelain @echo "" @echo "--- Сводка по изменениям (git diff --stat) ---" @git diff --stat @echo "" @echo "--- Проверка immutable-файлов (make protect-check) ---" @uv run python3 scripts/protect_files.py --check 2>&1 | head -10 || true @echo "" @echo "--- Проверка стиля кода (ruff) ---" @uv run ruff check . 2>&1 | tail -20 || true @echo "" @echo "=== Конец анализа. Сформируйте резюме по формату START-WITH-AGENT.md §5.2 ===" manual-branch: ## Сценарий 4: создать ветку для ручных фиксаций: make manual-branch SLUG=fix-typo @if [ -z "$(SLUG)" ]; then echo "ERROR: SLUG не задан. Пример: make manual-branch SLUG=fix-typo-readme"; exit 1; fi @echo "=== Создание ветки для ручных фиксаций ===" @git checkout main @git pull origin main 2>/dev/null || echo " ⚠ pull origin main не удался (нет remote или offline)" @TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \ BRANCH="fix/manual-$$TIMESTAMP-$(SLUG)"; \ git checkout -b "$$BRANCH"; \ echo " ✓ Ветка создана: $$BRANCH"; \ echo " Перенесите изменения в эту ветку (git stash pop, если нужно)" manual-commit: ## Сценарий 4: коммит ручных изменений: make manual-commit MSG="..." @if [ -z "$(MSG)" ]; then echo "ERROR: MSG не задано. Пример: make manual-commit MSG=\"fix(readme): исправить опечатку\""; exit 1; fi @echo "=== Коммит ручных изменений ===" @git add -A @git commit -m "$(MSG)" -m "(manual change by user)" -m "Signed-off-by: $$(git config user.name) <$$(git config user.email)>" @echo " ✓ Коммит создан" @BRANCH=$$(git branch --show-current); \ echo " Ветка: $$BRANCH"; \ git push origin "$$BRANCH" 2>/dev/null && echo " ✓ Push выполнен" || echo " ⚠ Push не удался (нет remote или offline)" manual-pr: ## Сценарий 4: создать PR для ручных изменений @echo "=== Создание PR для ручных изменений ===" @BRANCH=$$(git branch --show-current); \ echo "Ветка: $$BRANCH"; \ if command -v gh >/dev/null 2>&1; then \ gh pr create --title "Manual changes: $$BRANCH" --body "Ручные изменения пользователя" --base main --head $$BRANCH; \ else \ REMOTE_URL=$$(git remote get-url origin 2>/dev/null); \ OWNER_REPO=$$(echo $$REMOTE_URL | sed -n 's|.*[:/]\([^/]*/[^/]*\)\.git|\1|p'); \ echo "gh CLI не установлен. Создайте PR вручную на GitVerse:"; \ if [ -n "$$OWNER_REPO" ]; then \ echo " https://gitverse.ru/$$OWNER_REPO/pull/new/$$BRANCH"; \ else \ echo " <URL вашего репозитория>/pull/new/$$BRANCH"; \ fi; \ fi manual-merge-check: ## Сценарий 4: пост-merge диагностика ручных изменений @echo "=== ПОСТ-MERGE ДИАГНОСТИКА (аудит v31, START-WITH-AGENT.md §5.1 шаг 8) ===" @mkdir -p _meta/diagnostics @TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \ LOG="_meta/diagnostics/manual-$$TIMESTAMP.log"; \ { \ echo "=== Диагностика после merge ручных изменений — $$(date) ==="; \ echo ""; \ echo "Git state:"; \ echo " branch: $$(git branch --show-current)"; \ echo " last commit: $$(git log --oneline -1)"; \ echo " working tree: $$(if [ -z "$$(git status --porcelain)" ]; then echo clean; else echo dirty; fi)"; \ echo ""; \ echo "[1/3] make validate"; \ make validate 2>&1; \ echo ""; \ echo "[2/3] make protect-check"; \ uv run python3 scripts/protect_files.py --check 2>&1; \ echo ""; \ echo "[3/3] make docs-check"; \ make docs-check 2>&1; \ echo ""; \ echo "ИТОГ: $$([ $$? -eq 0 ] && echo 'ВСЁ OK' || echo 'ЕСТЬ ОШИБКИ (см. выше)')"; \ } > $$LOG 2>&1; \ echo " ✓ Лог диагностики: $$LOG"; \ echo " Содержимое:"; \ cat $$LOG # === Сценарий 2: пост-merge диагностика шага === diagnostics: ## Сценарий 2: пост-merge диагностика шага: make diagnostics STEP=X.Y @if [ -z "$(STEP)" ]; then echo "ERROR: STEP не задан. Пример: make diagnostics STEP=1.3"; exit 1; fi @echo "=== ПОСТ-MERGE ДИАГНОСТИКА ШАГА $(STEP) (аудит v31, START-WITH-AGENT.md §3.1 шаг 10) ===" @mkdir -p _meta/diagnostics @TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \ LOG="_meta/diagnostics/step-$(STEP)-$$TIMESTAMP.log"; \ { \ echo "=== Диагностика после merge шага $(STEP) — $$(date) ==="; \ echo ""; \ echo "Git state:"; \ echo " branch: $$(git branch --show-current)"; \ echo " last commit: $$(git log --oneline -1)"; \ echo " working tree: $$(if [ -z "$$(git status --porcelain)" ]; then echo clean; else echo dirty; fi)"; \ echo ""; \ echo "[1/3] make validate"; \ make validate 2>&1; \ echo ""; \ echo "[2/3] make protect-check"; \ uv run python3 scripts/protect_files.py --check 2>&1; \ echo ""; \ echo "[3/3] make docs-check"; \ make docs-check 2>&1; \ echo ""; \ echo "ИТОГ: $$([ $$? -eq 0 ] && echo 'ВСЁ OK' || echo 'ЕСТЬ ОШИБКИ (см. выше)')"; \ } > $$LOG 2>&1; \ echo " ✓ Лог диагностики: $$LOG"; \ echo " Содержимое:"; \ cat $$LOG